/* * @Author: 白哉 * @Description: * @Date: 2025年04月03日 星期四 11:04:31 * @Modify: */ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using Logic; using Logic.Action; using Logic.AI; using Logic.CrashSight; using Logic.Skill; using MemoryPack; using TH1_Core.Events; using TH1_Core.Managers; using TH1_Logic.Collect; using TH1_Logic.Config; using TH1_Logic.Core; using TH1_Logic.MatchConfig; using TH1_Logic.Net; using TH1_Logic.Steam; using TH1Renderer; using UnityEngine; using MemberInfo = TH1_Logic.Net.MemberInfo; namespace RuntimeData { public enum GameMode { DOMINATION, PERFECT, CREATIVE } // 一场游戏的设置数据 [MemoryPackable] public partial class MapConfig { public uint Id; public uint Width; public uint Height; public uint PlayerCount; public AIDifficult AIDiff; public GameMode GameMode; // 指定地图配置 public bool IsCustomMap; public string MapName; // 游戏结算配置 public MatchSettlementType MatchSettlement; public List PlayerSettlements; // 单机用 public uint selfCivId; public uint selfForceId; // 联机用 public List MultiCivs; private Dictionary _memberCivs; // 校验用 private string _hash; // 关卡限制数据 public List MatchLimits; // 超时时间 public bool IsLimitTime = false; public int TimeLimitSeconds = 180; // 水域类型,默认为 Pangea public Logic.MapWaterType WaterType = Logic.MapWaterType.Pangea; [MemoryPackConstructor] public MapConfig() { MultiCivs = new List(); PlayerSettlements = new List(); _memberCivs = new Dictionary(); MatchLimits = new List(); } 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; MultiCivs = new List(); _memberCivs = new Dictionary(); } // MemoryPack 反序列化之后的后处理 [MemoryPackOnDeserialized] public void OnAfterMemoryPackDeserialize() { if (TimeLimitSeconds == 0) TimeLimitSeconds = 180; // 旧存档兼容:WaterType 字段不存在时默认为 Pangea if (!System.Enum.IsDefined(typeof(Logic.MapWaterType), WaterType)) WaterType = Logic.MapWaterType.Pangea; } // 根据房间成员信息更新 mapconfig 信息 public void UpdateLobbyMember(Dictionary memberInfos) { RefreshMultiCivsDict(); foreach (var kv in memberInfos) { if (_memberCivs.ContainsKey(kv.Key)) continue; var civ = new MemberCiv(); civ.MemberId = kv.Key; civ.CivId = 0; civ.ForceId = 0; MultiCivs.Add(civ); } RefreshMultiCivsDict(); } // 内部刷新 private void RefreshMultiCivsDict() { if (_memberCivs.Count == MultiCivs.Count) return; _memberCivs.Clear(); foreach (var memberCiv in MultiCivs) _memberCivs[memberCiv.MemberId] = memberCiv; } public MemberCiv GetMemberCiv(ulong memberId) { foreach (var memberCiv in MultiCivs) { if (memberCiv.MemberId == memberId) return memberCiv; } return null; } // 主从端一致的更新某一个成员信息 public void UpdateMemberCiv(MemberCiv civ) { if (LobbyManager.Instance.Lobby.IsInLobby() && !LobbyManager.Instance.Lobby.IsLobbyOwner()) { GameNetSender.Instance.ChangeCiv(civ); return; } foreach (var memberCiv in MultiCivs) { if (memberCiv.MemberId != civ.MemberId) continue; memberCiv.CivId = civ.CivId; memberCiv.ForceId = civ.ForceId; return; } MultiCivs.Add(civ); } // 主从端一致的本地数据检测 public void CheckMapConfigChanged() { if (!LobbyManager.Instance.Lobby.IsInLobby()) return; byte[] bytes = MemoryPackSerializer.Serialize(this); var hash128 = new Hash128(); hash128.Append(bytes); var hash = hash128.ToString(); if (hash == _hash) return; _hash = hash; EventManager.Publish(new UpdateUIOutsideMultiplayRoomSetting()); if (LobbyManager.Instance.Lobby.IsLobbyOwner()) GameNetSender.Instance.UpdateLobbyData(Main.Instance.MapConfig); } // 从 MapData 中重绑定 MemberCiv 的 Player 信息 public bool ReBindPlayerInfoFromMapData(MapData mapData) { RefreshMultiCivsDict(); if (mapData.Net.Mode == NetMode.Single) { foreach (var player in mapData.PlayerMap.PlayerDataList) { if (player.PlayerCivId != selfCivId || player.PlayerForceId != selfForceId) continue; mapData.PlayerMap.SelfPlayerId = player.Id; } if (mapData.PlayerMap.SelfPlayerId == 0) { LogSystem.LogError($"单机模式指定地图中找不到指定的阵营信息, 启动游戏失败, " + $"地图 ID : {Id} 文明 ID : {selfCivId} 势力 ID : {selfForceId}"); return false; } } if (mapData.Net.Mode == NetMode.Multi) { foreach (var member in MultiCivs) { foreach (var player in mapData.PlayerMap.PlayerDataList) { if (player.PlayerCivId != member.CivId || player.PlayerForceId != member.ForceId) continue; member.PlayerId = player.PlayerCivId; } if (member.PlayerId == 0) { LogSystem.LogError($"联机模式指定地图中找不到指定的阵营信息, 启动游戏失败, " + $"地图 ID : {Id} 文明 ID : {member.CivId} 势力 ID : {member.ForceId}"); return false; } } } return true; } public void ClearMultiCivs() { MultiCivs?.Clear(); _memberCivs?.Clear(); } public void ChangeByMapConfig(MapConfig other) { if (other == null) { // 没有匹配到关卡配置时,回退为普通随机地图 IsCustomMap = false; MapName = null; return; } GameMode = other.GameMode; IsCustomMap = other.IsCustomMap; MapName = other.MapName; MatchSettlement = other.MatchSettlement; PlayerSettlements = other.PlayerSettlements; } // 刷新 Limits 移除重复 Limit public void RefreshLimits() { if (MatchLimits == null || MatchLimits.Count == 0) return; // 使用 HashSet 去重 var uniqueLimits = new HashSet(MatchLimits); MatchLimits.Clear(); MatchLimits.AddRange(uniqueLimits); } // 判断是否限制 public bool IsLimit(MatchLimitType limitType) { return MatchLimits != null && MatchLimits.Contains(limitType); } } [MemoryPackable] public partial class MemberCiv { public ulong MemberId; public uint PlayerId; public uint CivId; public uint 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; // 结算数据 public MatchSettlementInfo MatchSettlement; // 当前玩家 [MemoryPackIgnore] public PlayerData CurPlayer => PlayerMap?.GetPlayerData(Net.CurPlayerId); // 城市 -> 玩家, 将引用关系转化为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; [MemoryPackIgnore] 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; // Collect 调用 CollectManager.Instance.PlayerGameEndCollect(this, oldPlayerData, newPlayerData); //step #2 所有unit都销毁 var unitDataList = new List(); GetUnitDataListByCityId(cid,unitDataList); foreach (var unitData in unitDataList) unitData.Renderer(this)?.Die(); foreach (var unitData in unitDataList) { Main.UnitLogic.UnitUnnaturalDie(this,unitData); //unitData.Renderer(this) } //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.CityInfoRenderer(this)?.InstantUpdateCityInfo(); } //如果这次这个城市是newPlayer的原始首都,那么new Player要更换首都 if (newPlayerData.CradleCityId == cid) { city.IsCapital = true; newPlayerCapitalCityData.IsCapital = false; } //正式更换城市归属 CityToPlayerDict[cid] = pid; if (CityMap.GetCityById(cid, out var cityData)) { cityData.SetCityRenderer(this); if (GetGridDataByCityId(cid, out var gridData)) gridData.Renderer(this)?.InstantUpdateCityBuilding(cityData.Level,cityData.Civ(this)); } 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.Renderer(this)?.InstantUpdateUnit();*/ } public void SetUnitDataDie(UnitData unitData) { unitData.SetDie(); // 在清除绑定前获取所属城市id和格子id UnitToCityDict.TryGetValue(unitData.Id, out var cityId); GetGridIdByUnitId(unitData.Id, out var gridId); if (gridId != 0) _gridToUnitDict.Remove(gridId); // 清除数据层绑定 RemoveUnitData(unitData.Id); // 刷新原来所在格子的视觉(Fire等VFX) if (gridId != 0 && GridMap.GetGridDataByGid(gridId, out var gridData)) gridData.Renderer(this)?.InstantUpdateGrid(); // 刷新所属城市的CityInfo(人口数变化) if (cityId != 0 && CityMap.GetCityById(cityId, out var cityData)) cityData.CityInfoRenderer(this)?.InstantUpdateCityInfo(); } //改变小兵到城市的所属关系 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); } //只能给GridData/PlayerData/CityData等调用的核心方法,外部业务不允许调用 public bool GetUnitDataByGid_Core(uint gid, out UnitData unitData) { unitData = null; if (_gridToUnitDict.TryGetValue(gid, out var uid)) { return UnitMap.GetUnitDataByUnitId(uid, out unitData); } return false; } // 通过格子 gid 找小兵 data private 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 IsLeagueOrJustBreakByUnit(uint uidA, uint uidB) { if (!GetPlayerIdByUnitId(uidA, out var pidA)) return false; if (!GetPlayerIdByUnitId(uidB, out var pidB)) return false; return SameUnionOrJustBreakUnion(pidA, pidB); } // 判断格子是否为友方/刚背盟盟友的城市中心 public bool IsLeagueOrJustBreakCityCenter(uint unitId, GridData grid) { if (grid.Resource != ResourceType.CityCenter) return false; if (!GetCityDataByGid(grid.Id, out var city)) return false; if (!GetPlayerIdByCityId(city.Id, out var cityPid)) return false; if (!GetPlayerIdByUnitId(unitId, out var unitPid)) return false; return SameUnionOrJustBreakUnion(unitPid, cityPid); } // 判断是否是友军 public bool IsLeagueUnitByPlayer(uint playerId, uint uidB) { if (!GetPlayerIdByUnitId(uidB, out var pidB)) return false; return SameUnion(playerId, pidB); } // 判断是否是友军 public bool IsLeagueUnitByCity(uint cityId, uint uidB) { if (!GetPlayerIdByCityId(cityId, out var pidA)) return false; if (!GetPlayerIdByUnitId(uidB, out var pidB)) return false; return SameUnion(pidA, 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.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.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; } //判断当前map是不是Main.MapData public bool IsCurrentShowMap() { return this == Main.MapData; } //------------- 计算地图信息方法 -----------------// 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.Renderer(this)?.InstantUpdateGrid(true); CityMap.CityMapRenderMark = true; cityData.SetCityRenderer(this); return cityData; } //TODO 这里要研究下,怎么迭代动画系统 让创生的动画和数据分离 // 新建小兵数据 public bool AddUnitData(uint gid, uint cid, UnitFullType unitFullType,out UnitData newUnit,float waitTime = 0f) { //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; //Step #3 增加技能 AddUnitSkill(newUnit); OnAnyUnitCreate(this, newUnit); // collect 调用 CollectManager.Instance.OnAddUnitCollect(this, newUnit); if (waitTime > 0f) { var tmpUnit = newUnit; Timer.Instance.TimerRegister(this, () => { //新增renderer MapRenderer.Instance.RenderUpdateUnitMap(); //看情况是否显示,是否播放雾效 if(tmpUnit.Renderer(this)?.InstantUpdateUnit(true)??false) tmpUnit.Grid(Main.MapData)?.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog)); //刷新所属城市的CityInfo(人口数变化) if (CityMap.GetCityById(cid, out var cityData)) cityData.CityInfoRenderer(this)?.InstantUpdateCityInfo(); },waitTime,"REISEN ILLUSION ADDUNITDATA"); } else { //新增renderer MapRenderer.Instance.RenderUpdateUnitMap(); //看情况是否显示,是否播放雾效 if(newUnit.Renderer(this)?.InstantUpdateUnit(true)??false) newUnit.Grid(Main.MapData)?.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog)); //刷新所属城市的CityInfo(人口数变化) if (CityMap.GetCityById(cid, out var cityData)) cityData.CityInfoRenderer(this)?.InstantUpdateCityInfo(); } if (GetPlayerDataByUnitId(newUnit.Id, out var playerData)) { foreach (var kv in playerData.PlayerHeroData.HeroTaskDict) kv.Value.OnTrainUnit(this, playerData, newUnit); foreach (var item in playerData.MomentData.Items) item.OnTrainUnit(this, playerData, 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 ,通用类, 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.AddInitSkill(skill, this); //Step #2再添加小兵在配表中自带的技能 var t = new CommonActionId(); t.ActionType = CommonActionType.UnitSkill; //遍历所有techAtom foreach (var atom in player.TechTree.TechAtomCacheSet) { if(!Table.Instance.TechDataAssets.GetTechAtomInfo(atom,out var info))continue; if (!info.IsAddSkill) continue; if (!info.CheckCondition(unit.UnitFullType)) continue; unit.AddInitSkill(info.AddSkillType, 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; } // 获取 ID 单位 public IdentifierBase GetIdentifierBase(uint id) { if (PlayerMap.GetPlayerDataByPlayerID(id, out var player)) return player; if (CityMap.GetCityById(id, out var city)) return city; if (UnitMap.GetUnitDataByUnitId(id, out var unit)) return unit; if (GridMap.GetGridDataByGid(id, out var grid)) return grid; return null; } //-------------- 生命周期方法 -------------------// // 用于网络传输的默认反序列化构造 [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, NetMode netMode) { MapID = (uint)Guid.NewGuid().GetHashCode(); MapConfig = mapCfg; _idGenerator = new MapIdGenerator(); GridMap = new GridMapData(mapCfg, _idGenerator); PlayerMap = new PlayerMapData(this, _idGenerator, netMode); CityMap = new CityMapData(); UnitMap = new UnitMapData(); Net = new NetData(); MatchSettlement = new MatchSettlementInfo(); CityToPlayerDict = new Dictionary(); UnitToCityDict = new Dictionary(); UnitToGridDict = new Dictionary(); CityToGridDict = new Dictionary(); _gridToCityDict = new Dictionary(); _gridToUnitDict = new Dictionary(); MatchSettlement.Init(this, mapCfg); } public MapData(MapData copyMap) { MapID = copyMap.MapID; 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); MatchSettlement = new MatchSettlementInfo(copyMap.MatchSettlement); 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) { MapID = copyMap.MapID; 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); MatchSettlement.DeepCopy(copyMap.MatchSettlement); 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); // 刷新 foreach (var action in Net.Actions) { action.Param.MapData = this; action.Param.RefreshParams(); } } // 当场上有小兵受伤前 public void BeforeUnitDamaged(SettlementInfo info) { foreach (var unit in UnitMap.UnitList) { //避免在遍历的时候,直接改到了skillsList var copy = new List(unit.Skills); foreach (var skill in copy) skill.BeforeUnitDamaged(unit, this, info); } } // 当场上有小兵受伤时 public void OnUnitDamaged(SettlementInfo info) { foreach (var unit in UnitMap.UnitList) { //避免在遍历的时候,直接改到了skillsList var copy = new List(unit.Skills); foreach (var skill in copy) skill.OnUnitDamaged(unit, this, info); } } // 当场上有小兵移动时 public void OnAnyUnitMove(MapData map, UnitData moveUnit, GridData target, MoveType moveType) { foreach (var unit in UnitMap.UnitList) { var copy = new List(unit.Skills); foreach (var skill in copy) skill.OnAnyUnitMove(map, unit, moveUnit, target, moveType); } var gridCopy = new List(target.Skills); foreach (var skill in gridCopy) skill.OnAnyUnitMove(map, target, moveUnit, target, moveType); } // 当行为执行后 public void OnActionExecuted(ActionLogicBase logic, CommonActionParams param) { var unitListCopy = new List(UnitMap.UnitList); foreach (var unit in unitListCopy) { var copy = new List(unit.Skills); foreach (var skill in copy) skill.OnActionExecuted(logic, param, unit); } } // 当场小兵死上有亡时 public void OnAnyUnitDie(MapData map, UnitData dieUnit) { var unitListCopy = new List(UnitMap.UnitList); foreach (var unit in unitListCopy) { //避免在遍历的时候,直接改到了skillsList var copy = new List(unit.Skills); foreach (var skill in copy) skill.OnAnyUnitDie(map, unit, dieUnit); } } // 当场上有小兵创建时 public void OnAnyUnitCreate(MapData map, UnitData newUnit) { foreach (var unit in UnitMap.UnitList) { //避免在遍历的时候,直接改到了skillsList var copy = new List(unit.Skills); foreach (var skill in copy) skill.OnAnyUnitCreate(map, unit, newUnit); } } public static void SaveMatchConfig(MapConfig config) { if (config == null) return; // 改为二进制文件扩展名 string path = Application.persistentDataPath + "/../Config/match_config.dat"; int retryCount = 3; while (retryCount > 0) { try { byte[] bytes = MemoryPackSerializer.Serialize(config); File.WriteAllBytes(path, bytes); return; } catch (IOException ex) { retryCount--; if (retryCount <= 0) { LogSystem.LogError($"保存地图配置数据失败: {ex.Message}"); } } } } public static MapConfig GetMatchConfig() { string path = Application.persistentDataPath + "/../Config/match_config.dat"; if (!File.Exists(path)) return null; int retryCount = 3; while (retryCount > 0) { try { byte[] bytes = File.ReadAllBytes(path); var config = MemoryPackSerializer.Deserialize(bytes); config.ClearMultiCivs(); return config; } catch (IOException ex) { retryCount--; if (retryCount <= 0) { LogSystem.LogError($"读取地图数据失败: {ex.Message}"); return null; } } catch (MemoryPackSerializationException ex) { LogSystem.LogError($"地图数据反序列化失败,可能是版本不兼容: {ex.Message}"); return null; } catch (Exception ex) { LogSystem.LogError($"读取地图数据时发生未知错误: {ex.Message}"); return null; } } return null; } public static void SaveMapData(MapData map, bool isBegin=false, bool isEnd=false) { if (map == null) return; // 改为二进制文件扩展名 string path = Application.persistentDataPath + "/../Config/map_archive"; if (isBegin) path += "_begin"; else if (isEnd) path += "_end"; else path += "_continue"; if (map.Net.Mode == NetMode.Multi) path += "_multi"; path += $"_{map.MapID}.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(bool isMulti = false, bool isBegin = false, bool isEnd = false, uint mapId = 0) { string directory = Application.persistentDataPath + "/../Config/"; // 构建文件名模式 string pattern = "map_archive"; if (isBegin) pattern += "_begin"; else if (isEnd) pattern += "_end"; else pattern += "_continue"; if (isMulti) pattern += "_multi"; pattern += "_*.dat"; // 获取所有匹配的文件 if (!Directory.Exists(directory)) return null; var files = Directory.GetFiles(directory, pattern); if (files.Length == 0) return null; // 按文件修改时间从新到旧排序 var sortedFiles = files.OrderByDescending(File.GetLastWriteTime).ToList(); // 如果指定了 mapId,优先查找匹配的文件 string targetFile = sortedFiles[0]; if (mapId != 0) targetFile = sortedFiles.FirstOrDefault(f => f.Contains($"_{mapId}.dat")); if (targetFile == null) return null; int retryCount = 3; while (retryCount > 0) { try { byte[] bytes = File.ReadAllBytes(targetFile); var mapData = MemoryPackSerializer.Deserialize(bytes); // 版本校验:检查反序列化后的数据是否有效 if (mapData?.MapConfig == null || mapData.GridMap == null || mapData.PlayerMap == null) { LogSystem.LogError($"反序列化后的地图数据不完整,可能是版本不兼容"); return null; } return mapData; } catch (IOException ex) { retryCount--; if (retryCount <= 0) { LogSystem.LogError($"读取地图数据失败: {ex.Message}"); return null; } } catch (MemoryPackSerializationException ex) { LogSystem.LogError($"地图数据反序列化失败,可能是版本不兼容: {ex.Message}"); return null; } catch (Exception ex) { 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 // 更新回合 public void RefreshTurn() { // 联机状态下只有房主有权限更新 if (Main.MapData.Net.Mode == NetMode.Multi && !LobbyManager.Instance.Lobby.IsLobbyOwner()) return; if (PlayerMap.PlayerDataList.Count == 0) { LogSystem.LogError($"UpdateNextPlayer Error : PlayerDataList Count = 0!!!"); return; } // 检查下超时 if (Main.MapData.Net.Mode == NetMode.Multi && Main.MapData.MapConfig.IsLimitTime && Net.CurPlayerId != 0 && Time.time - Net.PlayerStartTime >= Main.MapData.MapConfig.TimeLimitSeconds) Main.PlayerLogic.EndPlayerTurn(Main.MapData, Net.CurPlayerId); if (Net.CurPlayerId != 0) return; // 获取下一位 var nextPlayer = GetNextPlayer(); if (nextPlayer == null) { LogSystem.LogError($"UpdateNextPlayer Error : nextPlayer is null!!!"); return; } if (Main.MapData.Net.Mode == NetMode.Spectator) { Main.PlayerLogic.StartPlayerTurn(this, nextPlayer.Id); return; } // 存档 SaveMapData(Main.MapData); AchievementDataManager.Instance.SaveAchievementData(); if (Main.MapData.Net.Mode == NetMode.Single) PlayerPrefs.SetInt("Archive", 1); if (Main.MapData.Net.Mode == NetMode.Multi) PlayerPrefs.SetInt("MultiArchive", 1); PlayerPrefs.Save(); // 设置当前玩家 Main.PlayerLogic.StartPlayerTurn(this, nextPlayer.Id); } // 获取下一位执行玩家 public PlayerData GetNextPlayer() { if (PlayerMap.PlayerDataList.Count == 0) { LogSystem.LogError($"UpdateNextPlayer Error : PlayerDataList Count = 0!!!"); return null; } var curTurn = uint.MaxValue; foreach (var player in PlayerMap.PlayerDataList) { if (!player.IsSurvival) continue; if (player.Turn >= curTurn) continue; curTurn = player.Turn; } foreach (var player in PlayerMap.PlayerDataList) { // 如果要做复活逻辑,需要处理复活玩家的 Turn if (!player.IsSurvival) continue; if (player.Turn > curTurn) continue; return player; } foreach (var player in PlayerMap.PlayerDataList) { if (!player.IsSurvival) continue; return player; } LogSystem.LogError($"UpdateNextPlayer Error : PlayerDataList Alive Count = 0!!!"); return PlayerMap.PlayerDataList[0]; } // 回合开始 public void OnTurnStart(uint playerId) { if (playerId == Net.CurPlayerId) return; if (!PlayerMap.GetPlayerDataByPlayerID(playerId, out var player)) return; Net.CurPlayerId = playerId; PlayerMap.OnTurnStart(this, player); CityMap.OnTurnStart(this, player); UnitMap.OnTurnStart(this, player); GridMap.OnTurnStart(this); Net.PlayerStartTime = Time.time; } public void OnAfterTurnStart(uint playerId) { if (!PlayerMap.GetPlayerDataByPlayerID(playerId, out var player)) return; UnitMap.OnAfterTurnStart(this, player); } // 回合结束 public void OnTurnEnd(uint playerId) { if (playerId != Net.CurPlayerId) return; if (!PlayerMap.GetPlayerDataByPlayerID(playerId, out var player)) return; PlayerMap.OnTurnEnd(this, player); CityMap.OnTurnEnd(this, player); UnitMap.OnTurnEnd(this, player); GridMap.OnTurnEnd(this); Net.CurPlayerId = 0; } // 游戏结束时获取胜利玩家 ID public PlayerData GetWinPlayer() { foreach (var player in PlayerMap.PlayerDataList) { if (MatchSettlement.IsWin(player.Id)) return player; } return null; } // 游戏是否结束 public bool CheckIfGameEnd(out bool isWin) { isWin = MatchSettlement.IsWin(PlayerMap.SelfPlayerId); return MatchSettlement.IsFinished; } // // 游戏是否结束 // public bool CheckIfGameEnd(out bool isWin) // { // #if GAME_AUTO_DEBUG // if (CurPlayer != null && CurPlayer.Turn > 40) // { // uint maxId = 0; // int maxScore = 0; // foreach (var player in PlayerMap.PlayerDataList) // { // if (player.PlayerScore <= maxScore) continue; // maxScore = player.PlayerScore; // maxId = player.Id; // } // isWin = maxId == PlayerMap.SelfPlayerId; // return true; // } // #endif // // if (Net.Mode == NetMode.Multi) return CheckIfGameEndMulti(out isWin); // // var aliveCount = 0; // foreach (var player in PlayerMap.PlayerDataList) // { // if (player.Alive) aliveCount++; // } // // isWin = aliveCount <= 1 && PlayerMap.SelfPlayerData.Alive; // // #if GAME_AUTO_DEBUG // var isLose = aliveCount <= 1 && !PlayerMap.SelfPlayerData.Alive; // #else // var isLose = !PlayerMap.SelfPlayerData.Alive; // #endif // // return isWin || isLose; // } // // public bool CheckIfGameEndMulti(out bool isWin) // { // var aliveCount = 0; // foreach (var player in PlayerMap.PlayerDataList) // { // if (player.Alive) aliveCount++; // } // // var alivePlayerCount = 0; // foreach (var kv in Net.Players) // { // if (kv.Key == 0 || kv.Value == 0) continue; // if(!PlayerMap.GetPlayerDataByPlayerID(kv.Value, out var player)) continue; // if (player.Alive) alivePlayerCount++; // } // // isWin = aliveCount <= 1 && PlayerMap.SelfPlayerData.Alive; // return alivePlayerCount == 0 || aliveCount <= 1; // } public bool CheckIsRealPlayer(uint playerId) { if (Net.Mode == NetMode.Multi) { foreach (var kv in Net.Players) { if (kv.Value == playerId) return true; } return false; } return playerId == PlayerMap.SelfPlayerId; } public bool CheckIsAI(uint playerId) { if (Net.Mode == NetMode.Multi) { foreach (var kv in Net.Players) { if (kv.Value == playerId) return false; } return true; } return playerId != PlayerMap.SelfPlayerId; } // 用于使用指定地图的地图相关内容重生成 public void RegenerateMap(MapRecordData recordData) { foreach (var grid in GridMap.GridList) { if (recordData.GridMap.GetGridDataByPos(grid.Pos.X, grid.Pos.Y, out var recordGrid)) { var id = grid.Id; grid.DeepCopy(recordGrid); grid.Id = id; } else { LogSystem.LogError($"RegenerateMap Error 找不到地图 {recordData.MapName} 的格子信息 Pos=({grid.Pos.X},{grid.Pos.Y})"); } } foreach (var playerInfo in recordData.PlayerInfos) { PlayerData p = null; foreach (var player in PlayerMap.PlayerDataList) { if(player.PlayerCivId == playerInfo.Civ && player.PlayerForceId == playerInfo.Force) { p = player; break; } } if (p == null) { LogSystem.LogError($"RegenerateMap Error 找不到地图 {recordData.MapName} 的玩家文明信息 Civ={playerInfo.Civ} Force={playerInfo.Force}"); } foreach (var cityInfo in playerInfo.Cities) { if (!GridMap.GetGridDataByPos(cityInfo.X, cityInfo.Y, out var cityGrid)) { LogSystem.LogError($"RegenerateMap Error 找不到地图 {recordData.MapName} 的城市格子信息 Pos=({cityInfo.X},{cityInfo.Y})"); } //建设一个新城市 CityData c = AddCityData(cityGrid.Id, p.Id); c.Level = cityInfo.Level; if (cityInfo.IsCapital) { //设置该城市为首都 Main.CityLogic.SetCapital(this, c.Id); //设置该玩家的文明摇篮城市为该城市 p.CradleCityId = c.Id; } foreach (var unitInfo in cityInfo.Units) { if (!GridMap.GetGridDataByPos(unitInfo.X, unitInfo.Y, out var unitGrid)) { LogSystem.LogError($"RegenerateMap Error 找不到地图 {recordData.MapName} 的城市格子信息 Pos=({unitInfo.X},{unitInfo.Y})"); } if (AddUnitData(unitGrid.Id, c.Id, unitInfo.UnitFullType, out var newUnit)) newUnit.SetFullActionPoint(); } } } } // 用于使用指定地图进游戏的清理工作 public void Clear(MapConfig config) { Net.Clear(); MatchSettlement.Init(this, config); } // 检查两个 map 的不同之处 public void CompareEqual(MapData map) { var differences = new List(); // 使用序列化比较各个组件 CompareComponent(MapConfig, map.MapConfig, "MapConfig", differences); CompareComponent(GridMap, map.GridMap, "GridMap", differences); CompareComponent(PlayerMap, map.PlayerMap, "PlayerMap", differences); CompareComponent(PlayerMap.PlayerDataList, map.PlayerMap.PlayerDataList, "PlayerMap.PlayerDataList", differences); CompareComponent(CityMap, map.CityMap, "CityMap", differences); CompareComponent(UnitMap, map.UnitMap, "UnitMap", differences); Net.CompareEqual(map.Net); // 比较字典 CompareComponent(CityToPlayerDict, map.CityToPlayerDict, "CityToPlayerDict", differences); CompareComponent(UnitToCityDict, map.UnitToCityDict, "UnitToCityDict", differences); CompareComponent(UnitToGridDict, map.UnitToGridDict, "UnitToGridDict", differences); CompareComponent(CityToGridDict, map.CityToGridDict, "CityToGridDict", differences); // 输出结果 foreach (var diff in differences) { LogSystem.LogWarning($" - {diff}"); } } // 检查两个 map 的不同之处 public List GetCompareEqual(MapData map) { var differences = new List(); // 使用序列化比较各个组件 CompareComponent(MapConfig, map.MapConfig, "MapConfig", differences); CompareComponent(GridMap, map.GridMap, "GridMap", differences); CompareComponent(PlayerMap, map.PlayerMap, "PlayerMap", differences); CompareComponent(PlayerMap.PlayerDataList, map.PlayerMap.PlayerDataList, "PlayerMap.PlayerDataList", differences); CompareComponent(CityMap, map.CityMap, "CityMap", differences); CompareComponent(UnitMap, map.UnitMap, "UnitMap", differences); Net.CompareEqual(map.Net); // 比较字典 CompareComponent(CityToPlayerDict, map.CityToPlayerDict, "CityToPlayerDict", differences); CompareComponent(UnitToCityDict, map.UnitToCityDict, "UnitToCityDict", differences); CompareComponent(UnitToGridDict, map.UnitToGridDict, "UnitToGridDict", differences); CompareComponent(CityToGridDict, map.CityToGridDict, "CityToGridDict", differences); return differences; } // 获取 player 是否在大回合内有没有行动 public bool GetPlayerHasActedInBigTurn(PlayerData player) { uint bigTurn = 0; foreach (var p in PlayerMap.PlayerDataList) { if (p.Turn >= bigTurn) bigTurn = p.Turn; } return player.Turn < bigTurn; } public static bool CompareComponent2(T obj1, T obj2) { try { var bytes1 = MemoryPackSerializer.Serialize(obj1); var bytes2 = MemoryPackSerializer.Serialize(obj2); return bytes1.SequenceEqual(bytes2); } catch (Exception ex) { LogSystem.LogError($"comparison failed: {ex.Message}"); return false; } } public static void CompareComponent(T obj1, T obj2, string name, List differences) { try { var bytes1 = MemoryPackSerializer.Serialize(obj1); var bytes2 = MemoryPackSerializer.Serialize(obj2); if (!bytes1.SequenceEqual(bytes2)) { differences.Add($"{name} differs (serialized data mismatch)"); } } catch (Exception ex) { differences.Add($"{name} comparison failed: {ex.Message}"); } } public static List FindDifferences(T obj1, T obj2, string path = "", HashSet visited = null) { var differences = new List(); visited ??= new HashSet(); if (obj1 == null && obj2 == null) return differences; if (obj1 == null || obj2 == null) { differences.Add($"{path}: one is null ({obj1?.ToString() ?? "null"} vs {obj2?.ToString() ?? "null"})"); return differences; } // 防止循环引用 if (visited.Contains(obj1)) return differences; visited.Add(obj1); // 使用运行时类型 var type1 = obj1.GetType(); var type2 = obj2.GetType(); if (type1 != type2) { differences.Add($"{path}: type mismatch ({type1.Name} vs {type2.Name})"); return differences; } var type = type1; // 处理基础类型 if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal) || type.IsEnum) { if (!obj1.Equals(obj2)) differences.Add($"{path}: {obj1} != {obj2}"); return differences; } // 处理集合类型 if (obj1 is IEnumerable enum1 && obj2 is IEnumerable enum2 && !(obj1 is string)) { var list1 = enum1.Cast().ToList(); var list2 = enum2.Cast().ToList(); if (list1.Count != list2.Count) { differences.Add($"{path}.Count: {list1.Count} != {list2.Count}"); } var minCount = Math.Min(list1.Count, list2.Count); for (int i = 0; i < minCount; i++) { var itemDiffs = FindDifferences(list1[i], list2[i], $"{path}[{i}]", new HashSet(visited)); differences.AddRange(itemDiffs); } return differences; } // 处理字典类型 if (type.GetInterfaces() .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))) { var keysProperty = type.GetProperty("Keys"); var itemProperty = type.GetProperty("Item"); if (keysProperty != null && itemProperty != null) { var keys1 = (IEnumerable)keysProperty.GetValue(obj1); var keys2 = (IEnumerable)keysProperty.GetValue(obj2); var keysList1 = keys1.Cast().ToHashSet(); var keysList2 = keys2.Cast().ToHashSet(); var allKeys = keysList1.Union(keysList2); foreach (var key in allKeys) { var hasKey1 = keysList1.Contains(key); var hasKey2 = keysList2.Contains(key); if (!hasKey1 || !hasKey2) { differences.Add($"{path}[{key}]: exists in {(hasKey1 ? "obj1" : "obj2")} only"); continue; } var value1 = itemProperty.GetValue(obj1, new[] { key }); var value2 = itemProperty.GetValue(obj2, new[] { key }); var itemDiffs = FindDifferences(value1, value2, $"{path}[{key}]", new HashSet(visited)); differences.AddRange(itemDiffs); } } return differences; } // 处理复杂对象 - 只检查 MemoryPack 序列化的成员 var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanRead && IsMemoryPackSerializable(p)); foreach (var prop in properties) { try { var value1 = prop.GetValue(obj1); var value2 = prop.GetValue(obj2); var propPath = string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}"; var propDiffs = FindDifferences(value1, value2, propPath, new HashSet(visited)); differences.AddRange(propDiffs); } catch (Exception ex) { differences.Add($"{path}.{prop.Name}: reflection error - {ex.Message}"); } } var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance) .Where(f => IsMemoryPackSerializable(f)); foreach (var field in fields) { try { var value1 = field.GetValue(obj1); var value2 = field.GetValue(obj2); var fieldPath = string.IsNullOrEmpty(path) ? field.Name : $"{path}.{field.Name}"; var fieldDiffs = FindDifferences(value1, value2, fieldPath, new HashSet(visited)); differences.AddRange(fieldDiffs); } catch (Exception ex) { differences.Add($"{path}.{field.Name}: reflection error - {ex.Message}"); } } return differences; } // 更完整的 MemoryPack 序列化检测 private static bool IsMemoryPackSerializable(System.Reflection.MemberInfo member) { // 检查是否有 MemoryPackIgnore 特性 if (member.GetCustomAttribute() != null) return false; // 检查是否有 MemoryPackInclude 特性(明确包含) if (member.GetCustomAttribute() != null) return true; // 对于属性:必须有 public getter 和 setter if (member is System.Reflection.PropertyInfo prop) { return prop.CanRead && prop.CanWrite && prop.GetMethod?.IsPublic == true && prop.SetMethod?.IsPublic == true; } // 对于字段:必须是 public 且非 readonly if (member is System.Reflection.FieldInfo field) { return field.IsPublic && !field.IsInitOnly && !field.IsLiteral; } return false; } // 简化的使用方法 public static bool CompareWithDetails(T obj1, T obj2) { var differences = FindDifferences(obj1, obj2); if (differences.Count > 0) { LogSystem.LogWarning($"Found {differences.Count} differences:"); foreach (var diff in differences.Take(10)) // 只显示前10个差异 { LogSystem.LogWarning($" - {diff}"); } if (differences.Count > 10) LogSystem.LogWarning($" ... and {differences.Count - 10} more differences"); } return differences.Count == 0; } public void FindDifferences(MapData target) { if (target == null) return; var allStr = ""; var diff = GetCompareEqual(target); foreach (var str in diff) allStr += str + "\n"; diff = FindDifferences(this, target); foreach (var str in diff) allStr += str + "\n"; LogSystem.LogError($"{allStr}"); for (int i = 0; i < Net.Actions.Count; i++) { if (i >= target.Net.Actions.Count) continue; if (Net.Actions[i].MapHash == target.Net.Actions[i].MapHash) continue; if (i == 0) { LogSystem.LogError($"Map不一致前后Action : 前:空" + $"后:{Main.MapData.Net.Actions[i].ActionId.GetStringLog()}"); } else { LogSystem.LogError($"Map不一致前后Action : 前:{Main.MapData.Net.Actions[i - 1].ActionId.GetStringLog()}" + $"后:{Main.MapData.Net.Actions[i].ActionId.GetStringLog()}"); } break; } } } // 地图 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; } } }