/* * @Author: 白哉 * @Description: * @Date: 2025年04月03日 星期四 11:04:31 * @Modify: */ using System; using System.Collections.Generic; using MemoryPack; using TH1_Logic.Core; using TH1Renderer; using UnityEngine; using UnityEngine.Animations; namespace RuntimeData { [MemoryPackable] public partial class GridMapData { public List GridList; private Dictionary _posToGridDict; private Dictionary _gridIdToGridDict; private MapConfig _mapCfg; [MemoryPackConstructor] public GridMapData() { GridList = new List(); _posToGridDict = new Dictionary(); _gridIdToGridDict = new Dictionary(); } public GridMapData(MapConfig mapCfg, MapIdGenerator idGenerator) { _mapCfg = mapCfg; GridList = new List(); _posToGridDict = new Dictionary(); _gridIdToGridDict = new Dictionary(); 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(); _posToGridDict = new Dictionary(); _gridIdToGridDict = new Dictionary(); 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); } } [MemoryPackOnDeserialized] public void OnAfterMemoryPackDeserialize() { _posToGridDict ??= new Dictionary(); _gridIdToGridDict ??= new Dictionary(); _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 GetAroundGridData(int xOffset, int yOffset, GridData gridData) { List gridDataList = new List(); if (gridData == null) return gridDataList; int radius = Mathf.Max(xOffset, yOffset); //从内圈到外圈按顺序将格子加入list for (int r = 0; r <= radius; r++) { 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 (Mathf.Max(Mathf.Abs(x - gridData.Pos.X) , Mathf.Abs(y - gridData.Pos.Y)) != r) continue; gridDataList.Add(aroundGridData); } } } return gridDataList; } // 获取目标周围的所有格子 public HashSet GetAroundGridDataSet(int xOffset, int yOffset, GridData gridData) { HashSet gridDataSet = new HashSet(); 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 GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData) { List gridDataList = new List(); 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 GetAroundGridIdList(int radius, GridData gridData, bool remainCenter = false) { List gridIdList = new List(); //从内圈到外圈按顺序将格子加入list for (int r = 0; r <= radius; r++) { 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 (Mathf.Max(Mathf.Abs(x - gridData.Pos.X) , Mathf.Abs(y - gridData.Pos.Y)) != r) 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 int CalcManhattanDistance(GridData gridDataA, GridData gridDataB) { return Mathf.Abs(gridDataA.Pos.X - gridDataB.Pos.X) + Mathf.Abs(gridDataA.Pos.Y - gridDataB.Pos.Y); } public void RefreshConnectInfo() { } public bool CalcBridgeMirror(GridData gridData) { var x = gridData.Pos.X; var y = gridData.Pos.Y; if (!GetGridDataByPos(x, y - 1, out var grid1)) return true; if (!GetGridDataByPos(x, y + 1, out var grid2)) return true; if (grid1.Terrain != TerrainType.Land) return true; if (grid2.Terrain != TerrainType.Land) return true; return false; } } public enum GridSpType { None,RemiliaGrid,KaguyaGrid,RemiliaGridDark,Max } // 每个格子包含5个层次的信息 [MemoryPackable] public partial 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 List SpTypeList; public bool[] WaterRoadForceId; // 文明层 public uint CivId; //奇观Id //建筑level-只对部分建筑生效 public int buildingLevel; //海军基底提供的每回合恢复次数 public int NavalBasePoint; //建筑的建立时间,目前只对temple有效 public uint BuildTime; //通用冷却cd参数,kaguyaFrenchAnimal吸引动物、EgyptianIrrigation生成农田,都会操纵这个参数 //建筑的kaguyaFrenchAnimal吸引量,每回合+1,>=2则可以在周围找个树林吸引一个动物,然后置0 public uint CommonColdTime; //-------- RenderData ---------// //cityborder相关的RenderMark [MemoryPackIgnore] public bool CityBorderRenderMark = false; [MemoryPackConstructor] public GridData() { SpTypeList = new List(); } public GridData(int x, int y,MapIdGenerator mapIdGenerator,MapConfig mapConfig) { SpTypeList = new List(); 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()); //拷贝Grid的SpTypeList SpTypeList = new List(); SpTypeList.Clear(); foreach (var SpType in copyData.SpTypeList) SpTypeList.Add(SpType); } 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); //拷贝Grid的SpTypeList SpTypeList ??= new List(); SpTypeList.Clear(); foreach (var SpType in copyData.SpTypeList) SpTypeList.Add(SpType); 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 Vector3 V3() => Table.Instance.GridToWorld(this,""); public bool IsMainMap() { if (!Main.MapData.GridMap.GetGridDataByGid(Id, out var gridData) || gridData != this) return false; return true; } public bool InMainSight() { if (!IsMainMap()) return false; return Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(Id); } //返回该格子周围是否存在没有视野的格子 public bool UnsightNearby(MapData map, PlayerData player) { var gridList = map.GridMap.GetAroundGridData(1, 1, this); foreach (var grid in gridList) { if (player.Sight.CheckIsInSight(grid.Id)) return true; } return false; } // 全局通知调用 public void OnTurnStart(MapData map) { //Step #1 处理海军点 TODO 将来要并入skill去 NavalBasePoint = 1; //Step #2 处理kaguyaFrenchAnimal(必须是该grid玩家的回合) if (Resource == ResourceType.KaguyaFrenchYard && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id)) { CommonColdTime++; if (CommonColdTime >= 2) { var gridList = map.GridMap.GetAroundGridData(1, 1, this); if (map.GetPlayerDataByTerritoryGridId(Id, out var player)) foreach (var grid in gridList) { if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) continue; if (gridPlayer.Id != player.Id) continue; if (grid.Vegetation != Vegetation.Trees) continue; if (grid.Resource != ResourceType.None) continue; grid.Resource = ResourceType.Animal; grid.Renderer(map)?.InstantUpdateGrid(true); grid.Renderer(Main.MapData)?.PlayVFX(new GridVFXParams(GridVFXType.Treasure)); CommonColdTime = 0; Main.CityLogic.UpdateGridBuildingData(map,this); Renderer(map)?.InstantUpdateGrid(true); Renderer(Main.MapData)?.PlayVFX(new GridVFXParams(GridVFXType.Fog)); break; } } } //Step #3 处理EgyptianIrrigation(必须是该grid玩家的回合) if (Resource == ResourceType.EgyptianIrrigation && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id)) { CommonColdTime++; if (CommonColdTime >= 2) { var gridList = map.GridMap.GetAroundGridData(1, 1, this); if (map.GetPlayerDataByTerritoryGridId(Id, out var player)) foreach (var grid in gridList) { if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) continue; if (gridPlayer.Id != player.Id) continue; if (grid.Terrain != TerrainType.Land) continue; if (grid.Vegetation == Vegetation.Trees) continue; if (grid.Feature == TerrainFeature.Mountain) continue; if (grid.Resource != ResourceType.None) continue; grid.Resource = ResourceType.Crop; grid.Renderer(map)?.InstantUpdateGrid(true); grid.Renderer(Main.MapData)?.PlayVFX(new GridVFXParams(GridVFXType.Treasure)); CommonColdTime = 0; //Main.CityLogic.UpdateGridBuildingData(map,this); //Renderer(map)?.SetUpdateGrid(true); //Renderer(Main.MapData)?.PlayVFX(new GridVFXParams(GridVFXType.Fog)); break; } } } //Step #4 处理RemiliaMilitary if (Resource == ResourceType.RemiliaMilitary && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id)) { if (!HasSpType(GridSpType.RemiliaGrid)) AddSpType(GridSpType.RemiliaGrid,map,null); Renderer(map)?.InstantUpdateGrid(); } //Step #5 处理所有技能 for (int i = Skills.Count - 1; i >= 0; i--) { var skill = Skills[i]; if (skill.IsFinished()) { Skills.RemoveAt(i); continue; } skill.OnTurnStart(this, map); } } public void OnTurnEnd(MapData map) { foreach (var skill in Skills) skill.OnTurnEnd(this, map); } //皮肤层的一些基础属性调用 public bool HasSpType(GridSpType type) { foreach(var t in SpTypeList) if (t == type) return true; return false; } public bool RemoveSpType(GridSpType type,MapData map) { foreach(var t in SpTypeList) if (t == type) { SpTypeList.Remove(t); if (type == GridSpType.RemiliaGrid && Unit(map, out var unit)) { unit.RemoveSkill(SkillType.ScarletMistRealTimeVampire,map); } return true; } return false; } public bool AddSpType(GridSpType type,MapData map, UnitData origin)// = null) { if (HasSpType(type)) return false; if (type == GridSpType.RemiliaGrid) { if(Resource == ResourceType.CityCenter) return false; if (Terrain != TerrainType.Land) return false; } SpTypeList.Add(type); origin?.HeroTask(map)?.OnAddSpType(map, type); //如果是remiliaGrid,且是斯卡雷特帝国的单位,给当前的unit填加skill if (type == GridSpType.RemiliaGrid && Unit(map,out var unit) && unit.Player(map,out var player) && player.PlayerCivId == 0) { unit.AddSkill(SkillType.ScarletMistRealTimeVampire); } return true; } public UnitData Unit(MapData map) { map.GetUnitDataByGid(Id, out var unit); return unit; } public bool Unit(MapData map,out UnitData unit) { return map.GetUnitDataByGid(Id, out unit); } public CityData City(MapData map) { map.GetCityDataByTerritoryGid(Id, out var city); return city; } public bool City(MapData map ,out CityData city) { return map.GetCityDataByTerritoryGid(Id, out city); } public CityData CityOnGrid(MapData map) { map.GetCityDataByGid(Id, out var city); return city; } public bool CityOnGrid(MapData map,out CityData city) { return map.GetCityDataByGid(Id, out city); } //返回当前grid提供了多少的cityExp public int CityExp() { if (!Table.Instance.GridAndResourceDataAssets.GetResourceInfo(Resource, out var resourceInfo)) return 0; if (resourceInfo.HasLevel) return resourceInfo.CityExpPerLevel * buildingLevel; return resourceInfo.Exp; } public GridRenderer Renderer(MapData map) { if (map != Main.MapData) return null; MapRenderer.Instance.ROGridMap.TryGetValue(Id, out var renderer); return renderer; } public PlayerData Player(MapData map) { map.GetPlayerDataByTerritoryGridId(Id, out var player); return player; } } // 位置基类 [MemoryPackable] public partial class MapPosition { public int X; public int Y; public uint PosId => CalculatePosId(X, Y); public Vector2Int V2() => new Vector2Int(X, Y); public static uint CalculatePosId(int x, int y) { return (uint)(x * 1000 + y); } [MemoryPackConstructor] 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; } // 重载 GetHashCode public override int GetHashCode() { return HashCode.Combine(X, Y); // 或者更简单的方式: // return X.GetHashCode() ^ Y.GetHashCode(); } // 重载 Equals(object) public override bool Equals(object obj) { return obj is MapPosition other && Equals(other); } // 实现 IEquatable public bool Equals(MapPosition other) { if (other == null) return false; return X == other.X && Y == other.Y; } // 重载 == 操作符(推荐) public static bool operator ==(MapPosition left, MapPosition right) { if (ReferenceEquals(left, right)) return true; if (left is null || right is null) return false; return left.Equals(right); } // 重载 != 操作符(推荐) public static bool operator !=(MapPosition left, MapPosition right) { return !(left == right); } } }