关卡任务数据相关

main
zhangaotian 2025-12-09 13:05:27 +08:00
parent 135dcdf25e
commit e2df98e934
5 changed files with 310 additions and 92 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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();

View File

@ -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);

View File

@ -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;
}
}