1550 lines
54 KiB
C#
1550 lines
54 KiB
C#
using TGS;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using System.Linq;
|
||
using Gameplay.Level;
|
||
using cfg;
|
||
using cfg.LevelCfg;
|
||
using Gameplay;
|
||
using Gameplay.Character;
|
||
using Gameplay.Utils;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using static Gameplay.Level.LevelData;
|
||
|
||
public partial class LevelEditorWindow
|
||
{
|
||
#region MainLife
|
||
|
||
private void Awake()
|
||
{
|
||
SceneView.duringSceneGui += OnSceneGUI;
|
||
}
|
||
|
||
protected override async void OnEnable()
|
||
{
|
||
await TerrainTypeConfig.Load();
|
||
_enemyNpcObjectWaveDic = new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
|
||
_midNpcObjectWaveDic = new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
|
||
_selfNpcObjectDic = new Dictionary<Vector2Int, GameObject>();
|
||
_showNpcInfos = new List<NpcEditorInfo>();
|
||
_blockPrefabDic = new Dictionary<int, GameObject>();
|
||
|
||
base.OnEnable();
|
||
}
|
||
|
||
protected override void OnDisable()
|
||
{
|
||
OnSetRetreatRegionColor();
|
||
OnCloseCaptureRegionColor();
|
||
ClearShowRegionColor();
|
||
StopMonitoringSphere();
|
||
ShowHiddenObjects();
|
||
SaveBlockSphereInfo();
|
||
DestroyBlockSphere();
|
||
|
||
currWindow = null;
|
||
DestroyNpcPrefab();
|
||
_enemyNpcObjectWaveDic = null;
|
||
_midNpcObjectWaveDic = null;
|
||
_selfNpcObjectDic = null;
|
||
_blockPrefabDic = null;
|
||
|
||
base.OnDisable();
|
||
}
|
||
|
||
protected override void OnDestroy()
|
||
{
|
||
OnSetRetreatRegionColor();
|
||
OnCloseCaptureRegionColor();
|
||
ClearShowRegionColor();
|
||
StopMonitoringSphere();
|
||
ShowHiddenObjects();
|
||
SaveBlockSphereInfo();
|
||
DestroyBlockSphere();
|
||
|
||
DestroyAllBlockObj();
|
||
ClearMinesRegionColor();
|
||
ClearBlockRegionColor();
|
||
ClearMonsterTriggerColor();
|
||
|
||
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 点击添加事件
|
||
|
||
private bool ResponseClickOnCell =>
|
||
isEditingEnemyNpc || isEditingSelfNpc || isEditingMidNpc || isEditingNpcPos || isEditingCaptureInfo ||
|
||
isEditingFallbackReusable || isEditingRetreat || isEditingRoadInfo || isEditingAreaInfo || canEditorBlockArea ||
|
||
canEditorMonsterTrigger || canEditorMinesArea || canEditorSearchLight || canEditorHoldFlagArea;
|
||
|
||
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 (isEditingRetreat)
|
||
{
|
||
EditingRetreat(cellIndex, opTrue);
|
||
}
|
||
else if (isEditingFallbackReusable)
|
||
{
|
||
EditingFallBack(clickPosition, opTrue);
|
||
}
|
||
else if (isEditingCaptureInfo)
|
||
{
|
||
EditingCapture(cellIndex, opTrue);
|
||
}
|
||
else if (isEditingNpcPos)
|
||
{
|
||
if (opTrue)
|
||
ChangeNpcPos(clickPosition, npcTroopId);
|
||
}
|
||
else if (isEditingEnemyNpc)
|
||
{
|
||
EditEnemyNpc(clickPosition, opTrue);
|
||
}
|
||
else if (isEditingSelfNpc)
|
||
{
|
||
EditSelfNpc(clickPosition, opTrue);
|
||
}
|
||
else if (isEditingMidNpc)
|
||
{
|
||
EditMidNpc(clickPosition, opTrue);
|
||
}
|
||
else if (isEditingRoadInfo && isEditingRoad)
|
||
{
|
||
EditRoad(cellIndex, opTrue);
|
||
}
|
||
else if (isEditingAreaInfo && isEditingArea)
|
||
{
|
||
EditArea(cellIndex, opTrue);
|
||
}
|
||
else if (_isEditorSingleMinesRegion && _currEditMinesRegionArea != null)
|
||
{
|
||
EditingMinesRegion(cellIndex, opTrue);
|
||
}
|
||
else if (_isEditorSingleBlockRegion && _currEditBlockRegion != null)
|
||
{
|
||
EditingBlockRegion(cellIndex, opTrue);
|
||
}
|
||
else if (_isEditorSingleDefenseRegion && _currEditDefenseRegion != null)
|
||
{
|
||
EditingDefenseRegion(cellIndex, opTrue);
|
||
}
|
||
else if (_isEditorSingleSearchLight && _currEditSearchLight != null)
|
||
{
|
||
EditingSearchLight(cellIndex, opTrue);
|
||
}
|
||
else if (_isEditorSingleHoldFlag && _currEditHoldFlagInfo != null)
|
||
{
|
||
EditingHoldFlag(cellIndex, opTrue);
|
||
}
|
||
}
|
||
|
||
private void EditingMinesRegion(int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
_currEditMinesRegionArea.AddPosition(clickPosition);
|
||
else
|
||
_currEditMinesRegionArea.RemovePosition(clickPosition);
|
||
}
|
||
|
||
private void EditingBlockRegion(int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
_currEditBlockRegion.AddPosition(clickPosition);
|
||
else
|
||
_currEditBlockRegion.RemovePosition(clickPosition);
|
||
}
|
||
|
||
private void EditingDefenseRegion(int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
_currEditDefenseRegion.AddPosition(clickPosition);
|
||
else
|
||
_currEditDefenseRegion.RemovePosition(clickPosition);
|
||
}
|
||
|
||
private void EditingSearchLight(int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
_currEditSearchLight.AddPosition(clickPosition);
|
||
else
|
||
_currEditSearchLight.RemovePosition(clickPosition);
|
||
}
|
||
|
||
private void EditingHoldFlag(int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
_currEditHoldFlagInfo.AddPosition(clickPosition);
|
||
else
|
||
_currEditHoldFlagInfo.RemovePosition(clickPosition);
|
||
}
|
||
|
||
private void EditingFallBack(Vector2Int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd && !enemyRetreatEditorInfo.fallbackReusablePoints.Contains(clickPosition))
|
||
{
|
||
DebugUtil.Log("复用撤离点添加: {0}", clickPosition);
|
||
enemyRetreatEditorInfo.fallbackReusablePoints.Add(clickPosition);
|
||
}
|
||
else if (!isAdd && enemyRetreatEditorInfo.fallbackReusablePoints.Contains(clickPosition))
|
||
{
|
||
DebugUtil.Log("复用撤离点移除: {0}", clickPosition);
|
||
enemyRetreatEditorInfo.fallbackReusablePoints.Remove(clickPosition);
|
||
}
|
||
}
|
||
|
||
private void EditingRetreat(int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd && retreatEditorInfo.All(x => x.retreatPoint != clickPosition))
|
||
{
|
||
retreatEditorInfo.Add(new RetreatEditorInfo
|
||
{
|
||
retreatPoint = clickPosition,
|
||
retreatFlag = false,
|
||
});
|
||
_curMap.TerrainGridSystem.CellToggleRegionSurface(clickPosition, true, RetreatRegionColor);
|
||
DebugUtil.Log("撤离区添加: {0}", clickPosition);
|
||
}
|
||
else if (!isAdd && retreatEditorInfo.Any(x => x.retreatPoint == clickPosition))
|
||
{
|
||
retreatEditorInfo =
|
||
retreatEditorInfo.Where(x => x.retreatPoint != clickPosition).ToList();
|
||
_curMap.TerrainGridSystem.CellHideRegionSurface(clickPosition);
|
||
DebugUtil.Log("撤离区移除: {0}", clickPosition);
|
||
}
|
||
}
|
||
|
||
private void EditingCapture(int clickPosition, bool isAdd)
|
||
{
|
||
if (_currEditingCaptureInfo == null) return;
|
||
|
||
if (isAdd && _currEditingCaptureInfo.capturePointEditorInfos.All(x => x.capturePoint != clickPosition))
|
||
{
|
||
_currEditingCaptureInfo.capturePointEditorInfos.Add(new CaptureEditorInfo.CapturePointEditorInfo
|
||
{
|
||
belongRegionID = _currEditingCaptureInfo.captureRegionID,
|
||
capturePoint = clickPosition,
|
||
captureFlag = false,
|
||
});
|
||
_curMap.TerrainGridSystem.CellToggleRegionSurface(clickPosition, true, CaptureRegionColor);
|
||
DebugUtil.Log($"占领区{_currEditingCaptureInfo.captureRegionID} 添加: {clickPosition}");
|
||
}
|
||
else if (!isAdd && _currEditingCaptureInfo.capturePointEditorInfos.Any(x => x.capturePoint == clickPosition))
|
||
{
|
||
_currEditingCaptureInfo.capturePointEditorInfos =
|
||
_currEditingCaptureInfo.capturePointEditorInfos.Where(x => x.capturePoint != clickPosition).ToList();
|
||
_curMap.TerrainGridSystem.CellHideRegionSurface(clickPosition);
|
||
DebugUtil.Log($"占领区{_currEditingCaptureInfo.captureRegionID} 移除: {clickPosition}");
|
||
}
|
||
}
|
||
|
||
private void ChangeNpcPos(Vector2Int clickPosition, int npcTroopID)
|
||
{
|
||
if (null == _currNpcEditorInfo)
|
||
return;
|
||
|
||
var oldPos = _currEditingNpcInfo.position;
|
||
if (oldPos == clickPosition)
|
||
return;
|
||
|
||
var newWorldPos = _curMap.BlockPosition2WorldPosition(clickPosition);
|
||
if (ETroopsId.Enemy == npcTroopID &&
|
||
_enemyNpcObjectWaveDic.TryGetValue(_currEditingNpcInfo.waveIndex, out var enemyNpcObjectDic))
|
||
{
|
||
if (enemyNpcObjectDic.TryGetValue(_currNpcEditorInfo, out var npcObj))
|
||
{
|
||
npcObj.transform.position = newWorldPos;
|
||
_currEditingNpcInfo.position = clickPosition;
|
||
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
|
||
}
|
||
}
|
||
else if (ETroopsId.Neutral == npcTroopID &&
|
||
_midNpcObjectWaveDic.TryGetValue(_currEditingNpcInfo.waveIndex, out var midNpcObjectDic))
|
||
{
|
||
if (midNpcObjectDic.TryGetValue(_currNpcEditorInfo, out var npcObj))
|
||
{
|
||
npcObj.transform.position = newWorldPos;
|
||
_currEditingNpcInfo.position = clickPosition;
|
||
DebugUtil.Log("怪物: [ {0} ] 移动到 [ {1} ]位置上", _currEditingNpcInfo.id, clickPosition);
|
||
}
|
||
}
|
||
else if (ETroopsId.Self == npcTroopID && _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);
|
||
}
|
||
}
|
||
|
||
#region 敌方NPC编辑方法
|
||
|
||
private void EditEnemyNpc(Vector2Int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
{
|
||
AddEnemyNpc(clickPosition);
|
||
}
|
||
else
|
||
{
|
||
if (Event.current != null && (Event.current.control || Event.current.command))
|
||
{
|
||
RemoveEnemyNpc(clickPosition);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("删除NPC需要按住Ctrl键");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void AddEnemyNpc(Vector2Int clickPosition)
|
||
{
|
||
if (enemyNpcObject == null || curEnemyMonsterID == 0)
|
||
{
|
||
DebugUtil.LogError("请选择正确的NPC!!");
|
||
return;
|
||
}
|
||
|
||
_enemyNpcObjectWaveDic ??= new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
|
||
|
||
NPCInfo npcInfo = new()
|
||
{
|
||
id = curEnemyMonsterID,
|
||
position = clickPosition,
|
||
index = currentEnemyNpcInfo.Count + 1,
|
||
troopId = ETroopsId.Enemy,
|
||
};
|
||
|
||
var level = 0;
|
||
var aiType = 0;
|
||
var monsterName = "";
|
||
var monsterConfigDic = MonsterCfg.DataMap;
|
||
if (monsterConfigDic.TryGetValue(curEnemyMonsterID, out var monster))
|
||
{
|
||
level = monster.MonsterLevel;
|
||
aiType = monster.AIType;
|
||
monsterName = monster.NameRead;
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curEnemyMonsterID);
|
||
}
|
||
|
||
var npcEditor = new NpcEditorInfo(npcInfo, currentEnemyNpcInfo.Count + 1, level, aiType, ETroopsId.Enemy,
|
||
monsterName);
|
||
currentEnemyNpcInfo.Add(npcEditor);
|
||
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
|
||
if (_enemyNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
var obj = Instantiate(enemyNpcObject, pos, enemyNpcObject.transform.rotation);
|
||
if (enemyObjRoot != null)
|
||
obj.transform.SetParent(enemyObjRoot.transform);
|
||
npcObjectDic.Add(npcEditor, obj);
|
||
}
|
||
else
|
||
{
|
||
var obj = Instantiate(enemyNpcObject, pos, enemyNpcObject.transform.rotation);
|
||
if (enemyObjRoot != null)
|
||
obj.transform.SetParent(enemyObjRoot.transform);
|
||
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
|
||
{ { npcEditor, obj } };
|
||
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
||
}
|
||
|
||
DebugUtil.Log(
|
||
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ]怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {enemyNpcObject.name}");
|
||
RefreshEnemyNpcGroups();
|
||
}
|
||
|
||
private void RemoveEnemyNpc(Vector2Int clickPosition)
|
||
{
|
||
var npc = currentEnemyNpcInfo.LastOrDefault(npc => npc.npcInfo.position == clickPosition);
|
||
if (npc != null)
|
||
{
|
||
if (_enemyNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
if (npcObjectDic.TryGetValue(npc, out var obj))
|
||
{
|
||
DebugUtil.Log(
|
||
$"从[ {clickPosition} ]位置上移除了[ {npc.npcInfo.waveIndex} ]波的[{npc.id}]NPC, 其预制体名字为:{obj.name}");
|
||
DestroyImmediate(obj);
|
||
npcObjectDic.Remove(npc);
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError($"波次{npc.npcInfo.waveIndex}中没有怪物{npc.id}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("没有波次{0}的怪物信息", npc.npcInfo.waveIndex);
|
||
}
|
||
|
||
currentEnemyNpcInfo.Remove(npc);
|
||
}
|
||
|
||
ReorderEnemyNpcID();
|
||
RefreshEnemyNpcGroups();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 已方NPC编辑方法
|
||
|
||
private void EditSelfNpc(Vector2Int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
{
|
||
AddSelfNpc(clickPosition);
|
||
}
|
||
else
|
||
{
|
||
if (Event.current != null && (Event.current.control || Event.current.command))
|
||
{
|
||
RemoveSelfNpc(clickPosition);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("删除NPC需要按住Ctrl键");
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
NPCInfo npcInfo = new()
|
||
{
|
||
id = curSelfNpcID,
|
||
position = clickPosition,
|
||
troopId = ETroopsId.Self,
|
||
};
|
||
|
||
var level = 0;
|
||
var aiType = 0;
|
||
var monsterName = "";
|
||
var monsterConfigDic = MonsterCfg.DataMap;
|
||
if (monsterConfigDic.TryGetValue(curSelfNpcID, out var monster))
|
||
{
|
||
level = monster.MonsterLevel;
|
||
aiType = monster.AIType;
|
||
monsterName = monster.NameRead;
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("获取己方Npc[{0}]配置数据错误", curSelfNpcID);
|
||
}
|
||
|
||
currentSelfNpcInfo.Add(new NpcEditorInfo(npcInfo, SelfID + currentSelfNpcInfo.Count + 1, level, aiType,
|
||
ETroopsId.Self, monsterName));
|
||
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
var obj = Instantiate(selfNpcObject, pos, selfNpcObject.transform.rotation);
|
||
if (selfObjRoot != null)
|
||
{
|
||
obj.transform.SetParent(selfObjRoot.transform);
|
||
}
|
||
|
||
_selfNpcObjectDic.Add(npcInfo.position, obj);
|
||
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();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 中立NPC编辑方法
|
||
|
||
private void EditMidNpc(Vector2Int clickPosition, bool isAdd)
|
||
{
|
||
if (isAdd)
|
||
AddMidNpc(clickPosition);
|
||
else
|
||
{
|
||
if (Event.current != null && (Event.current.control || Event.current.command))
|
||
{
|
||
RemoveMidNpc(clickPosition);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("删除NPC需要按住Ctrl键");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void AddMidNpc(Vector2Int clickPosition)
|
||
{
|
||
if (midNpcObject == null || curMidMonsterID == 0)
|
||
{
|
||
DebugUtil.LogError("请选择正确的NPC!!");
|
||
return;
|
||
}
|
||
|
||
_midNpcObjectWaveDic ??= new Dictionary<int, Dictionary<NpcEditorInfo, GameObject>>();
|
||
|
||
NPCInfo npcInfo = new()
|
||
{
|
||
id = curMidMonsterID,
|
||
position = clickPosition,
|
||
index = currentMidNpcInfo.Count + 1,
|
||
troopId = ETroopsId.Neutral,
|
||
isOpenInvestigate = false,
|
||
};
|
||
|
||
var level = 0;
|
||
var aiType = 0;
|
||
var monsterName = "";
|
||
var monsterConfigDic = MonsterCfg.DataMap;
|
||
if (monsterConfigDic.TryGetValue(curMidMonsterID, out var monster))
|
||
{
|
||
level = monster.MonsterLevel;
|
||
aiType = monster.AIType;
|
||
monsterName = monster.NameRead;
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curMidMonsterID);
|
||
}
|
||
|
||
var npcEditor = new NpcEditorInfo(npcInfo, MidID + currentMidNpcInfo.Count + 1, level, aiType, ETroopsId.Neutral,
|
||
monsterName);
|
||
currentMidNpcInfo.Add(npcEditor);
|
||
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
|
||
if (_midNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
var obj = Instantiate(midNpcObject, pos, midNpcObject.transform.rotation);
|
||
if (midObjRoot != null)
|
||
obj.transform.SetParent(midObjRoot.transform);
|
||
npcObjectDic.Add(npcEditor, obj);
|
||
}
|
||
else
|
||
{
|
||
var obj = Instantiate(midNpcObject, pos, midNpcObject.transform.rotation);
|
||
if (midObjRoot != null)
|
||
obj.transform.SetParent(midObjRoot.transform);
|
||
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
|
||
{ { npcEditor, obj } };
|
||
_midNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
||
}
|
||
|
||
DebugUtil.Log(
|
||
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ] 中立怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {midNpcObject.name}");
|
||
}
|
||
|
||
private void RemoveMidNpc(Vector2Int clickPosition)
|
||
{
|
||
var npc = currentMidNpcInfo.LastOrDefault(npc => npc.npcInfo.position == clickPosition);
|
||
if (npc != null)
|
||
{
|
||
if (_midNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
if (npcObjectDic.TryGetValue(npc, out var obj))
|
||
{
|
||
DebugUtil.Log(
|
||
$"从[ {clickPosition} ]位置上移除了[ {npc.npcInfo.waveIndex} ]波的[{npc.id}] 中立NPC, 其预制体名字为:{obj.name}");
|
||
DestroyImmediate(obj);
|
||
npcObjectDic.Remove(npc);
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError($"波次{npc.npcInfo.waveIndex}中没有怪物{npc.id}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("没有波次{0}的怪物信息", npc.npcInfo.waveIndex);
|
||
}
|
||
|
||
currentMidNpcInfo.Remove(npc);
|
||
}
|
||
|
||
ReorderMidNpcID();
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 编辑路线
|
||
/// </summary>
|
||
/// <param name="clickPosition"></param>
|
||
/// <param name="isAdd"></param>
|
||
private void EditRoad(int clickPosition, bool isAdd)
|
||
{
|
||
if (_currEditingRoadInfo == null) return;
|
||
|
||
if (isAdd && !_currEditingRoadInfo.roadPoints.Any(x => x.cellIndex == clickPosition))
|
||
{
|
||
_currEditingRoadInfo.roadPoints.Add(new PatrolInfo()
|
||
{
|
||
cellIndex = clickPosition,
|
||
waitTime = 0f,
|
||
});
|
||
}
|
||
else if (!isAdd && _currEditingRoadInfo.roadPoints.Any(x => x.cellIndex == clickPosition))
|
||
{
|
||
_currEditingRoadInfo.roadPoints =
|
||
_currEditingRoadInfo.roadPoints.Where(x => x.cellIndex != clickPosition).ToList();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 编辑区域
|
||
/// </summary>
|
||
/// <param name="clickPosition"></param>
|
||
/// <param name="isAdd"></param>
|
||
private void EditArea(int clickPosition, bool isAdd)
|
||
{
|
||
if (null == _currEditingAreaInfo)
|
||
return;
|
||
|
||
if (isAdd && !_currEditingAreaInfo.areaPoints.Any(x => x.cellIndex == clickPosition))
|
||
{
|
||
_currEditingAreaInfo.areaPoints.Add(new PatrolInfo()
|
||
{
|
||
cellIndex = clickPosition,
|
||
waitTime = 0f,
|
||
});
|
||
}
|
||
else if (!isAdd && _currEditingAreaInfo.areaPoints.Any(x => x.cellIndex == clickPosition))
|
||
{
|
||
_currEditingAreaInfo.areaPoints =
|
||
_currEditingAreaInfo.areaPoints.Where(x => x.cellIndex != clickPosition).ToList();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 加载
|
||
|
||
private void LoadLevel(LevelData levelData)
|
||
{
|
||
DestroyNpcPrefab();
|
||
_enemyNpcObjectWaveDic.Clear();
|
||
_midNpcObjectWaveDic.Clear();
|
||
_showNpcInfos.Clear();
|
||
_selfNpcObjectDic.Clear();
|
||
|
||
currentLevelData = levelData;
|
||
_mapManager = FindObjectOfType<MapManager>();
|
||
_curMap = _mapManager.Map;
|
||
_curMap.TerrainGridSystem.OnMouseClickOnGrid += OnCellClick;
|
||
|
||
// 加载关卡任务
|
||
taskInfos = currentLevelData.taskInfos.Select(t => new TaskInfoEditor(t)).ToList();
|
||
|
||
// 加载评分条件
|
||
scoreTasks = currentLevelData.scoreTaskID.Select(s => new ScoreTaskEditor(s)).ToList();
|
||
|
||
// 加载触发器
|
||
levelTriggerData = currentLevelData.levelTriggerDatas.Select(e => new LevelTriggerDataEditor(e)).ToList();
|
||
|
||
//加载地区信息
|
||
curRegionData = new RegionData
|
||
{
|
||
preparingRegionId = currentLevelData.preparingRegionId,
|
||
preparingRegionToward = currentLevelData.preparingRegionToward,
|
||
pvpDefendRegionId = currentLevelData.pvpDefendRegionId,
|
||
pvpDefendToward = currentLevelData.pvpDefendToward
|
||
};
|
||
|
||
// 加载占领数据
|
||
captureEditorInfos.Clear();
|
||
foreach (var captureInfo in currentLevelData.CapturePoints)
|
||
{
|
||
var captureEditor = new CaptureEditorInfo()
|
||
{
|
||
captureRegionID = captureInfo.captureRegionID,
|
||
captureProgress = captureInfo.CaptureProgress,
|
||
captureEffectID = captureInfo.captureEffectID,
|
||
captureEffectPoint = captureInfo.captureEffectPoint,
|
||
};
|
||
|
||
foreach (var point in captureInfo.capturePoints)
|
||
{
|
||
var capturePoint = new CaptureEditorInfo.CapturePointEditorInfo()
|
||
{
|
||
belongRegionID = captureInfo.captureRegionID,
|
||
capturePoint = point,
|
||
captureFlag = point == captureInfo.captureFlag,
|
||
};
|
||
captureEditor.capturePointEditorInfos.Add(capturePoint);
|
||
}
|
||
|
||
captureEditorInfos.Add(captureEditor);
|
||
}
|
||
|
||
DestroyAllBlockObj();
|
||
UpdateMinesRegion();
|
||
UpdateBlockRegion();
|
||
UpdateMonsterTrigger();
|
||
|
||
// 加载撤离数据
|
||
retreatEditorInfo.Clear();
|
||
foreach (var point in currentLevelData.retreatPoints.retreatPoints)
|
||
{
|
||
var retreatInfo = new RetreatEditorInfo()
|
||
{
|
||
retreatPoint = point,
|
||
retreatFlag = point == currentLevelData.retreatPoints.retreatFlag,
|
||
};
|
||
retreatEditorInfo.Add(retreatInfo);
|
||
}
|
||
|
||
// 加载敌人撤离数据
|
||
enemyRetreatEditorInfo = new EnemyRetreatEditorInfo();
|
||
foreach (var fallback in currentLevelData.enemyRetreatReusablePoints)
|
||
{
|
||
var pos = _curMap.TGSCellIndex2BlockPosition(fallback);
|
||
enemyRetreatEditorInfo.fallbackReusablePoints.Add(pos);
|
||
}
|
||
|
||
// 加载指定路线
|
||
roadEditorInfos = currentLevelData.patrolRoadData.Select(roadInfo => new PrescribedRoadEditorInfo
|
||
{
|
||
roadID = roadInfo.id,
|
||
roadPoints = roadInfo.patrolInfos,
|
||
}).ToList();
|
||
|
||
|
||
// 加载指定区域
|
||
areaEditorInfos = currentLevelData.patrolRegionData.Select(areaInfo => new PrescribedAreaEditorInfo
|
||
{
|
||
areaID = areaInfo.id,
|
||
areaPoints = areaInfo.patrolInfos,
|
||
}).ToList();
|
||
|
||
// 加载关卡进度数据
|
||
progressGuideDatas = currentLevelData.progressGuideDatas.Select(p => new ProgressGuideDataEditor(p)).ToList();
|
||
|
||
// 加载关卡引导立标数据
|
||
setFlagEditorInfos = currentLevelData.setFlagGuideDatas.Select(f => new SetFlagGuideDataEditor(f)).ToList();
|
||
|
||
// 加载信息板数据
|
||
infoPanelGuideDatas =
|
||
currentLevelData.infoPanelGuideDatas.Select(i => new InfoPanelGuideDataEditor(i)).ToList();
|
||
|
||
// 加载关卡广播
|
||
listAIBroadcastData = currentLevelData.listAIBroadcastData.Select(a => new AIBroadcastDataEditor(a)).ToList();
|
||
|
||
// 加载雷信息
|
||
radarInfoList = currentLevelData.radarInfos.Select(r => new RadarEditorInfo(r)).ToList();
|
||
|
||
// 探照灯
|
||
searchLightList = currentLevelData.searchLightInfos.Select(s => new SearchLightEditorInfo(s)).ToList();
|
||
|
||
// 轰炸区
|
||
bombAreaInfoList = currentLevelData.bombAreaInfos.Select(b => new BombAreaInfoEditor(b)).ToList();
|
||
|
||
// 战旗
|
||
holdFlagEditorList = currentLevelData.holdGFlagInfos.Select(h => new HoldFlagEditorInfo(h))
|
||
.ToList();
|
||
|
||
// 加载当前关卡的Monster配置
|
||
LoadAllNpcConfig();
|
||
|
||
// 加载敌方NPC到场景中
|
||
LoadEnemyNpcInfo();
|
||
|
||
// 加载中立NPC到场景中
|
||
LoadMidNpcInfo();
|
||
|
||
// 加载己方NPC到场景中
|
||
LoadSelfNpcInfo();
|
||
|
||
// 加载波次信息
|
||
LoadWaveInfo();
|
||
|
||
// 加载阻挡球
|
||
LoadBlockSphereInfo();
|
||
}
|
||
|
||
private void LoadAllNpcConfig()
|
||
{
|
||
var monsterConfigDic = MonsterCfg.DataList;
|
||
var levelName = Path.GetFileNameWithoutExtension(_levelEditor.LevelDataJson.name);
|
||
_allMonsterIDs.Clear();
|
||
foreach (var dataMonster in monsterConfigDic)
|
||
{
|
||
if (!dataMonster.LevelJsonName.EndsWith(levelName))
|
||
continue;
|
||
var monsterName = $"{dataMonster.MonsterID} - {dataMonster.NameRead}";
|
||
_allMonsterIDs.Add(monsterName, dataMonster.MonsterID);
|
||
}
|
||
}
|
||
|
||
private void LoadEnemyNpcInfo()
|
||
{
|
||
var monsterConfigDic = MonsterCfg.DataMap;
|
||
currentEnemyNpcInfo = 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, monsterClassData.MonsterLevel, monsterClassData.AIType,
|
||
ETroopsId.Enemy, monsterClassData.NameRead);
|
||
currentEnemyNpcInfo.Add(npcEditor);
|
||
|
||
var npcObj = LoadEnemyNpcPrefab(npcInfo.id);
|
||
if (npcObj != null)
|
||
{
|
||
var setPos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
var yaw = LevelUtils.SetCharaterToward(npcInfo.npcToward);
|
||
var obj = Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0));
|
||
if (enemyObjRoot == null)
|
||
enemyObjRoot = new GameObject("enemyObjRoot");
|
||
if (obj != null)
|
||
obj.transform.SetParent(enemyObjRoot.transform);
|
||
if (_enemyNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
npcObjectDic.Add(npcEditor, obj);
|
||
}
|
||
else
|
||
{
|
||
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
|
||
{
|
||
{ npcEditor, obj }
|
||
};
|
||
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
||
}
|
||
}
|
||
|
||
index++;
|
||
}
|
||
|
||
// 敌方 NPC 加载完成后刷新一次分组视图
|
||
RefreshEnemyNpcGroups();
|
||
}
|
||
|
||
private void LoadMidNpcInfo()
|
||
{
|
||
var monsterConfigDic = MonsterCfg.DataMap;
|
||
currentMidNpcInfo = new();
|
||
var index = MidID + 1;
|
||
foreach (var npcInfo in currentLevelData.midNpcInfos)
|
||
{
|
||
if (!monsterConfigDic.TryGetValue(npcInfo.id, out var monsterClassData))
|
||
{
|
||
DebugUtil.LogError("请检查配置,没有 {0} 的怪物类ID", npcInfo.id);
|
||
continue;
|
||
}
|
||
|
||
var npcEditor = new NpcEditorInfo(npcInfo, index, monsterClassData.MonsterLevel, monsterClassData.AIType,
|
||
ETroopsId.Neutral, monsterClassData.NameRead);
|
||
currentMidNpcInfo.Add(npcEditor);
|
||
|
||
var npcObj = LoadMidNpcPrefab(npcInfo.id);
|
||
if (npcObj != null)
|
||
{
|
||
var setPos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
||
var yaw = LevelUtils.SetCharaterToward(npcInfo.npcToward);
|
||
var obj = Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0));
|
||
if (midObjRoot == null)
|
||
midObjRoot = new GameObject("midObjRoot");
|
||
|
||
if (obj != null)
|
||
obj.transform.SetParent(midObjRoot.transform);
|
||
if (_midNpcObjectWaveDic.TryGetValue(npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
npcObjectDic.Add(npcEditor, obj);
|
||
}
|
||
else
|
||
{
|
||
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
|
||
{ { npcEditor, obj } };
|
||
_midNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
||
}
|
||
}
|
||
|
||
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, monsterClassData.MonsterLevel,
|
||
monsterClassData.AIType, ETroopsId.Self, monsterClassData.NameRead);
|
||
currentSelfNpcInfo.Add(npcEditor);
|
||
var npcObj = LoadSelfNpcPrefab(selfNpcInfo.id);
|
||
if (npcObj != null)
|
||
{
|
||
var setPos = _curMap.BlockPosition2WorldPosition(selfNpcInfo.position);
|
||
var yaw = LevelUtils.SetCharaterToward(selfNpcInfo.npcToward);
|
||
var obj = Instantiate(npcObj, setPos, Quaternion.Euler(0, yaw, 0));
|
||
|
||
if (selfObjRoot == null)
|
||
selfObjRoot = new GameObject("selfObjRoot");
|
||
|
||
if (obj != null)
|
||
obj.transform.SetParent(selfObjRoot.transform);
|
||
|
||
_selfNpcObjectDic.Add(selfNpcInfo.position, obj);
|
||
}
|
||
|
||
index++;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 检查关卡
|
||
|
||
private const string PreparingRegionErrorMessage = "关卡出战区设置错误,该地图没有:<{0}>号地区。\n";
|
||
private const string FixedMessage = "已将错误区域设置为默认<0>号地区。";
|
||
|
||
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 (!string.IsNullOrEmpty(errorMessage))
|
||
{
|
||
CustomPopUpWindow.PopUp(errorMessage + FixedMessage);
|
||
}
|
||
}
|
||
|
||
private bool CheckTask()
|
||
{
|
||
foreach (var taskInfoEditor in taskInfos)
|
||
{
|
||
if (taskInfoEditor.isErrorTaskID)
|
||
{
|
||
DebugUtil.LogError($"有错误任务ID {taskInfoEditor.taskID}, 无法保存关卡");
|
||
CustomPopUpWindow.PopUp($"有错误任务ID {taskInfoEditor.taskID}, 无法保存关卡");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/*if (!IsTaskInvalid(successTaskID))
|
||
{
|
||
DebugUtil.LogError("胜利条件错误, 无法保存关卡");
|
||
CustomPopUpWindow.PopUp("胜利条件错误, 无法保存关卡");
|
||
return false;
|
||
}
|
||
|
||
if (!IsTaskInvalid(failTaskID))
|
||
DebugUtil.LogError("失败条件错误");
|
||
|
||
for (var i = 0; i < starTaskIDs.Count; i++)
|
||
{
|
||
if (!IsTaskInvalid(starTaskIDs[i]))
|
||
DebugUtil.LogError($"{i + 1}星级条件错误");
|
||
}*/
|
||
|
||
return true;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 内部方法
|
||
|
||
private void ProcessCondition()
|
||
{
|
||
foreach (var taskInfo in currentLevelData.taskInfos)
|
||
{
|
||
ProcessCondition(taskInfo.taskCondition);
|
||
}
|
||
}
|
||
|
||
private void ProcessCondition(ConditionModifier conditionModifier)
|
||
{
|
||
// 撤退数量特殊处理
|
||
if (conditionModifier.conditionType == ConditionType.CombatEvacuationCount &&
|
||
conditionModifier.conditionArgs.Count > 0)
|
||
{
|
||
currentLevelData.fallBackCount = (int)conditionModifier.conditionArgs[0].Arg;
|
||
}
|
||
|
||
conditionModifier._args = new();
|
||
foreach (var param in conditionModifier.conditionArgs)
|
||
{
|
||
conditionModifier._args.Add(param.Arg);
|
||
}
|
||
|
||
if (ConditionUtil.IsNeedUncertainParamCondition(conditionModifier.conditionType))
|
||
{
|
||
foreach (var param in conditionModifier.uncertainParam)
|
||
{
|
||
conditionModifier._args.Add(param);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关卡加载时 加载敌方预制体
|
||
/// </summary>
|
||
/// <param name="monsterID">Monster表ID</param>
|
||
/// <returns></returns>
|
||
private GameObject LoadEnemyNpcPrefab(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 EMonsterType.Vehicle:
|
||
{
|
||
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 EMonsterType.EnemyCharacter:
|
||
{
|
||
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;
|
||
}
|
||
case EMonsterType.FightBuild:
|
||
{
|
||
if (FightBuildCfg.DataMap.TryGetValue(curMonsterClassID, out var buildData))
|
||
{
|
||
var prefabPath = PathEx.GetFightBuildingPath(buildData.ModelPath);
|
||
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||
if (obj == null)
|
||
{
|
||
DebugUtil.LogError("请检查敌方建筑类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("请检查敌方建筑类: {0}的配置是否存在", curMonsterClassID);
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
default: return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关卡加载时 加载中立预制体
|
||
/// </summary>
|
||
/// <param name="monsterID">Monster表ID</param>
|
||
/// <returns></returns>
|
||
private GameObject LoadMidNpcPrefab(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 EMonsterType.Vehicle:
|
||
{
|
||
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 EMonsterType.EnemyCharacter:
|
||
{
|
||
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;
|
||
}
|
||
case EMonsterType.FightBuild:
|
||
{
|
||
if (FightBuildCfg.DataMap.TryGetValue(curMonsterClassID, out var buildData))
|
||
{
|
||
var prefabPath = PathEx.GetFightBuildingPath(buildData.ModelPath);
|
||
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||
if (obj == null)
|
||
{
|
||
DebugUtil.LogError("请检查敌方建筑类: {0}的预制体路径是否存在,对应的完整路径: {1}", curMonsterClassID, prefabPath);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("请检查敌方建筑类: {0}的配置是否存在", curMonsterClassID);
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
default: return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关卡加载时 加载己方预制体
|
||
/// </summary>
|
||
/// <param name="monsterID">Monster表ID</param>
|
||
/// <returns></returns>
|
||
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 EMonsterType.Vehicle:
|
||
{
|
||
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 EMonsterType.SelfCharacter:
|
||
{
|
||
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("请检查己方NPC类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("请检查己方NPC类: {0}的配置是否存在", curFightNpcID);
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
case EMonsterType.FightBuild:
|
||
{
|
||
if (FightBuildCfg.DataMap.TryGetValue(curFightNpcID, out var buildData))
|
||
{
|
||
var prefabPath = PathEx.GetFightBuildingPath(buildData.ModelPath);
|
||
obj = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||
if (obj == null)
|
||
{
|
||
DebugUtil.LogError("请检查己方建筑类: {0}的预制体路径是否存在,对应的完整路径: {1}", curFightNpcID, prefabPath);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
DebugUtil.LogError("请检查己方建筑类: {0}的配置是否存在", curFightNpcID);
|
||
}
|
||
|
||
return obj;
|
||
}
|
||
default: return null;
|
||
}
|
||
}
|
||
|
||
private void DestroyNpcPrefab()
|
||
{
|
||
if (selfObjRoot != null)
|
||
DestroyImmediate(selfObjRoot);
|
||
if (midObjRoot != null)
|
||
DestroyImmediate(midObjRoot);
|
||
if (enemyObjRoot != null)
|
||
DestroyImmediate(enemyObjRoot);
|
||
}
|
||
|
||
private void ChangeEnemyNpcToward(NpcEditorInfo npcEditorInfo, CharacterToward npcToward)
|
||
{
|
||
if (_enemyNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj))
|
||
{
|
||
var yaw = LevelUtils.SetCharaterToward(npcToward);
|
||
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ChangeMidNpcToward(NpcEditorInfo npcEditorInfo, CharacterToward npcToward)
|
||
{
|
||
if (_midNpcObjectWaveDic.TryGetValue(npcEditorInfo.npcInfo.waveIndex, out var npcObjectDic))
|
||
{
|
||
if (npcObjectDic.TryGetValue(npcEditorInfo, out var npcObj))
|
||
{
|
||
var yaw = LevelUtils.SetCharaterToward(npcToward);
|
||
npcObj.transform.eulerAngles = new Vector3(0, yaw, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ChangeSelfNpcToward(Vector2Int pos, 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.scoreTarget.Any(modifier => modifier.conditionType == conditionType))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Draw
|
||
|
||
private bool IsShowNpcID => _showNpcInfos != null && _showNpcInfos.Count > 0;
|
||
|
||
private List<NpcEditorInfo> _showNpcInfos;
|
||
|
||
private void OnSceneGUI(SceneView sceneView)
|
||
{
|
||
DrawRoadPath();
|
||
DrawAreaPath();
|
||
DrawNpcID();
|
||
DrawSphereID();
|
||
}
|
||
|
||
private void DrawRoadPath()
|
||
{
|
||
if (null == _currEditingRoadInfo)
|
||
return;
|
||
|
||
Vector3 lastWorldPos = Vector3.zero;
|
||
for (int i = 0; i < _currEditingRoadInfo.roadPoints.Count; i++)
|
||
{
|
||
var gridPos = _currEditingRoadInfo.roadPoints[i];
|
||
var worldPos = _curMap.TGSCellIndex2WorldPosition(gridPos.cellIndex);
|
||
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 void DrawAreaPath()
|
||
{
|
||
if (null == _currEditingAreaInfo)
|
||
return;
|
||
|
||
for (int i = 0; i < _currEditingAreaInfo.areaPoints.Count; ++i)
|
||
{
|
||
var gridPos = _currEditingAreaInfo.areaPoints[i];
|
||
var worldPos = _curMap.TGSCellIndex2WorldPosition(gridPos.cellIndex);
|
||
Handles.color = Color.blue;
|
||
Handles.DrawSolidDisc(worldPos, Vector3.up, 1.4f);
|
||
}
|
||
}
|
||
|
||
private GUIStyle labelStyle;
|
||
|
||
private GUIStyle labelStyleSelf;
|
||
|
||
private GUIStyle labelStyleSphere;
|
||
|
||
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 (isShowAllEnemyNpcID &&
|
||
_enemyNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var enemyNpcObjectDic))
|
||
{
|
||
var waveID = npc.npcInfo.waveIndex;
|
||
if (enemyNpcObjectDic.TryGetValue(npc, out var npcObj))
|
||
{
|
||
var groupsText = npc.npcInfo.groups is { Count: > 0 }
|
||
? string.Join(",", npc.npcInfo.groups)
|
||
: "-";
|
||
var content = $"{waveID},{npc.id}|{groupsText}";
|
||
Handles.Label(npcObj.transform.position, content, labelStyle);
|
||
}
|
||
}
|
||
else if (isShowAllMidNpcID &&
|
||
_midNpcObjectWaveDic.TryGetValue(npc.npcInfo.waveIndex, out var midNpcObjectDic))
|
||
{
|
||
var waveID = npc.npcInfo.waveIndex;
|
||
if (midNpcObjectDic.TryGetValue(npc, out var npcObj))
|
||
{
|
||
var groupsText = npc.npcInfo.groups is { Count: > 0 }
|
||
? string.Join(",", npc.npcInfo.groups)
|
||
: "-";
|
||
var content = $"{waveID},{npc.id}|{groupsText}";
|
||
Handles.Label(npcObj.transform.position, content, labelStyle);
|
||
}
|
||
}
|
||
else if (isShowAllSelfNpcID && _selfNpcObjectDic.TryGetValue(npc.npcInfo.position, out var selfNpcObj))
|
||
{
|
||
Handles.Label(selfNpcObj.transform.position, npc.id.ToString(), labelStyleSelf);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void DrawSphereID()
|
||
{
|
||
if (!canEditorBlockSphere)
|
||
return;
|
||
|
||
labelStyleSphere ??= new GUIStyle
|
||
{
|
||
fontSize = 30,
|
||
fontStyle = FontStyle.Bold,
|
||
normal =
|
||
{
|
||
textColor = Color.black
|
||
}
|
||
};
|
||
|
||
foreach (var blockSpereEditor in blockSphereEditorList)
|
||
{
|
||
if (blockSpereEditor.sphere != null)
|
||
{
|
||
var pos = new Vector3(blockSpereEditor.sphere.transform.position.x, Calibre,
|
||
blockSpereEditor.sphere.transform.position.z);
|
||
Handles.Label(pos, blockSpereEditor.cellIndex.ToString(),
|
||
labelStyleSphere);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 波次信息加载
|
||
|
||
/// <summary>
|
||
/// 加载波次信息(从NPC数据中重建)
|
||
/// </summary>
|
||
private void LoadWaveInfo()
|
||
{
|
||
waveInfoEditors.Clear();
|
||
|
||
if (currentEnemyNpcInfo == null || currentEnemyNpcInfo.Count == 0)
|
||
{
|
||
DebugUtil.Log("没有敌方NPC,无需加载波次信息");
|
||
return;
|
||
}
|
||
|
||
// 从NPC数据中收集波次信息
|
||
// 结构: waveID -> List<groupID>
|
||
var waveGroupsMap = new Dictionary<int, HashSet<int>>();
|
||
|
||
foreach (var npc in currentEnemyNpcInfo)
|
||
{
|
||
// 只处理标记为中途生成的NPC
|
||
if (npc.npcInfo.isHideOnLoad && npc.npcInfo.waveIndex > 0)
|
||
{
|
||
if (!waveGroupsMap.ContainsKey(npc.npcInfo.waveIndex))
|
||
{
|
||
waveGroupsMap[npc.npcInfo.waveIndex] = new HashSet<int>();
|
||
}
|
||
|
||
// 收集该NPC所属的所有组
|
||
if (npc.npcInfo.groups != null)
|
||
{
|
||
foreach (var groupId in npc.npcInfo.groups)
|
||
{
|
||
waveGroupsMap[npc.npcInfo.waveIndex].Add(groupId);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 创建波次编辑器
|
||
foreach (var kvp in waveGroupsMap.OrderBy(x => x.Key))
|
||
{
|
||
var waveEditor = new WaveInfoEditor(kvp.Key, kvp.Value.ToList());
|
||
waveInfoEditors.Add(waveEditor);
|
||
}
|
||
|
||
DebugUtil.Log($"成功从NPC数据中重建 {waveInfoEditors.Count} 个波次信息");
|
||
}
|
||
|
||
#endregion
|
||
} |