/* * @Author: 白哉 * @Description: * @Date: 2025年04月03日 星期四 11:04:31 * @Modify: */ using System; using System.Collections.Generic; using System.IO; using Logic; using Logic.Action; using Logic.AI; using Logic.CrashSight; using MemoryPack; using ParadoxNotion; using TH1_Logic.Core; using TH1_Logic.HeroTask; using UnityEngine; namespace RuntimeData { public enum GameMode { PERFECT, DOMINATION, CREATIVE } // 一场游戏的设置数据 [MemoryPackable] public partial class MapConfig { public uint Width; public uint Height; public uint PlayerCount; public AIDifficult AIDiff; public GameMode GameMode; public uint selfCivId; public uint selfForceId; [MemoryPackConstructor] public MapConfig() { } public MapConfig(uint width, uint height, uint playerCount,uint civId, uint forceId, AIDifficult aiDiff = AIDifficult.LUNATIC) { Width = width; Height = height; PlayerCount = playerCount; AIDiff = aiDiff; selfCivId = civId; selfForceId = forceId; } } // 一场游戏的地图数据 [MemoryPackable] public partial class MapData { //--------------- 基础核心数据 ---------------// // 地图 ID public uint MapID; // 地图配置数据 public MapConfig MapConfig; // 地块数据 public GridMapData GridMap; // 玩家数据 public PlayerMapData PlayerMap; // 城市数据 public CityMapData CityMap; // 小兵数据 public UnitMapData UnitMap; // 网络数据 public NetData Net; // 城市 -> 玩家, 将引用关系转化为ID关系 public Dictionary CityToPlayerDict; // 小兵 -> 城市, 将引用关系转化为ID关系 public Dictionary UnitToCityDict; // 小兵 -> 格子, 将引用关系转化为ID关系 public Dictionary UnitToGridDict; // 城市 -> 格子, 将引用关系转化为ID关系 public Dictionary CityToGridDict; //--------------- 核心数据派生的数据 ---------------// // 格子 -> 小兵, 缓存数据 private Dictionary _gridToUnitDict; // 格子 -> 城市, 缓存数据 private Dictionary _gridToCityDict; //--------------- 其他数据 ---------------// // 地图 ID 生成器 [MemoryPackInclude] private MapIdGenerator _idGenerator; public MapIdGenerator IdGenerator => _idGenerator; //-------------- 读写城市方法 -------------------// // 通过格子 gid 找城市 cid public bool GetCityIdByGridId(uint gid, out uint cid) { return _gridToCityDict.TryGetValue(gid, out cid); } // 通过格子 gid 找城市数据 cityData public bool GetCityDataByGid(uint gid, out CityData cityData) { if (_gridToCityDict.TryGetValue(gid, out var cid)) { return CityMap.GetCityById(_gridToCityDict[gid], out cityData); } cityData = null; return false; } // 通过小兵 ID 找城市 cid public bool GetCityIdByUnitId(uint uid, out uint cid) { return UnitToCityDict.TryGetValue(uid, out cid); } // 通过小兵 ID 找城市 data public bool GetCityDataByUnitId(uint uid, out CityData cityData) { if (UnitToCityDict.TryGetValue(uid, out var cid)) { return CityMap.GetCityById(cid, out cityData); } cityData = null; return false; } // 通过领土格子 gid 找城市数据 cityData public bool GetCityDataByTerritoryGid(uint gid, out CityData cityData) { return CityMap.GetCityDataByTerritoryGridId(gid, out cityData); } // 通过玩家 ID 找城市列表 public void GetCityDataListByPlayerId(uint pid, List cityDataList=null) { if (cityDataList == null) cityDataList = new List(); foreach (var kv in CityToPlayerDict) { if (kv.Value != pid) continue; if (!CityMap.GetCityById(kv.Key, out var cityData)) continue; cityDataList.Add(cityData); } } // 通过玩家 ID 找城市Set public void GetCityDataListByPlayerId(uint pid, HashSet cityDataList=null) { if (cityDataList == null) cityDataList = new HashSet(); foreach (var kv in CityToPlayerDict) { if (kv.Value != pid) continue; if (!CityMap.GetCityById(kv.Key, out var cityData)) continue; cityDataList.Add(cityData); } } // 通过玩家 ID 找城市 Set public HashSet GetCityDataSetByPlayerId(uint pid) { var citySet = new HashSet(); foreach (var kv in CityToPlayerDict) { if (kv.Value != pid) continue; if (!CityMap.GetCityById(kv.Key, out var cityData)) continue; citySet.Add(cityData); } return citySet; } //通过玩家ID 找 领土Set public HashSet GetGridDataSetByPlayerId(uint pid) { var citySet = GetCityDataSetByPlayerId(pid); var gridSet = new HashSet(); foreach (var city in citySet) { var cityGridSet = new HashSet(); city.Territory.GetAllTerritoryArea(cityGridSet); foreach (var gid in cityGridSet) { GridMap.GetGridDataByGid(gid, out var grid); gridSet.Add(grid); } } return gridSet; } //通过玩家ID 找 领土ID Set public HashSet GetGridIdSetByPlayerId(uint pid) { var citySet = GetCityDataSetByPlayerId(pid); var gridSet = new HashSet(); foreach (var city in citySet) { var cityGridSet = new HashSet(); city.Territory.GetAllTerritoryArea(cityGridSet); gridSet.UnionWith(cityGridSet); } return gridSet; } // 通过玩家 ID 查找首都城市 cityData public bool GetCapitalCityDataByPlayerId(uint pid, out CityData cityData) { var cityList = new List(); GetCityDataListByPlayerId(pid, cityList); foreach (var city in cityList) { if (!city.IsCapital) continue; cityData = city; return true; } cityData = null; return false; } // 改变城市所属关系 public bool SetCityIdToPlayerId(uint cid, uint pid) { if (!GetPlayerDataByCityId(cid, out var oldPlayerData)) return false; if(!PlayerMap.GetPlayerDataByPlayerID(pid, out var newPlayerData)) return false; if (!GetCapitalCityDataByPlayerId(oldPlayerData.Id, out var oldPlayerCapitalCityData)) return false; if (!GetCapitalCityDataByPlayerId(pid, out var newPlayerCapitalCityData)) return false; if (!CityMap.GetCityById(cid, out var city)) return false; //playerData.RenderMark = true; //part #1 如果cid的老东家没有别的城市了,判负 if (GetCityCount(oldPlayerData.Id) == 1) { //step #1 玩家死亡(设置mark) oldPlayerData.Alive = false; oldPlayerData.DieMark = true; //step #2 所有unit都销毁 var unitDataList = new List(); GetUnitDataListByCityId(cid,unitDataList); foreach (var unitData in unitDataList) SetUnitDataDie(unitData); //step #3 撤销首都 city.IsCapital = false; } //否则要将所有unit转移到老东家的capital下面去 else { //如果cid的老东家的首都就是cid,那么老东家首先要更换首都 if (oldPlayerCapitalCityData.Id == cid) { //遍历cityList,找到第一个老东家的其他城市,让他变成新的首都 foreach (var tmpCityData in CityMap.CityList) { if (tmpCityData.Id == cid) continue; if (GetPlayerIdByCityId(tmpCityData.Id, out var tmpPlayerId) && tmpPlayerId == oldPlayerData.Id) { tmpCityData.IsCapital = true; oldPlayerCapitalCityData.IsCapital = false; oldPlayerCapitalCityData = tmpCityData; break; } } } //开始将所有cid的小兵都转移到oldPlayerCapitalCityData下去 var tmpUnitDataList = new List(); GetUnitDataListByCityId(cid, tmpUnitDataList); foreach(var tmpUnitData in tmpUnitDataList) SetUnitIdToCityId(tmpUnitData.Id,oldPlayerCapitalCityData.Id); oldPlayerCapitalCityData.RenderMark = true; } //如果这次这个城市是newPlayer的原始首都,那么new Player要更换首都 if (newPlayerData.CradleCityId == cid) { city.IsCapital = true; newPlayerCapitalCityData.IsCapital = false; } //正式更换城市归属 CityToPlayerDict[cid] = pid; if (CityMap.GetCityById(cid, out var cityData)) { cityData.RenderMark = true; if (GetGridDataByCityId(cid, out var gridData)) gridData.CityBuildingRenderMark = true; } return true; } //改变小兵的位置 public void SetUnitIdToGridId(uint uid, uint gid) { if (UnitToGridDict.TryGetValue(uid, out var oldGid)) { _gridToUnitDict.Remove(oldGid); } UnitToGridDict[uid] = gid; _gridToUnitDict[gid] = uid; if (UnitMap.GetUnitDataByUnitId(uid, out var unitData)) unitData.RenderMark = true; } public void SetUnitDataDie(UnitData unitData) { unitData.Alive = false; // 标记原city的rendermark if (GetCityDataByUnitId(unitData.Id, out var cityData)) cityData.CityInfoRenderMark = true; // 清除数据层绑定 RemoveUnitData(unitData.Id); if (GetGridIdByUnitId(unitData.Id, out var gridId)) _gridToUnitDict.Remove(gridId); UnitMap.UnitMapRenderMark = true; } //改变小兵到城市的所属关系 public void SetUnitIdToCityId(uint uid, uint cid) { UnitToCityDict[uid] = cid; //if (CityMap.GetCityById(cid, out var cityData)) cityData.RenderMark = true; //if (PlayerMap.GetPlayerDataByPlayerID(pid, out var playerData)) playerData.RenderMark = true; } //获得一个玩家有多少个城市 public int GetCityCount(uint pid) { int ret = 0; foreach (var cityData in CityMap.CityList) if (CityToPlayerDict.ContainsKey(cityData.Id) && CityToPlayerDict[cityData.Id] == pid) ret++; return ret; } //获得一共城市有多少单位 public int GetUnitCount(uint cid) { int ret = 0; foreach (var unit in UnitMap.UnitList) if (UnitToCityDict.ContainsKey(unit.Id) && UnitToCityDict[unit.Id] == cid) ret+= unit.GetPopulation(); return ret; } //-------------- 读写小兵方法 -------------------// // 通过格子 gid 找小兵 uid public bool GetUnitIdByGid(uint gid, out uint uid) { return _gridToUnitDict.TryGetValue(gid, out uid); } // 通过格子 gid 找小兵 data public bool GetUnitDataByGid(uint gid, out UnitData unitData) { unitData = null; /*foreach(var tt in UnitMap.UnitList) if (GetGridDataByUnitId(tt.Id,out var gridData)) if (gridData.Id == gid) { unitData = tt; return true; } return false;*/ //TODO _gridToUnitDict这个缓存有错,和unitToGrid没有对上。 暂时用了上面的办法,之后需要修改这里的缓存 if (_gridToUnitDict.TryGetValue(gid, out var uid)) { return UnitMap.GetUnitDataByUnitId(uid, out unitData); } return false; } // 通过城市 ID 找小兵列表 public void GetUnitDataListByCityId(uint cid, List unitDataList=null) { if (unitDataList == null) unitDataList = new List(); foreach (var kv in UnitToCityDict) { if (kv.Value != cid) continue; if (!UnitMap.GetUnitDataByUnitId(kv.Key, out var unitData)) continue; unitDataList.Add(unitData); } } // 通过玩家 ID 找小兵列表 public void GetUnitDataListByPlayerId(uint pid, List unitDataList=null) { if (unitDataList == null) unitDataList = new List(); foreach (var kv in CityToPlayerDict) { if (kv.Value != pid) continue; GetUnitDataListByCityId(kv.Key, unitDataList); } } // 通过城市 ID 找小兵 Set public void GetUnitDataListByCityId(uint cid, HashSet unitDataList=null) { if (unitDataList == null) unitDataList = new HashSet(); foreach (var kv in UnitToCityDict) { if (kv.Value != cid) continue; if (!UnitMap.GetUnitDataByUnitId(kv.Key, out var unitData)) continue; unitDataList.Add(unitData); } } // 通过玩家 ID 找小兵 Set public void GetUnitDataListByPlayerId(uint pid, HashSet unitDataList=null) { if (unitDataList == null) unitDataList = new HashSet(); foreach (var kv in CityToPlayerDict) { if (kv.Value != pid) continue; GetUnitDataListByCityId(kv.Key, unitDataList); } } // 通过玩家 ID 找敌方小兵列表 public void GetOtherUnitDataListByPlayerId(uint pid, List unitDataList=null) { if (unitDataList == null) unitDataList = new List(); foreach (var kv in CityToPlayerDict) { if (kv.Value == pid) continue; GetUnitDataListByCityId(kv.Key, unitDataList); } } // 判断是否是友军 public bool IsLeagueUnitByUnit(uint uidA, uint uidB) { if (!GetPlayerIdByUnitId(uidA, out var pidA)) return false; if (!GetPlayerIdByUnitId(uidB, out var pidB)) return false; return SameUnion(pidA, pidB); } // 判断是否是友军 public bool IsLeagueUnitByPlayer(uint playerId, uint uidB) { if (!GetPlayerIdByUnitId(uidB, out var pidB)) return false; return SameUnion(playerId, pidB); } public bool GetUnitByGiantType(GiantType giantType, uint unitLevel, out UnitData unitData) { foreach(var t in UnitMap.UnitList) if (t.GiantType == giantType && t.UnitLevel == unitLevel) { unitData = t; return true; } unitData = null; return false; } //-------------- 读写玩家方法 -------------------// // 通过城市找玩家 pid public bool GetPlayerIdByCityId(uint cityId, out uint playerId) { return CityToPlayerDict.TryGetValue(cityId, out playerId); } // 通过城市找玩家 data public bool GetPlayerDataByCityId(uint cityId, out PlayerData playerData) { playerData = null; if (CityToPlayerDict.TryGetValue(cityId, out var playerId)) { playerData = PlayerMap.GetPlayerData(playerId); } return playerData != null; } // 通过小兵 uid 找城市 pid public bool GetPlayerIdByUnitId(uint uid, out uint pid) { if (UnitToCityDict.TryGetValue(uid, out var cid)) { return CityToPlayerDict.TryGetValue(cid, out pid); } pid = 0; return false; } // 通过小兵 uid 找城市数据 playerData public bool GetPlayerDataByUnitId(uint uid, out PlayerData playerData) { playerData = null; if (UnitToCityDict.TryGetValue(uid, out var cid)) { if (CityToPlayerDict.TryGetValue(cid, out var pid)) { playerData = PlayerMap.GetPlayerData(pid); } } return playerData != null; } // 通过领土位置找玩家 public bool GetPlayerDataByTerritoryGridId(uint gid, out PlayerData playerData) { if (CityMap.GetCityDataByTerritoryGridId(gid, out var cityData)) { return GetPlayerDataByCityId(cityData.Id, out playerData); } playerData = null; return false; } //判断playerA和playerB是否同一个联盟 public bool SameUnion(uint pidA, uint pidB) { if (pidA == pidB) return true; if (!PlayerMap.GetPlayerDataByPlayerID(pidA, out var pA)) return false; pA.DiplomacyData.GetCountryDiplomacyInfo(pidB,out var dipInfo); return dipInfo.DiplomacyState == DiplomacyState.League; } public bool SameUnionByUnitId(uint uidA, uint uidB) { if (!GetPlayerDataByUnitId(uidA, out var pA)) return false; if (!GetPlayerDataByUnitId(uidB, out var pB)) return false; return SameUnion(pA.Id,pB.Id); } public bool SameUnionOrJustBreakUnion(uint pidA, uint pidB) { if (pidA == pidB) return true; if (!PlayerMap.GetPlayerDataByPlayerID(pidA, out var pA)) return false; pA.DiplomacyData.GetCountryDiplomacyInfo(pidB,out var dipInfo); return dipInfo.DiplomacyState is DiplomacyState.League or DiplomacyState.LeagueRupture ; } //-------------- 查询方法方法 -------------------// // 通过小兵 uid 找格子 gid public bool GetGridIdByUnitId(uint uid, out uint gid) { return UnitToGridDict.TryGetValue(uid, out gid); } // 通过小兵 uid 找格子数据 data public bool GetGridDataByUnitId(uint uid, out GridData data) { if (UnitToGridDict.TryGetValue(uid, out var gid)) { return GridMap.GetGridDataByGid(gid, out data); } data = null; return false; } // 通过城市 cid 找格子 gid public bool GetGridIdByCityId(uint cid, out uint gid) { return CityToGridDict.TryGetValue(cid, out gid); } //根据曼哈顿距离返回最近的城市 public bool GetNearestCity(UnitData unit,out CityData outCity) { outCity = null; int score = -1; var unitPlayer = unit.Player(this); var unitGrid = unit.Grid(this); if (unitPlayer == null || unitGrid == null) return false; foreach (var city in CityMap.CityList) { var cityPlayer = city.Player(this); var cityGrid = city.Grid(this); if (cityPlayer == null || cityGrid == null || cityPlayer != unitPlayer) continue; if (score == -1 || GridMap.CalcManhattanDistance(cityGrid, unitGrid) < score) { outCity = city; score = GridMap.CalcManhattanDistance(cityGrid, unitGrid); } } return score != -1; } // 通过城市 cid 找格子数据 data public bool GetGridDataByCityId(uint cid, out GridData data) { if (CityToGridDict.TryGetValue(cid, out var gid)) { return GridMap.GetGridDataByGid(gid, out data); } data = null; return false; } //------------- 计算地图信息方法 -----------------// public bool CheckIfNearByGridSamePlayer(GridData gridData, int dir) { dir--; int x = gridData.Pos.X + dir % 3 - 1, y = gridData.Pos.Y + dir / 3 - 1; if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height) return false; if (GridMap.GetGridDataByPos(x, y, out GridData gridData2)) { GetPlayerDataByTerritoryGridId(gridData.Id,out var p1); GetPlayerDataByTerritoryGridId(gridData2.Id,out var p2); return p1 == p2; } return false; } public bool CheckIfNearByGridSameCity(GridData gridData, int dir) { dir--; int x = gridData.Pos.X + dir % 3 - 1, y = gridData.Pos.Y + dir / 3 - 1; if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height) return false; if (GridMap.GetGridDataByPos(x, y, out GridData gridData2)) { GetCityDataByTerritoryGid(gridData.Id,out var c1); GetCityDataByTerritoryGid(gridData2.Id,out var c2); return c1 == c2; } return false; } //返回在gid周围1格内,dir方向的那格是否是freeland public bool CheckIfNearByGridFreeland(GridData gridData, int dir,out GridData gridData2) { gridData2 = null; dir--; int x = (int)gridData.Pos.X + dir % 3 - 1, y = (int)gridData.Pos.Y + dir / 3 - 1; if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height) return false; if(GridMap.GetGridDataByPos(x, y, out gridData2)) return !GetPlayerDataByTerritoryGridId(gridData2.Id,out var playerData); return false; } //判断grid的dir方向是不是道路或者海路联通的。注意要区分 陆地港口桥梁 和 港口海域 两种情况,港口是两种情况都出现的 public bool CheckIfNearByGridRoadCanConnenct(GridData gridData, int dir) { int x = gridData.Pos.X + dir % 3 - 1, y = gridData.Pos.Y + dir / 3 - 1; if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height) return false; if (!GridMap.GetGridDataByPos(x, y, out var gridData2)) return false; //如果有一个没有road return if (gridData.Feature != TerrainFeature.Road || gridData2.Feature != TerrainFeature.Road) return false; //判断主权情况 均有主权且不是一个同盟要return false if (GetPlayerDataByTerritoryGridId(gridData.Id, out var playerData1) && GetPlayerDataByTerritoryGridId(gridData2.Id, out var playerData2) && !SameUnion(playerData1.Id, playerData2.Id)) return false; //排除一个是非港口海域,一个是陆地的情况 if (gridData.Terrain != TerrainType.Land && gridData.Resource != ResourceType.Port && gridData2.Terrain == TerrainType.Land) { //bridge的情况特判 if (gridData.Resource == ResourceType.Bridge) return true; return false; } if (gridData2.Terrain != TerrainType.Land && gridData2.Resource != ResourceType.Port && gridData.Terrain == TerrainType.Land) { //bridge的情况特判 if (gridData2.Resource == ResourceType.Bridge) return true; return false; } //剩下情况都是true return true; } //返回在gid周围1个内,dir方向的那格是不是land public bool CheckIfNearByGridIsLand(GridData gridData, int dir,out GridData gridData2) { gridData2 = null; dir--; int x = (int)gridData.Pos.X + dir % 3 - 1, y = (int)gridData.Pos.Y + dir / 3 - 1; if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height) return false; GridMap.GetGridDataByPos(x, y, out gridData2); return gridData2.Terrain == TerrainType.Land; } public List GetNearby1RadiusFreelandGidList(uint gid) { var ret = new List(); GridMap.GetGridDataByGid(gid,out GridData gridData); //遍历周围一圈方向,将所有freeland加入列表 for (int dir = 1; dir <= 9; dir++) if(CheckIfNearByGridFreeland(gridData, dir, out var gridData2)) ret.Add(gridData2.Id); return ret; } //返回在gridData上是否存在建设桥梁的基础地理条件 public bool HasBridgeSideLand(GridData gridData,out bool isMirror) { isMirror = false; if (gridData.Terrain == TerrainType.Land) return false; GridData t; if (CheckIfNearByGridIsLand(gridData, 2, out t) && CheckIfNearByGridIsLand(gridData, 8, out t)) return true; isMirror = true; if (CheckIfNearByGridIsLand(gridData, 4, out t) && CheckIfNearByGridIsLand(gridData, 6, out t)) return true; return false; } // 获取玩家的所有领土格子 ID public HashSet GetPlayerTerritoryGridIdSet(uint pid) { var gridSet = new HashSet(); var cityList = new List(); GetCityDataListByPlayerId(pid, cityList); foreach (var cityData in cityList) { cityData.Territory.GetAllTerritoryArea(gridSet); } return gridSet; } public bool CheckIfCityFullPopulation(CityData cityData) { if (cityData == null) { LogSystem.LogError($"CheckIfCityFullPopulation cityData is null"); return true; } return GetUnitCount(cityData.Id) >= cityData.Level; } public uint GetMapSize() { return MapConfig.Width * MapConfig.Height; } //-------------- 新建数据的方法 -------------------// // 新建城市数据 public CityData AddCityData(uint gid, uint pid) { var player = PlayerMap.GetPlayerData(pid); uint civId = player.PlayerCivId; CityLibrary nameEnum = player.PlayerNamerData.GetSetNextCity(); CityData cityData = CityMap.AddCityData(GetNearby1RadiusFreelandGidList(gid), nameEnum, _idGenerator); CityToPlayerDict[cityData.Id] = pid; CityToGridDict[cityData.Id] = gid; _gridToCityDict[gid] = cityData.Id; //建立city会在改格子上自动建立road if (GridMap.GetGridDataByGid(gid, out var gridData)) gridData.Feature = TerrainFeature.Road; //处理rendermark var gridIdSet = GetPlayerTerritoryGridIdSet(pid); foreach(var gridId in gridIdSet) if (GridMap.GetGridDataByGid(gridId, out var gridDataTerr)) gridDataTerr.RenderMark = true; CityMap.CityMapRenderMark = true; cityData.RenderMark = true; cityData.CityInfoRenderMark = true; return cityData; } // 新建小兵数据 public bool AddUnitData(uint gid, uint cid, UnitFullType unitFullType,out UnitData newUnit) { //Step #1 先检查是否能生成(比如在水上生成landOnly的单位就不行) newUnit = null; if (!GridMap.GetGridDataByGid(gid, out GridData grid)) return false; if(!CheckLandTypeForGrid(unitFullType, grid)) return false; //Step #2 再生成单位 newUnit = UnitMap.AddUnitData(unitFullType, _idGenerator); UnitToCityDict[newUnit.Id] = cid; UnitToGridDict[newUnit.Id] = gid; _gridToUnitDict[gid] = newUnit.Id; UnitMap.UnitMapRenderMark = true; newUnit.RenderMark = true; AddUnitSkill(newUnit); return true; } // public bool CheckLandTypeForGrid(UnitFullType unitFullType, GridData grid) { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitFullType, out var info)) return false; if (grid.Terrain is TerrainType.Land) return info.LandType is LandType.Fly or LandType.LandAndWater or LandType.LandAndPort or LandType.LandOnly or LandType.WaterAndAshore; if (grid.Terrain is TerrainType.ShallowSea or TerrainType.DeepSea) { if (grid.Resource == ResourceType.Bridge) { return true; } if (grid.Resource == ResourceType.Port) { return info.LandType is LandType.Fly or LandType.WaterAndAshore or LandType.WaterOnly or LandType.LandAndWater or LandType.LandAndPort; } return info.LandType is LandType.Fly or LandType.WaterAndAshore or LandType.WaterOnly or LandType.LandAndWater; } return true; } //给Unit增加skill ,通用类,TODO 将来给techlist 加入tech类,tech类再关联action类,会比较好 public void AddUnitSkill(UnitData unit) { if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, unit.UnitLevel,out var unitInfo)) return; if (!GetPlayerDataByUnitId(unit.Id, out var player)) return; var common = new List() { SkillType.WATERDEFENSE ,SkillType.WATERMOVE,SkillType.OCEANDEFENSE,SkillType.OCEANMOVE,SkillType.MOUNTAINDEFENSE,SkillType.MOUNTAINMOVE,SkillType.FORESTDEFENSE}; //Step #1先添加小兵在配表中自带的技能 foreach (var skill in unitInfo.Skills) unit.AddSkill(skill); //Step #2再添加小兵在配表中自带的技能 var t = new CommonActionId(); t.ActionType = CommonActionType.UnitSkill; foreach (var s in common) { t.SkillType = s; if (!player.TechTree.CheckActionCan(t)) continue; unit.AddSkill(s); } //Step #3 最后处理特判情况 //先处理辉夜的KaguyaFrenchWarriorSynergy if (unit.UnitType == UnitType.Warrior && player.TechTree.CheckIfHasTech(TechType.KaguyaHunting)) unit.AddSkill(SkillType.KAGUYAFRENCHSYNERGY); if (unit.UnitType == UnitType.Catapult && player.TechTree.CheckIfHasTech(TechType.KaguyaMath)) unit.AddSkill(SkillType.KAGUYAFRENCHSYNERGY); //处理bambooMove if (player.TechTree.CheckIfHasTechAtom(TechAtom.KaguyaFrenchBambooMove)) unit.AddSkill(SkillType.BAMBOOMOVE); //Step #4 处理SkillAdd的生命周期 foreach (var skill in unitInfo.Skills) if(unit.GetSkill(skill, out var ski))ski.OnSkillAdd(unit,this); } // 移除小兵数据 public void RemoveUnitData(uint uid) { UnitMap.RemoveUnitData(uid); if (UnitToGridDict.Remove(uid, out var gid)) { _gridToUnitDict.Remove(gid); } UnitToGridDict.Remove(uid); UnitToCityDict.Remove(uid); } // 获取所有 ID 单位 public List GetAllIdentifierBase() { var list = new List(); foreach (var idBase in PlayerMap.PlayerDataList) list.Add(idBase); foreach (var idBase in CityMap.CityList) list.Add(idBase); foreach (var idBase in UnitMap.UnitList) list.Add(idBase); foreach (var idBase in GridMap.GridList) list.Add(idBase); return list; } //-------------- 生命周期方法 -------------------// // 用于网络传输的默认反序列化构造 [MemoryPackConstructor] public MapData() { CityToPlayerDict = new Dictionary(); UnitToCityDict = new Dictionary(); UnitToGridDict = new Dictionary(); CityToGridDict = new Dictionary(); _gridToCityDict = new Dictionary(); _gridToUnitDict = new Dictionary(); _idGenerator = new MapIdGenerator(); } // 从地图配置初始化 public MapData(MapConfig mapCfg) { MapID = 0; MapConfig = mapCfg; _idGenerator = new MapIdGenerator(); GridMap = new GridMapData(mapCfg, _idGenerator); PlayerMap = new PlayerMapData(mapCfg, _idGenerator); CityMap = new CityMapData(); UnitMap = new UnitMapData(); Net = new NetData(); CityToPlayerDict = new Dictionary(); UnitToCityDict = new Dictionary(); UnitToGridDict = new Dictionary(); CityToGridDict = new Dictionary(); _gridToCityDict = new Dictionary(); _gridToUnitDict = new Dictionary(); } public MapData(MapData copyMap) { MapConfig = copyMap.MapConfig; _idGenerator = copyMap._idGenerator.DeepCopy(); GridMap = new GridMapData(copyMap.GridMap); PlayerMap = new PlayerMapData(copyMap.PlayerMap); CityMap = new CityMapData(copyMap.CityMap); UnitMap = new UnitMapData(copyMap.UnitMap); Net = new NetData(copyMap.Net); CityToPlayerDict = new Dictionary(); UnitToCityDict = new Dictionary(); UnitToGridDict = new Dictionary(); CityToGridDict = new Dictionary(); _gridToCityDict = new Dictionary(); _gridToUnitDict = new Dictionary(); foreach (var kv in copyMap.CityToPlayerDict) CityToPlayerDict[kv.Key] = kv.Value; foreach (var kv in copyMap.UnitToCityDict) UnitToCityDict[kv.Key] = kv.Value; foreach (var kv in copyMap.UnitToGridDict) UnitToGridDict[kv.Key] = kv.Value; foreach (var kv in copyMap.CityToGridDict) CityToGridDict[kv.Key] = kv.Value; foreach (var kv in copyMap._gridToCityDict) _gridToCityDict[kv.Key] = kv.Value; foreach (var kv in copyMap._gridToUnitDict) _gridToUnitDict[kv.Key] = kv.Value; } // 获取深拷贝的一份新的地图数据 public MapData GetDeepCopyMapData() { return new MapData(this); } // 深度拷贝另一封 map 数据 public void DeepCopy(MapData copyMap) { MapConfig = copyMap.MapConfig; _idGenerator.DeepCopy(copyMap._idGenerator); GridMap.DeepCopy(copyMap.GridMap); PlayerMap.DeepCopy(copyMap.PlayerMap); CityMap.DeepCopy(copyMap.CityMap); UnitMap.DeepCopy(copyMap.UnitMap); Net.DeepCopy(copyMap.Net); CityToPlayerDict.Clear(); UnitToCityDict.Clear(); UnitToGridDict.Clear(); CityToGridDict.Clear(); _gridToCityDict.Clear(); _gridToUnitDict.Clear(); foreach (var kv in copyMap.CityToPlayerDict) CityToPlayerDict[kv.Key] = kv.Value; foreach (var kv in copyMap.UnitToCityDict) UnitToCityDict[kv.Key] = kv.Value; foreach (var kv in copyMap.UnitToGridDict) UnitToGridDict[kv.Key] = kv.Value; foreach (var kv in copyMap.CityToGridDict) CityToGridDict[kv.Key] = kv.Value; foreach (var kv in copyMap._gridToCityDict) _gridToCityDict[kv.Key] = kv.Value; foreach (var kv in copyMap._gridToUnitDict) _gridToUnitDict[kv.Key] = kv.Value; } // MemoryPack 反序列化之后的后处理 [MemoryPackOnDeserialized] public void OnAfterMemoryPackDeserialize() { // 重绑定 ID Net.RefreshPlayerNet(this); // 重建缓存字典 _gridToCityDict ??= new Dictionary(); _gridToUnitDict ??= new Dictionary(); _gridToCityDict.Clear(); _gridToUnitDict.Clear(); // 从主字典重建反向索引 foreach (var kv in CityToGridDict) _gridToCityDict[kv.Value] = kv.Key; foreach (var kv in UnitToGridDict) _gridToUnitDict[kv.Value] = kv.Key; // 重绑定 GridMap.BindMapConfig(MapConfig); } // 当场上有小兵受伤时 public void OnUnitDamaged(SettlementInfo info) { foreach (var idBase in GetAllIdentifierBase()) { foreach (var skill in idBase.Skills) skill.OnUnitDamaged(idBase, this, info); } } // 当场上有小兵移动时 public void OnOtherUnitMove(UnitData moveUnit, GridData target) { foreach (var idBase in GetAllIdentifierBase()) { foreach (var skill in idBase.Skills) skill.OnOtherUnitMove(idBase, moveUnit, target, this); } } public static void SaveMapData(MapData map) { if (map == null) return; // 改为二进制文件扩展名 string path = Application.persistentDataPath + "/map_archive.dat"; int retryCount = 3; while (retryCount > 0) { try { byte[] bytes = MemoryPackSerializer.Serialize(map); File.WriteAllBytes(path, bytes); return; } catch (IOException ex) { retryCount--; if (retryCount <= 0) { LogSystem.LogError($"保存地图数据失败: {ex.Message}"); } } } } public static MapData GetMapData() { string path = Application.persistentDataPath + "/map_archive.dat"; // 改为二进制文件扩展名 if (!File.Exists(path)) return null; int retryCount = 3; while (retryCount > 0) { try { byte[] bytes = File.ReadAllBytes(path); return MemoryPackSerializer.Deserialize(bytes); } catch (IOException ex) { retryCount--; if (retryCount <= 0) { LogSystem.LogError($"读取地图数据失败: {ex.Message}"); return null; } } } return null; } public GameRecord ExportGameRecord() { var gameRecord = new GameRecord(); gameRecord.Mode = MapConfig.GameMode; gameRecord.AIDiff = MapConfig.AIDiff; gameRecord.Turn = PlayerMap.SelfPlayerData.Turn; gameRecord.PlayerCivId = PlayerMap.SelfPlayerData.PlayerCivId; gameRecord.PlayerForceId = PlayerMap.SelfPlayerData.PlayerForceId; gameRecord.Score = PlayerMap.SelfPlayerData.PlayerScore; DateTime now = DateTime.Now; gameRecord.Time = now.ToString("yyyy.MM.dd HH:mm"); var cityList = new List(); GetCityDataListByPlayerId(PlayerMap.SelfPlayerId, cityList); gameRecord.CityCount = (uint)cityList.Count; gameRecord.MapWidth = MapConfig.Width; gameRecord.MapHeight = MapConfig.Height; gameRecord.PlayerCount = MapConfig.PlayerCount; return gameRecord; } #region --- 判断数据间关系的快捷方法 --- //判断gid是否属于pid public bool CheckIfGidBelongPid(uint gid,uint pid) { if(!GetPlayerDataByTerritoryGridId(gid, out var playerData)) return false; if (playerData.Id != pid) return false; return true; } //判断gid是否属于pid的联盟 public bool CheckIfGidBelongPidUnion(uint gid,uint pid) { if(!GetPlayerDataByTerritoryGridId(gid, out var playerData)) return false; return SameUnion(playerData.Id,pid); } public bool CheckCityIdBelongPlayerId(uint cid,uint pid) { return GetPlayerIdByCityId(cid, out var pid1) && pid1 == pid; } public bool CheckUnitIdBelongPlayerId(uint uid,uint pid) { return GetPlayerIdByUnitId(uid, out var pid1) && pid1 == pid; } //判断一个unit是否属于pid及其盟友 public bool CheckUnitIdBelongPlayerIdUnion(uint uid,uint pid) { return GetPlayerIdByUnitId(uid, out var pid1) && SameUnion(pid1,pid); } //判断一个grid是否属于pid及其盟友 public bool CheckGridIdBelongPlayerIdUnion(uint gid,uint pid) { //TODO #外交系统更新 return GetPlayerDataByTerritoryGridId(gid, out var player) && player.Id == pid; } public bool CheckUnitIdBelongCityId(uint uid,uint cid) { return GetCityIdByUnitId(uid, out var cid1) && cid1 == cid; } public bool CheckCityIdBelongSelfPlayer(uint cid) { return GetPlayerIdByCityId(cid, out var pid1) && pid1 == PlayerMap.SelfPlayerData.Id; } public bool CheckUnitIdBelongSelfPlayer(uint uid) { return GetPlayerIdByUnitId(uid, out var pid1) && pid1 == PlayerMap.SelfPlayerData.Id; } #endregion //判断英雄的真实level public uint GetGiantRealLv(GiantType giantType, PlayerData player) { if (player == null) return 0; //获取unitTypeInfo,level=0时的信息 if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType.Giant, giantType,0, out var Lv0Info)) return 0; //确认玩家最新的hero的unitLevel,获得level=realLv的info var realLv = Table.Instance.GiantExpToLevel(player.giantExp[(uint)Lv0Info.ChessType]); //判断有没有解锁科技,没有的话,realLv = 0 var action = new CommonActionId(); action.ActionType = CommonActionType.TrainUnit; action.UnitType = UnitType.Giant; action.GiantType = giantType; if (!player.TechTree.CheckActionCan(action)) realLv = 0; return (uint)realLv; } // 获取下一位执行玩家 public PlayerData GetNextPlayer(MapData map) { if (map.PlayerMap.PlayerDataList.Count == 0) { LogSystem.LogError($"UpdateNextPlayer Error : PlayerDataList Count = 0!!!"); return null; } var curTurn = map.PlayerMap.PlayerDataList[0].Turn; foreach (var player in map.PlayerMap.PlayerDataList) { if (player.Turn >= curTurn) continue; return player; } return map.PlayerMap.PlayerDataList[0]; } } // 地图 ID 生成器 [MemoryPackable] public partial class MapIdGenerator { [MemoryPackInclude] private uint _idGenerator; [MemoryPackConstructor] public MapIdGenerator() { _idGenerator = 0; } public uint GeneratorId() { return ++_idGenerator; } public MapIdGenerator DeepCopy() { var copy = new MapIdGenerator(); copy._idGenerator = _idGenerator; return copy; } public void DeepCopy(MapIdGenerator copy) { _idGenerator = copy._idGenerator; } } }