564 lines
19 KiB
C#
564 lines
19 KiB
C#
using cfg.AreaCfg;
|
|
using Framework;
|
|
using Gameplay.Level;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Gameplay.Area.Around;
|
|
using Gameplay.Area.AttackArea;
|
|
using Gameplay.Area.Ready;
|
|
using Gameplay.Character;
|
|
using Gameplay.Common;
|
|
using Gameplay.Skill;
|
|
using Gameplay.Unit;
|
|
using Gameplay.Unit.Data;
|
|
using UnityEngine;
|
|
|
|
namespace Gameplay.Area
|
|
{
|
|
using Skill;
|
|
|
|
public class AreaManager
|
|
{
|
|
|
|
public static bool ENABLE_LOG = false;
|
|
|
|
private static AreaManager _instance;
|
|
public static AreaManager instance
|
|
{
|
|
get
|
|
{
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
public static void CreateInstance(Map map)
|
|
{
|
|
if (_instance != null)
|
|
{
|
|
DebugUtil.LogError("请勿重复创建AreaManager");
|
|
return;
|
|
}
|
|
_instance = new AreaManager(map);
|
|
}
|
|
|
|
private readonly Dictionary<int, List<Vector2Int>> _areaDict;
|
|
// public readonly SkillAreaSelector skillAreaSelector;
|
|
public readonly SkillAreaSelector2 skillAreaSelector2;
|
|
public readonly PlayerSkillAreaSelector playerSkillAreaSelector;
|
|
|
|
internal readonly Dictionary<int, List<GameUnit>> _cellIndex2Units;
|
|
|
|
private Map _map;
|
|
|
|
public readonly AttackAreaView attackArea;
|
|
|
|
public readonly ReadyAreaManager readyAreaManager;
|
|
|
|
public readonly AttackAreaManager attackAreaManager;
|
|
|
|
public readonly MoveAreaView moveAreaView;
|
|
|
|
// public SkillAreaSelecter skillAreaSelecter => _skillAreaSelecter;
|
|
|
|
private Dictionary<int, AroundCellData> _aroundCellDatas = new Dictionary<int, AroundCellData>();
|
|
|
|
private Dictionary<int, Vector3> _cellIndexToWorldPosCache = new Dictionary<int, Vector3>();
|
|
|
|
public bool isInSkillPreview
|
|
{
|
|
get
|
|
{
|
|
return skillAreaSelector2.isInPreview || playerSkillAreaSelector.isInPreview;
|
|
}
|
|
}
|
|
|
|
public ParentGroup parentGroup = new ParentGroup();
|
|
|
|
private readonly IEventHandler _eventHandler;
|
|
|
|
private Queue<List<int>> _cellIndexListRing;
|
|
private Dictionary<int, int> _distanceCache;
|
|
|
|
private AreaManager(Map map)
|
|
{
|
|
_areaDict = new Dictionary<int, List<Vector2Int>>();
|
|
skillAreaSelector2 = new SkillAreaSelector2();
|
|
playerSkillAreaSelector = new PlayerSkillAreaSelector();
|
|
_cellIndex2Units = new Dictionary<int, List<GameUnit>>();
|
|
attackArea = new AttackAreaView();
|
|
|
|
_map = map;
|
|
|
|
readyAreaManager = new ReadyAreaManager(_map);
|
|
attackAreaManager = new AttackAreaManager();
|
|
|
|
moveAreaView = new MoveAreaView();
|
|
|
|
_eventHandler = new AreaEventHandler(this);
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
_InitMathV3();
|
|
parentGroup.Init();
|
|
moveAreaView.Init(_map);
|
|
attackArea.Init();
|
|
readyAreaManager.Init();
|
|
attackAreaManager.Init();
|
|
_eventHandler.RegisterEvents();
|
|
|
|
_cellIndexListRing = new Queue<List<int>>();
|
|
for (int i = 0; i < 10; ++i)
|
|
{
|
|
var list = new List<int>();
|
|
_cellIndexListRing.Enqueue(list);
|
|
}
|
|
|
|
_distanceCache = new Dictionary<int, int>();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
readyAreaManager.Dispose();
|
|
attackAreaManager.Dispose();
|
|
moveAreaView.Dispose();
|
|
playerSkillAreaSelector.Dispose();
|
|
parentGroup.Dispose();
|
|
_aroundCellDatas.Clear();
|
|
_eventHandler.Dispose();
|
|
_instance = null;
|
|
}
|
|
|
|
internal void _AddUnitToCellIndex(int cellIndex, GameUnit unit)
|
|
{
|
|
if (_cellIndex2Units.TryGetValue(cellIndex, out var unitList))
|
|
{
|
|
unitList.Add(unit);
|
|
}
|
|
else
|
|
{
|
|
unitList = new List<GameUnit>();
|
|
unitList.Add(unit);
|
|
_cellIndex2Units.Add(cellIndex, unitList);
|
|
}
|
|
if (CodeDefine.ENABLE_FIGHT_NORMAL_ATTACK_LOG)
|
|
{
|
|
_Log($"add unit:{unit.GetID()} to cellIndex:{cellIndex}");
|
|
}
|
|
}
|
|
|
|
internal void _OnUnitRemove(GameUnit unit)
|
|
{
|
|
var cellIndex = unit.transData.cellIndex;
|
|
|
|
if (_cellIndex2Units.TryGetValue(cellIndex, out var unitList))
|
|
{
|
|
unitList.Remove(unit);
|
|
}
|
|
}
|
|
|
|
private static void _Log(string str)
|
|
{
|
|
if (!ENABLE_LOG) return;
|
|
DebugUtil.Log("[AreaManager] {0}", str);
|
|
}
|
|
|
|
|
|
public DataArea GetConfigById(int areaId)
|
|
{
|
|
return TableManager.Instance.Tables.AreaConfig.Get(areaId);
|
|
}
|
|
|
|
private List<int> _emptyList = new List<int>();
|
|
public List<int> GetCellIndexsAround(int centerCellIndex,
|
|
int distance, bool containsCenter = true)
|
|
{
|
|
var key = AroundCellData.GetKey(centerCellIndex, distance, containsCenter);
|
|
if (_aroundCellDatas.TryGetValue(key, out var data))
|
|
{
|
|
return data.aroundCellIndexs;
|
|
}
|
|
|
|
var list = _GetCellIndexAround(centerCellIndex, distance, containsCenter);
|
|
var aroundData = new AroundCellData(centerCellIndex, distance, containsCenter);
|
|
aroundData.aroundCellIndexs = list;
|
|
_aroundCellDatas.Add(key, aroundData);
|
|
return list;
|
|
}
|
|
|
|
private List<int> _GetCellIndexAround(int centerCellIndex,
|
|
int distance, bool containsCenter = true)
|
|
{
|
|
if (distance <= 0)
|
|
{
|
|
if (containsCenter)
|
|
{
|
|
return new List<int>()
|
|
{
|
|
centerCellIndex
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return _emptyList;
|
|
}
|
|
}
|
|
var list = new List<int>();
|
|
if (containsCenter)
|
|
{
|
|
list.Insert(0, centerCellIndex);
|
|
}
|
|
AroundHelper.GetAround(centerCellIndex, distance, list);
|
|
return list;
|
|
}
|
|
|
|
public bool IsCellEmpty(int cellIndex)
|
|
{
|
|
var unitList = GetUnitsByCellIndex(cellIndex);
|
|
return unitList.Count == 0;
|
|
}
|
|
|
|
public int GetDistance(int cellIndex1,
|
|
int cellIndex2)
|
|
{
|
|
if (cellIndex1 == cellIndex2)
|
|
{
|
|
return 0;
|
|
}
|
|
var key = cellIndex1 * 10000 + cellIndex2;
|
|
if (_distanceCache.TryGetValue(key, out var distance))
|
|
{
|
|
return distance;
|
|
}
|
|
var newDistance = _map.TerrainGridSystem.CellGetHexagonDistance(cellIndex1, cellIndex2);
|
|
_distanceCache.Add(key, newDistance);
|
|
var invertedKey = cellIndex2 * 10000 + cellIndex1;
|
|
_distanceCache.Add(invertedKey, newDistance);
|
|
return newDistance;
|
|
|
|
// 实测TGS的方法性能更好 0.66 vs 0.8
|
|
// var mathPoint1 = AroundHelper.GetMathPosV3FromTable(cellIndex1);
|
|
// var mathPoint2 = AroundHelper.GetMathPosV3FromTable(cellIndex2);
|
|
// var offsetX = Mathf.Abs(mathPoint1.x - mathPoint2.x);
|
|
// var offsetY = Mathf.Abs(mathPoint1.y - mathPoint2.y);
|
|
// var offsetZ = Mathf.Abs(mathPoint1.z - mathPoint2.z);
|
|
// return Mathf.Max(offsetX, Mathf.Max(offsetY, offsetZ));
|
|
// return _map.TerrainGridSystem.CellGetHexagonDistance(cellIndex1, cellIndex2);
|
|
}
|
|
|
|
public Vector3 GetWorldPosByIndex(int cellIndex)
|
|
{
|
|
// Check if the position is already cached
|
|
if (_cellIndexToWorldPosCache.TryGetValue(cellIndex, out Vector3 cachedPos))
|
|
{
|
|
return cachedPos;
|
|
}
|
|
|
|
// Calculate and cache the position if not already present
|
|
var getPos = _map.TerrainGridSystem.CellGetPosition(cellIndex);
|
|
_cellIndexToWorldPosCache[cellIndex] = getPos;
|
|
return getPos;
|
|
}
|
|
|
|
public int GetCellIndexByPos(Vector3 pos)
|
|
{
|
|
if (_map.WorldPosition2TGSCellIndex(pos, out var result))
|
|
{
|
|
return result;
|
|
}
|
|
DebugUtil.LogError("无法获取到CellIndex:" + pos);
|
|
return -1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 注意: 返回的list 是循环使用的
|
|
/// </summary>
|
|
/// <param name="centerCellIndex"></param>
|
|
/// <param name="areaId"></param>
|
|
/// <returns></returns>
|
|
public List<int> GetCellIndexs(int centerCellIndex,
|
|
int areaId)
|
|
{
|
|
var getList = _cellIndexListRing.Dequeue();
|
|
_cellIndexListRing.Enqueue(getList);
|
|
getList.Clear();
|
|
GetCellIndexs(centerCellIndex, areaId, getList);
|
|
return getList;
|
|
}
|
|
|
|
public void GetCellIndexs(int centerCellIndex,
|
|
int areaId,
|
|
List<int> list)
|
|
{
|
|
var map = LevelManager.Instance.CurrentLevel.Map;
|
|
var posVec = map.TGSCellIndex2BlockPosition(centerCellIndex);
|
|
var offsetList = _GetOffsetList(areaId);
|
|
for (var i = 0; i < offsetList.Count; ++i)
|
|
{
|
|
var offset = offsetList[i];
|
|
var calcPos = posVec + offset;
|
|
if (posVec.x % 2 == 1 && calcPos.x % 2 == 0)
|
|
{
|
|
calcPos.y += 1;
|
|
}
|
|
var cellIndex = map.BlockPosition2TGSCellIndex(calcPos);
|
|
if (cellIndex == -1)
|
|
{
|
|
continue;
|
|
}
|
|
var getSuccess = map.GetRuntimeBlockProperty(cellIndex, out var blockData);
|
|
if (!getSuccess)
|
|
{
|
|
continue;
|
|
}
|
|
if (blockData.selectable == false)
|
|
{
|
|
continue;
|
|
}
|
|
list.Add(cellIndex);
|
|
}
|
|
}
|
|
|
|
private List<Vector2Int> _GetOffsetList(int areaId)
|
|
{
|
|
if (_areaDict.TryGetValue(areaId, out var offsetList))
|
|
{
|
|
return offsetList;
|
|
}
|
|
else
|
|
{
|
|
// 不存在,现场进行加载
|
|
var areaData = GetConfigById(areaId);
|
|
if (areaData == null)
|
|
{
|
|
DebugUtil.LogError("AreaId:{0}不存在", areaId);
|
|
return null;
|
|
}
|
|
offsetList = new List<Vector2Int>();
|
|
var intList = areaData.Points;
|
|
for (int i = 0; i < intList.Count; i += 2)
|
|
{
|
|
offsetList.Add(new Vector2Int(intList[i], intList[i + 1]));
|
|
}
|
|
_areaDict.Add(areaId, offsetList);
|
|
return offsetList;
|
|
}
|
|
}
|
|
|
|
public int SearchNearCanClaimCellIndexWithAttack(GameUnit searchUnit, int maxDistance = 2)
|
|
{
|
|
if (searchUnit.controlData.targetEnemy == null)
|
|
{
|
|
return SearchNearCanClaimCellIndex(searchUnit, maxDistance);
|
|
}
|
|
var cellIndex = searchUnit.transData.cellIndex;
|
|
if (!CheckContainsMoveUnit(cellIndex))
|
|
{
|
|
return cellIndex;
|
|
}
|
|
var enemy = searchUnit.controlData.targetEnemy;
|
|
var enemyCellIndex = enemy.transData.cellIndex;
|
|
for (int i = 1; i <= maxDistance; i++)
|
|
{
|
|
var minSteps = i - 1;
|
|
var maxSteps = i;
|
|
var list = _map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps,
|
|
canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells);
|
|
for (int j = 0; j < list.Count; j++)
|
|
{
|
|
// 先判断是否可到达
|
|
var checkIndex = list[j];
|
|
if (!CheckMapCellAvailable(checkIndex))
|
|
{
|
|
continue;
|
|
}
|
|
if (!PathFinder.CanBasePass(cellIndex, searchUnit))
|
|
{
|
|
continue;
|
|
}
|
|
if (!CheckContainsMoveUnit(checkIndex))
|
|
{
|
|
// 在判断该位置是否可攻击
|
|
var searchArea = attackAreaManager.GetAttackArea(checkIndex, searchUnit.fightData.attackDistance);
|
|
if (searchArea.canAttackCells.Contains(enemyCellIndex))
|
|
{
|
|
if (maxSteps > 1)
|
|
{
|
|
// 大于1之后需要额外判断一下是否有路径
|
|
var path = PathFinder.FindPath(_map, checkIndex, searchUnit);
|
|
if (path.Count > 0)
|
|
{
|
|
return checkIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return checkIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DebugUtil.LogG("没有找到可用的位置,返回中心位置");
|
|
// 没有找到,返回原来的
|
|
return cellIndex;
|
|
}
|
|
|
|
public int SearchNearCanClaimCellIndex(GameUnit searchUnit, int maxDistance = 2)
|
|
{
|
|
var cellIndex = searchUnit.transData.cellIndex;
|
|
if (!CheckContainsMoveUnit(cellIndex))
|
|
{
|
|
return cellIndex;
|
|
}
|
|
cellIndex = SearchNearCanClaimCellIndex(cellIndex, searchUnit, maxDistance);
|
|
// 没有找到,返回原来的
|
|
return cellIndex;
|
|
}
|
|
|
|
public int SearchNearCanClaimCellIndex(int startCellIndex, GameUnit searchUnit,
|
|
int maxDistance = 2)
|
|
{
|
|
var cellIndex = startCellIndex;
|
|
for (int i = 1; i <= maxDistance; i++)
|
|
{
|
|
var minSteps = i - 1;
|
|
var maxSteps = i;
|
|
var list = _map.TerrainGridSystem.CellGetNeighboursWithinRange(cellIndex, minSteps, maxSteps,
|
|
canCrossCheckType: TGS.CanCrossCheckType.IgnoreCanCrossCheckOnAllCells);
|
|
for (int j = 0; j < list.Count; j++)
|
|
{
|
|
var checkIndex = list[j];
|
|
if (!CheckMapCellAvailable(checkIndex))
|
|
{
|
|
continue;
|
|
}
|
|
if (!PathFinder.CanBasePass(cellIndex, searchUnit))
|
|
{
|
|
continue;
|
|
}
|
|
if (CheckContainsMoveUnit(checkIndex))
|
|
{
|
|
continue;
|
|
}
|
|
if (maxSteps > 1)
|
|
{
|
|
// 大于1之后需要额外判断一下是否有路径
|
|
var path = PathFinder.FindPath(_map, checkIndex, searchUnit);
|
|
if (path.Count > 0)
|
|
{
|
|
return cellIndex;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return checkIndex;
|
|
}
|
|
}
|
|
}
|
|
DebugUtil.LogG("没有找到可用的位置,返回初始位置");
|
|
return cellIndex;
|
|
}
|
|
|
|
public bool CheckContainsMoveEnemy(int cellIndex)
|
|
{
|
|
var allUnits = GameUnitManager.instance.infightUnits.unitList;
|
|
for (int i = 0; i < allUnits.Count; i++)
|
|
{
|
|
var unit = allUnits[i];
|
|
if (!unit.statusData.isOnFloor) continue;
|
|
if (unit.commonData.troopId == ETroopsId.Self) continue;
|
|
if (unit.commonData.canClaimStandCellIndex && unit.transData.cellIndex == cellIndex)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool CheckContainsMoveUnit(int cellIndex)
|
|
{
|
|
var allUnits = GameUnitManager.instance.infightUnits.unitList;
|
|
for (int i = 0; i < allUnits.Count; i++)
|
|
{
|
|
var unit = allUnits[i];
|
|
if (!unit.statusData.isOnFloor) continue;
|
|
if (unit.commonData.canClaimStandCellIndex && unit.transData.cellIndex == cellIndex)
|
|
{
|
|
return true;
|
|
}
|
|
if (unit.commonData.canClaimTargetCellIndex && unit.controlData.targetCellIndex == cellIndex)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public List<GameUnit> GetUnitsByCellIndex(int cellIndex)
|
|
{
|
|
if (_cellIndex2Units.TryGetValue(cellIndex, out var result))
|
|
{
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
var newList = new List<GameUnit>();
|
|
_cellIndex2Units.Add(cellIndex, newList);
|
|
return newList;
|
|
}
|
|
}
|
|
|
|
public TGS.Cell GetCellByIndex(int cellIndex)
|
|
{
|
|
return _map.TerrainGridSystem.cells[cellIndex];
|
|
}
|
|
|
|
public bool CheckMapCellAvailable(int cellIndex)
|
|
{
|
|
var mapCellData = LevelManager.Instance.CurrentLevel.Map.GetBlockData(cellIndex);
|
|
if (mapCellData == null)
|
|
{
|
|
return false;
|
|
}
|
|
var propData = mapCellData.GetTerrainTypeProperty();
|
|
if (propData == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!propData.selectable)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private void _InitMathV3()
|
|
{
|
|
AroundHelper.ClearCache();
|
|
var cellList = _map.TerrainGridSystem.cells;
|
|
for (int i = 0; i < cellList.Count; i++)
|
|
{
|
|
var cell = cellList[i];
|
|
var mathPos = AroundHelper.GetMathPosV3(cell.index);
|
|
cell.mathPointV3 = mathPos;
|
|
}
|
|
}
|
|
|
|
public void LogicUpdate(float dt)
|
|
{
|
|
// skillAreaSelector.LogicUpdate(dt);
|
|
attackArea.LogicUpdate(dt);
|
|
readyAreaManager.LogicUpdate(dt);
|
|
attackAreaManager.LogicUpdate(dt);
|
|
moveAreaView.LogicUpdate();
|
|
}
|
|
|
|
public void FrameClear()
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
}
|