1775 lines
55 KiB
C#
1775 lines
55 KiB
C#
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<LevelEditorWindow>();
|
||
window.position = GUIHelper.GetEditorWindowRect().AlignCenter(800, 600);
|
||
window.titleContent = new GUIContent("关卡编辑器");
|
||
}
|
||
|
||
public static void OpenWindow(LevelEditor levelEditor)
|
||
{
|
||
MapEditorSettings.Init();
|
||
currWindow = GetWindow<LevelEditorWindow>();
|
||
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;
|
||
|
||
private static readonly Color GroupHighlightColor = new Color(1f, 0.5f, 0f, 0.8f);
|
||
|
||
#endregion
|
||
|
||
#region 任务相关
|
||
|
||
[BoxGroup("任务列表")] [LabelText("任务列表"), Indent]
|
||
public List<TaskInfoEditor> taskInfos = new List<TaskInfoEditor>();
|
||
|
||
#endregion
|
||
|
||
#region 关卡数据
|
||
|
||
[FoldoutGroup("当前关卡数据")] [HideLabel]
|
||
public LevelData currentLevelData = null;
|
||
|
||
#endregion
|
||
|
||
#region 触发器
|
||
|
||
[FoldoutGroup("当前关卡数据")] [BoxGroup("当前关卡数据/触发器列表")] [Indent, LabelText("触发器")]
|
||
public List<LevelTriggerDataEditor> levelTriggerData = new List<LevelTriggerDataEditor>();
|
||
|
||
#endregion
|
||
|
||
#region 波次管理
|
||
|
||
[FoldoutGroup("当前关卡数据")]
|
||
[BoxGroup("当前关卡数据/波次管理")]
|
||
[LabelText("编辑波次"), Indent]
|
||
[OnValueChanged(nameof(OnIsEditingWaveChanged))]
|
||
[SerializeField]
|
||
public bool isEditingWave;
|
||
|
||
private void OnIsEditingWaveChanged()
|
||
{
|
||
if (isEditingWave)
|
||
{
|
||
// 刷新波次信息显示
|
||
RefreshWaveInfoEditors();
|
||
}
|
||
}
|
||
|
||
[FoldoutGroup("当前关卡数据")]
|
||
[BoxGroup("当前关卡数据/波次管理")]
|
||
[LabelText("波次列表(管理怪物组)"), Indent]
|
||
[ShowIf(nameof(isEditingWave))]
|
||
[InfoBox("说明:波次管理基于怪物组ID,不是NPC索引。一个怪物组只能属于一个波次。直接添加项来创建新波次,波次ID可修改。", InfoMessageType.Info)]
|
||
[ListDrawerSettings(
|
||
CustomRemoveIndexFunction = nameof(CustomRemoveWave),
|
||
DraggableItems = false,
|
||
ShowFoldout = true
|
||
)]
|
||
public List<WaveInfoEditor> waveInfoEditors = new();
|
||
|
||
private void CustomRemoveWave(int index)
|
||
{
|
||
if (index >= 0 && index < waveInfoEditors.Count)
|
||
{
|
||
var wave = waveInfoEditors[index];
|
||
|
||
// 清除该波次中所有怪物组的怪物的波次信息
|
||
if (currentEnemyNpcInfo != null)
|
||
{
|
||
foreach (var groupId in wave.enemies)
|
||
{
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
if (npc.npcInfo.groups != null && npc.npcInfo.groups.Contains(groupId))
|
||
{
|
||
npc.npcInfo.waveIndex = 0;
|
||
npc.npcInfo.isHideOnLoad = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
waveInfoEditors.RemoveAt(index);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 刷新波次信息编辑器
|
||
/// </summary>
|
||
[FoldoutGroup("当前关卡数据")]
|
||
[BoxGroup("当前关卡数据/波次管理")]
|
||
[Button("刷新至怪物属性"), Indent]
|
||
[ShowIf(nameof(isEditingWave))]
|
||
private void RefreshWaveInfoEditors()
|
||
{
|
||
if (waveInfoEditors == null)
|
||
waveInfoEditors = new List<WaveInfoEditor>();
|
||
|
||
if (currentEnemyNpcInfo == null)
|
||
return;
|
||
|
||
// 第一步:重建 _enemyNpcObjectWaveDic 字典
|
||
// 创建新的字典结构
|
||
var newWaveDic = new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
|
||
|
||
// 遍历所有NPC,根据当前的waveIndex重新组织字典
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
int oldWaveIndex = npc.npcInfo.waveIndex;
|
||
|
||
// 从旧字典中找到这个NPC的GameObject
|
||
GameObject npcObj = null;
|
||
if (_enemyNpcObjectWaveDic != null && _enemyNpcObjectWaveDic.TryGetValue(oldWaveIndex, out var oldNpcDic))
|
||
{
|
||
if (oldNpcDic.TryGetValue(npc, out npcObj))
|
||
{
|
||
// 找到了GameObject
|
||
}
|
||
}
|
||
|
||
// 如果找不到,尝试在所有波次中查找
|
||
if (npcObj == null && _enemyNpcObjectWaveDic != null)
|
||
{
|
||
foreach (var waveDic in _enemyNpcObjectWaveDic.Values)
|
||
{
|
||
if (waveDic.TryGetValue(npc, out npcObj))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 将GameObject添加到新字典中(按照即将更新的waveIndex)
|
||
if (npcObj != null)
|
||
{
|
||
// 先暂存到旧的waveIndex,后面会更新
|
||
if (!newWaveDic.ContainsKey(oldWaveIndex))
|
||
{
|
||
newWaveDic[oldWaveIndex] = new Dictionary<NpcEditorInfo, GameObject>();
|
||
}
|
||
newWaveDic[oldWaveIndex][npc] = npcObj;
|
||
}
|
||
}
|
||
|
||
// 第二步:清空所有NPC的波次信息
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
npc.npcInfo.waveIndex = 0;
|
||
npc.npcInfo.isHideOnLoad = false;
|
||
}
|
||
|
||
// 第三步:根据波次编辑器更新NPC属性
|
||
var finalWaveDic = new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
|
||
|
||
foreach (var wave in waveInfoEditors)
|
||
{
|
||
foreach (var groupId in wave.enemies)
|
||
{
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
if (npc.npcInfo.groups != null && npc.npcInfo.groups.Contains(groupId))
|
||
{
|
||
npc.npcInfo.waveIndex = wave.waveID;
|
||
npc.npcInfo.isHideOnLoad = true;
|
||
|
||
// 更新到最终字典(使用新的waveIndex)
|
||
// 从临时字典中找到GameObject
|
||
GameObject npcObj = null;
|
||
foreach (var waveDic in newWaveDic.Values)
|
||
{
|
||
if (waveDic.TryGetValue(npc, out npcObj))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (npcObj != null)
|
||
{
|
||
if (!finalWaveDic.ContainsKey(wave.waveID))
|
||
{
|
||
finalWaveDic[wave.waveID] = new Dictionary<NpcEditorInfo, GameObject>();
|
||
}
|
||
finalWaveDic[wave.waveID][npc] = npcObj;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 第四步:处理不在任何波次中的NPC(waveIndex = 0)
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
if (npc.npcInfo.waveIndex == 0)
|
||
{
|
||
// 从临时字典中找到GameObject
|
||
GameObject npcObj = null;
|
||
foreach (var waveDic in newWaveDic.Values)
|
||
{
|
||
if (waveDic.TryGetValue(npc, out npcObj))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (npcObj != null)
|
||
{
|
||
if (!finalWaveDic.ContainsKey(0))
|
||
{
|
||
finalWaveDic[0] = new Dictionary<NpcEditorInfo, GameObject>();
|
||
}
|
||
finalWaveDic[0][npc] = npcObj;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 第五步:替换原字典
|
||
_enemyNpcObjectWaveDic = finalWaveDic;
|
||
|
||
DebugUtil.Log("已刷新波次信息至怪物属性,并更新了预制体字典");
|
||
}
|
||
|
||
#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<ScoreTaskEditor> scoreTasks = new List<ScoreTaskEditor>();
|
||
|
||
#endregion
|
||
|
||
#region 关卡引导
|
||
|
||
[FoldoutGroup("关卡引导")] [BoxGroup("关卡引导/关卡进度")] [LabelText("关卡进度"), Indent]
|
||
public List<ProgressGuideDataEditor> progressGuideDatas = new();
|
||
|
||
[FoldoutGroup("关卡引导")] [BoxGroup("关卡引导/立标数据")] [LabelText("立标数据"), Indent]
|
||
public List<SetFlagGuideDataEditor> setFlagEditorInfos = new();
|
||
|
||
[FoldoutGroup("关卡引导")] [BoxGroup("关卡引导/信息板数据")] [LabelText("信息板数据"), Indent]
|
||
public List<InfoPanelGuideDataEditor> infoPanelGuideDatas = new();
|
||
|
||
#endregion
|
||
|
||
#region 关卡AI广播
|
||
|
||
[FoldoutGroup("关卡AI广播")] [BoxGroup("关卡AI广播/数据")] [LabelText("AI广播数据"), Indent]
|
||
public List<AIBroadcastDataEditor> listAIBroadcastData = new();
|
||
|
||
#endregion
|
||
|
||
#region 地区相关
|
||
|
||
/// <summary>
|
||
/// 每种地区对应的显示颜色
|
||
/// </summary>
|
||
private static readonly Dictionary<ShowRegionType, Color> ShowRegionTypeColors =
|
||
new Dictionary<ShowRegionType, Color>
|
||
{
|
||
{ ShowRegionType.CaptureRegion, CaptureRegionColor },
|
||
{ ShowRegionType.RetreatRegion, RetreatRegionColor },
|
||
{ ShowRegionType.EnemyRetreatRegion, Color.green },
|
||
{ ShowRegionType.MinesRegion, Color.black },
|
||
{ ShowRegionType.DefenseRegion, Color.magenta },
|
||
{ ShowRegionType.BlockRegion, Color.blue },
|
||
};
|
||
|
||
[FoldoutGroup("地区相关")] [LabelText("显示地区颜色")] [EnumToggleButtons] [OnValueChanged(nameof(OnShowRegionTypeChanged))]
|
||
public ShowRegionType showRegionType;
|
||
|
||
/// <summary>
|
||
/// 根据当前选择的地区类型集合,高亮对应的地区格子,每种地区使用独立颜色。
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按地区类型获取需要高亮的格子索引列表
|
||
/// </summary>
|
||
private List<int> GetRegionPoints(ShowRegionType type)
|
||
{
|
||
var points = new HashSet<int>();
|
||
|
||
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.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> 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<CaptureEditorInfo> captureEditorInfos = new();
|
||
|
||
#endregion
|
||
|
||
#region 雷达
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[BoxGroup("地区相关/编辑雷达信息")]
|
||
[HorizontalGroup("地区相关/编辑雷达信息/radar")]
|
||
[LabelText("编辑雷达信息"), Indent]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
private bool canEditorRadarInfo;
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[VerticalGroup("地区相关/编辑雷达信息/radarInfo"), Indent]
|
||
[LabelText("雷达列表")]
|
||
[ShowIf("canEditorRadarInfo")]
|
||
public List<RadarEditorInfo> radarInfoList = new();
|
||
|
||
#endregion
|
||
|
||
#region 雷区 点击
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[BoxGroup("地区相关/雷区信息")]
|
||
[HorizontalGroup("地区相关/雷区信息/Region")]
|
||
[LabelText("编辑雷区"), Indent]
|
||
[OnValueChanged("OnCanEditorMinesAreaTypeChanged")]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
private bool canEditorMinesArea;
|
||
|
||
private void OnCanEditorMinesAreaTypeChanged()
|
||
{
|
||
if (canEditorMinesArea)
|
||
{
|
||
CloseBlockRegion();
|
||
CloseMonsterTrigger();
|
||
CloseHoldFlag();
|
||
CloseSearchLight();
|
||
}
|
||
else
|
||
{
|
||
CloseMinesRegion();
|
||
}
|
||
SceneView.RepaintAll();
|
||
}
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[VerticalGroup("地区相关/雷区信息/RegionInfo"), Indent]
|
||
[LabelText("雷区列表")]
|
||
[ShowIf("canEditorMinesArea")]
|
||
[ListDrawerSettings(CustomAddFunction = "CustomAddMinesRegion",
|
||
CustomRemoveIndexFunction = "CustomRemoveMinesRegion",
|
||
DraggableItems = false)]
|
||
public List<MinesRegionArea> 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)
|
||
{
|
||
CloseMinesRegion();
|
||
CloseMinesRegion();
|
||
CloseMonsterTrigger();
|
||
CloseHoldFlag();
|
||
}
|
||
else
|
||
{
|
||
CloseBlockRegion();
|
||
}
|
||
SceneView.RepaintAll();
|
||
}
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[LabelText("阻挡区列表")]
|
||
[ShowIf("canEditorBlockArea")]
|
||
[VerticalGroup("地区相关/阻挡区信息/RegionInfo"), Indent]
|
||
[ListDrawerSettings(CustomAddFunction = "CustomAddBlockRegion",
|
||
CustomRemoveIndexFunction = "CustomRemoveBlockRegionIndex",
|
||
DraggableItems = false)]
|
||
public List<BlockRegionArea> 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("OnCanEditorMonsterTriggerChanged")]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
private bool canEditorMonsterTrigger;
|
||
|
||
private void OnCanEditorMonsterTriggerChanged()
|
||
{
|
||
if (canEditorMonsterTrigger)
|
||
{
|
||
CloseBlockRegion();
|
||
CloseMinesRegion();
|
||
CloseSearchLight();
|
||
CloseHoldFlag();
|
||
}
|
||
else
|
||
{
|
||
CloseMonsterTrigger();
|
||
}
|
||
SceneView.RepaintAll();
|
||
}
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[LabelText("刷怪触发区列表")]
|
||
[ShowIf("canEditorMonsterTrigger")]
|
||
[VerticalGroup("地区相关/刷怪触发区/RegionInfo"), Indent]
|
||
[ListDrawerSettings(CustomAddFunction = "CustomAddDefenseRegion",
|
||
CustomRemoveIndexFunction = "CustomRemoveDefenseRegion",
|
||
DraggableItems = false)]
|
||
public List<MonsterTriggerEditorInfo> defenseRegionList = new();
|
||
|
||
private void CustomAddDefenseRegion()
|
||
{
|
||
var region = new LevelData.Region();
|
||
currentLevelData.defenseRegions.Add(region);
|
||
UpdateMonsterTrigger();
|
||
}
|
||
|
||
private void CustomRemoveDefenseRegion(int index)
|
||
{
|
||
ClearMonsterTriggerColor();
|
||
currentLevelData.defenseRegions.RemoveAt(index);
|
||
UpdateMonsterTrigger();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 战旗 点击
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[BoxGroup("地区相关/战旗")]
|
||
[HorizontalGroup("地区相关/战旗/Region")]
|
||
[LabelText("编辑战旗信息"), Indent]
|
||
[OnValueChanged("OnCanEditorHoldFlagAreaChanged")]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
private bool canEditorHoldFlagArea;
|
||
|
||
private void OnCanEditorHoldFlagAreaChanged()
|
||
{
|
||
if (canEditorHoldFlagArea)
|
||
{
|
||
CloseBlockRegion();
|
||
CloseMonsterTrigger();
|
||
CloseMinesRegion();
|
||
CloseSearchLight();
|
||
}
|
||
else
|
||
{
|
||
CloseHoldFlag();
|
||
}
|
||
|
||
SceneView.RepaintAll();
|
||
}
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[LabelText("战旗争夺区列表")]
|
||
[ShowIf("canEditorHoldFlagArea")]
|
||
[VerticalGroup("地区相关/战旗/RegionInfo"), Indent]
|
||
[ListDrawerSettings(CustomAddFunction = "CustomAddHoldFlag",
|
||
CustomRemoveIndexFunction = "CustomRemoveHoldFlag",
|
||
DraggableItems = false)]
|
||
public List<HoldFlagEditorInfo> holdFlagEditorList = new();
|
||
|
||
private void CustomAddHoldFlag()
|
||
{
|
||
var holdFlag = new HoldFlagData();
|
||
currentLevelData.holdGFlagInfos.Add(holdFlag);
|
||
UpdateHoldFlag();
|
||
}
|
||
|
||
private void CustomRemoveHoldFlag(int index)
|
||
{
|
||
ClearHoldFlagColor();
|
||
currentLevelData.holdGFlagInfos.RemoveAt(index);
|
||
UpdateHoldFlag();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 探照灯 点击
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[BoxGroup("地区相关/探照灯")]
|
||
[HorizontalGroup("地区相关/探照灯/Region")]
|
||
[LabelText("编辑探照灯"), Indent]
|
||
[OnValueChanged("OnCanEditorSearchLightChanged")]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
private bool canEditorSearchLight;
|
||
|
||
private void OnCanEditorSearchLightChanged()
|
||
{
|
||
if (canEditorSearchLight)
|
||
{
|
||
CloseBlockRegion();
|
||
CloseMonsterTrigger();
|
||
CloseMinesRegion();
|
||
CloseHoldFlag();
|
||
}
|
||
else
|
||
{
|
||
CloseSearchLight();
|
||
}
|
||
SceneView.RepaintAll();
|
||
}
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[VerticalGroup("地区相关/探照灯/RegionInfo"), Indent]
|
||
[LabelText("探照灯列表")]
|
||
[ShowIf("canEditorSearchLight")]
|
||
[ListDrawerSettings(CustomAddFunction = "CustomAddSearchLight",
|
||
CustomRemoveIndexFunction = "CustomRemoveSearchLight",
|
||
DraggableItems = false)]
|
||
public List<SearchLightEditorInfo> searchLightList = new();
|
||
|
||
private void CustomAddSearchLight()
|
||
{
|
||
var searchLight = new LevelData.SearchLightInfo();
|
||
currentLevelData.searchLightInfos.Add(searchLight);
|
||
UpdateSearchLightInfo();
|
||
}
|
||
|
||
private void CustomRemoveSearchLight(int index)
|
||
{
|
||
ClearSearchLightColor();
|
||
currentLevelData.searchLightInfos.RemoveAt(index);
|
||
UpdateSearchLightInfo();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 轰炸区
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[BoxGroup("地区相关/编辑轰炸区")]
|
||
[HorizontalGroup("地区相关/编辑轰炸区/bomb")]
|
||
[LabelText("编辑编辑轰炸区"), Indent]
|
||
[LabelWidth(100)]
|
||
[SerializeField]
|
||
private bool canEditorBombAreaInfo;
|
||
|
||
[FoldoutGroup("地区相关")]
|
||
[VerticalGroup("地区相关/编辑轰炸区/bombInfo"), Indent]
|
||
[LabelText("轰炸区列表")]
|
||
[ShowIf("canEditorBombAreaInfo")]
|
||
public List<BombAreaInfoEditor> bombAreaInfoList = new();
|
||
|
||
#endregion
|
||
|
||
#endregion
|
||
|
||
#region 指定路线
|
||
|
||
[BoxGroup("制定路线")] [LabelText("编辑指定路径"), Indent]
|
||
public bool isEditingRoad;
|
||
|
||
[BoxGroup("制定路线")] [LabelText("编辑指定路线"), Indent] [ShowIf("isEditingRoad")]
|
||
public List<PrescribedRoadEditorInfo> roadEditorInfos = new();
|
||
|
||
#endregion
|
||
|
||
#region 指定区域
|
||
|
||
[BoxGroup("巡逻区域")] [LabelText("编辑巡逻区域"), Indent]
|
||
public bool isEditingArea;
|
||
|
||
[BoxGroup("巡逻区域")] [LabelText("编辑巡逻区域"), Indent] [ShowIf("isEditingArea")]
|
||
public List<PrescribedAreaEditorInfo> 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<TerrainGridSystem>().gameObject;
|
||
Selection.activeGameObject = obj;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 敌方Npc
|
||
|
||
private GameObject enemyObjRoot;
|
||
|
||
/// <summary>
|
||
/// 波次/怪物ID对应的NPC预制体
|
||
/// </summary>
|
||
private Dictionary<int, Dictionary<NpcEditorInfo, GameObject>> _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 string curEnemyMonsterIDStr;
|
||
|
||
private int curEnemyMonsterID;
|
||
|
||
[BoxGroup("敌方Npc")]
|
||
[PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly]
|
||
[LabelText("NPC预制体预览图"), ShowIf("isEditingEnemyNpc")]
|
||
public GameObject enemyNpcObject;
|
||
|
||
/// <summary>
|
||
/// 真实敌人列表
|
||
/// </summary>
|
||
[HideInInspector] public List<NpcEditorInfo> currentEnemyNpcInfo = null;
|
||
|
||
private IEnumerable<string> GetEnemyMonsterIDs()
|
||
{
|
||
if (string.IsNullOrEmpty(selectEnemyMonsterId))
|
||
return _allMonsterIDs.Keys;
|
||
_selectMonsterIDs.Clear();
|
||
foreach (var enemyMonster in _allMonsterIDs.Keys)
|
||
{
|
||
if (enemyMonster.Contains(selectEnemyMonsterId))
|
||
_selectMonsterIDs.Add(enemyMonster);
|
||
}
|
||
|
||
return _selectMonsterIDs;
|
||
}
|
||
|
||
private void EnemyMonsterIDChanged()
|
||
{
|
||
if (!_allMonsterIDs.TryGetValue(curEnemyMonsterIDStr, out var id))
|
||
return;
|
||
curEnemyMonsterID = id;
|
||
var obj = LoadEnemyNpcPrefab(curEnemyMonsterID);
|
||
if (obj != null)
|
||
enemyNpcObject = obj;
|
||
}
|
||
|
||
private void OnIsEditingEnemyNpcChanged()
|
||
{
|
||
if (isEditingEnemyNpc)
|
||
{
|
||
isEditingSelfNpc = false;
|
||
isEditingMidNpc = false;
|
||
return;
|
||
}
|
||
|
||
foreach (var npcEditorInfo in currentEnemyNpcInfo)
|
||
{
|
||
npcEditorInfo.isChangePos = false;
|
||
}
|
||
|
||
isEditingNpcPos = false;
|
||
currWindow._currNpcEditorInfo = null;
|
||
currWindow._currEditingNpcInfo = null;
|
||
|
||
// 清除所有组的高亮显示
|
||
ClearAllGroupHighlights();
|
||
}
|
||
|
||
private void ShowAllEnemyNpcId()
|
||
{
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
npc.isShowNpcID = isShowAllEnemyNpcID;
|
||
if (isShowAllEnemyNpcID)
|
||
{
|
||
if (!_showNpcInfos.Contains(npc))
|
||
{
|
||
_showNpcInfos.Add(npc);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
_showNpcInfos.Remove(npc);
|
||
}
|
||
}
|
||
}
|
||
|
||
[BoxGroup("敌方Npc")]
|
||
[Button("保存"), ShowIf("isEditingEnemyNpc")]
|
||
private void SaveNewGroup()
|
||
{
|
||
if (enemyNpcGroups == null || enemyNpcGroups.Count == 0)
|
||
{
|
||
DebugUtil.LogWarning("没有分组需要保存");
|
||
return;
|
||
}
|
||
|
||
int changedCount = 0;
|
||
foreach (var group in enemyNpcGroups)
|
||
{
|
||
if (group != null)
|
||
{
|
||
group.ApplyGroupIdChange(false);
|
||
changedCount++;
|
||
}
|
||
}
|
||
|
||
// 所有修改完成后,统一刷新一次视图
|
||
RefreshEnemyNpcGroups();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 敌方 NPC 的分组展示数据
|
||
/// </summary>
|
||
[BoxGroup("敌方Npc")]
|
||
[LabelText("按组查看怪物"), Indent, ShowIf("isEditingEnemyNpc")]
|
||
[ShowInInspector]
|
||
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, DraggableItems = false, ShowFoldout = true)]
|
||
public List<EnemyNpcGroupView> enemyNpcGroups = new();
|
||
|
||
/// <summary>
|
||
/// 清除所有组的高亮显示
|
||
/// </summary>
|
||
private void ClearAllGroupHighlights()
|
||
{
|
||
if (enemyNpcGroups == null)
|
||
return;
|
||
|
||
foreach (var group in enemyNpcGroups)
|
||
{
|
||
if (group != null)
|
||
{
|
||
// 强制清除高亮
|
||
group.ForceClearHighlight();
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 己方Npc
|
||
|
||
private const int SelfID = 800;
|
||
|
||
private GameObject selfObjRoot;
|
||
|
||
private Dictionary<Vector2Int, GameObject> _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 string curSelfNpcIDStr;
|
||
|
||
private 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<NpcEditorInfo> currentSelfNpcInfo = null;
|
||
|
||
private IEnumerable<string> GetSelfNpcIDs()
|
||
{
|
||
if (string.IsNullOrEmpty(selectSelfMonsterId))
|
||
return _allMonsterIDs.Keys;
|
||
_selectMonsterIDs.Clear();
|
||
foreach (var enemyMonster in _allMonsterIDs.Keys)
|
||
{
|
||
if (enemyMonster.Contains(selectSelfMonsterId))
|
||
_selectMonsterIDs.Add(enemyMonster);
|
||
}
|
||
|
||
return _selectMonsterIDs;
|
||
}
|
||
|
||
private void SelfNpcIDChanged()
|
||
{
|
||
if (!_allMonsterIDs.TryGetValue(curSelfNpcIDStr, out var id))
|
||
return;
|
||
curSelfNpcID = id;
|
||
var obj = LoadSelfNpcPrefab(curSelfNpcID);
|
||
if (obj != null)
|
||
selfNpcObject = obj;
|
||
}
|
||
|
||
private void OnIsEditingSelfNpcChanged()
|
||
{
|
||
if (isEditingSelfNpc)
|
||
{
|
||
isEditingEnemyNpc = false;
|
||
isEditingMidNpc = false;
|
||
return;
|
||
}
|
||
|
||
foreach (var npcEditorInfo in currentSelfNpcInfo)
|
||
{
|
||
npcEditorInfo.isChangePos = false;
|
||
}
|
||
|
||
isEditingNpcPos = false;
|
||
currWindow._currNpcEditorInfo = null;
|
||
currWindow._currEditingNpcInfo = null;
|
||
}
|
||
|
||
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 const int MidID = 500;
|
||
|
||
private GameObject midObjRoot;
|
||
|
||
/// <summary>
|
||
/// 中立 波次/怪物ID对应的NPC预制体
|
||
/// </summary>
|
||
private Dictionary<int, Dictionary<NpcEditorInfo, GameObject>> _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 string curMidMonsterIDStr;
|
||
|
||
private 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<NpcEditorInfo> currentMidNpcInfo = null;
|
||
|
||
private IEnumerable<string> GetMidMonsterIDs()
|
||
{
|
||
if (string.IsNullOrEmpty(selectMidMonsterId))
|
||
return _allMonsterIDs.Keys;
|
||
_selectMonsterIDs.Clear();
|
||
foreach (var enemyMonster in _allMonsterIDs.Keys)
|
||
{
|
||
if (enemyMonster.Contains(selectMidMonsterId))
|
||
_selectMonsterIDs.Add(enemyMonster);
|
||
}
|
||
|
||
return _selectMonsterIDs;
|
||
}
|
||
|
||
private void MidMonsterIDChanged()
|
||
{
|
||
if (!_allMonsterIDs.TryGetValue(curMidMonsterIDStr, out var id))
|
||
return;
|
||
curMidMonsterID = id;
|
||
var obj = LoadMidNpcPrefab(curMidMonsterID);
|
||
if (obj != null)
|
||
midNpcObject = obj;
|
||
}
|
||
|
||
private void OnIsEditingMidNpcChanged()
|
||
{
|
||
if (isEditingMidNpc)
|
||
{
|
||
isEditingEnemyNpc = false;
|
||
isEditingSelfNpc = false;
|
||
return;
|
||
}
|
||
|
||
foreach (var npcEditorInfo in currentMidNpcInfo)
|
||
{
|
||
npcEditorInfo.isChangePos = false;
|
||
}
|
||
|
||
isEditingNpcPos = false;
|
||
currWindow._currNpcEditorInfo = null;
|
||
currWindow._currEditingNpcInfo = null;
|
||
}
|
||
|
||
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<Renderer>();
|
||
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<BlockSpereEditorInfo> editingBlockSphereList = new();
|
||
|
||
[HideInInspector] public List<BlockSpereEditorInfo> 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();
|
||
ProcessCondition();
|
||
|
||
// 地区
|
||
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 波次信息
|
||
|
||
// 验证波次ID是否有重复且大于0
|
||
if (waveInfoEditors != null && waveInfoEditors.Count > 0)
|
||
{
|
||
var waveIdSet = new HashSet<int>();
|
||
foreach (var waveEditor in waveInfoEditors)
|
||
{
|
||
if (waveEditor.waveID <= 0)
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"波次ID必须大于0,当前有波次ID为 {waveEditor.waveID}!", "确定");
|
||
return;
|
||
}
|
||
|
||
if (!waveIdSet.Add(waveEditor.waveID))
|
||
{
|
||
EditorUtility.DisplayDialog("错误", $"波次ID {waveEditor.waveID} 重复,请修改后再保存!", "确定");
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新所有敌方NPC的波次信息(基于怪物组)
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
bool inWave = false;
|
||
|
||
if (npc.npcInfo.groups != null && npc.npcInfo.groups.Count > 0)
|
||
{
|
||
// 检查该NPC的任何一个组是否在某个波次中
|
||
foreach (var wave in waveInfoEditors)
|
||
{
|
||
foreach (var groupId in npc.npcInfo.groups)
|
||
{
|
||
if (wave.enemies.Contains(groupId))
|
||
{
|
||
npc.npcInfo.waveIndex = wave.waveID;
|
||
npc.npcInfo.isHideOnLoad = true;
|
||
inWave = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (inWave) break;
|
||
}
|
||
}
|
||
|
||
// 如果不在任何波次中,清除波次信息
|
||
if (!inWave)
|
||
{
|
||
npc.npcInfo.waveIndex = 0;
|
||
npc.npcInfo.isHideOnLoad = false;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 怪物组信息
|
||
|
||
// 保存敌方NPC的分组信息
|
||
currentLevelData.groupInfos.Clear();
|
||
if (enemyNpcGroups != null)
|
||
{
|
||
foreach (var groupView in enemyNpcGroups)
|
||
{
|
||
// 跳过"无编组怪物"组(groupId为-1)
|
||
if (groupView.groupId < 0)
|
||
continue;
|
||
|
||
var groupInfo = new GroupInfo
|
||
{
|
||
groupID = groupView.groupId,
|
||
groupName = groupView.groupName
|
||
};
|
||
currentLevelData.groupInfos.Add(groupInfo);
|
||
}
|
||
}
|
||
|
||
#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.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();
|
||
|
||
// 雷达信息
|
||
currentLevelData.radarInfos = radarInfoList.Select(r => r.radarInfo).ToList();
|
||
|
||
// 探照灯
|
||
currentLevelData.searchLightInfos = searchLightList.Select(s => s.info).ToList();
|
||
|
||
// 轰炸区
|
||
currentLevelData.bombAreaInfos = bombAreaInfoList.Select(b => b.info).ToList();
|
||
|
||
// 战旗
|
||
currentLevelData.holdGFlagInfos = holdFlagEditorList.Select(h => h.info).ToList();
|
||
|
||
// 阻挡球
|
||
SaveBlockSphereInfo();
|
||
|
||
_levelEditor.SaveLevelData();
|
||
_mapManager.SaveMapData();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 关卡ID相关
|
||
|
||
/// <summary>
|
||
/// 关卡ID 通过关卡数据 json文件名 获得
|
||
/// </summary>
|
||
[LabelText("手动指定关卡ID"), Indent, LabelWidth(100), ShowIf("needManualInputLevelID")]
|
||
[InfoBox("无法正确获得关卡ID,请手动指定")]
|
||
[SerializeField]
|
||
public string levelID;
|
||
|
||
/// <summary>
|
||
/// 需要手动输入关卡ID
|
||
/// </summary>
|
||
[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;
|
||
}
|
||
|
||
// 注意:波次存储的是怪物组ID,不是NPC ID,所以不需要更新波次信息
|
||
}
|
||
|
||
private void ReorderMidNpcID()
|
||
{
|
||
for (var i = 0; i < currentMidNpcInfo.Count; i++)
|
||
{
|
||
currentMidNpcInfo[i].id = MidID + 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
|
||
} |