2025-12-10 17:39:34 +08:00
|
|
|
|
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;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
using System.IO;
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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();
|
2025-12-10 19:41:23 +08:00
|
|
|
|
ClearShowRegionColor();
|
2025-12-10 17:39:34 +08:00
|
|
|
|
StopMonitoringSphere();
|
|
|
|
|
|
ShowHiddenObjects();
|
|
|
|
|
|
SaveBlockSphereInfo();
|
|
|
|
|
|
DestroyBlockSphere();
|
|
|
|
|
|
|
|
|
|
|
|
currWindow = null;
|
|
|
|
|
|
DestroyNpcPrefab();
|
|
|
|
|
|
_enemyNpcObjectWaveDic = null;
|
|
|
|
|
|
_midNpcObjectWaveDic = null;
|
|
|
|
|
|
_selfNpcObjectDic = null;
|
|
|
|
|
|
_blockPrefabDic = null;
|
|
|
|
|
|
|
|
|
|
|
|
base.OnDisable();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
OnSetRetreatRegionColor();
|
|
|
|
|
|
OnCloseCaptureRegionColor();
|
2025-12-10 19:41:23 +08:00
|
|
|
|
ClearShowRegionColor();
|
2025-12-10 17:39:34 +08:00
|
|
|
|
StopMonitoringSphere();
|
|
|
|
|
|
ShowHiddenObjects();
|
|
|
|
|
|
SaveBlockSphereInfo();
|
|
|
|
|
|
DestroyBlockSphere();
|
|
|
|
|
|
|
|
|
|
|
|
DestroyAllBlockObj();
|
|
|
|
|
|
ClearMinesRegionColor();
|
|
|
|
|
|
ClearBlockRegionColor();
|
|
|
|
|
|
ClearDefenseRegionColor();
|
|
|
|
|
|
|
|
|
|
|
|
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 =>
|
2026-05-22 14:47:01 +08:00
|
|
|
|
isEditingEnemyNpc || isEditingSelfNpc || isEditingMidNpc || isEditingNpcPos || isEditingCaptureInfo || isEditingFallbackReusable ||
|
2025-12-10 17:39:34 +08:00
|
|
|
|
isEditingRetreat || isEditingRoadInfo ||
|
2026-06-08 16:30:57 +08:00
|
|
|
|
isEditingAreaInfo || canEditorBlockArea || canEditorDefenseArea || canEditorMinesArea || canEditorSearchLight;
|
2025-12-10 17:39:34 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2026-05-22 14:47:01 +08:00
|
|
|
|
if (isEditingRetreat)
|
2025-12-10 17:39:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-06-08 16:30:57 +08:00
|
|
|
|
else if (_isEditorSearchLight && _currEditSearchLight != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
EditingSearchLight(cellIndex, opTrue);
|
|
|
|
|
|
}
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-06-08 16:30:57 +08:00
|
|
|
|
|
|
|
|
|
|
private void EditingSearchLight(int clickPosition, bool isAdd)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isAdd)
|
|
|
|
|
|
_currEditSearchLight.AddPosition(clickPosition);
|
|
|
|
|
|
else
|
|
|
|
|
|
_currEditSearchLight.RemovePosition(clickPosition);
|
|
|
|
|
|
}
|
2025-12-10 17:39:34 +08:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
{
|
2025-12-22 15:33:08 +08:00
|
|
|
|
if (Event.current != null && (Event.current.control || Event.current.command))
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveEnemyNpc(clickPosition);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("删除NPC需要按住Ctrl键");
|
|
|
|
|
|
}
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
var monsterName = "";
|
2025-12-10 17:39:34 +08:00
|
|
|
|
var monsterConfigDic = MonsterCfg.DataMap;
|
|
|
|
|
|
if (monsterConfigDic.TryGetValue(curEnemyMonsterID, out var monster))
|
|
|
|
|
|
{
|
|
|
|
|
|
level = monster.MonsterLevel;
|
|
|
|
|
|
aiType = monster.AIType;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
monsterName = monster.NameRead;
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curEnemyMonsterID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:33:08 +08:00
|
|
|
|
var npcEditor = new NpcEditorInfo(npcInfo, currentEnemyNpcInfo.Count + 1, level, aiType, ETroopsId.Enemy,
|
|
|
|
|
|
monsterName);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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);
|
2025-12-10 18:54:43 +08:00
|
|
|
|
if (enemyObjRoot != null)
|
|
|
|
|
|
obj.transform.SetParent(enemyObjRoot.transform);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
npcObjectDic.Add(npcEditor, obj);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var obj = Instantiate(enemyNpcObject, pos, enemyNpcObject.transform.rotation);
|
2025-12-10 18:54:43 +08:00
|
|
|
|
if (enemyObjRoot != null)
|
|
|
|
|
|
obj.transform.SetParent(enemyObjRoot.transform);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
|
|
|
|
|
|
{ { npcEditor, obj } };
|
|
|
|
|
|
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DebugUtil.Log(
|
|
|
|
|
|
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ]怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {enemyNpcObject.name}");
|
2025-12-10 18:54:43 +08:00
|
|
|
|
RefreshEnemyNpcGroups();
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
2025-12-10 18:54:43 +08:00
|
|
|
|
RefreshEnemyNpcGroups();
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-12-22 15:33:08 +08:00
|
|
|
|
#region 已方NPC编辑方法
|
2025-12-10 17:39:34 +08:00
|
|
|
|
|
|
|
|
|
|
private void EditSelfNpc(Vector2Int clickPosition, bool isAdd)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isAdd)
|
|
|
|
|
|
{
|
|
|
|
|
|
AddSelfNpc(clickPosition);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-12-22 15:33:08 +08:00
|
|
|
|
if (Event.current != null && (Event.current.control || Event.current.command))
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveSelfNpc(clickPosition);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("删除NPC需要按住Ctrl键");
|
|
|
|
|
|
}
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
var monsterName = "";
|
2025-12-10 17:39:34 +08:00
|
|
|
|
var monsterConfigDic = MonsterCfg.DataMap;
|
|
|
|
|
|
if (monsterConfigDic.TryGetValue(curSelfNpcID, out var monster))
|
|
|
|
|
|
{
|
|
|
|
|
|
level = monster.MonsterLevel;
|
|
|
|
|
|
aiType = monster.AIType;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
monsterName = monster.NameRead;
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("获取己方Npc[{0}]配置数据错误", curSelfNpcID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
currentSelfNpcInfo.Add(new NpcEditorInfo(npcInfo, SelfID + currentSelfNpcInfo.Count + 1, level, aiType,
|
2025-12-22 15:33:08 +08:00
|
|
|
|
ETroopsId.Self, monsterName));
|
2025-12-10 17:39:34 +08:00
|
|
|
|
var pos = _curMap.BlockPosition2WorldPosition(npcInfo.position);
|
2025-12-10 18:54:43 +08:00
|
|
|
|
var obj = Instantiate(selfNpcObject, pos, selfNpcObject.transform.rotation);
|
|
|
|
|
|
if (selfObjRoot != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
obj.transform.SetParent(selfObjRoot.transform);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_selfNpcObjectDic.Add(npcInfo.position, obj);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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
|
2025-12-22 15:33:08 +08:00
|
|
|
|
{
|
|
|
|
|
|
if (Event.current != null && (Event.current.control || Event.current.command))
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveMidNpc(clickPosition);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("删除NPC需要按住Ctrl键");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
var monsterName = "";
|
2025-12-10 17:39:34 +08:00
|
|
|
|
var monsterConfigDic = MonsterCfg.DataMap;
|
|
|
|
|
|
if (monsterConfigDic.TryGetValue(curMidMonsterID, out var monster))
|
|
|
|
|
|
{
|
|
|
|
|
|
level = monster.MonsterLevel;
|
|
|
|
|
|
aiType = monster.AIType;
|
2025-12-22 15:33:08 +08:00
|
|
|
|
monsterName = monster.NameRead;
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DebugUtil.LogError("获取怪物[{0}]配置数据错误", curMidMonsterID);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-29 14:41:29 +08:00
|
|
|
|
var npcEditor = new NpcEditorInfo(npcInfo, MidID + currentMidNpcInfo.Count + 1, level, aiType, ETroopsId.Neutral,
|
2025-12-22 15:33:08 +08:00
|
|
|
|
monsterName);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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);
|
2025-12-10 18:54:43 +08:00
|
|
|
|
if (midObjRoot != null)
|
|
|
|
|
|
obj.transform.SetParent(midObjRoot.transform);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
npcObjectDic.Add(npcEditor, obj);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var obj = Instantiate(midNpcObject, pos, midNpcObject.transform.rotation);
|
2025-12-10 18:54:43 +08:00
|
|
|
|
if (midObjRoot != null)
|
|
|
|
|
|
obj.transform.SetParent(midObjRoot.transform);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
npcObjectDic = new Dictionary<NpcEditorInfo, GameObject>
|
|
|
|
|
|
{ { npcEditor, obj } };
|
|
|
|
|
|
_midNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DebugUtil.Log(
|
2025-12-10 18:54:43 +08:00
|
|
|
|
$"添加了第[ {npcInfo.waveIndex} ]波的[ {npcInfo.index} ] 中立怪物到 [ {npcInfo.position} ]位置上, 其预制体为: {midNpcObject.name}");
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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(
|
2025-12-10 18:54:43 +08:00
|
|
|
|
$"从[ {clickPosition} ]位置上移除了[ {npc.npcInfo.waveIndex} ]波的[{npc.id}] 中立NPC, 其预制体名字为:{obj.name}");
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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
|
|
|
|
|
|
};
|
2026-06-04 15:42:02 +08:00
|
|
|
|
|
2025-12-10 17:39:34 +08:00
|
|
|
|
// 加载占领数据
|
|
|
|
|
|
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();
|
|
|
|
|
|
UpdateDefenseRegion();
|
|
|
|
|
|
|
|
|
|
|
|
// 加载撤离数据
|
|
|
|
|
|
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();
|
2026-06-04 15:42:02 +08:00
|
|
|
|
|
|
|
|
|
|
// 加载雷信息
|
|
|
|
|
|
radarInfoList = currentLevelData.radarInfos.Select(r => new RadarEditorInfo(r)).ToList();
|
2025-12-10 17:39:34 +08:00
|
|
|
|
|
2026-06-08 16:30:57 +08:00
|
|
|
|
// 探照灯
|
2026-06-09 15:35:41 +08:00
|
|
|
|
searchLightList = currentLevelData.searchLightInfos.Select(s => new SearchLightEditorInfo(s)).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
// 轰炸区
|
|
|
|
|
|
bombAreaInfoList = currentLevelData.bombAreaInfos.Select(b => new BombAreaInfoEditor(b)).ToList();
|
2026-06-08 16:30:57 +08:00
|
|
|
|
|
2025-12-22 15:33:08 +08:00
|
|
|
|
// 加载当前关卡的Monster配置
|
|
|
|
|
|
LoadAllNpcConfig();
|
|
|
|
|
|
|
2025-12-10 17:39:34 +08:00
|
|
|
|
// 加载敌方NPC到场景中
|
|
|
|
|
|
LoadEnemyNpcInfo();
|
|
|
|
|
|
|
|
|
|
|
|
// 加载中立NPC到场景中
|
|
|
|
|
|
LoadMidNpcInfo();
|
|
|
|
|
|
|
|
|
|
|
|
// 加载己方NPC到场景中
|
|
|
|
|
|
LoadSelfNpcInfo();
|
|
|
|
|
|
|
2025-12-29 19:07:03 +08:00
|
|
|
|
// 加载波次信息
|
|
|
|
|
|
LoadWaveInfo();
|
|
|
|
|
|
|
2025-12-10 17:39:34 +08:00
|
|
|
|
// 加载阻挡球
|
|
|
|
|
|
LoadBlockSphereInfo();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:33:08 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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,
|
2025-12-22 15:33:08 +08:00
|
|
|
|
ETroopsId.Enemy, monsterClassData.NameRead);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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>
|
2025-12-10 18:54:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
{ npcEditor, obj }
|
|
|
|
|
|
};
|
2025-12-10 17:39:34 +08:00
|
|
|
|
_enemyNpcObjectWaveDic.Add(npcInfo.waveIndex, npcObjectDic);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
|
}
|
2025-12-10 18:54:43 +08:00
|
|
|
|
|
|
|
|
|
|
// 敌方 NPC 加载完成后刷新一次分组视图
|
|
|
|
|
|
RefreshEnemyNpcGroups();
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void LoadMidNpcInfo()
|
|
|
|
|
|
{
|
|
|
|
|
|
var monsterConfigDic = MonsterCfg.DataMap;
|
|
|
|
|
|
currentMidNpcInfo = new();
|
2025-12-29 14:41:29 +08:00
|
|
|
|
var index = MidID + 1;
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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,
|
2025-12-22 15:33:08 +08:00
|
|
|
|
ETroopsId.Neutral, monsterClassData.NameRead);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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,
|
2025-12-22 15:33:08 +08:00
|
|
|
|
monsterClassData.AIType, ETroopsId.Self, monsterClassData.NameRead);
|
2025-12-10 17:39:34 +08:00
|
|
|
|
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()
|
|
|
|
|
|
{
|
2025-12-11 18:02:28 +08:00
|
|
|
|
foreach (var taskInfoEditor in taskInfos)
|
|
|
|
|
|
{
|
2025-12-22 15:33:08 +08:00
|
|
|
|
if (taskInfoEditor.isErrorTaskID)
|
|
|
|
|
|
{
|
2025-12-11 18:02:28 +08:00
|
|
|
|
DebugUtil.LogError($"有错误任务ID {taskInfoEditor.taskID}, 无法保存关卡");
|
|
|
|
|
|
CustomPopUpWindow.PopUp($"有错误任务ID {taskInfoEditor.taskID}, 无法保存关卡");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-22 15:33:08 +08:00
|
|
|
|
|
2025-12-17 16:44:57 +08:00
|
|
|
|
/*if (!IsTaskInvalid(successTaskID))
|
2025-12-10 17:39:34 +08:00
|
|
|
|
{
|
|
|
|
|
|
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}星级条件错误");
|
2025-12-17 16:44:57 +08:00
|
|
|
|
}*/
|
2025-12-10 17:39:34 +08:00
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-04 13:40:25 +08:00
|
|
|
|
if (ConditionUtil.IsNeedUncertainParamCondition(conditionModifier.conditionType))
|
2025-12-10 17:39:34 +08:00
|
|
|
|
{
|
2026-01-04 13:40:25 +08:00
|
|
|
|
foreach (var param in conditionModifier.uncertainParam)
|
|
|
|
|
|
{
|
|
|
|
|
|
conditionModifier._args.Add(param);
|
|
|
|
|
|
}
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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
|
2025-12-29 19:07:03 +08:00
|
|
|
|
|
|
|
|
|
|
#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
|
2025-12-10 17:39:34 +08:00
|
|
|
|
}
|