2026-04-09 11:22:14 +08:00

2248 lines
84 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* @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<PlayerSettlementInfo> PlayerSettlements;
// 单机用
public uint selfCivId;
public uint selfForceId;
// 联机用
public List<MemberCiv> MultiCivs;
private Dictionary<ulong, MemberCiv> _memberCivs;
// 校验用
private string _hash;
// 关卡限制数据
public List<MatchLimitType> MatchLimits;
// 超时时间
public bool IsLimitTime = false;
public int TimeLimitSeconds = 180;
// 水域类型,默认为 Pangea
public Logic.MapWaterType WaterType = Logic.MapWaterType.Pangea;
[MemoryPackConstructor]
public MapConfig()
{
MultiCivs = new List<MemberCiv>();
PlayerSettlements = new List<PlayerSettlementInfo>();
_memberCivs = new Dictionary<ulong, MemberCiv>();
MatchLimits = new List<MatchLimitType>();
}
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<MemberCiv>();
_memberCivs = new Dictionary<ulong, MemberCiv>();
}
// 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<ulong, MemberInfo> 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<MatchLimitType>(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<uint, uint> CityToPlayerDict;
// 小兵 -> 城市, 将引用关系转化为ID关系
public Dictionary<uint, uint> UnitToCityDict;
// 小兵 -> 格子, 将引用关系转化为ID关系
public Dictionary<uint, uint> UnitToGridDict;
// 城市 -> 格子, 将引用关系转化为ID关系
public Dictionary<uint, uint> CityToGridDict;
//--------------- 核心数据派生的数据 ---------------//
// 格子 -> 小兵, 缓存数据
private Dictionary<uint, uint> _gridToUnitDict;
// 格子 -> 城市, 缓存数据
private Dictionary<uint, uint> _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<CityData> cityDataList=null)
{
if (cityDataList == null) cityDataList = new List<CityData>();
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<CityData> cityDataList=null)
{
if (cityDataList == null) cityDataList = new HashSet<CityData>();
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<CityData> GetCityDataSetByPlayerId(uint pid)
{
var citySet = new HashSet<CityData>();
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<GridData> GetGridDataSetByPlayerId(uint pid)
{
var citySet = GetCityDataSetByPlayerId(pid);
var gridSet = new HashSet<GridData>();
foreach (var city in citySet)
{
var cityGridSet = new HashSet<uint>();
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<uint> GetGridIdSetByPlayerId(uint pid)
{
var citySet = GetCityDataSetByPlayerId(pid);
var gridSet = new HashSet<uint>();
foreach (var city in citySet)
{
var cityGridSet = new HashSet<uint>();
city.Territory.GetAllTerritoryArea(cityGridSet);
gridSet.UnionWith(cityGridSet);
}
return gridSet;
}
// 通过玩家 ID 查找首都城市 cityData
public bool GetCapitalCityDataByPlayerId(uint pid, out CityData cityData)
{
var cityList = new List<CityData>();
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<UnitData>();
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<UnitData>();
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();
// 清除数据层绑定
RemoveUnitData(unitData.Id);
if (GetGridIdByUnitId(unitData.Id, out var gridId))
_gridToUnitDict.Remove(gridId);
}
//改变小兵到城市的所属关系
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<UnitData> unitDataList=null)
{
if (unitDataList == null) unitDataList = new List<UnitData>();
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<UnitData> unitDataList=null)
{
if (unitDataList == null) unitDataList = new List<UnitData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
GetUnitDataListByCityId(kv.Key, unitDataList);
}
}
// 通过城市 ID 找小兵 Set
public void GetUnitDataListByCityId(uint cid, HashSet<UnitData> unitDataList=null)
{
if (unitDataList == null) unitDataList = new HashSet<UnitData>();
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<UnitData> unitDataList=null)
{
if (unitDataList == null) unitDataList = new HashSet<UnitData>();
foreach (var kv in CityToPlayerDict)
{
if (kv.Value != pid) continue;
GetUnitDataListByCityId(kv.Key, unitDataList);
}
}
// 通过玩家 ID 找敌方小兵列表
public void GetOtherUnitDataListByPlayerId(uint pid, List<UnitData> unitDataList=null)
{
if (unitDataList == null) unitDataList = new List<UnitData>();
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 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<uint> GetNearby1RadiusFreelandGidList(uint gid)
{
var ret = new List<uint>();
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<uint> GetPlayerTerritoryGridIdSet(uint pid)
{
var gridSet = new HashSet<uint>();
var cityList = new List<CityData>();
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));
},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));
}
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>() { 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<IdentifierBase> GetAllIdentifierBase()
{
var list = new List<IdentifierBase>();
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<uint, uint>();
UnitToCityDict = new Dictionary<uint, uint>();
UnitToGridDict = new Dictionary<uint, uint>();
CityToGridDict = new Dictionary<uint, uint>();
_gridToCityDict = new Dictionary<uint, uint>();
_gridToUnitDict = new Dictionary<uint, uint>();
_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<uint, uint>();
UnitToCityDict = new Dictionary<uint, uint>();
UnitToGridDict = new Dictionary<uint, uint>();
CityToGridDict = new Dictionary<uint, uint>();
_gridToCityDict = new Dictionary<uint, uint>();
_gridToUnitDict = new Dictionary<uint, uint>();
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<uint, uint>();
UnitToCityDict = new Dictionary<uint, uint>();
UnitToGridDict = new Dictionary<uint, uint>();
CityToGridDict = new Dictionary<uint, uint>();
_gridToCityDict = new Dictionary<uint, uint>();
_gridToUnitDict = new Dictionary<uint, uint>();
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<uint, uint>();
_gridToUnitDict ??= new Dictionary<uint, uint>();
_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<SkillBase>(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<SkillBase>(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<SkillBase>(unit.Skills);
foreach (var skill in copy) skill.OnAnyUnitMove(map, unit, moveUnit, target, moveType);
}
var gridCopy = new List<SkillBase>(target.Skills);
foreach (var skill in gridCopy) skill.OnAnyUnitMove(map, target, moveUnit, target, moveType);
}
// 当行为执行后
public void OnActionExecuted(ActionLogicBase logic, CommonActionParams param)
{
foreach (var unit in UnitMap.UnitList)
{
var copy = new List<SkillBase>(unit.Skills);
foreach (var skill in copy) skill.OnActionExecuted(logic, param, unit);
}
}
// 当场小兵死上有亡时
public void OnAnyUnitDie(MapData map, UnitData dieUnit)
{
var unitListCopy = new List<UnitData>(UnitMap.UnitList);
foreach (var unit in unitListCopy)
{
//避免在遍历的时候直接改到了skillsList
var copy = new List<SkillBase>(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<SkillBase>(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<MapConfig>(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<MapData>(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<CityData>();
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<string>();
// 使用序列化比较各个组件
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<string> GetCompareEqual(MapData map)
{
var differences = new List<string>();
// 使用序列化比较各个组件
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>(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>(T obj1, T obj2, string name, List<string> 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<string> FindDifferences<T>(T obj1, T obj2, string path = "", HashSet<object> visited = null)
{
var differences = new List<string>();
visited ??= new HashSet<object>();
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<object>().ToList();
var list2 = enum2.Cast<object>().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<object>(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<object>().ToHashSet();
var keysList2 = keys2.Cast<object>().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<object>(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<object>(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<object>(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<MemoryPackIgnoreAttribute>() != null)
return false;
// 检查是否有 MemoryPackInclude 特性(明确包含)
if (member.GetCustomAttribute<MemoryPackIncludeAttribute>() != 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>(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;
}
}
}