关卡任务数据相关
parent
135dcdf25e
commit
e2df98e934
|
|
@ -140,7 +140,11 @@ public class ConditionModifier
|
|||
{
|
||||
foreach (var type in _filterTypes)
|
||||
{
|
||||
dropDownList.Add(ConditionUtil.conditionNameCfg[type], type);
|
||||
if (!ConditionUtil.conditionNameCfg.TryGetValue(type, out var name))
|
||||
{
|
||||
name = type.ToString();
|
||||
}
|
||||
dropDownList.Add(name, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ public static class ConditionUtil
|
|||
{ ConditionType.CombatDefenseTime, new[] { "防守区域ID", "防守时间(秒)" } },
|
||||
{ ConditionType.CombatDefenseWave, new[] { "防守区域ID", "防守波数" } },
|
||||
{ ConditionType.CombatEndTask, new[] { "任务ID" } },
|
||||
{ ConditionType.CombatHaveTime, new[] { "组id","时长(s)" } },
|
||||
{ ConditionType.CombatHaveTime, new[] { "组id", "时长(s)" } },
|
||||
{ ConditionType.CombatInVehicleByCharacterId, new[] { "角色ID" } },
|
||||
{ ConditionType.CombatInVehicleByJob, new[] { "职业" } },
|
||||
{ ConditionType.CombatInVehicleByCount, new[] { "数量" } },
|
||||
|
|
@ -126,6 +126,8 @@ public static class ConditionUtil
|
|||
|
||||
public static readonly List<ConditionType> AllLevelConditionFilter = new()
|
||||
{
|
||||
ConditionType.CombatAllCharacterInVehicle,
|
||||
|
||||
ConditionType.CombatKillAllEnemy,
|
||||
ConditionType.CombatEvacuationCount,
|
||||
ConditionType.CombatAllAlliesDead,
|
||||
|
|
@ -207,6 +209,36 @@ public static class ConditionUtil
|
|||
ConditionType.CombatInCellBySelfNpc,
|
||||
};
|
||||
|
||||
private static readonly int CombatStartConditionValue = 500;
|
||||
private static readonly int CombatEndConditionValue = 529;
|
||||
|
||||
public static string GetConditionLocalization(ConditionType conditionType)
|
||||
{
|
||||
return conditionNameCfg.GetValueOrDefault(conditionType);
|
||||
}
|
||||
|
||||
public static List<ConditionType> GetAllConditionFilter()
|
||||
{
|
||||
return GetConditionFilter(CombatStartConditionValue, CombatEndConditionValue);
|
||||
}
|
||||
|
||||
public static List<ConditionType> GetConditionFilter(int start = 0, int end = 0)
|
||||
{
|
||||
var result = new List<ConditionType>();
|
||||
var allValues = System.Enum.GetValues(typeof(ConditionType));
|
||||
|
||||
foreach (ConditionType type in allValues)
|
||||
{
|
||||
int value = (int)type;
|
||||
if (value >= start && value <= end)
|
||||
{
|
||||
result.Add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region API
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ namespace Gameplay.Level
|
|||
[Serializable]
|
||||
public class LevelData
|
||||
{
|
||||
#region class
|
||||
|
||||
[Serializable]
|
||||
public class NPCInfo
|
||||
{
|
||||
|
|
@ -57,15 +59,22 @@ namespace Gameplay.Level
|
|||
public List<PatrolInfo> patrolInfos = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 巡逻路径
|
||||
/// </summary>
|
||||
public List<PatrolData> patrolRoadData = new();
|
||||
[Serializable]
|
||||
public class TaskInfo
|
||||
{
|
||||
[HideInInspector] public int taskID;
|
||||
[LabelText("任务类型")] public ConditionModifier taskCondition = new(ConditionUtil.GetAllConditionFilter());
|
||||
[LabelText("任务注释")] public string taskNote; // 策划使用备注
|
||||
[LabelText("任务文本替换")] public string taskDesc; // 手写stringKey
|
||||
[LabelText("前置任务")] public int preTaskID;
|
||||
[LabelText("后置任务")] public int postTaskID;
|
||||
|
||||
/// <summary>
|
||||
/// 巡逻区域
|
||||
/// </summary>
|
||||
public List<PatrolData> patrolRegionData = new();
|
||||
[OnDeserialized]
|
||||
public void OnAfterDeserialize(StreamingContext context)
|
||||
{
|
||||
taskCondition.SetFilterTypes(ConditionUtil.GetAllConditionFilter());
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SpawnMonsterData
|
||||
|
|
@ -330,7 +339,8 @@ namespace Gameplay.Level
|
|||
[LabelText("AI行为目标")] [ShowIf("@EAIAction.None != AIAction")]
|
||||
public E_AIActionTarget AIActionTarget;
|
||||
|
||||
[LabelText("单位目标id")] [ShowIf("@EAIAction.None != AIAction && AIActionTarget != E_AIActionTarget.SelfTroop")]
|
||||
[LabelText("单位目标id")]
|
||||
[ShowIf("@EAIAction.None != AIAction && AIActionTarget != E_AIActionTarget.SelfTroop")]
|
||||
public int actionTargetId;
|
||||
|
||||
[LabelText("AI状态切换")] public EAIState AIState;
|
||||
|
|
@ -407,31 +417,87 @@ namespace Gameplay.Level
|
|||
Toward12 = 0,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 占领数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CaptureInfo
|
||||
{
|
||||
[LabelText("占领区ID")] public int captureRegionID;
|
||||
|
||||
[LabelText("占领点位")] public List<int> capturePoints = new List<int>();
|
||||
|
||||
[LabelText("进度标识点")] public int captureFlag;
|
||||
|
||||
[LabelText("占领时间")] public int CaptureProgress = 0;
|
||||
|
||||
[LabelText("占领地面特效ID")] public int captureEffectID;
|
||||
|
||||
[LabelText("占领地面完成特效ID")] public int captureFinishEffectID;
|
||||
|
||||
[LabelText("占领地面特效创建地格")] public int captureEffectPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 撤离数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RetreatInfo
|
||||
{
|
||||
public List<int> retreatPoints = new List<int>();
|
||||
|
||||
public int retreatFlag;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public LevelData()
|
||||
{
|
||||
}
|
||||
|
||||
[HideInInspector] public List<TaskInfo> taskInfos = new();
|
||||
|
||||
/// <summary>
|
||||
/// 巡逻路径
|
||||
/// </summary>
|
||||
[HideInInspector] public List<PatrolData> patrolRoadData = new();
|
||||
|
||||
/// <summary>
|
||||
/// 巡逻区域
|
||||
/// </summary>
|
||||
[HideInInspector] public List<PatrolData> patrolRegionData = new();
|
||||
|
||||
[LabelText("出战区")] [HideInInspector] public int preparingRegionId;
|
||||
|
||||
[LabelText("出战区朝向")] [HideInInspector] public CharacterToward preparingRegionToward;
|
||||
|
||||
[LabelText("撤退区")] [HideInInspector] public int fallbackRegionId;
|
||||
|
||||
[LabelText("敌方撤退区")] [HideInInspector] public int enemyFallbackRegionId;
|
||||
|
||||
[LabelText("PVP防守区")] [HideInInspector]
|
||||
public int pvpDefendRegionId;
|
||||
|
||||
[LabelText("PVP防守方朝向")] [HideInInspector]
|
||||
public CharacterToward pvpDefendToward;
|
||||
|
||||
[BoxGroup("支线")] [LabelText("设置该关卡为评分关卡")]
|
||||
|
||||
#region 关卡胜利失败星级条件
|
||||
|
||||
[HideInInspector] public int successTaskID;
|
||||
|
||||
[HideInInspector] public int failTaskID;
|
||||
|
||||
[HideInInspector] public List<int> starTaskIDs = new() { 0, 0, 0 };
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 评分关卡条件
|
||||
|
||||
[BoxGroup("支线(战役)")] [LabelText("设置该关卡为评分关卡")]
|
||||
public bool scoreLevel = false;
|
||||
|
||||
[BoxGroup("支线")]
|
||||
[BoxGroup("支线(战役)")]
|
||||
[LabelText("评分目标"), ShowIf("scoreLevel")]
|
||||
[ListDrawerSettings(CustomAddFunction = "OnAddScoreCondition")]
|
||||
public List<ConditionModifier> scoreTarget = new List<ConditionModifier>(15);
|
||||
public List<ConditionModifier> scoreTarget = new(15);
|
||||
|
||||
private void OnAddScoreCondition()
|
||||
{
|
||||
|
|
@ -440,6 +506,8 @@ namespace Gameplay.Level
|
|||
scoreTarget.Add(conditionModifier);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[BoxGroup("防守相关设置")] [LabelText("防守关卡数据")]
|
||||
public List<SpawnMonsterData> spawnMonsterDatas;
|
||||
|
||||
|
|
@ -498,44 +566,12 @@ namespace Gameplay.Level
|
|||
/// </summary>
|
||||
[HideInInspector] public List<MapData.BlockSphereInfo> BlockSphereInfos = new();
|
||||
|
||||
/// <summary>
|
||||
/// 占领数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CaptureInfo
|
||||
{
|
||||
[LabelText("占领区ID")] public int captureRegionID;
|
||||
|
||||
[LabelText("占领点位")] public List<int> capturePoints = new List<int>();
|
||||
|
||||
[LabelText("进度标识点")] public int captureFlag;
|
||||
|
||||
[LabelText("占领时间")] public int CaptureProgress = 0;
|
||||
|
||||
[LabelText("占领地面特效ID")] public int captureEffectID;
|
||||
|
||||
[LabelText("占领地面完成特效ID")] public int captureFinishEffectID;
|
||||
|
||||
[LabelText("占领地面特效创建地格")] public int captureEffectPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 撤离数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RetreatInfo
|
||||
{
|
||||
public List<int> retreatPoints = new List<int>();
|
||||
|
||||
public int retreatFlag;
|
||||
}
|
||||
|
||||
[HideInInspector] public List<CaptureInfo> CapturePoints = new(8);
|
||||
|
||||
[HideInInspector] public List<int> flagStartPoints = new List<int>();
|
||||
|
||||
[HideInInspector] public List<int> flagEndPoints = new List<int>();
|
||||
|
||||
|
||||
[HideInInspector] public List<int> enemyRetreatReusablePoints = new List<int>();
|
||||
|
||||
[HideInInspector] public RetreatInfo retreatPoints = new RetreatInfo();
|
||||
|
|
|
|||
|
|
@ -79,6 +79,179 @@ public class LevelEditorWindow : OdinEditorWindow
|
|||
|
||||
#endregion
|
||||
|
||||
#region 任务相关
|
||||
|
||||
private const int TaskStartID = 1000;
|
||||
|
||||
[Serializable]
|
||||
public class TaskInfoEditor
|
||||
{
|
||||
[ReadOnly] public int taskID;
|
||||
|
||||
[HideInInspector] public string taskName;
|
||||
|
||||
[LabelText("任务详情")] public LevelData.TaskInfo taskInfo;
|
||||
|
||||
public TaskInfoEditor()
|
||||
{
|
||||
taskID = TaskStartID + currWindow.taskInfos.Count;
|
||||
taskInfo = new LevelData.TaskInfo
|
||||
{
|
||||
taskID = currWindow.GetCorrectTaskID()
|
||||
};
|
||||
}
|
||||
|
||||
public TaskInfoEditor(LevelData.TaskInfo taskInfo)
|
||||
{
|
||||
if (taskInfo == null)
|
||||
{
|
||||
taskID = currWindow.GetCorrectTaskID();
|
||||
return;
|
||||
}
|
||||
|
||||
taskID = taskInfo.taskID;
|
||||
this.taskInfo = taskInfo;
|
||||
|
||||
var localization = ConditionUtil.GetConditionLocalization(taskInfo.taskCondition.conditionType);
|
||||
if (string.IsNullOrEmpty(localization))
|
||||
{
|
||||
taskName = taskID + "-无效条件-" + taskInfo.taskNote;
|
||||
}
|
||||
else
|
||||
{
|
||||
taskName = taskID + $"-{localization}" + $"-{taskInfo.taskNote}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetCorrectTaskID()
|
||||
{
|
||||
if (taskInfos.Count == 0)
|
||||
return TaskStartID;
|
||||
|
||||
var lastTaskID = taskInfos[^1].taskID;
|
||||
var checkID = lastTaskID + 1;
|
||||
|
||||
while (taskInfos.Any(t => t.taskID == checkID))
|
||||
{
|
||||
checkID++;
|
||||
}
|
||||
|
||||
return checkID;
|
||||
}
|
||||
|
||||
[BoxGroup("任务列表")] [LabelText("任务列表"), Indent]
|
||||
public List<TaskInfoEditor> taskInfos = new List<TaskInfoEditor>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region 胜利失败星级条件
|
||||
|
||||
[HorizontalGroup("1")] [LabelText("胜利条件"), Indent]
|
||||
public int successTaskID;
|
||||
|
||||
[HorizontalGroup("1")]
|
||||
[Button("搜索/选择胜利任务", ButtonSizes.Small)]
|
||||
private void OpenSuccessTaskSelector()
|
||||
{
|
||||
ShowTaskSelector("选择胜利条件任务", null, id => successTaskID = id);
|
||||
}
|
||||
|
||||
[HorizontalGroup("2")] [LabelText("失败条件"), Indent]
|
||||
public int failTaskID;
|
||||
|
||||
[HorizontalGroup("2")]
|
||||
[Button("搜索/选择失败任务", ButtonSizes.Small)]
|
||||
private void OpenFailTaskSelector()
|
||||
{
|
||||
ShowTaskSelector("选择失败条件任务", null, id => failTaskID = id);
|
||||
}
|
||||
|
||||
[HorizontalGroup("3")] [LabelText("1星条件"), Indent]
|
||||
public int oneStarTaskID;
|
||||
|
||||
[HorizontalGroup("3")]
|
||||
[Button("搜索/选择1星任务", ButtonSizes.Small)]
|
||||
private void OpenOneStarTaskSelector()
|
||||
{
|
||||
ShowTaskSelector("选择1星条件任务", null, id => oneStarTaskID = id);
|
||||
}
|
||||
|
||||
[HorizontalGroup("4")] [LabelText("2星条件"), Indent]
|
||||
public int twoStarTaskID;
|
||||
|
||||
[HorizontalGroup("4")]
|
||||
[Button("搜索/选择2星任务", ButtonSizes.Small)]
|
||||
private void OpenTwoStarTaskSelector()
|
||||
{
|
||||
ShowTaskSelector("选择2星条件任务", null, id => twoStarTaskID = id);
|
||||
}
|
||||
|
||||
[HorizontalGroup("5")] [LabelText("3星条件"), Indent]
|
||||
public int threeStarTaskID;
|
||||
|
||||
[HorizontalGroup("5")]
|
||||
[Button("搜索/选择3星任务", ButtonSizes.Small)]
|
||||
private void OpenThreeStarTaskSelector()
|
||||
{
|
||||
ShowTaskSelector("选择3星条件任务", null, id => threeStarTaskID = id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通用带搜索任务选择器
|
||||
/// title: 弹窗标题
|
||||
/// onTaskNameSelected: 需要设置字符串字段时(如 successTaskID)
|
||||
/// onTaskIdSelected: 需要设置 int 字段时(如 failTaskID、星级条件),会从 taskName 前缀解析 taskID
|
||||
/// </summary>
|
||||
private void ShowTaskSelector(string titleStr, Action<string> onTaskNameSelected, Action<int> onTaskIdSelected)
|
||||
{
|
||||
if (taskInfos == null || taskInfos.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("任务列表为空", "请先在任务列表中添加任务。", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
var taskNames = taskInfos
|
||||
.Where(t => t != null && !string.IsNullOrEmpty(t.taskName))
|
||||
.Select(t => t.taskName)
|
||||
.ToList();
|
||||
|
||||
if (taskNames.Count == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("任务列表为空", "当前任务没有可用的名称。", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
var selector = new GenericSelector<string>(titleStr, false, taskNames);
|
||||
|
||||
selector.SelectionConfirmed += selection =>
|
||||
{
|
||||
var first = selection.FirstOrDefault();
|
||||
if (string.IsNullOrEmpty(first))
|
||||
return;
|
||||
|
||||
// 1. 直接回写任务名
|
||||
onTaskNameSelected?.Invoke(first);
|
||||
|
||||
// 2. 如果需要 ID,则从 taskName 前缀里解析(形如:1001-xxx-xxx)
|
||||
if (onTaskIdSelected != null)
|
||||
{
|
||||
var dashIndex = first.IndexOf('-');
|
||||
var idStr = dashIndex > 0 ? first.Substring(0, dashIndex) : first;
|
||||
if (int.TryParse(idStr, out var id))
|
||||
{
|
||||
onTaskIdSelected(id);
|
||||
}
|
||||
}
|
||||
|
||||
Repaint();
|
||||
};
|
||||
|
||||
selector.ShowInPopup();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 关卡数据
|
||||
|
||||
[BoxGroup("当前关卡数据")] [LabelText("当前关卡数据"), Indent]
|
||||
|
|
@ -423,10 +596,6 @@ public class LevelEditorWindow : OdinEditorWindow
|
|||
|
||||
[LabelText("出战区朝向")] public LevelData.CharacterToward preparingRegionToward;
|
||||
|
||||
[LabelText("撤退区")] public int fallbackRegionId;
|
||||
|
||||
[LabelText("敌方撤退区")] public int enemyFallbackRegionId;
|
||||
|
||||
[LabelText("PVP防守区")] public int pvpDefendRegionId;
|
||||
|
||||
[LabelText("PVP防守方朝向")] public LevelData.CharacterToward pvpDefendToward;
|
||||
|
|
@ -1854,10 +2023,15 @@ public class LevelEditorWindow : OdinEditorWindow
|
|||
{
|
||||
CheckCorrectLevel();
|
||||
|
||||
// 条件
|
||||
currentLevelData.taskInfos.Clear();
|
||||
foreach (var taskInfoEditor in taskInfos)
|
||||
{
|
||||
currentLevelData.taskInfos.Add(taskInfoEditor.taskInfo);
|
||||
}
|
||||
|
||||
// 地区
|
||||
currentLevelData.preparingRegionId = curRegionData.preparingRegionId;
|
||||
currentLevelData.fallbackRegionId = curRegionData.fallbackRegionId;
|
||||
currentLevelData.enemyFallbackRegionId = curRegionData.enemyFallbackRegionId;
|
||||
currentLevelData.preparingRegionToward = curRegionData.preparingRegionToward;
|
||||
currentLevelData.pvpDefendRegionId = curRegionData.pvpDefendRegionId;
|
||||
currentLevelData.pvpDefendToward = curRegionData.pvpDefendToward;
|
||||
|
|
@ -2171,12 +2345,16 @@ public class LevelEditorWindow : OdinEditorWindow
|
|||
|
||||
currentLevelData = levelData;
|
||||
|
||||
taskInfos.Clear();
|
||||
foreach (var taskInfo in currentLevelData.taskInfos)
|
||||
{
|
||||
taskInfos.Add(new TaskInfoEditor(taskInfo));
|
||||
}
|
||||
|
||||
//加载地区信息
|
||||
curRegionData = new RegionData
|
||||
{
|
||||
preparingRegionId = currentLevelData.preparingRegionId,
|
||||
fallbackRegionId = currentLevelData.fallbackRegionId,
|
||||
enemyFallbackRegionId = currentLevelData.enemyFallbackRegionId,
|
||||
preparingRegionToward = currentLevelData.preparingRegionToward,
|
||||
pvpDefendRegionId = currentLevelData.pvpDefendRegionId,
|
||||
pvpDefendToward = currentLevelData.pvpDefendToward
|
||||
|
|
@ -3086,20 +3264,6 @@ public class LevelEditorWindow : OdinEditorWindow
|
|||
curRegionData.preparingRegionId = 0;
|
||||
}
|
||||
|
||||
if (!_curMap.MapData.IsRegionExist(curRegionData.fallbackRegionId))
|
||||
{
|
||||
errorMessage += string.Format(FallbackRegionErrorMessage,
|
||||
curRegionData.fallbackRegionId);
|
||||
curRegionData.fallbackRegionId = 0;
|
||||
}
|
||||
|
||||
if (!_curMap.MapData.IsRegionExist(curRegionData.enemyFallbackRegionId))
|
||||
{
|
||||
errorMessage += string.Format(EnemyFallbackRegionErrorMessage,
|
||||
curRegionData.enemyFallbackRegionId);
|
||||
curRegionData.enemyFallbackRegionId = 0;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
CustomPopUpWindow.PopUp(errorMessage + FixedMessage);
|
||||
|
|
|
|||
|
|
@ -314,31 +314,13 @@ public class CheckLevelRegionData : LevelValidatorBase
|
|||
ErrorMessage = $"【地图数据:{MapDataPath} / 关卡数据:{LevelDataPath} 】出战区【{LevelData.preparingRegionId}】错误,不存在 !";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RegionIsLegal(LevelData.fallbackRegionId))
|
||||
{
|
||||
ErrorMessage = $"【地图数据:{MapDataPath} / 关卡数据:{LevelDataPath} 】撤退区【{LevelData.fallbackRegionId}】错误,不存在 !";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RegionIsLegal(LevelData.enemyFallbackRegionId))
|
||||
{
|
||||
ErrorMessage =
|
||||
$"【地图数据:{MapDataPath} / 关卡数据:{LevelDataPath} 】敌方撤退区【{LevelData.enemyFallbackRegionId}】错误,不存在 !";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!RegionIsLegal(LevelData.pvpDefendRegionId))
|
||||
{
|
||||
ErrorMessage = $"【地图数据:{MapDataPath} / 关卡数据:{LevelDataPath} 】PVP防守区【{LevelData.pvpDefendRegionId}】错误,不存在 !";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LevelData.fallbackRegionId == LevelData.enemyFallbackRegionId)
|
||||
{
|
||||
WarningMessage = $"【地图数据:{MapDataPath} / 关卡数据:{LevelDataPath} 】敌人撤退区域和我方撤退区域相同 !";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue