511 lines
18 KiB
C#
511 lines
18 KiB
C#
/*
|
||
* @Author: 白哉
|
||
* @Description:
|
||
* @Date: 2025年04月03日 星期四 11:04:31
|
||
* @Modify:
|
||
*/
|
||
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using Logic;
|
||
using TH1Renderer;
|
||
using UnityEngine;
|
||
|
||
|
||
namespace RuntimeData
|
||
{
|
||
|
||
[Serializable]
|
||
public class GridMapData : ISerializationCallbackReceiver
|
||
{
|
||
public List<GridData> GridList;
|
||
private Dictionary<uint, GridData> _posToGridDict;
|
||
private Dictionary<uint, GridData> _gridIdToGridDict;
|
||
|
||
private MapConfig _mapCfg;
|
||
|
||
public GridMapData()
|
||
{
|
||
GridList = new List<GridData>();
|
||
_posToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict = new Dictionary<uint, GridData>();
|
||
}
|
||
|
||
public GridMapData(MapConfig mapCfg, MapIdGenerator idGenerator)
|
||
{
|
||
_mapCfg = mapCfg;
|
||
GridList = new List<GridData>();
|
||
_posToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict = new Dictionary<uint, GridData>();
|
||
|
||
for (int x = 0; x < mapCfg.Width; x++)
|
||
{
|
||
for (int y = 0; y < mapCfg.Height; y++)
|
||
{
|
||
var gridData = new GridData(x, y, idGenerator,mapCfg);
|
||
GridList.Add(gridData);
|
||
_gridIdToGridDict[gridData.Id] = gridData;
|
||
_posToGridDict[gridData.Pos.PosId] = gridData;
|
||
}
|
||
}
|
||
}
|
||
|
||
public GridMapData(GridMapData copyData)
|
||
{
|
||
_mapCfg = copyData._mapCfg;
|
||
GridList = new List<GridData>();
|
||
_posToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict = new Dictionary<uint, GridData>();
|
||
|
||
foreach (var grid in copyData.GridList)
|
||
{
|
||
var gridData = new GridData(grid);
|
||
GridList.Add(gridData);
|
||
_gridIdToGridDict[gridData.Id] = gridData;
|
||
_posToGridDict[gridData.Pos.PosId] = gridData;
|
||
}
|
||
}
|
||
|
||
public void DeepCopy(GridMapData copyData)
|
||
{
|
||
_mapCfg = copyData._mapCfg;
|
||
_posToGridDict.Clear();
|
||
_gridIdToGridDict.Clear();
|
||
|
||
for (int i = 0; i < copyData.GridList.Count; i++)
|
||
{
|
||
var copyGrid = copyData.GridList[i];
|
||
if (i >= GridList.Count)
|
||
{
|
||
var gridData = new GridData(copyGrid);
|
||
GridList.Add(gridData);
|
||
_gridIdToGridDict[gridData.Id] = gridData;
|
||
_posToGridDict[gridData.Pos.PosId] = gridData;
|
||
}
|
||
else
|
||
{
|
||
GridList[i].DeepCopy(copyGrid);
|
||
_gridIdToGridDict[GridList[i].Id] = GridList[i];
|
||
_posToGridDict[GridList[i].Pos.PosId] = GridList[i];
|
||
}
|
||
}
|
||
for (int i = GridList.Count - 1; i >= copyData.GridList.Count; i--)
|
||
{
|
||
_gridIdToGridDict.Remove(GridList[i].Id);
|
||
_posToGridDict.Remove(GridList[i].Pos.PosId);
|
||
GridList.RemoveAt(i);
|
||
}
|
||
}
|
||
|
||
public void OnBeforeSerialize()
|
||
{
|
||
|
||
}
|
||
|
||
public void OnAfterDeserialize()
|
||
{
|
||
_posToGridDict ??= new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict ??= new Dictionary<uint, GridData>();
|
||
_posToGridDict.Clear();
|
||
_gridIdToGridDict.Clear();
|
||
foreach (var grid in GridList)
|
||
{
|
||
_gridIdToGridDict[grid.Id] = grid;
|
||
_posToGridDict[grid.Pos.PosId] = grid;
|
||
}
|
||
}
|
||
|
||
public void BindMapConfig(MapConfig cfg)
|
||
{
|
||
_mapCfg = cfg;
|
||
}
|
||
|
||
public void OnTurnStart(MapData map)
|
||
{
|
||
foreach (var grid in GridList) grid.OnTurnStart(map);
|
||
}
|
||
|
||
public void OnTurnEnd(MapData map)
|
||
{
|
||
foreach (var grid in GridList) grid.OnTurnEnd(map);
|
||
}
|
||
|
||
// 通过 gid 获取格子数据
|
||
public bool GetGridDataByGid(uint gid, out GridData data)
|
||
{
|
||
return _gridIdToGridDict.TryGetValue(gid, out data);
|
||
}
|
||
|
||
// 通过坐标获取格子数据
|
||
public bool GetGridDataByPos(int x, int y, out GridData gridData)
|
||
{
|
||
return _posToGridDict.TryGetValue(MapPosition.CalculatePosId(x, y), out gridData);
|
||
}
|
||
|
||
public bool GetGridDataByVector3Pos(Vector3 pos, out GridData gridData)
|
||
{
|
||
var p = Table.Instance.WorldToGrid(pos);
|
||
return _posToGridDict.TryGetValue(MapPosition.CalculatePosId(p.x, p.y), out gridData);
|
||
}
|
||
|
||
|
||
// 获取目标周围的所有格子
|
||
public List<GridData> GetAroundGridData(int xOffset, int yOffset, GridData gridData)
|
||
{
|
||
List<GridData> gridDataList = new List<GridData>();
|
||
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - yOffset; y <= gridData.Pos.Y + yOffset; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
gridDataList.Add(aroundGridData);
|
||
}
|
||
}
|
||
|
||
return gridDataList;
|
||
}
|
||
|
||
// 获取目标周围的所有格子
|
||
public HashSet<GridData> GetAroundGridDataSet(int xOffset, int yOffset, GridData gridData)
|
||
{
|
||
HashSet<GridData> gridDataSet = new HashSet<GridData>();
|
||
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - yOffset; y <= gridData.Pos.Y + yOffset; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
gridDataSet.Add(aroundGridData);
|
||
}
|
||
}
|
||
|
||
return gridDataSet;
|
||
}
|
||
|
||
// 获取目标周围的所有格子,但是按照优先2468方向,返回有序的List
|
||
public List<GridData> GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData)
|
||
{
|
||
List<GridData> gridDataList = new List<GridData>();
|
||
for (int i = 1; i <= 9; i++)
|
||
{
|
||
//下方的公式使得遍历顺序是 2468 13579 也就是优先bfs上下左右,再考虑斜的方向
|
||
int dirForHuman = (i <= 4) ? (2 * i) : (2 * (i - 5) + 1);
|
||
int dirForComputer = dirForHuman - 1;
|
||
int x = gridData.Pos.X + dirForComputer % 3 - 1;
|
||
int y = gridData.Pos.Y + dirForComputer / 3 - 1;
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
gridDataList.Add(aroundGridData);
|
||
}
|
||
|
||
return gridDataList;
|
||
}
|
||
|
||
public List<uint> GetAroundGridIdList(int radius, GridData gridData, bool remainCenter = false)
|
||
{
|
||
List<uint> gridIdList = new List<uint>();
|
||
for (int x = gridData.Pos.X - radius; x <= gridData.Pos.X + radius; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - radius; y <= gridData.Pos.Y + radius; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (!remainCenter && x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
gridIdList.Add(aroundGridData.Id);
|
||
}
|
||
}
|
||
return gridIdList;
|
||
}
|
||
|
||
// 获取周围某个格子
|
||
public bool GetUpGridData(GridData gridData, int xOffset, int yOffset, out GridData upGridData)
|
||
{
|
||
return GetGridDataByPos(gridData.Pos.X + xOffset, gridData.Pos.Y + yOffset, out upGridData);
|
||
}
|
||
|
||
// 判断是否在左右边界
|
||
public bool IsInLeftOrRightBorder(GridData gridData)
|
||
{
|
||
return gridData.Pos.X == 0 || gridData.Pos.X == _mapCfg.Width - 1;
|
||
}
|
||
|
||
// 判断是否在上下边界
|
||
public bool IsInUpOrDownBorder(GridData gridData)
|
||
{
|
||
return gridData.Pos.Y == 0 || gridData.Pos.Y == _mapCfg.Height - 1;
|
||
}
|
||
|
||
//-------- 计算类方法 --------//
|
||
public int CalcDistance(GridData gridDataA, GridData gridDataB)
|
||
{
|
||
return Mathf.Max(Mathf.Abs(gridDataA.Pos.X - gridDataB.Pos.X),Mathf.Abs(gridDataA.Pos.Y - gridDataB.Pos.Y));
|
||
}
|
||
|
||
public void RefreshConnectInfo()
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
|
||
public class GridVFXRenderMark
|
||
{
|
||
//1play 0stop
|
||
public GridVFXType Type;
|
||
public bool PlayOrStop;
|
||
public bool RenderMark;
|
||
public uint CivId;
|
||
public int Damage;
|
||
|
||
public GridVFXRenderMark(GridVFXType gridVFXType,bool renderMark = true, bool playOrStop = true)
|
||
{
|
||
Type = gridVFXType;
|
||
RenderMark = renderMark;
|
||
PlayOrStop = playOrStop;
|
||
}
|
||
public void CopyFrom(GridVFXRenderMark other)
|
||
{
|
||
if (other == null) return;
|
||
Type = other.Type;
|
||
PlayOrStop = other.PlayOrStop;
|
||
RenderMark = other.RenderMark;
|
||
CivId = other.CivId;
|
||
Damage = other.Damage;
|
||
}
|
||
}
|
||
|
||
// 每个格子包含5个层次的信息
|
||
[Serializable]
|
||
public class GridData : IdentifierBase
|
||
{
|
||
|
||
//-------- BaseData ---------//
|
||
|
||
// 位置信息
|
||
public MapPosition Pos;
|
||
|
||
public bool CityBuildingRenderMark = false;
|
||
|
||
// 海陆层
|
||
public TerrainType Terrain;
|
||
// 地形层
|
||
public TerrainFeature Feature;
|
||
// 植被层
|
||
public Vegetation Vegetation;
|
||
// 资源层+建筑层
|
||
public ResourceType Resource;
|
||
public WonderLibrary Wonder;
|
||
public ResourceType ResourceUnderBuilding;
|
||
|
||
public bool[] WaterRoadForceId;
|
||
// 文明层
|
||
public uint CivId;
|
||
//奇观Id
|
||
|
||
//建筑level-只对部分建筑生效
|
||
public int buildingLevel;
|
||
//建筑的建立时间,目前只对temple有效
|
||
public uint BuildTime;
|
||
//-------- RenderData ---------//
|
||
|
||
//gridVFX相关的RenderMark
|
||
public bool VFXRenderMarkHurt = false;
|
||
public bool VFXRenderMarkDie = false;
|
||
public bool VFXRenderMarkDieHintStart = false;
|
||
public int VFXRenderMarkDieHintStartParam = 0;
|
||
public bool VFXRenderMarkDieHintEnd = false;
|
||
public bool VFXRenderMarkTreasure = false;
|
||
public bool VFXRenderMarkHeal = false;
|
||
public bool VFXRenderMarkFlag = false;
|
||
public uint VFXRenderMarkFlagCivId = 0;
|
||
public bool VFXRenderMarkFog = false;
|
||
public bool VFXRenderMark = false;
|
||
public Dictionary<GridVFXType, GridVFXRenderMark> RenderMarkVFXDict;
|
||
|
||
//cityborder相关的RenderMark
|
||
public bool CityBorderRenderMark = false;
|
||
|
||
|
||
//用来设置vfxRenderMark, 单独做成一个方法,是为了避免dict值为空的情况
|
||
public void SetGridVFXRenderMark(GridVFXRenderMark tmp)
|
||
{
|
||
if (RenderMarkVFXDict == null || !RenderMarkVFXDict.TryGetValue(tmp.Type, out var target))
|
||
{
|
||
InitRenderMark();
|
||
if (!RenderMarkVFXDict.TryGetValue(tmp.Type, out target))
|
||
return;
|
||
}
|
||
|
||
target.CopyFrom(tmp);
|
||
|
||
}
|
||
public void InitRenderMark()
|
||
{
|
||
RenderMarkVFXDict = new Dictionary<GridVFXType,GridVFXRenderMark>();
|
||
foreach(GridVFXType type in Enum.GetValues(typeof(GridVFXType)))
|
||
RenderMarkVFXDict[type] = new GridVFXRenderMark(type,false);
|
||
}
|
||
|
||
public GridData(int x, int y,MapIdGenerator mapIdGenerator,MapConfig mapConfig)
|
||
{
|
||
Pos = new MapPosition(x,y);
|
||
Id = mapIdGenerator.GeneratorId();
|
||
WaterRoadForceId = new bool[Table.Instance.MaxForceCount];
|
||
}
|
||
|
||
//仅用于AI计算data,不需要render的部分
|
||
public GridData(GridData copyData)
|
||
{
|
||
Pos = new MapPosition(copyData.Pos.X, copyData.Pos.Y);
|
||
Id = copyData.Id;
|
||
Terrain = copyData.Terrain;
|
||
Feature = copyData.Feature;
|
||
Vegetation = copyData.Vegetation;
|
||
Resource = copyData.Resource;
|
||
ResourceUnderBuilding = copyData.ResourceUnderBuilding;
|
||
CivId = copyData.CivId;
|
||
Wonder = copyData.Wonder;
|
||
buildingLevel = copyData.buildingLevel;
|
||
WaterRoadForceId = new bool[copyData.WaterRoadForceId.Length];
|
||
Array.Copy(copyData.WaterRoadForceId, WaterRoadForceId, copyData.WaterRoadForceId.Length);
|
||
foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill());
|
||
}
|
||
|
||
public void DeepCopy(GridData copyData)
|
||
{
|
||
Pos.X = copyData.Pos.X;
|
||
Pos.Y = copyData.Pos.Y;
|
||
Id = copyData.Id;
|
||
Terrain = copyData.Terrain;
|
||
Feature = copyData.Feature;
|
||
Vegetation = copyData.Vegetation;
|
||
Resource = copyData.Resource;
|
||
ResourceUnderBuilding = copyData.ResourceUnderBuilding;
|
||
CivId = copyData.CivId;
|
||
Wonder = copyData.Wonder;
|
||
buildingLevel = copyData.buildingLevel;
|
||
WaterRoadForceId = new bool[copyData.WaterRoadForceId.Length];
|
||
Array.Copy(copyData.WaterRoadForceId, WaterRoadForceId, copyData.WaterRoadForceId.Length);
|
||
|
||
foreach (var skill in copyData.Skills)
|
||
{
|
||
AddSkill(skill.GetSkillType());
|
||
if (!GetSkill(skill.GetSkillType(), out var selfSkill)) continue;
|
||
selfSkill.DeepCopy(skill);
|
||
}
|
||
for (int i = Skills.Count - 1; i >= 0; i--)
|
||
{
|
||
if (copyData.GetSkill(Skills[i].GetSkillType(), out _)) continue;
|
||
Skills.RemoveAt(i);
|
||
}
|
||
}
|
||
|
||
public void SetGridData(TerrainType ter,TerrainFeature fea,Vegetation veg, ResourceType res,uint civ)
|
||
{
|
||
Terrain = ter;
|
||
Feature = fea;
|
||
Vegetation = veg;
|
||
Resource = res;
|
||
ResourceUnderBuilding = ResourceType.None;
|
||
CivId = civ;
|
||
}
|
||
|
||
public bool IsRoadBridgePort()
|
||
{
|
||
return Feature == TerrainFeature.Road || Resource == ResourceType.Port || Resource == ResourceType.Bridge;
|
||
}
|
||
|
||
// 全局通知调用
|
||
public void OnTurnStart(MapData map)
|
||
{
|
||
for (int i = Skills.Count - 1; i >= 0; i--)
|
||
{
|
||
var skill = Skills[i];
|
||
if (skill.IsFinished())
|
||
{
|
||
Skills.RemoveAt(i);
|
||
continue;
|
||
}
|
||
skill.OnTurnStart(this, map);
|
||
}
|
||
|
||
//TODO 不应该写在这里,应该由CityLogic处理或者PlayerLogic处理,反正应该在Logic模块,StartNextTurn那个函数处理
|
||
//处理神庙升级的模块
|
||
|
||
if (map.GetPlayerDataByTerritoryGridId(Id, out var player)
|
||
&&
|
||
(Resource == ResourceType.Temple
|
||
|| Resource == ResourceType.WaterTemple
|
||
|| Resource == ResourceType.ForestTemple
|
||
|| Resource == ResourceType.MountainTemple)
|
||
)
|
||
{
|
||
int newLevel = (int)(player.Turn - BuildTime) / 2 + 1;
|
||
if (newLevel > 5) newLevel = 5;
|
||
if (newLevel != buildingLevel)
|
||
{
|
||
int delta = newLevel - buildingLevel;
|
||
buildingLevel = newLevel;
|
||
RenderMark = true;
|
||
VFXRenderMarkFog = true;
|
||
if (delta > 0)
|
||
{
|
||
var main = GameObject.Find("Main").GetComponent<Main>();
|
||
//如果既是非AI的真实mapdata,又是玩家的各自
|
||
if (Main.MapData == map && player.Id == map.PlayerMap.SelfPlayerId)
|
||
{
|
||
var faithPanel = GameObject.Find("UICanvas/TopBarPanel/FaithPanel/Icon").transform;
|
||
var startPos = Table.Instance.GridToWorld(this);
|
||
var endPos = Camera.main.ScreenToWorldPoint(faithPanel.position);
|
||
MapRenderer.Instance.ProjectileManager.CreateProjectile(main,map,startPos,endPos,ProjectileType.Faith,ProjectileMoveType.CoinParabola,delta * 50);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
public void OnTurnEnd(MapData map)
|
||
{
|
||
foreach (var skill in Skills) skill.OnTurnEnd(this, map);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// 位置基类
|
||
[Serializable]
|
||
public class MapPosition
|
||
{
|
||
public int X;
|
||
public int Y;
|
||
public uint PosId => CalculatePosId(X, Y);
|
||
|
||
|
||
public static uint CalculatePosId(int x, int y)
|
||
{
|
||
return (uint)(x * 1000 + y);
|
||
}
|
||
|
||
public MapPosition()
|
||
{
|
||
X = 0;
|
||
Y = 0;
|
||
}
|
||
|
||
public MapPosition(int x,int y)
|
||
{
|
||
X = x;
|
||
Y = y;
|
||
}
|
||
|
||
public void SetPosition(MapPosition pos)
|
||
{
|
||
X = pos.X;
|
||
Y = pos.Y;
|
||
}
|
||
}
|
||
|
||
} |