NLDClient-yudde/ProjectNLD/Assets/Editor/LevelEditor/LevelEditorWindow.cs

1468 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;
#endregion
#region 任务相关
[BoxGroup("任务列表")] [LabelText("任务列表"), Indent]
public List<TaskInfoEditor> taskInfos = new List<TaskInfoEditor>();
#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<int> starTaskIDs = new List<int>() { 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<ScoreTaskEditor> scoreTasks = new List<ScoreTaskEditor>();
#endregion
#region 触发器
[FoldoutGroup("当前关卡数据")] [BoxGroup("当前关卡数据/触发器列表")] [Indent, LabelText("触发器")]
public List<LevelTriggerDataEditor> levelTriggerData = new List<LevelTriggerDataEditor>();
#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.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;
/// <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.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> 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("地区相关/扛棋相关")] [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<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)
{
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<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("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<DefenseRegionArea> 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<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 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;
/// <summary>
/// 该关卡的敌方NPCID
/// </summary>
[HideInInspector] public List<int> enemyMonsterIDs;
private IEnumerable<int> GetEnemyMonsterIDs()
{
enemyMonsterIDs ??= new List<int>();
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);
}
}
}
/// <summary>
/// 敌方 NPC 的分组展示数据
/// </summary>
[BoxGroup("敌方Npc")]
[LabelText("按组查看怪物"), Indent, ShowIf("isEditingEnemyNpc")]
[ShowInInspector]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, DraggableItems = false, ShowFoldout = true)]
public List<EnemyNpcGroupView> enemyNpcGroups = new();
#endregion
#region 己方Npc
private const int SelfID = 200;
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 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;
/// <summary>
/// 该关卡的己方NPCID
/// </summary>
[HideInInspector] public List<int> selfMonsterIDs;
private IEnumerable<int> GetSelfNpcIDs()
{
selfMonsterIDs ??= new List<int>();
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;
/// <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 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;
/// <summary>
/// 该关卡的中立NPCID
/// </summary>
[HideInInspector] public List<int> midMonsterIDs;
private IEnumerable<int> GetMidMonsterIDs()
{
midMonsterIDs ??= new List<int>();
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<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();
/*// 胜利失败星级条件
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相关
/// <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;
}
}
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
}