using TGS; using Framework; using System.IO; using UnityEditor; using UnityEngine; using System.Linq; using Gameplay.Level; using Sirenix.Utilities; using Sirenix.OdinInspector; using Sirenix.Utilities.Editor; using System.Collections.Generic; using cfg.FightCfg; using cfg.LevelCfg; using cfg.MonsterCfg; using cfg.VehicleCfg; using Sirenix.OdinInspector.Editor; using Level = cfg.LevelCfg.Level; using ObjectFieldAlignment = Sirenix.OdinInspector.ObjectFieldAlignment; using static Gameplay.Level.LevelData; public partial class LevelEditorWindow : OdinEditorWindow { [MenuItem("Tools/*地图场景/关卡编辑器", false, (int)NLDMenuID.LevelEditor)] private static void OpenLevelEditorWindow() { var window = GetWindow(); window.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 600); window.titleContent = new GUIContent("关卡编辑器"); } public static void OpenWindow(LevelEditor levelEditor) { MapEditorSettings.Init(); currWindow = GetWindow(); currWindow.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 600); currWindow.currentLevelData = null; currWindow._levelEditor = levelEditor; currWindow.LoadLevel(levelEditor.LevelData); var level = Path.GetFileNameWithoutExtension(levelEditor.LevelDataJson.name); currWindow.levelID = level.Substring(level.Length - 6); //currWindow.GetLevelMonsterClass(); DebugUtil.Log("关卡数据:{0}", currWindow.levelID); } private static LevelEditorWindow currWindow; private MapManager _mapManager; private Map _curMap; private LevelEditor _levelEditor; #region 配置表 private Level LevelCfg => EditorTableManager.instance.tables.Level; private Monster MonsterCfg => EditorTableManager.instance.tables.Monster; private MonsterClass MonsterClassCfg => EditorTableManager.instance.tables.MonsterClass; private VehicleConfig VehicleCfg => EditorTableManager.instance.tables.VehicleConfig; private FightNPCConfig FightNpcCfg => EditorTableManager.instance.tables.FightNPCConfig; private BuildConfig FightBuildCfg => EditorTableManager.instance.tables.BuildConfig; #endregion #region 地块颜色 private static readonly Color RegionColor = MapUtils.GetRGBAColor("FF960072"); private static readonly Color ShowRegionColor = Color.red; private static readonly Color CaptureRegionColor = Color.yellow; private static readonly Color RetreatRegionColor = Color.cyan; #endregion #region 任务相关 [BoxGroup("任务列表")] [LabelText("任务列表"), Indent] public List taskInfos = new List(); #endregion /*#region 胜利失败星级条件 private int successTaskID; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/1")] [LabelText("胜利条件"), Indent] [ReadOnly] [InfoBox("胜利条件任务无效,请在任务列表中检查配置。", InfoMessageType.Error, "@!IsTaskInvalid(successTaskID)")] public string successTask; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/1")] [Button("搜索/选择胜利任务", ButtonSizes.Small)] [InfoBox("胜利条件任务无效,请在任务列表中检查配置。", InfoMessageType.Error, "@!IsTaskInvalid(successTaskID)")] private void OpenSuccessTaskSelector() { ShowTaskSelector("选择胜利条件任务", taskName => successTask = taskName, id => successTaskID = id); } private int failTaskID; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/2")] [LabelText("失败条件"), Indent] [ReadOnly] public string failTask; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/2")] [Button("搜索/选择失败任务", ButtonSizes.Small)] private void OpenFailTaskSelector() { ShowTaskSelector("选择失败条件任务", taskName => failTask = taskName, id => failTaskID = id); } private List starTaskIDs = new List() { 0, 0, 0 }; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/3")] [LabelText("1星条件"), Indent] [ReadOnly] public string oneStarTask; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/3")] [Button("搜索/选择1星任务", ButtonSizes.Small)] private void OpenOneStarTaskSelector() { ShowTaskSelector("选择1星条件任务", taskName => oneStarTask = taskName, id => starTaskIDs[0] = id); } [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/4")] [LabelText("2星条件"), Indent] [ReadOnly] public string twoStarTask; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/4")] [Button("搜索/选择2星任务", ButtonSizes.Small)] private void OpenTwoStarTaskSelector() { ShowTaskSelector("选择2星条件任务", taskName => twoStarTask = taskName, id => starTaskIDs[1] = id); } [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/5")] [LabelText("3星条件"), Indent] [ReadOnly] public string threeStarTask; [FoldoutGroup("胜利失败星级条件")] [HorizontalGroup("胜利失败星级条件/5")] [Button("搜索/选择3星任务", ButtonSizes.Small)] private void OpenThreeStarTaskSelector() { ShowTaskSelector("选择3星条件任务", taskName => threeStarTask = taskName, id => starTaskIDs[2] = id); } #endregion*/ #region 关卡数据 [FoldoutGroup("当前关卡数据")] [BoxGroup("当前关卡数据/数据")] [HideLabel] public LevelData currentLevelData = null; #endregion #region 战役关卡 [FoldoutGroup("当前关卡数据")] [BoxGroup("当前关卡数据/支线(战役)")] [LabelText("设置该关卡为评分关卡"), Indent] [OnValueChanged(nameof(OnScoreLevelChanged))] public bool scoreLevel; private void OnScoreLevelChanged() { currentLevelData.scoreLevel = scoreLevel; } [FoldoutGroup("当前关卡数据")] [BoxGroup("当前关卡数据/支线(战役)")] [Indent, ShowIf("@currentLevelData.scoreLevel")] [LabelText("评分条件")] public List scoreTasks = new List(); #endregion #region 触发器 [FoldoutGroup("当前关卡数据")] [BoxGroup("当前关卡数据/触发器列表")] [Indent, LabelText("触发器")] public List levelTriggerData = new List(); #endregion #region 关卡引导 [FoldoutGroup("关卡引导")] [BoxGroup("关卡引导/关卡进度")] [LabelText("关卡进度"), Indent] public List progressGuideDatas = new(); [FoldoutGroup("关卡引导")] [BoxGroup("关卡引导/立标数据")] [LabelText("立标数据"), Indent] public List setFlagEditorInfos = new(); [FoldoutGroup("关卡引导")] [BoxGroup("关卡引导/信息板数据")] [LabelText("信息板数据"), Indent] public List infoPanelGuideDatas = new(); #endregion #region 关卡AI广播 [FoldoutGroup("关卡AI广播")] [BoxGroup("关卡AI广播/数据")] [LabelText("AI广播数据"), Indent] public List listAIBroadcastData = new(); #endregion #region 地区相关 /// /// 每种地区对应的显示颜色 /// private static readonly Dictionary ShowRegionTypeColors = new Dictionary { { ShowRegionType.CaptureRegion, CaptureRegionColor }, { ShowRegionType.RetreatRegion, RetreatRegionColor }, { ShowRegionType.EnemyRetreatRegion, Color.green }, { ShowRegionType.FlagStratRegion, new Color(1f, 0.5f, 0f) }, { ShowRegionType.FlagEndRegion, new Color(1f, 0.3f, 0.3f) }, { ShowRegionType.MinesRegion, Color.black }, { ShowRegionType.DefenseRegion, Color.magenta }, { ShowRegionType.BlockRegion, Color.blue }, }; [FoldoutGroup("地区相关")] [LabelText("显示地区颜色")] [EnumToggleButtons] [OnValueChanged(nameof(OnShowRegionTypeChanged))] public ShowRegionType showRegionType; /// /// 根据当前选择的地区类型集合,高亮对应的地区格子,每种地区使用独立颜色。 /// private void OnShowRegionTypeChanged() { if (_curMap == null || currentLevelData == null) return; ClearShowRegionColor(); if (showRegionType == ShowRegionType.None) { SceneView.RepaintAll(); return; } var mask = showRegionType; if (mask.HasFlag(ShowRegionType.All)) { mask |= ShowRegionType.All; } foreach (var kv in ShowRegionTypeColors) { var type = kv.Key; var color = kv.Value; if (!mask.HasFlag(type)) continue; var points = GetRegionPoints(type); if (points == null || points.Count == 0) continue; _curMap.SetBlocksColor(points, true, color); } SceneView.RepaintAll(); } /// /// 按地区类型获取需要高亮的格子索引列表 /// private List GetRegionPoints(ShowRegionType type) { var points = new HashSet(); switch (type) { case ShowRegionType.CaptureRegion: if (captureEditorInfos != null) { foreach (var capture in captureEditorInfos) { if (capture?.capturePointEditorInfos == null) continue; foreach (var p in capture.capturePointEditorInfos) { points.Add(p.capturePoint); } } } break; case ShowRegionType.RetreatRegion: if (retreatEditorInfo != null) { foreach (var info in retreatEditorInfo) { points.Add(info.retreatPoint); } } break; case ShowRegionType.EnemyRetreatRegion: if (enemyRetreatEditorInfo?.fallbackReusablePoints != null) { foreach (var pos in enemyRetreatEditorInfo.fallbackReusablePoints) { points.Add(_curMap.BlockPosition2TGSCellIndex(pos)); } } break; case ShowRegionType.FlagStratRegion: if (flagEditorInfo?.flagStartPoints != null) { foreach (var pos in flagEditorInfo.flagStartPoints) { points.Add(_curMap.BlockPosition2TGSCellIndex(pos)); } } break; case ShowRegionType.FlagEndRegion: if (flagEditorInfo?.flagEndPoints != null) { foreach (var pos in flagEditorInfo.flagEndPoints) { points.Add(_curMap.BlockPosition2TGSCellIndex(pos)); } } break; case ShowRegionType.MinesRegion: if (minesRegionList != null) { foreach (var area in minesRegionList) { if (area?.Positions == null) continue; foreach (var cell in area.Positions) { points.Add(cell); } } } break; case ShowRegionType.DefenseRegion: if (defenseRegionList != null) { foreach (var area in defenseRegionList) { if (area?.Positions == null) continue; foreach (var cell in area.Positions) { points.Add(cell); } } } break; case ShowRegionType.BlockRegion: if (blockRegionList != null) { foreach (var area in blockRegionList) { if (area?.Positions == null) continue; foreach (var cell in area.Positions) { points.Add(cell); } } } break; } return points.ToList(); } #region 撤离相关 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/撤离相关")] [VerticalGroup("地区相关/撤离相关/编辑")] [LabelText("编辑撤离数据"), Indent] [OnValueChanged("OnIsEditingRetreatChanged")] public bool isEditingRetreat; [FoldoutGroup("地区相关")] [BoxGroup("地区相关/撤离相关")] [LabelText("当前撤离数据"), Indent] [ShowIf("isEditingRetreat")] [ListDrawerSettings(CustomRemoveIndexFunction = "CustomRemoveRetreatPoint", DraggableItems = false)] public List retreatEditorInfo = new(); private void CustomRemoveRetreatPoint(int index) { if (index >= retreatEditorInfo.Count) return; var point = retreatEditorInfo[index].retreatPoint; _curMap.TerrainGridSystem.CellHideRegionSurface(point); retreatEditorInfo.RemoveAt(index); } #endregion #region 敌人撤离相关 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/敌人撤离相关")] [LabelText("编辑敌人撤离数据"), Indent] [OnValueChanged("OnIsEditingEnemyRetreatChanged")] public bool isEditingEnemyRetreat; private void OnIsEditingEnemyRetreatChanged() { if (isEditingEnemyRetreat) return; isEditingFallbackReusable = false; } private bool isEditingFallbackReusable; [FoldoutGroup("地区相关")] [BoxGroup("地区相关/敌人撤离相关")] [LabelText("当前撤离数据"), Indent] [ShowIf("isEditingEnemyRetreat")] public EnemyRetreatEditorInfo enemyRetreatEditorInfo = new(); #endregion #region 出战防守区 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/出战防守")] [LabelText("当前地区数据"), Indent] public RegionData curRegionData = null; #endregion #region 占领相关 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/占领相关")] [VerticalGroup("地区相关/占领相关/编辑")] [LabelText("编辑占领区"), Indent] public bool isEditingCapture = false; [FoldoutGroup("地区相关")] [BoxGroup("地区相关/占领相关")] [VerticalGroup("地区相关/占领相关/编辑")] [LabelText("编辑占领区"), Indent] [ShowIf("isEditingCapture")] public List captureEditorInfos = new(); #endregion #region 扛旗相关 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/扛棋相关")] [LabelText("当前扛旗数据"), Indent] public FlagEditorInfo flagEditorInfo = new(); #endregion #region 雷区 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/雷区信息")] [HorizontalGroup("地区相关/雷区信息/Region")] [LabelText("编辑雷区"), Indent] [OnValueChanged("OnCanEditorMinesAreaTypeChanged")] [LabelWidth(100)] [SerializeField] private bool canEditorMinesArea; private void OnCanEditorMinesAreaTypeChanged() { if (canEditorMinesArea) { canEditorBlockArea = false; canEditorDefenseArea = false; _isEditorSingleBlockRegion = false; _isEditorSingleDefenseRegion = false; ClearDefenseRegionColor(); ClearBlockRegionColor(); } else { ClearMinesRegionColor(); SceneView.RepaintAll(); _isEditorSingleMinesRegion = false; _currEditMinesRegionArea = null; } } [FoldoutGroup("地区相关")] [VerticalGroup("地区相关/雷区信息/RegionInfo"), Indent] [LabelText("雷区列表")] [ShowIf("canEditorMinesArea")] [ListDrawerSettings(CustomAddFunction = "CustomAddMinesRegion", CustomRemoveIndexFunction = "CustomRemoveMinesRegion", DraggableItems = false)] public List minesRegionList = new(); private void CustomAddMinesRegion() { var region = new LevelData.Region(); currentLevelData.minesRegions.Add(region); UpdateMinesRegion(); } private void CustomRemoveMinesRegion(int index) { ClearMinesRegionColor(); currentLevelData.minesRegions.RemoveAt(index); UpdateMinesRegion(); } #endregion #region 阻挡区信息 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/阻挡区信息")] [HorizontalGroup("地区相关/阻挡区信息/Region")] [LabelText("编辑阻挡区"), Indent] [OnValueChanged("OnCanEditorBlockAreaChanged")] [LabelWidth(100)] [SerializeField] private bool canEditorBlockArea; private void OnCanEditorBlockAreaChanged() { if (canEditorBlockArea) { canEditorMinesArea = false; canEditorDefenseArea = false; _isEditorSingleDefenseRegion = false; ClearDefenseRegionColor(); ClearMinesRegionColor(); } else { ClearBlockRegionColor(); SceneView.RepaintAll(); _currEditBlockRegion = null; _isEditorSingleBlockRegion = false; } } [FoldoutGroup("地区相关")] [LabelText("阻挡区列表")] [ShowIf("canEditorBlockArea")] [VerticalGroup("地区相关/阻挡区信息/RegionInfo"), Indent] [ListDrawerSettings(CustomAddFunction = "CustomAddBlockRegion", CustomRemoveIndexFunction = "CustomRemoveBlockRegionIndex", DraggableItems = false)] public List blockRegionList = new(); private void CustomAddBlockRegion() { var region = new BlockRegion(); currentLevelData.blockRegions.Add(region); UpdateBlockRegion(); } private void CustomRemoveBlockRegionIndex(int index) { if (index < blockRegionList.Count) { if (blockRegionList[index].obj != null) { DebugUtil.LogError($"删除{blockRegionList[index].id}的指示"); DestroyImmediate(blockRegionList[index].obj); } } ClearBlockRegionColor(); currentLevelData.blockRegions.RemoveAt(index); UpdateBlockRegion(); } #endregion #region 刷怪触发区 [FoldoutGroup("地区相关")] [BoxGroup("地区相关/刷怪触发区")] [HorizontalGroup("地区相关/刷怪触发区/Region")] [LabelText("编辑触发区"), Indent] [OnValueChanged("OnCanEditorDefenseAreaChanged")] [LabelWidth(100)] [SerializeField] private bool canEditorDefenseArea; private void OnCanEditorDefenseAreaChanged() { if (canEditorDefenseArea) { canEditorBlockArea = false; canEditorMinesArea = false; _isEditorSingleBlockRegion = false; ClearBlockRegionColor(); ClearMinesRegionColor(); } else { ClearDefenseRegionColor(); SceneView.RepaintAll(); _currEditDefenseRegion = null; _isEditorSingleDefenseRegion = false; } } [FoldoutGroup("地区相关")] [LabelText("刷怪触发区列表")] [ShowIf("canEditorDefenseArea")] [VerticalGroup("地区相关/刷怪触发区/RegionInfo"), Indent] [ListDrawerSettings(CustomAddFunction = "CustomAddDefenseRegion", CustomRemoveIndexFunction = "CustomRemoveDefenseRegion", DraggableItems = false)] public List defenseRegionList = new(); private void CustomAddDefenseRegion() { var region = new LevelData.Region(); currentLevelData.defenseRegions.Add(region); UpdateDefenseRegion(); } private void CustomRemoveDefenseRegion(int index) { ClearDefenseRegionColor(); currentLevelData.defenseRegions.RemoveAt(index); UpdateDefenseRegion(); } #endregion #endregion #region 指定路线 [BoxGroup("制定路线")] [LabelText("编辑指定路径"), Indent] public bool isEditingRoad; [BoxGroup("制定路线")] [LabelText("编辑指定路线"), Indent] [ShowIf("isEditingRoad")] public List roadEditorInfos = new(); #endregion #region 指定区域 [BoxGroup("巡逻区域")] [LabelText("编辑巡逻区域"), Indent] public bool isEditingArea; [BoxGroup("巡逻区域")] [LabelText("编辑巡逻区域"), Indent] [ShowIf("isEditingArea")] public List areaEditorInfos = new(); #endregion #region 显示地图索引 [BoxGroup("显示地格索引")] [LabelText("显示类型"), Indent] [OnValueChanged("ShowCellIndex")] [LabelWidth(100)] [SerializeField] private CellDebugInfo cellDebugInfo = CellDebugInfo.Nothing; private void ShowCellIndex() { if (_curMap == null || _curMap.TerrainGridSystem == null) return; _curMap.TerrainGridSystem.displayCellDebugInfo = cellDebugInfo; var obj = FindObjectOfType().gameObject; Selection.activeGameObject = obj; } #endregion #region 敌方Npc private GameObject enemyObjRoot; /// /// 波次/怪物ID对应的NPC预制体 /// private Dictionary> _enemyNpcObjectWaveDic; [BoxGroup("敌方Npc")] [LabelText("编辑NPC"), Indent] [LabelWidth(100)] [SerializeField] [OnValueChanged("OnIsEditingEnemyNpcChanged")] public bool isEditingEnemyNpc; [BoxGroup("敌方Npc")] [LabelText("显示所有怪物序号ID"), Indent] [OnValueChanged("ShowAllEnemyNpcId")] [SerializeField] public bool isShowAllEnemyNpcID; [BoxGroup("敌方Npc")] [LabelText("筛选怪物ID"), Indent, LabelWidth(100)] [SerializeField] [ShowIf("isEditingEnemyNpc")] public string selectEnemyMonsterId; [BoxGroup("敌方Npc")] [LabelText("怪物ID"), Indent, LabelWidth(100)] [SerializeField] [OnValueChanged("EnemyMonsterIDChanged")] [ValueDropdown("GetEnemyMonsterIDs")] [ShowIf("isEditingEnemyNpc")] public int curEnemyMonsterID; [BoxGroup("敌方Npc")] [PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly] [LabelText("NPC预制体预览图"), ShowIf("isEditingEnemyNpc")] public GameObject enemyNpcObject; /// /// 真实敌人列表 /// [HideInInspector] public List currentEnemyNpcInfo = null; /// /// 该关卡的敌方NPCID /// [HideInInspector] public List enemyMonsterIDs; private IEnumerable GetEnemyMonsterIDs() { enemyMonsterIDs ??= new List(); enemyMonsterIDs.Clear(); if (null == selectEnemyMonsterId) return enemyMonsterIDs; foreach (var monster in MonsterCfg.DataMap) { string strID = monster.Key.ToString(); if (!strID.StartsWith(selectEnemyMonsterId)) continue; switch (monster.Value.NpcType) { case EMonsterType.EnemyCharacter: case EMonsterType.Vehicle: case EMonsterType.FightBuild: { enemyMonsterIDs.Add(monster.Key); } break; case EMonsterType.SelfCharacter: break; default: DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}"); break; } } return enemyMonsterIDs; } private void EnemyMonsterIDChanged() { var obj = LoadEnemyNpcPrefab(curEnemyMonsterID); if (obj != null) enemyNpcObject = obj; } private void OnIsEditingEnemyNpcChanged() { if (isEditingEnemyNpc && isEditingSelfNpc && isEditingMidNpc) { isEditingSelfNpc = false; isEditingMidNpc = false; } } private void ShowAllEnemyNpcId() { foreach (var npc in currentEnemyNpcInfo) { npc.isShowNpcID = isShowAllEnemyNpcID; if (isShowAllEnemyNpcID) { if (!_showNpcInfos.Contains(npc)) { _showNpcInfos.Add(npc); } } else { _showNpcInfos.Remove(npc); } } } /// /// 敌方 NPC 的分组展示数据 /// [BoxGroup("敌方Npc")] [LabelText("按组查看怪物"), Indent, ShowIf("isEditingEnemyNpc")] [ShowInInspector] [ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, DraggableItems = false, ShowFoldout = true)] public List enemyNpcGroups = new(); #endregion #region 己方Npc private const int SelfID = 200; private GameObject selfObjRoot; private Dictionary _selfNpcObjectDic; [BoxGroup("己方Npc")] [LabelText("编辑己方NPC"), Indent] [LabelWidth(100)] [OnValueChanged("OnIsEditingSelfNpcChanged")] [SerializeField] public bool isEditingSelfNpc; [BoxGroup("己方Npc")] [LabelText("显示所有己方序号ID"), Indent] [OnValueChanged("ShowAllSelfNpcId")] [SerializeField] public bool isShowAllSelfNpcID; [BoxGroup("己方Npc")] [LabelText("筛选怪物ID"), Indent, LabelWidth(100)] [SerializeField] [ShowIf("isEditingSelfNpc")] public string selectSelfMonsterId; [BoxGroup("己方Npc")] [LabelText("己方NpcID"), Indent, LabelWidth(100)] [SerializeField] [OnValueChanged("SelfNpcIDChanged")] [ValueDropdown("GetSelfNpcIDs")] [ShowIf("isEditingSelfNpc")] public int curSelfNpcID; [BoxGroup("己方Npc")] [PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly] [LabelText("己方NPC预制体预览图"), ShowIf("isEditingSelfNpc")] public GameObject selfNpcObject; [BoxGroup("己方Npc")] [LabelText("当前己方NPC列表"), Indent, ShowIf("isEditingSelfNpc")] [ListDrawerSettings(HideAddButton = true, HideRemoveButton = true)] public List currentSelfNpcInfo = null; /// /// 该关卡的己方NPCID /// [HideInInspector] public List selfMonsterIDs; private IEnumerable GetSelfNpcIDs() { selfMonsterIDs ??= new List(); selfMonsterIDs.Clear(); if (null == selectSelfMonsterId) return selfMonsterIDs; foreach (var monster in MonsterCfg.DataMap) { string strID = monster.Key.ToString(); if (!strID.StartsWith(selectSelfMonsterId)) continue; switch (monster.Value.NpcType) { case EMonsterType.SelfCharacter: case EMonsterType.Vehicle: case EMonsterType.FightBuild: { selfMonsterIDs.Add(monster.Key); } break; case EMonsterType.EnemyCharacter: break; default: DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}"); break; } } return selfMonsterIDs; } private void SelfNpcIDChanged() { var obj = LoadSelfNpcPrefab(curSelfNpcID); if (obj != null) selfNpcObject = obj; } private void OnIsEditingSelfNpcChanged() { if (isEditingEnemyNpc && isEditingSelfNpc && isEditingMidNpc) { isEditingEnemyNpc = false; isEditingMidNpc = false; } } private void ShowAllSelfNpcId() { foreach (var npc in currentSelfNpcInfo) { npc.isShowNpcID = isShowAllSelfNpcID; if (isShowAllSelfNpcID) { if (!_showNpcInfos.Contains(npc)) { _showNpcInfos.Add(npc); } } else { _showNpcInfos.Remove(npc); } } } #endregion #region 中立Npc private GameObject midObjRoot; /// /// 中立 波次/怪物ID对应的NPC预制体 /// private Dictionary> _midNpcObjectWaveDic; [BoxGroup("中立Npc")] [LabelText("编辑NPC"), Indent] [LabelWidth(100)] [SerializeField] [OnValueChanged("OnIsEditingMidNpcChanged")] public bool isEditingMidNpc; [BoxGroup("中立Npc")] [LabelText("显示所有怪物序号ID"), Indent] [OnValueChanged("ShowAllMidNpcId")] [SerializeField] public bool isShowAllMidNpcID; [BoxGroup("中立Npc")] [LabelText("筛选怪物ID"), Indent, LabelWidth(100)] [SerializeField] [ShowIf("isEditingMidNpc")] public string selectMidMonsterId; [BoxGroup("中立Npc")] [LabelText("怪物ID"), Indent, LabelWidth(100)] [SerializeField] [OnValueChanged("MidMonsterIDChanged")] [ValueDropdown("GetMidMonsterIDs")] [ShowIf("isEditingMidNpc")] public int curMidMonsterID; [BoxGroup("中立Npc")] [PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly] [LabelText("NPC预制体预览图"), ShowIf("isEditingMidNpc")] public GameObject midNpcObject; [BoxGroup("中立Npc")] [LabelText("当前怪物列表"), Indent, ShowIf("isEditingMidNpc")] [ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, ShowFoldout = true)] public List currentMidNpcInfo = null; /// /// 该关卡的中立NPCID /// [HideInInspector] public List midMonsterIDs; private IEnumerable GetMidMonsterIDs() { midMonsterIDs ??= new List(); midMonsterIDs.Clear(); if (null == selectMidMonsterId) return midMonsterIDs; foreach (var monster in MonsterCfg.DataMap) { string strID = monster.Key.ToString(); if (!strID.StartsWith(selectMidMonsterId)) continue; switch (monster.Value.NpcType) { case EMonsterType.Vehicle: case EMonsterType.FightBuild: { midMonsterIDs.Add(monster.Key); } break; case EMonsterType.EnemyCharacter: case EMonsterType.SelfCharacter: break; default: DebugUtil.LogError($"未处理类型:{monster.Value.NpcType}"); break; } } return midMonsterIDs; } private void MidMonsterIDChanged() { var obj = LoadMidNpcPrefab(curMidMonsterID); if (obj != null) midNpcObject = obj; } private void OnIsEditingMidNpcChanged() { if (isEditingEnemyNpc && isEditingSelfNpc && isEditingMidNpc) { isEditingEnemyNpc = false; isEditingSelfNpc = false; } } private void ShowAllMidNpcId() { foreach (var npc in currentMidNpcInfo) { npc.isShowNpcID = isShowAllMidNpcID; if (isShowAllMidNpcID) { if (!_showNpcInfos.Contains(npc)) { _showNpcInfos.Add(npc); } } else { _showNpcInfos.Remove(npc); } } } #endregion #region 阻挡球编辑 [BoxGroup("攻击阻挡球")] [HorizontalGroup("攻击阻挡球/Block")] [LabelText("编辑攻击阻挡球"), Indent] [OnValueChanged("OnCanEditorBlockSphereChanged")] [LabelWidth(100)] [SerializeField] private bool canEditorBlockSphere; private void OnCanEditorBlockSphereChanged() { if (canEditorBlockSphere) { showBuildings = false; HideObjectsExcept(); blockSphereEditorList.Clear(); UpdateBlockSphereInfo(); foreach (var cellIndex in banAttackCells) { if (blockSphereInfo.TryGetValue(cellIndex, out var sphereInfo)) { var sphere = CreateSphere(cellIndex, sphereInfo.offestPos, Vector3.one * sphereInfo.scale); var blockEditorInfo = new BlockSpereEditorInfo(sphereInfo, sphere); blockSphereEditorList.Add(blockEditorInfo); } else { var pos = _curMap.TGSCellIndex2WorldPosition(cellIndex); var sphere = CreateSphere(cellIndex, pos, Vector3.one * Calibre); var blockEditorInfo = new BlockSpereEditorInfo(cellIndex, sphere); blockSphereEditorList.Add(blockEditorInfo); } } editingBlockSphereList.Clear(); foreach (var editorInfo in blockSphereEditorList) { if (editorInfo.isChanged) { editingBlockSphereList.Add(editorInfo); } } } else { // 清理所有临时材质 foreach (var editorInfo in blockSphereEditorList) { if (editorInfo.editingMaterial != null) { if (editorInfo.sphere != null) { var renderer = editorInfo.sphere.GetComponent(); if (renderer != null) { renderer.sharedMaterial = editorInfo.originalSharedMaterial; } } DestroyImmediate(editorInfo.editingMaterial); } } showBuildings = true; ShowHiddenObjects(); DestroyBlockSphere(); } } [LabelText("展示建筑")] [HorizontalGroup("攻击阻挡球/BlockBuildings"), Indent] [ShowIf("canEditorBlockSphere")] [OnValueChanged("OnShowBuildingsChanged")] public bool showBuildings; private void OnShowBuildingsChanged() { if (showBuildings) { ShowHiddenObjects(); } else { HideObjectsExcept(); } } [LabelText("添加地格")] [HorizontalGroup("攻击阻挡球/新数据"), Indent] [ShowIf("canEditorBlockSphere")] public int addCellIndex; [Button("添加编辑")] [HorizontalGroup("攻击阻挡球/新数据/Add"), Indent] [ShowIf("canEditorBlockSphere")] public void AddBlockSphere() { if (!banAttackCells.Contains(addCellIndex)) { EditorUtility.DisplayDialog("错误", $"地格 {addCellIndex} 不是攻击阻挡地格!", "确定"); return; } foreach (var blockSpereEditor in editingBlockSphereList) { if (blockSpereEditor.cellIndex == addCellIndex) { return; } } foreach (var blockSpereEditor in blockSphereEditorList) { if (blockSpereEditor.cellIndex == addCellIndex) { editingBlockSphereList.Add(blockSpereEditor); break; } } } [LabelText("阻挡球列表")] [HorizontalGroup("攻击阻挡球/EditList"), Indent] [ShowIf("canEditorBlockSphere")] [ListDrawerSettings(HideAddButton = true, CustomRemoveIndexFunction = "CustomRemoveBlockSphereIndex")] public List editingBlockSphereList = new(); [HideInInspector] public List blockSphereEditorList = new(); private void CustomRemoveBlockSphereIndex(int index) { if (index < editingBlockSphereList.Count) { editingBlockSphereList[index].Revert(); DebugUtil.Log($"删除并还原{editingBlockSphereList[index].cellIndex}的阻挡球编辑数据"); editingBlockSphereList.RemoveAt(index); } } #endregion #region 保存 [Button("保存当前关卡"), Indent] private void SaveCurrentLevel() { CheckCorrectLevel(); if (!CheckTask()) return; // 条件 currentLevelData.taskInfos = taskInfos.Select(t => t.taskInfo).ToList(); /*// 胜利失败星级条件 currentLevelData.successTaskID = successTaskID; currentLevelData.failTaskID = failTaskID; for (var i = 0; i < starTaskIDs.Count; i++) { currentLevelData.starTaskIDs[i] = starTaskIDs[i]; }*/ // 地区 currentLevelData.preparingRegionId = curRegionData.preparingRegionId; currentLevelData.preparingRegionToward = curRegionData.preparingRegionToward; currentLevelData.pvpDefendRegionId = curRegionData.pvpDefendRegionId; currentLevelData.pvpDefendToward = curRegionData.pvpDefendToward; // 评分关卡 currentLevelData.scoreTaskID = scoreTasks.Select(s => s.scoreTaskID).ToList(); //触发器 currentLevelData.levelTriggerDatas = levelTriggerData.Select(e => e.levelTriggerData).ToList(); #region 敌方NPC foreach (var npcEditor in currentEnemyNpcInfo) { npcEditor.npcInfo.index = npcEditor.id; } currentLevelData.npcInfos = currentEnemyNpcInfo.Select(editorNpcInfo => editorNpcInfo.npcInfo).ToList(); #endregion #region 己方NPC foreach (var npcFightEditorInfo in currentSelfNpcInfo) { npcFightEditorInfo.npcInfo.index = npcFightEditorInfo.id; } currentLevelData.selfNpcInfos = currentSelfNpcInfo.Select(editorFightNpcInfo => editorFightNpcInfo.npcInfo).ToList(); #endregion #region 中立NPC foreach (var npcEditor in currentMidNpcInfo) { npcEditor.npcInfo.index = npcEditor.id; } currentLevelData.midNpcInfos = currentMidNpcInfo.Select(editorNpcInfo => editorNpcInfo.npcInfo).ToList(); #endregion // 占领数据 currentLevelData.CapturePoints.Clear(); foreach (var captureInfo in captureEditorInfos) { var points = captureInfo.capturePointEditorInfos.Select(x => x.capturePoint).ToList(); var flagPoint = 0; foreach (var pointData in captureInfo.capturePointEditorInfos) { if (pointData.captureFlag) flagPoint = pointData.capturePoint; } var capture = new CaptureInfo { captureRegionID = captureInfo.captureRegionID, capturePoints = points, captureFlag = flagPoint, CaptureProgress = captureInfo.captureProgress, captureEffectID = captureInfo.captureEffectID, captureEffectPoint = captureInfo.captureEffectPoint, }; currentLevelData.CapturePoints.Add(capture); } // 扛旗数据 currentLevelData.flagStartPoints.Clear(); currentLevelData.flagEndPoints.Clear(); foreach (var flag in flagEditorInfo.flagStartPoints) { var cellIndex = _curMap.BlockPosition2TGSCellIndex(flag); currentLevelData.flagStartPoints.Add(cellIndex); } foreach (var flag in flagEditorInfo.flagEndPoints) { var cellIndex = _curMap.BlockPosition2TGSCellIndex(flag); currentLevelData.flagEndPoints.Add(cellIndex); } // 撤离数据 currentLevelData.retreatPoints.retreatPoints.Clear(); currentLevelData.retreatPoints.retreatFlag = -1; foreach (var retreatPoint in retreatEditorInfo) { if (retreatPoint.retreatFlag) currentLevelData.retreatPoints.retreatFlag = retreatPoint.retreatPoint; currentLevelData.retreatPoints.retreatPoints.Add(retreatPoint.retreatPoint); } // 撤离数据 currentLevelData.enemyRetreatReusablePoints.Clear(); foreach (var fallback in enemyRetreatEditorInfo.fallbackReusablePoints) { var cellIndex = _curMap.BlockPosition2TGSCellIndex(fallback); currentLevelData.enemyRetreatReusablePoints.Add(cellIndex); } // 指定路径 currentLevelData.patrolRoadData = roadEditorInfos.Select(editorInfo => new PatrolData { id = editorInfo.roadID, patrolInfos = editorInfo.roadPoints, }).ToList(); // 指定区域 currentLevelData.patrolRegionData = areaEditorInfos.Select(editorInfo => new PatrolData { id = editorInfo.areaID, patrolInfos = editorInfo.areaPoints, }).ToList(); // 关卡进度 currentLevelData.progressGuideDatas = progressGuideDatas.Select(p => p.progressGuideData).ToList(); // 立标数据 currentLevelData.setFlagGuideDatas = setFlagEditorInfos.Select(f => f.flagGuideData).ToList(); // 信息板数据 currentLevelData.infoPanelGuideDatas = infoPanelGuideDatas.Select(i => i.infoPanelGuideData).ToList(); // 关卡广播 currentLevelData.listAIBroadcastData = listAIBroadcastData.Select(a => a.aiBroadcastData).ToList(); // 处理条件 ProcessCondition(); // 阻挡球 SaveBlockSphereInfo(); _levelEditor.SaveLevelData(); _mapManager.SaveMapData(); } #endregion #region 关卡ID相关 /// /// 关卡ID 通过关卡数据 json文件名 获得 /// [LabelText("手动指定关卡ID"), Indent, LabelWidth(100), ShowIf("needManualInputLevelID")] [InfoBox("无法正确获得关卡ID,请手动指定")] [SerializeField] public string levelID; /// /// 需要手动输入关卡ID /// [HideInInspector] public bool needManualInputLevelID; #endregion #region Npc某些值改变时响应事件 private void ReorderEnemyNpcID() { for (var i = 0; i < currentEnemyNpcInfo.Count; i++) { currentEnemyNpcInfo[i].id = i + 1; currentEnemyNpcInfo[i].npcInfo.index = currentEnemyNpcInfo[i].id; } } private void ReorderMidNpcID() { for (var i = 0; i < currentMidNpcInfo.Count; i++) { currentMidNpcInfo[i].id = i + 1; currentMidNpcInfo[i].npcInfo.index = currentMidNpcInfo[i].id; } } private void ReorderSelfNpcID() { for (var i = 0; i < currentSelfNpcInfo.Count; i++) { currentSelfNpcInfo[i].id = SelfID + i + 1; } } private void ChangeEnemyNpcID(NpcEditorInfo npcEditorInfo, int newMonsterID) { if (_enemyNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic)) { if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj)) { var newObj = LoadEnemyNpcPrefab(newMonsterID); if (newObj != null) { var obj = Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation); if (enemyObjRoot == null) enemyObjRoot = new GameObject("enemyObjRoot"); if (obj != null) obj.transform.SetParent(enemyObjRoot.transform); npcEditorInfo.npcInfo.id = newMonsterID; npcEditorInfo.level = MonsterCfg.DataMap[newMonsterID].MonsterLevel; npcObjectDic[npcEditorInfo] = obj; DestroyImmediate(npcObj); DebugUtil.Log( $"从[ {npcEditorInfo.npcInfo.position} ]的怪物ID改为:[ {npcEditorInfo.npcInfo.id} ]"); } } } } private void ChangeMidNpcID(NpcEditorInfo npcEditorInfo, int newMonsterID) { if (_midNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic)) { if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj)) { var newObj = LoadMidNpcPrefab(newMonsterID); if (newObj != null) { var obj = Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation); if (midObjRoot == null) midObjRoot = new GameObject("midObjRoot"); if (obj != null) obj.transform.SetParent(midObjRoot.transform); npcEditorInfo.npcInfo.id = newMonsterID; npcEditorInfo.level = MonsterCfg.DataMap[newMonsterID].MonsterLevel; npcObjectDic[npcEditorInfo] = obj; DestroyImmediate(npcObj); DebugUtil.Log( $"从[ {npcEditorInfo.npcInfo.position} ]的怪物ID改为:[ {npcEditorInfo.npcInfo.id} ]"); } } } } private void ChangeSelfNpcID(NPCInfo npcInfoInfo, int newMonsterID) { if (_selfNpcObjectDic.TryGetValue(npcInfoInfo.position, out var npcObj)) { var newObj = LoadSelfNpcPrefab(newMonsterID); if (newObj != null) { var obj = Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation); if (selfObjRoot == null) selfObjRoot = new GameObject("selfObjRoot"); if (obj != null) obj.transform.SetParent(selfObjRoot.transform); npcInfoInfo.id = newMonsterID; _selfNpcObjectDic[npcInfoInfo.position] = obj; DestroyImmediate(npcObj); DebugUtil.Log("位置上[ {0} ]的fight怪物ID改为: [ {1} ] ", npcInfoInfo.position, npcInfoInfo.id); } } } #endregion }