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

1531 lines
48 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 System;
using Gameplay;
using Framework;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Linq;
using Gameplay.Utils;
using Gameplay.Level;
using Sirenix.Utilities;
using Sirenix.OdinInspector;
using Sirenix.Utilities.Editor;
using System.Collections.Generic;
using cfg;
using cfg.FightCfg;
using cfg.MonsterCfg;
using cfg.VehicleCfg;
using Sirenix.OdinInspector.Editor;
using Level = cfg.LevelCfg.Level;
using ObjectFieldAlignment = Sirenix.OdinInspector.ObjectFieldAlignment;
public class LevelEditorWindow : OdinEditorWindow
{
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);
}
#region 编辑器类
[Serializable]
public class RegionData
{
[LabelText("出战区")] [OnValueChanged("OnRegionChanged")]
public int preparingRegionId;
[LabelText("出战区朝向")] public LevelData.CharacterToward preparingRegionToward;
[LabelText("撤退区")] public int fallbackRegionId;
[LabelText("敌方撤退区")] public int enemyFallbackRegionId;
[LabelText("PVP防守区")] public int pvpDefendRegionId;
[LabelText("PVP防守方朝向")] public LevelData.CharacterToward pvpDefendToward;
private void OnRegionChanged()
{
var regionsList = currWindow._curMap.MapData.regions;
for (int i = 0; i < regionsList.Count; i++)
{
currWindow._curMap.SetRegionColor(i, false, RegionColor);
}
currWindow._curMap.SetRegionColor(preparingRegionId, true, RegionColor);
}
}
[Serializable]
public class NpcEditorInfo
{
[HideInInspector] public bool selfNpc;
[ReadOnly] [LabelText("序号ID")] public int id;
[LabelText("MonsterID")] [ValueDropdown("GetMonsterIDs")] [OnValueChanged("ChangeNpcID")]
public int monsterId;
private IEnumerable<int> GetMonsterIDs()
{
if (!selfNpc)
return currWindow.GetMonsterIDs();
return currWindow.GetSelfNpcIDs();
}
private void ChangeNpcID()
{
if (!selfNpc)
currWindow.ChangeNpcID(npcInfo, monsterId);
else
currWindow.ChangeFightNpcID(npcInfo, monsterId);
}
[LabelText("朝向")] [OnValueChanged("NpcTowardOnChange")]
public LevelData.CharacterToward npcToward;
private void NpcTowardOnChange()
{
npcInfo.npcToward = npcToward;
if (!selfNpc)
currWindow.ChangeNpcToward(npcInfo.position, npcToward);
else
currWindow.ChangeSelfNpcToward(npcInfo.position, npcToward);
}
[HideLabel] public LevelData.NPCInfo npcInfo;
public NpcEditorInfo(LevelData.NPCInfo npcInfo, int index, bool isSelfNpc = false)
{
this.npcInfo = npcInfo;
monsterId = npcInfo.id;
id = index;
selfNpc = isSelfNpc;
npcToward = this.npcInfo.npcToward;
}
[LabelText("位置调整")] [OnValueChanged("ChangeNpcPos")]
public bool isChangePos;
private void ChangeNpcPos()
{
if (isChangePos)
{
currWindow.isEditingPatrolInfo = false;
if (currWindow._currEditingNpcInfo != null)
DebugUtil.LogWarning("检查其他怪物是否处于位置调整或编辑巡逻信息状态");
}
currWindow.isEditingNpcPos = isChangePos;
currWindow._currEditingNpcInfo = isChangePos ? npcInfo : null;
}
[LabelText("显示该怪物序号")] [OnValueChanged("ShowNpcID")]
public bool isShowNpcID;
private void ShowNpcID()
{
if (isShowNpcID && !currWindow._showNpcInfos.Contains(this))
currWindow._showNpcInfos.Add(this);
else
{
currWindow._showNpcInfos.Remove(this);
}
}
private bool _isEditing;
[Button("开始编辑巡逻信息")]
[HideIf("_isEditing")]
private void StartEditNpc()
{
currWindow.StartEditPatrolInfo(npcInfo);
_isEditing = true;
}
[Button("完成编辑巡逻信息")]
[ShowIf("_isEditing")]
private void FinishEditNpc()
{
currWindow.FinishEditPatrolInfo();
_isEditing = false;
}
}
[Serializable]
public class CaptureEditorInfo
{
[LabelText("占领点位")] public Vector2Int capturePoint;
[LabelText("占领时间")] public int captureProgress;
}
[Serializable]
public class FlagEditorInfo
{
[LabelText("扛旗起点")] public List<Vector2Int> flagStartPoints = new();
[LabelText("扛旗终点")] public List<Vector2Int> flagEndPoints = new();
[LabelText("添加起点")] [OnValueChanged("EditingStartPoints")]
public bool isEditingStart;
[LabelText("添加终点")] [OnValueChanged("EditingEndPoints")]
public bool isEditingEnd;
private void EditingStartPoints()
{
currWindow.isEditingFlagStart = isEditingStart;
if (isEditingStart)
{
isEditingEnd = false;
currWindow.isEditingFlagEnd = false;
}
}
private void EditingEndPoints()
{
currWindow.isEditingFlagEnd = isEditingEnd;
if (isEditingEnd)
{
isEditingStart = false;
currWindow.isEditingFlagStart = false;
}
}
}
[Serializable]
public class FallbackEditorInfo
{
[LabelText("单次撤离点")] public List<Vector2Int> fallbackSinglePoints = new();
[LabelText("复用撤离点")] public List<Vector2Int> fallbackReusablePoints = new();
[LabelText("添加单次撤离点")] [OnValueChanged("EditingFallbackPoints")]
public bool isEditingSinglePoints;
[LabelText("添加复用撤离点")] [OnValueChanged("EditingMultiplexPoint")]
public bool isEditingReusablePoints;
private void EditingFallbackPoints()
{
currWindow.isEditingFallbackSingle = isEditingSinglePoints;
if (isEditingSinglePoints)
{
isEditingReusablePoints = false;
currWindow.isEditingFallbackReusable = false;
}
}
private void EditingMultiplexPoint()
{
currWindow.isEditingFallbackReusable = isEditingReusablePoints;
if (isEditingReusablePoints)
{
isEditingSinglePoints = false;
currWindow.isEditingFallbackSingle = false;
}
}
}
#endregion
#region 配置表
private Level LevelCfg => EditorTableManager.instance.tables.Level;
private Monster MonsterCfg => EditorTableManager.instance.tables.Monster;
private VehicleConfig VehicleCfg => EditorTableManager.instance.tables.VehicleConfig;
private FightNPCConfig FightNpcCfg => EditorTableManager.instance.tables.FightNPCConfig;
#endregion
private const int SelfID = 200;
private static readonly Color RegionColor = MapUtils.GetRGBAColor("FF960072");
private static LevelEditorWindow currWindow;
private MapManager _mapManager;
private Dictionary<Vector2Int, GameObject> _npcObjectDic;
private Dictionary<Vector2Int, GameObject> _selfNpcObjectDic;
private Map _curMap;
private LevelEditor _levelEditor;
private LevelData.NPCInfo _currEditingNpcInfo;
private bool isEditingPatrolInfo;
private bool isEditingNpcPos;
private bool isEditingFlagStart;
private bool isEditingFlagEnd;
private bool isEditingFallbackSingle;
private bool isEditingFallbackReusable;
private bool IsShowNpcID => _showNpcInfos != null && _showNpcInfos.Count > 0;
private List<NpcEditorInfo> _showNpcInfos;
private bool ResponseClickOnCell =>
isEditingNpc || isEditingSelfNpc || isEditingNpcPos || isEditingPatrolInfo || isEditingFlagStart ||
isEditingFlagEnd || isEditingCapture ||
isEditingFallbackReusable || isEditingFallbackSingle;
[LabelText("当前关卡数据"), Indent] [OnValueChanged("OnCurrentLevelDataChanged")]
public LevelData currentLevelData = null;
[LabelText("当前占领数据"), Indent] [ShowIf("EditorCaptureInfo")]
public List<CaptureEditorInfo> captureEditorInfo = new();
[LabelText("编辑占领数据"), Indent] [ShowIf("EditorCaptureInfo")]
public bool isEditingCapture = false;
private bool EditorCaptureInfo()
{
return CheckConditionSame(ConditionType.CombatLeastOccupy);
}
[LabelText("当前扛旗数据"), Indent] [ShowIf("EditorFlagInfo")]
public FlagEditorInfo flagEditorInfo = new();
private bool EditorFlagInfo()
{
return CheckConditionSame(ConditionType.CombatSubmitFlag);
}
[LabelText("当前撤离数据"), Indent] [ShowIf("EditorFallbackInfo")]
public FallbackEditorInfo fallbackEditorInfo = new();
private bool EditorFallbackInfo()
{
return CheckConditionSame(ConditionType.CombatFallBack);
}
[LabelText("当前地区数据"), Indent] [OnValueChanged("OnCurrentLevelDataChanged")]
public RegionData curRegionData = null;
[LabelText("显示索引地图"), Indent] [LabelWidth(100)] [OnValueChanged("ShowCellCoordinates")] [SerializeField]
public bool isShowCellCoordinates;
private void ShowCellCoordinates()
{
if (_curMap == null || _curMap.TerrainGridSystem == null) return;
_curMap.TerrainGridSystem.displayCellDebugInfo =
isShowCellCoordinates ? CellDebugInfo.CellCoordinates : CellDebugInfo.Nothing;
var obj = FindObjectOfType<TerrainGridSystem>().gameObject;
Selection.activeGameObject = obj;
}
/// <summary>
/// 关卡ID 通过关卡数据 json文件名 获得
/// </summary>
[LabelText("手动指定关卡ID"), Indent, LabelWidth(100), ShowIf("needManualInputLevelID")]
[OnValueChanged("GetLevelMonsterClass")]
[InfoBox("无法正确获得关卡ID请手动指定")]
[SerializeField]
public string levelID;
#region 敌方Npc
[LabelText("编辑NPC"), Indent] [LabelWidth(100)] [SerializeField] [OnValueChanged("OnIsEditingNpcChanged")]
public bool isEditingNpc;
[LabelText("显示所有怪物序号ID"), Indent] [OnValueChanged("ShowAllNpcId")] [SerializeField]
public bool isShowAllNpcID;
[LabelText("怪物ID"), Indent, LabelWidth(100)]
[SerializeField]
[OnValueChanged("MonsterIDChanged")]
[ValueDropdown("GetMonsterIDs")]
[ShowIf("isEditingNpc")]
public int curMonsterID;
[PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly] [LabelText("NPC预制体预览图"), ShowIf("isEditingNpc")]
public GameObject npcObject;
[LabelText("当前怪物列表"), Indent, ShowIf("isEditingNpc")]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true)]
public List<NpcEditorInfo> currentNpcInfo = null;
private IEnumerable<int> GetMonsterIDs()
{
return monsterIDs;
}
private void MonsterIDChanged()
{
var obj = LoadNpcPrefab(curMonsterID);
if (obj != null)
npcObject = obj;
}
private void OnIsEditingNpcChanged()
{
if (isEditingNpc && isEditingSelfNpc)
isEditingSelfNpc = false;
}
private void ShowAllNpcId()
{
foreach (var npc in currentNpcInfo)
{
npc.isShowNpcID = isShowAllNpcID;
if (isShowAllNpcID)
{
if (!_showNpcInfos.Contains(npc))
{
_showNpcInfos.Add(npc);
}
}
else
{
_showNpcInfos.Remove(npc);
}
}
}
#endregion
#region 己方Npc
[LabelText("编辑己方NPC"), Indent] [LabelWidth(100)] [OnValueChanged("OnIsEditingSelfNpcChanged")] [SerializeField]
public bool isEditingSelfNpc;
[LabelText("显示所有己方序号ID"), Indent] [OnValueChanged("ShowAllSelfNpcId")] [SerializeField]
public bool isShowAllSelfNpcID;
[LabelText("己方NpcID"), Indent, LabelWidth(100)]
[SerializeField]
[OnValueChanged("SelfNpcIDChanged")]
[ValueDropdown("GetSelfNpcIDs")]
[ShowIf("isEditingSelfNpc")]
public int curSelfNpcID;
[PreviewField(150, ObjectFieldAlignment.Center), Indent, ReadOnly]
[LabelText("己方NPC预制体预览图"), ShowIf("isEditingSelfNpc")]
public GameObject selfNpcObject;
[LabelText("当前己方NPC列表"), Indent, ShowIf("isEditingSelfNpc")]
[ListDrawerSettings(HideAddButton = true, HideRemoveButton = true)]
public List<NpcEditorInfo> currentSelfNpcInfo = null;
private IEnumerable<int> GetSelfNpcIDs()
{
return selfMonsterIDs;
}
private void SelfNpcIDChanged()
{
var obj = LoadSelfNpcPrefab(curSelfNpcID);
if (obj != null)
selfNpcObject = obj;
}
private void OnIsEditingSelfNpcChanged()
{
if (isEditingNpc && isEditingSelfNpc)
isEditingNpc = 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 保存
[Button("保存当前关卡"), Indent]
private void SaveCurrentLevel()
{
CheckCorrectLevel();
// 地区
currentLevelData.preparingRegionId = curRegionData.preparingRegionId;
currentLevelData.fallbackRegionId = curRegionData.fallbackRegionId;
currentLevelData.enemyFallbackRegionId = curRegionData.enemyFallbackRegionId;
currentLevelData.preparingRegionToward = curRegionData.preparingRegionToward;
currentLevelData.pvpDefendRegionId = curRegionData.pvpDefendRegionId;
currentLevelData.pvpDefendToward = curRegionData.pvpDefendToward;
// NPC
foreach (var npcEditor in currentNpcInfo)
{
npcEditor.npcInfo.index = npcEditor.id;
}
currentLevelData.npcInfos = currentNpcInfo.Select(editorNpcInfo => editorNpcInfo.npcInfo).ToList();
// 己方NPC
foreach (var npcFightEditorInfo in currentSelfNpcInfo)
{
npcFightEditorInfo.npcInfo.index = npcFightEditorInfo.id;
}
currentLevelData.selfNpcInfos =
currentSelfNpcInfo.Select(editorFightNpcInfo => editorFightNpcInfo.npcInfo).ToList();
// 条件
ProcessCondition();
// 占领数据
currentLevelData.CapturePoints.Clear();
foreach (var captureInfo in captureEditorInfo)
{
var capture = new LevelData.CaptureInfo
{
CapturePoint = _curMap.BlockPosition2TGSCellIndex(captureInfo.capturePoint),
CaptureProgress = captureInfo.captureProgress,
};
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.fallbackSinglePoints.Clear();
currentLevelData.fallbackReusablePoints.Clear();
foreach (var fallback in fallbackEditorInfo.fallbackSinglePoints)
{
var cellIndex = _curMap.BlockPosition2TGSCellIndex(fallback);
currentLevelData.fallbackSinglePoints.Add(cellIndex);
}
foreach (var fallback in fallbackEditorInfo.fallbackReusablePoints)
{
var cellIndex = _curMap.BlockPosition2TGSCellIndex(fallback);
currentLevelData.fallbackReusablePoints.Add(cellIndex);
}
_levelEditor.SaveLevelData();
}
private void ProcessCondition()
{
ProcessConditionForList(currentLevelData.stages.Select(stage => stage.successCondition));
ProcessConditionForList(currentLevelData.starTarget);
ProcessConditionForList(currentLevelData.scoreTarget);
}
private void ProcessConditionForList(IEnumerable<ConditionModifier> conditionModifiers)
{
foreach (var conditionModifier in conditionModifiers)
{
if (conditionModifier.conditionType == ConditionType.CombatKillEnemy)
{
conditionModifier.conditionArgs.Clear();
foreach (var enemy in conditionModifier.killEnemyIDList)
{
conditionModifier.conditionArgs.Add(new Param("", enemy, ""));
}
}
}
foreach (var conditionModifier in conditionModifiers)
{
if (conditionModifier.conditionType == ConditionType.CombatFallBack &&
conditionModifier.conditionArgs.Count > 0)
{
currentLevelData.fallBackCount = (int)conditionModifier.conditionArgs[0].Arg;
}
}
}
#endregion
/// <summary>
/// 该关卡的怪物ID
/// </summary>
[HideInInspector] public List<int> monsterIDs;
/// <summary>
/// 该关卡的怪物ID
/// </summary>
[HideInInspector] public List<int> selfMonsterIDs;
/// <summary>
/// 该关卡的剧怪物ID
/// </summary>
[HideInInspector] public List<int> fightMonsterIDs;
/// <summary>
/// 需要手动输入关卡ID
/// </summary>
[HideInInspector] public bool needManualInputLevelID;
public void GetLevelMonsterClass()
{
if (!int.TryParse(levelID, out var levelIndex) ||
!LevelCfg.DataMap.TryGetValue(levelIndex, out var level))
{
needManualInputLevelID = true;
DebugUtil.LogError("无对应的关卡ID请手动输入关卡ID");
curMonsterID = 0;
npcObject = null;
return;
}
monsterIDs = new List<int>();
selfMonsterIDs = new List<int>();
foreach (var monster in MonsterCfg.DataMap)
{
var strID = monster.Key.ToString();
var tempID = strID.Substring(0, 6);
//关卡ID相同
if (tempID.Equals(levelID))
{
switch (monster.Value.NpcType)
{
case 2:
selfMonsterIDs.Add(monster.Key);
break;
case 3:
selfMonsterIDs.Add(monster.Key);
break;
default:
monsterIDs.Add(monster.Key);
break;
}
}
}
if (monsterIDs.Count <= 0)
{
DebugUtil.LogError("请检查Monster表没有{0}关卡的怪物数据", levelID);
}
}
#region 加载
private void LoadLevel(LevelData levelData)
{
DestroyNpcPrefab();
_npcObjectDic.Clear();
_showNpcInfos.Clear();
_selfNpcObjectDic.Clear();
currentLevelData = levelData;
//加载地区信息
curRegionData = new RegionData
{
preparingRegionId = currentLevelData.preparingRegionId,
fallbackRegionId = currentLevelData.fallbackRegionId,
enemyFallbackRegionId = currentLevelData.enemyFallbackRegionId,
preparingRegionToward = currentLevelData.preparingRegionToward,
pvpDefendRegionId = currentLevelData.pvpDefendRegionId,
pvpDefendToward = currentLevelData.pvpDefendToward
};
_mapManager = FindObjectOfType<MapManager>();
_curMap = _mapManager.Map;
_curMap.TerrainGridSystem.OnMouseClickOnGrid += OnCellClick;
// 加载占领数据
captureEditorInfo = new List<CaptureEditorInfo>();
foreach (var captureInfo in currentLevelData.CapturePoints)
{
var captureEditor = new CaptureEditorInfo()
{
capturePoint = _curMap.TGSCellIndex2BlockPosition(captureInfo.CapturePoint),
captureProgress = captureInfo.CaptureProgress,
};
captureEditorInfo.Add(captureEditor);
}
// 加载扛旗数据
flagEditorInfo = new FlagEditorInfo();
foreach (var flagStartPoint in currentLevelData.flagStartPoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(flagStartPoint);
flagEditorInfo.flagStartPoints.Add(pos);
}
foreach (var flagEndPoint in currentLevelData.flagEndPoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(flagEndPoint);
flagEditorInfo.flagEndPoints.Add(pos);
}
// 加载撤离数据
fallbackEditorInfo = new FallbackEditorInfo();
foreach (var fallback in currentLevelData.fallbackSinglePoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(fallback);
fallbackEditorInfo.fallbackSinglePoints.Add(pos);
}
foreach (var fallback in currentLevelData.fallbackReusablePoints)
{
var pos = _curMap.TGSCellIndex2BlockPosition(fallback);
fallbackEditorInfo.fallbackReusablePoints.Add(pos);
}
// 加载NPC到场景中
LoadNpcInfo();
// 加载己方NPC
LoadSelfNpcInfo();
}
private void LoadNpcInfo()
{
var monsterConfigDic = MonsterCfg.DataMap;
currentNpcInfo = new();
var index = 1;
foreach (var npcInfo in currentLevelData.npcInfos)
{
if (!monsterConfigDic.TryGetValue(npcInfo.id, out var monsterClassData))
{
DebugUtil.LogError("请检查配置,没有 {0} 的怪物类ID", npcInfo.id);
continue;
}
var npcEditor = new NpcEditorInfo(npcInfo, index);
currentNpcInfo.Add(npcEditor);
npcInfo.level = monsterClassData.MonsterLevel;
var npcObj = LoadNpcPrefab(npcInfo.id);
if (npcObj != null)
{
var setPos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
var yaw = LevelUtils.SetCharaterToward(npcInfo.npcToward);
_npcObjectDic.Add(npcInfo.position, Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0)));
}
index++;
}
}
private void LoadSelfNpcInfo()
{
var monsterConfigDic = MonsterCfg.DataMap;
currentSelfNpcInfo = new();
var index = SelfID + 1;
foreach (var selfNpcInfo in currentLevelData.selfNpcInfos)
{
if (!monsterConfigDic.TryGetValue(selfNpcInfo.id, out var monsterClassData))
{
DebugUtil.LogError("请检查配置,没有 {0} 的怪物类ID, 已经删除对应", selfNpcInfo.id);
continue;
}
var npcEditor = new NpcEditorInfo(selfNpcInfo, index, true);
currentSelfNpcInfo.Add(npcEditor);
selfNpcInfo.level = monsterClassData.MonsterLevel;
var npcObj = LoadSelfNpcPrefab(selfNpcInfo.id);
if (npcObj != null)
{
var setPos = _curMap.BlockPosition2WorldPosition(selfNpcInfo.position);
var yaw = LevelUtils.SetCharaterToward(selfNpcInfo.npcToward);
_selfNpcObjectDic.Add(selfNpcInfo.position, Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0)));
}
index++;
}
}
#endregion
#region 点击添加事件
private void OnCellClick(TerrainGridSystem tgs, int cellindex, int buttonindex)
{
if (!ResponseClickOnCell) return;
var clickPosition = _curMap.TGSCellIndex2BlockPosition(cellindex);
int leftButton = 0;
int rightButton = 1;
if (buttonindex != leftButton && buttonindex != rightButton)
return;
bool opTrue = buttonindex == leftButton;
if (isEditingFlagStart)
{
EditingFlagStart(clickPosition, true, opTrue);
}
else if (isEditingFlagEnd)
{
EditingFlagStart(clickPosition, false, opTrue);
}
else if (isEditingFallbackSingle)
{
EditingFallBack(clickPosition, true, opTrue);
}
else if (isEditingFallbackReusable)
{
EditingFallBack(clickPosition, false, opTrue);
}
else if (isEditingCapture)
{
EditingCapture(clickPosition, opTrue);
}
else if (isEditingPatrolInfo)
{
if (opTrue)
AddPatrolInfo(clickPosition);
else
RemovePatrolInfo(clickPosition);
}
else if (isEditingNpcPos)
{
if (opTrue)
ChangeNpcPos(clickPosition);
}
else if (isEditingNpc)
{
EditNpc(clickPosition, opTrue);
}
else if (isEditingSelfNpc)
{
EditSelfNpc(clickPosition, opTrue);
}
}
private void EditingFallBack(Vector2Int clickPosition, bool isEditingSingle, bool isAdd)
{
if (isEditingSingle)
{
if (isAdd && !fallbackEditorInfo.fallbackSinglePoints.Contains(clickPosition))
{
DebugUtil.Log("单次撤离点添加: {0}", clickPosition);
fallbackEditorInfo.fallbackSinglePoints.Add(clickPosition);
}
else if (!isAdd && fallbackEditorInfo.fallbackSinglePoints.Contains(clickPosition))
{
DebugUtil.Log("单次撤离点移除: {0}", clickPosition);
fallbackEditorInfo.fallbackSinglePoints.Remove(clickPosition);
}
}
else
{
if (isAdd && !fallbackEditorInfo.fallbackReusablePoints.Contains(clickPosition))
{
DebugUtil.Log("复用撤离点添加: {0}", clickPosition);
fallbackEditorInfo.fallbackReusablePoints.Add(clickPosition);
}
else if (!isAdd && fallbackEditorInfo.fallbackReusablePoints.Contains(clickPosition))
{
DebugUtil.Log("复用撤离点移除: {0}", clickPosition);
fallbackEditorInfo.fallbackReusablePoints.Remove(clickPosition);
}
}
}
private void EditingFlagStart(Vector2Int clickPosition, bool isEditingStart, bool isAdd)
{
if (isEditingStart)
{
if (isAdd && !flagEditorInfo.flagStartPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗起点添加: {0}", clickPosition);
flagEditorInfo.flagStartPoints.Add(clickPosition);
}
else if (!isAdd && flagEditorInfo.flagStartPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗起点移除: {0}", clickPosition);
flagEditorInfo.flagStartPoints.Remove(clickPosition);
}
}
else
{
if (isAdd && !flagEditorInfo.flagEndPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗终点添加: {0}", clickPosition);
flagEditorInfo.flagEndPoints.Add(clickPosition);
}
else if (!isAdd && flagEditorInfo.flagEndPoints.Contains(clickPosition))
{
DebugUtil.Log("扛旗终点移除: {0}", clickPosition);
flagEditorInfo.flagEndPoints.Remove(clickPosition);
}
}
}
private void EditingCapture(Vector2Int clickPosition, bool isAdd)
{
var isExist = false;
CaptureEditorInfo existCapture = null;
foreach (var capture in captureEditorInfo)
{
if (capture.capturePoint == clickPosition)
{
isExist = true;
existCapture = capture;
}
}
if (isAdd)
{
if (!isExist)
{
DebugUtil.Log("占领点添加: {0}", clickPosition);
captureEditorInfo.Add(new CaptureEditorInfo()
{
capturePoint = clickPosition
});
}
}
else
{
if (existCapture != null)
{
DebugUtil.Log("占领点移除: {0}", clickPosition);
captureEditorInfo.Remove(existCapture);
}
}
}
private void ChangeNpcPos(Vector2Int clickPosition)
{
var oldPos = _currEditingNpcInfo.position;
if (oldPos == clickPosition) return;
var newWorldPos = _curMap.BlockPosition2WorldPosition(clickPosition);
if (_npcObjectDic.TryGetValue(oldPos, out var npcObj))
{
npcObj.transform.position = newWorldPos;
_currEditingNpcInfo.position = clickPosition;
_npcObjectDic.Remove(oldPos);
_npcObjectDic.Add(clickPosition, npcObj);
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
}
else if (_selfNpcObjectDic.TryGetValue(oldPos, out var selfNpcObj))
{
selfNpcObj.transform.position = newWorldPos;
_currEditingNpcInfo.position = clickPosition;
_selfNpcObjectDic.Remove(oldPos);
_selfNpcObjectDic.Add(clickPosition, selfNpcObj);
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
}
else
{
DebugUtil.LogError("需要更改的旧位置上没有怪物:{0}", oldPos);
}
}
private void EditNpc(Vector2Int clickPosition, bool isAdd)
{
if (isAdd)
{
AddNpc(clickPosition);
}
else
{
RemoveNpc(clickPosition);
}
}
private void AddNpc(Vector2Int clickPosition)
{
if (npcObject == null || curMonsterID == 0)
{
DebugUtil.LogError("请选择正确的NPC");
return;
}
_npcObjectDic ??= new Dictionary<Vector2Int, GameObject>();
if (_npcObjectDic.ContainsKey(clickPosition))
{
DebugUtil.LogError("该位置已有NPC存在请重新选");
return;
}
LevelData.NPCInfo npcInfo = new LevelData.NPCInfo
{
id = curMonsterID,
position = clickPosition
};
var monsterConfigDic = MonsterCfg.DataMap;
if (monsterConfigDic.TryGetValue(curMonsterID, out var monster))
{
npcInfo.level = monster.MonsterLevel;
}
else
{
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curMonsterID);
}
currentNpcInfo.Add(new NpcEditorInfo(npcInfo, currentNpcInfo.Count + 1));
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
_npcObjectDic.Add(npcInfo.position, Instantiate(npcObject, pos, npcObject.transform.rotation));
DebugUtil.Log("添加了怪物: [ {0} ] 到 [ {1} ]位置上, 其预制体为: {2}", npcInfo.id, npcInfo.position, npcObject.name);
}
private void RemoveNpc(Vector2Int clickPosition)
{
if (_npcObjectDic.TryGetValue(clickPosition, out var npcObj))
{
var npc = currentNpcInfo.FirstOrDefault(npc => npc.npcInfo.position == clickPosition);
if (npc != null)
{
currentNpcInfo.Remove(npc);
DebugUtil.Log("从[ {0} ]位置上移除了[{1}]NPC", clickPosition, npcObj.name);
DestroyImmediate(npcObj);
_npcObjectDic.Remove(clickPosition);
}
}
ReorderNpcID();
}
private void EditSelfNpc(Vector2Int clickPosition, bool isAdd)
{
if (isAdd)
{
AddSelfNpc(clickPosition);
}
else
{
RemoveSelfNpc(clickPosition);
}
}
private void AddSelfNpc(Vector2Int clickPosition)
{
if (selfNpcObject == null || curSelfNpcID == 0)
{
DebugUtil.LogError("请选择正确的己方NPC");
return;
}
_selfNpcObjectDic ??= new Dictionary<Vector2Int, GameObject>();
if (_selfNpcObjectDic.ContainsKey(clickPosition))
{
DebugUtil.LogError("该位置已有NPC存在请重新选");
return;
}
LevelData.NPCInfo npcInfo = new LevelData.NPCInfo
{
id = curSelfNpcID,
position = clickPosition
};
currentSelfNpcInfo.Add(new NpcEditorInfo(npcInfo, SelfID + currentSelfNpcInfo.Count + 1));
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
_selfNpcObjectDic.Add(npcInfo.position, Instantiate(selfNpcObject, pos, selfNpcObject.transform.rotation));
DebugUtil.Log("添加了己方Npc: [ {0} ] 到 [ {1} ]位置上, 其预制体为: {2}", npcInfo.id, npcInfo.position, selfNpcObject.name);
}
private void RemoveSelfNpc(Vector2Int clickPosition)
{
if (_selfNpcObjectDic.TryGetValue(clickPosition, out var npcObj))
{
var npc = currentSelfNpcInfo.FirstOrDefault(npcFight => npcFight.npcInfo.position == clickPosition);
if (npc != null)
{
currentSelfNpcInfo.Remove(npc);
DebugUtil.Log("从[ {0} ]位置上移除了[{1}] 己方NPC", clickPosition, npcObj.name);
DestroyImmediate(npcObj);
_selfNpcObjectDic.Remove(clickPosition);
}
}
ReorderSelfNpcID();
}
private void AddPatrolInfo(Vector2Int clickPosition)
{
_currEditingNpcInfo.patrolInfos.Add(new LevelData.PatrolInfo()
{
patrolPoint = clickPosition,
});
}
private void RemovePatrolInfo(Vector2Int clickPosition)
{
var index = _currEditingNpcInfo.patrolInfos.FindLastIndex(pInfo => pInfo.patrolPoint == clickPosition);
if (index >= 0)
_currEditingNpcInfo.patrolInfos.RemoveAt(index);
}
#endregion
#region 值改变时响应时间事件
private void OnCurrentLevelDataChanged()
{
Debug.Log($"{GetType()}.OnCurrentLevelDataChanged");
}
private void StartEditPatrolInfo(LevelData.NPCInfo npcInfo)
{
isEditingPatrolInfo = true;
_currEditingNpcInfo = npcInfo;
}
private void FinishEditPatrolInfo()
{
isEditingPatrolInfo = false;
_currEditingNpcInfo = null;
}
private void ReorderNpcID()
{
for (var i = 0; i < currentNpcInfo.Count; i++)
{
currentNpcInfo[i].id = i + 1;
}
}
private void ReorderSelfNpcID()
{
for (var i = 0; i < currentSelfNpcInfo.Count; i++)
{
currentSelfNpcInfo[i].id = SelfID + i + 1;
}
}
private void ChangeNpcID(LevelData.NPCInfo npcInfo, int newMonsterID)
{
if (_npcObjectDic.TryGetValue(npcInfo.position, out var npcObj))
{
var newObj = LoadNpcPrefab(newMonsterID);
if (newObj != null)
{
npcInfo.id = newMonsterID;
_npcObjectDic[npcInfo.position] =
Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation);
DestroyImmediate(npcObj);
DebugUtil.Log("位置上[ {0} ]的怪物ID改为: [ {1} ] ", npcInfo.position, npcInfo.id);
}
}
}
private void ChangeFightNpcID(LevelData.NPCInfo npcInfoInfo, int newMonsterID)
{
if (_selfNpcObjectDic.TryGetValue(npcInfoInfo.position, out var npcObj))
{
var newObj = LoadSelfNpcPrefab(newMonsterID);
if (newObj != null)
{
npcInfoInfo.id = newMonsterID;
_selfNpcObjectDic[npcInfoInfo.position] =
Instantiate(newObj, npcObj.transform.position, npcObj.transform.rotation);
DestroyImmediate(npcObj);
DebugUtil.Log("位置上[ {0} ]的fight怪物ID改为: [ {1} ] ", npcInfoInfo.position, npcInfoInfo.id);
}
}
}
#endregion
#region 检查关卡
private const string PreparingRegionErrorMessage = "关卡出战区设置错误,该地图没有:<{0}>号地区。\n";
private const string FallbackRegionErrorMessage = "关卡撤退区设置错误,该地图没有:<{0}>号地区。\n";
private const string EnemyFallbackRegionErrorMessage = "关卡敌方撤退区设置错误,该地图没有:<{0}>号地区。\n";
private const string FixedMessage = "已将错误区域设置为默认<0>号地区。";
private const string CaptureErrorMessage = "占领条件参数大于配置的占领区域。";
private void CheckCorrectLevel()
{
if (_curMap == null)
{
return;
}
var errorMessage = string.Empty;
if (!_curMap.MapData.IsRegionExist(curRegionData.preparingRegionId))
{
errorMessage += string.Format(PreparingRegionErrorMessage,
curRegionData.preparingRegionId);
curRegionData.preparingRegionId = 0;
}
if (!_curMap.MapData.IsRegionExist(curRegionData.fallbackRegionId))
{
errorMessage += string.Format(FallbackRegionErrorMessage,
curRegionData.fallbackRegionId);
curRegionData.fallbackRegionId = 0;
}
if (!_curMap.MapData.IsRegionExist(curRegionData.enemyFallbackRegionId))
{
errorMessage += string.Format(EnemyFallbackRegionErrorMessage,
curRegionData.enemyFallbackRegionId);
curRegionData.enemyFallbackRegionId = 0;
}
// 检查占领条件相关
if (!CheckLevelCondition())
{
CustomPopUpWindow.PopUp(CaptureErrorMessage);
}
if (!string.IsNullOrEmpty(errorMessage))
{
CustomPopUpWindow.PopUp(errorMessage + FixedMessage);
}
}
private bool CheckLevelCondition()
{
if (currentLevelData.stages.Any(stage => stage.successCondition.conditionType == ConditionType.CombatLeastOccupy
&& stage.successCondition.conditionArgs[0].Arg >
captureEditorInfo.Count))
{
return false;
}
if (currentLevelData.starTarget.Any(modifier => modifier.conditionType == ConditionType.CombatLeastOccupy
&& modifier.conditionArgs[0].Arg > captureEditorInfo.Count))
{
return false;
}
if (currentLevelData.scoreTarget.Any(modifier => modifier.conditionType == ConditionType.CombatLeastOccupy
&& modifier.conditionArgs[0].Arg > captureEditorInfo.Count))
{
return false;
}
return true;
}
#endregion
#region 内部方法
private GameObject LoadNpcPrefab(int monsterID)
{
GameObject obj = null;
var curMonsterClassID = 0;
if (MonsterCfg.DataMap.TryGetValue(monsterID, out var monster))
{
curMonsterClassID = monster.MonsterClassID;
}
else
{
return null;
}
switch (monster.NpcType)
{
case 1:
{
if (VehicleCfg.DataMap.TryGetValue(curMonsterClassID, out var vehicle))
{
var prefabPath = PathEx.GetVehicleGameModelPath(vehicle.ID);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查载具类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查载具类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
case 0:
{
var monsterClassCfg = EditorTableManager.instance.tables.MonsterClass;
if (monsterClassCfg.DataMap.TryGetValue(curMonsterClassID, out var monsterClass))
{
var prefabPath = PathEx.GetCharacterFightModelPath(monsterClass.NameId, monsterClass.WeaponId);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查怪物类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查怪物类: {0}的配置是否存在", curMonsterClassID);
}
return obj;
}
default: return null;
}
}
private GameObject LoadSelfNpcPrefab(int monsterID)
{
GameObject obj = null;
var curFightNpcID = 0;
if (MonsterCfg.DataMap.TryGetValue(monsterID, out var monster))
{
curFightNpcID = monster.MonsterClassID;
}
else
{
return null;
}
switch (monster.NpcType)
{
case 3:
{
if (VehicleCfg.DataMap.TryGetValue(curFightNpcID, out var vehicle))
{
var prefabPath = PathEx.GetVehicleGameModelPath(vehicle.ID);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查载具类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查载具类: {0}的配置是否存在", curFightNpcID);
}
return obj;
}
case 2:
{
if (FightNpcCfg.DataMap.TryGetValue(curFightNpcID, out var fightNpc))
{
var prefabPath = PathEx.GetCharacterFightModelPath(fightNpc.NameId, fightNpc.WeaponId);
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
if (obj == null)
{
DebugUtil.LogError("请检查FightNpc类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
}
}
else
{
DebugUtil.LogError("请检查FightNpc类: {0}的配置是否存在", curFightNpcID);
}
return obj;
}
default: return null;
}
}
private void DestroyNpcPrefab()
{
foreach (var npc in _npcObjectDic)
{
DestroyImmediate(npc.Value);
}
foreach (var npc in _selfNpcObjectDic)
{
DestroyImmediate(npc.Value);
}
}
private void ChangeNpcToward(Vector2Int pos, LevelData.CharacterToward npcToward)
{
if (_npcObjectDic.TryGetValue(pos, out var npcObj))
{
var yaw = LevelUtils.SetCharaterToward(npcToward);
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
}
}
private void ChangeSelfNpcToward(Vector2Int pos, LevelData.CharacterToward npcToward)
{
if (_selfNpcObjectDic.TryGetValue(pos, out var npcObj))
{
var yaw = LevelUtils.SetCharaterToward(npcToward);
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
}
}
private bool CheckConditionSame(ConditionType conditionType)
{
if (currentLevelData.stages.Any(stage => stage.successCondition.conditionType == conditionType))
{
return true;
}
if (currentLevelData.starTarget.Any(modifier => modifier.conditionType == conditionType))
{
return true;
}
if (currentLevelData.scoreTarget.Any(modifier => modifier.conditionType == conditionType))
{
return true;
}
return false;
}
#endregion
#region MainLife
private void Awake()
{
SceneView.duringSceneGui += OnSceneGUI;
}
protected override void OnEnable()
{
TerrainTypeConfig.Load();
_npcObjectDic = new Dictionary<Vector2Int, GameObject>();
_showNpcInfos = new List<NpcEditorInfo>();
_selfNpcObjectDic = new Dictionary<Vector2Int, GameObject>();
base.OnEnable();
}
protected override void OnDisable()
{
currWindow = null;
DestroyNpcPrefab();
_npcObjectDic = null;
_selfNpcObjectDic = null;
base.OnDisable();
}
protected override void OnDestroy()
{
base.OnDestroy();
if (_curMap != null)
_curMap.TerrainGridSystem.OnMouseClickOnGrid -= OnCellClick;
SaveCurrentLevel();
SceneView.duringSceneGui -= OnSceneGUI;
}
protected override void OnGUI()
{
base.OnGUI();
if (_levelEditor == null || _mapManager == null || _mapManager.Map != _curMap ||
_levelEditor.LevelData != currentLevelData)
{
Debug.LogError("数据已变更,请重新打开关卡编辑器");
Close();
}
}
#endregion
#region Draw
private void OnSceneGUI(SceneView sceneView)
{
DrawPatrolPath();
DrawNpcID();
}
private void DrawPatrolPath()
{
if (!isEditingPatrolInfo) return;
Vector3 lastWorldPos = Vector3.zero;
for (int i = 0; i < _currEditingNpcInfo.patrolInfos.Count; i++)
{
var gridPos = _currEditingNpcInfo.patrolInfos[i].patrolPoint;
var worldPos = _curMap.BlockPosition2WorldPosition(gridPos);
Handles.color = Color.blue;
Handles.DrawSolidDisc(worldPos, Vector3.up, 0.4f);
if (i > 0)
{
Handles.color = Color.yellow;
EditorUtil.DrawArrowInScene(lastWorldPos, worldPos, 0.5f);
}
//Handles.color = Color.black;
Handles.Label(worldPos, i.ToString());
lastWorldPos = worldPos;
}
}
private GUIStyle labelStyle;
private GUIStyle labelStyleSelf;
private void DrawNpcID()
{
if (!IsShowNpcID) return;
labelStyle ??= new GUIStyle
{
fontSize = 38,
fontStyle = FontStyle.Bold,
normal =
{
textColor = Color.red
}
};
labelStyleSelf ??= new GUIStyle
{
fontSize = 38,
fontStyle = FontStyle.Bold,
normal =
{
textColor = Color.green
}
};
foreach (var npc in _showNpcInfos)
{
if (_npcObjectDic.TryGetValue(npc.npcInfo.position, out var npcObj))
{
Handles.Label(npcObj.transform.position, npc.id.ToString(), labelStyle);
}
else if (_selfNpcObjectDic.TryGetValue(npc.npcInfo.position, out var selfNpcObj))
{
Handles.Label(selfNpcObj.transform.position, npc.id.ToString(), labelStyleSelf);
}
}
}
#endregion
}