1389 lines
52 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.Generic;
using System.IO;
using System.Linq;
using Logic;
using Logic.Action;
using Logic.AI;
using Logic.CrashSight;
using MemoryPack;
using ParadoxNotion;
using TH1_Logic.Core;
using TH1_Logic.HeroTask;
using TH1_Logic.Net;
using UnityEngine;
namespace RuntimeData
{
public enum GameMode
{
PERFECT,
DOMINATION,
CREATIVE
}
// 一场游戏的设置数据
[MemoryPackable]
public partial class MapConfig
{
public uint Width;
public uint Height;
public uint PlayerCount;
public AIDifficult AIDiff;
public GameMode GameMode;
public uint selfCivId;
public uint selfForceId;
[MemoryPackConstructor]
public MapConfig()
{
}
public MapConfig(uint width, uint height, uint playerCount,uint civId, uint forceId, AIDifficult aiDiff = AIDifficult.LUNATIC)
{
Width = width;
Height = height;
PlayerCount = playerCount;
AIDiff = aiDiff;
selfCivId = civId;
selfForceId = forceId;
}
}
// 一场游戏的地图数据
[MemoryPackable]
public partial class MapData
{
//--------------- 基础核心数据 ---------------//
// 地图 ID
public uint MapID;
// 地图配置数据
public MapConfig MapConfig;
// 地块数据
public GridMapData GridMap;
// 玩家数据
public PlayerMapData PlayerMap;
// 城市数据
public CityMapData CityMap;
// 小兵数据
public UnitMapData UnitMap;
// 网络数据
public NetData Net;
// 当前玩家
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;
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;
//step #2 所有unit都销毁
var unitDataList = new List<UnitData>();
GetUnitDataListByCityId(cid,unitDataList);
foreach (var unitData in unitDataList)
SetUnitDataDie(unitData);
//step #3 撤销首都
city.IsCapital = false;
}
//否则要将所有unit转移到老东家的capital下面去
else
{
//如果cid的老东家的首都就是cid那么老东家首先要更换首都
if (oldPlayerCapitalCityData.Id == cid)
{
//遍历cityList找到第一个老东家的其他城市让他变成新的首都
foreach (var tmpCityData in CityMap.CityList)
{
if (tmpCityData.Id == cid) continue;
if (GetPlayerIdByCityId(tmpCityData.Id, out var tmpPlayerId) && tmpPlayerId == oldPlayerData.Id)
{
tmpCityData.IsCapital = true;
oldPlayerCapitalCityData.IsCapital = false;
oldPlayerCapitalCityData = tmpCityData;
break;
}
}
}
//开始将所有cid的小兵都转移到oldPlayerCapitalCityData下去
var tmpUnitDataList = new List<UnitData>();
GetUnitDataListByCityId(cid, tmpUnitDataList);
foreach(var tmpUnitData in tmpUnitDataList)
SetUnitIdToCityId(tmpUnitData.Id,oldPlayerCapitalCityData.Id);
oldPlayerCapitalCityData.CityInfoRenderer(this)?.SetUpdateCityInfo(true);
}
//如果这次这个城市是newPlayer的原始首都那么new Player要更换首都
if (newPlayerData.CradleCityId == cid)
{
city.IsCapital = true;
newPlayerCapitalCityData.IsCapital = false;
}
//正式更换城市归属
CityToPlayerDict[cid] = pid;
if (CityMap.GetCityById(cid, out var cityData))
{
cityData.SetCityRenderer(this);
if (GetGridDataByCityId(cid, out var gridData))
gridData.Renderer(this)?.SetUpdateCityBuilding(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.RenderMark = true;
}
public void SetUnitDataDie(UnitData unitData)
{
unitData.Alive = false;
// 清除数据层绑定
RemoveUnitData(unitData.Id);
if (GetGridIdByUnitId(unitData.Id, out var gridId))
_gridToUnitDict.Remove(gridId);
UnitMap.UnitMapRenderMark = true;
}
//改变小兵到城市的所属关系
public void SetUnitIdToCityId(uint uid, uint cid)
{
UnitToCityDict[uid] = cid;
//if (CityMap.GetCityById(cid, out var cityData)) cityData.RenderMark = true;
//if (PlayerMap.GetPlayerDataByPlayerID(pid, out var playerData)) playerData.RenderMark = true;
}
//获得一个玩家有多少个城市
public int GetCityCount(uint pid)
{
int ret = 0;
foreach (var cityData in CityMap.CityList)
if (CityToPlayerDict.ContainsKey(cityData.Id) && CityToPlayerDict[cityData.Id] == pid)
ret++;
return ret;
}
//获得一共城市有多少单位
public int GetUnitCount(uint cid)
{
int ret = 0;
foreach (var unit in UnitMap.UnitList)
if (UnitToCityDict.ContainsKey(unit.Id) && UnitToCityDict[unit.Id] == cid)
ret+= unit.GetPopulation();
return ret;
}
//-------------- 读写小兵方法 -------------------//
// 通过格子 gid 找小兵 uid
public bool GetUnitIdByGid(uint gid, out uint uid)
{
return _gridToUnitDict.TryGetValue(gid, out uid);
}
// 通过格子 gid 找小兵 data
public bool GetUnitDataByGid(uint gid, out UnitData unitData)
{
unitData = null;
/*foreach(var tt in UnitMap.UnitList)
if (GetGridDataByUnitId(tt.Id,out var gridData))
if (gridData.Id == gid)
{
unitData = tt;
return true;
}
return false;*/
//TODO _gridToUnitDict这个缓存有错和unitToGrid没有对上。 暂时用了上面的办法,之后需要修改这里的缓存
if (_gridToUnitDict.TryGetValue(gid, out var uid))
{
return UnitMap.GetUnitDataByUnitId(uid, out unitData);
}
return false;
}
// 通过城市 ID 找小兵列表
public void GetUnitDataListByCityId(uint cid, List<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 GetUnitByGiantType(GiantType giantType, uint unitLevel, out UnitData unitData)
{
foreach(var t in UnitMap.UnitList)
if (t.GiantType == giantType && t.UnitLevel == unitLevel)
{
unitData = t;
return true;
}
unitData = null;
return false;
}
//-------------- 读写玩家方法 -------------------//
// 通过城市找玩家 pid
public bool GetPlayerIdByCityId(uint cityId, out uint playerId)
{
return CityToPlayerDict.TryGetValue(cityId, out playerId);
}
// 通过城市找玩家 data
public bool GetPlayerDataByCityId(uint cityId, out PlayerData playerData)
{
playerData = null;
if (CityToPlayerDict.TryGetValue(cityId, out var playerId))
{
playerData = PlayerMap.GetPlayerData(playerId);
}
return playerData != null;
}
// 通过小兵 uid 找城市 pid
public bool GetPlayerIdByUnitId(uint uid, out uint pid)
{
if (UnitToCityDict.TryGetValue(uid, out var cid))
{
return CityToPlayerDict.TryGetValue(cid, out pid);
}
pid = 0;
return false;
}
// 通过小兵 uid 找城市数据 playerData
public bool GetPlayerDataByUnitId(uint uid, out PlayerData playerData)
{
playerData = null;
if (UnitToCityDict.TryGetValue(uid, out var cid))
{
if (CityToPlayerDict.TryGetValue(cid, out var pid))
{
playerData = PlayerMap.GetPlayerData(pid);
}
}
return playerData != null;
}
// 通过领土位置找玩家
public bool GetPlayerDataByTerritoryGridId(uint gid, out PlayerData playerData)
{
if (CityMap.GetCityDataByTerritoryGridId(gid, out var cityData))
{
return GetPlayerDataByCityId(cityData.Id, out playerData);
}
playerData = null;
return false;
}
//判断playerA和playerB是否同一个联盟
public bool SameUnion(uint pidA, uint pidB)
{
if (pidA == pidB) return true;
if (!PlayerMap.GetPlayerDataByPlayerID(pidA, out var pA)) return false;
pA.DiplomacyData.GetCountryDiplomacyInfo(pidB,out var dipInfo);
return dipInfo.DiplomacyState == DiplomacyState.League;
}
public bool SameUnionByUnitId(uint uidA, uint uidB)
{
if (!GetPlayerDataByUnitId(uidA, out var pA)) return false;
if (!GetPlayerDataByUnitId(uidB, out var pB)) return false;
return SameUnion(pA.Id,pB.Id);
}
public bool SameUnionOrJustBreakUnion(uint pidA, uint pidB)
{
if (pidA == pidB) return true;
if (!PlayerMap.GetPlayerDataByPlayerID(pidA, out var pA)) return false;
pA.DiplomacyData.GetCountryDiplomacyInfo(pidB,out var dipInfo);
return dipInfo.DiplomacyState is DiplomacyState.League or DiplomacyState.LeagueRupture ;
}
//-------------- 查询方法方法 -------------------//
// 通过小兵 uid 找格子 gid
public bool GetGridIdByUnitId(uint uid, out uint gid)
{
return UnitToGridDict.TryGetValue(uid, out gid);
}
// 通过小兵 uid 找格子数据 data
public bool GetGridDataByUnitId(uint uid, out GridData data)
{
if (UnitToGridDict.TryGetValue(uid, out var gid))
{
return GridMap.GetGridDataByGid(gid, out data);
}
data = null;
return false;
}
// 通过城市 cid 找格子 gid
public bool GetGridIdByCityId(uint cid, out uint gid)
{
return CityToGridDict.TryGetValue(cid, out gid);
}
//根据曼哈顿距离返回最近的城市
public bool GetNearestCity(UnitData unit,out CityData outCity)
{
outCity = null;
int score = -1;
var unitPlayer = unit.Player(this);
var unitGrid = unit.Grid(this);
if (unitPlayer == null || unitGrid == null) return false;
foreach (var city in CityMap.CityList)
{
var cityPlayer = city.Player(this);
var cityGrid = city.Grid(this);
if (cityPlayer == null || cityGrid == null || cityPlayer != unitPlayer) continue;
if (score == -1 || GridMap.CalcManhattanDistance(cityGrid, unitGrid) < score)
{
outCity = city;
score = GridMap.CalcManhattanDistance(cityGrid, unitGrid);
}
}
return score != -1;
}
// 通过城市 cid 找格子数据 data
public bool GetGridDataByCityId(uint cid, out GridData data)
{
if (CityToGridDict.TryGetValue(cid, out var gid))
{
return GridMap.GetGridDataByGid(gid, out data);
}
data = null;
return false;
}
//判断当前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)?.SetUpdateGrid(true);
CityMap.CityMapRenderMark = true;
cityData.SetCityRenderer(this);
return cityData;
}
// 新建小兵数据
public bool AddUnitData(uint gid, uint cid, UnitFullType unitFullType,out UnitData newUnit)
{
//Step #1 先检查是否能生成比如在水上生成landOnly的单位就不行)
newUnit = null;
if (!GridMap.GetGridDataByGid(gid, out GridData grid)) return false;
if(!CheckLandTypeForGrid(unitFullType, grid))
return false;
//Step #2 再生成单位
newUnit = UnitMap.AddUnitData(unitFullType, _idGenerator);
UnitToCityDict[newUnit.Id] = cid;
UnitToGridDict[newUnit.Id] = gid;
_gridToUnitDict[gid] = newUnit.Id;
UnitMap.UnitMapRenderMark = true;
newUnit.RenderMark = true;
AddUnitSkill(newUnit);
return true;
}
//
public bool CheckLandTypeForGrid(UnitFullType unitFullType, GridData grid)
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitFullType, out var info)) return false;
if (grid.Terrain is TerrainType.Land)
return info.LandType is LandType.Fly or LandType.LandAndWater or LandType.LandAndPort
or LandType.LandOnly or LandType.WaterAndAshore;
if (grid.Terrain is TerrainType.ShallowSea or TerrainType.DeepSea)
{
if (grid.Resource == ResourceType.Bridge)
{
return true;
}
if (grid.Resource == ResourceType.Port)
{
return info.LandType is LandType.Fly or LandType.WaterAndAshore or LandType.WaterOnly
or LandType.LandAndWater or LandType.LandAndPort;
}
return info.LandType is LandType.Fly or LandType.WaterAndAshore or LandType.WaterOnly
or LandType.LandAndWater;
}
return true;
}
//给Unit增加skill ,通用类TODO 将来给techlist 加入tech类tech类再关联action类会比较好
public void AddUnitSkill(UnitData unit)
{
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unit.UnitType, unit.GiantType, unit.UnitLevel,out var unitInfo)) return;
if (!GetPlayerDataByUnitId(unit.Id, out var player)) return;
var common = new List<SkillType>() { SkillType.WATERDEFENSE ,SkillType.WATERMOVE,SkillType.OCEANDEFENSE,SkillType.OCEANMOVE,SkillType.MOUNTAINDEFENSE,SkillType.MOUNTAINMOVE,SkillType.FORESTDEFENSE};
//Step #1先添加小兵在配表中自带的技能
foreach (var skill in unitInfo.Skills)
unit.AddSkill(skill);
//Step #2再添加小兵在配表中自带的技能
var t = new CommonActionId();
t.ActionType = CommonActionType.UnitSkill;
foreach (var s in common)
{
t.SkillType = s;
if (!player.TechTree.CheckActionCan(t)) continue;
unit.AddSkill(s);
}
//Step #3 最后处理特判情况
//先处理辉夜的KaguyaFrenchWarriorSynergy
if (unit.UnitType == UnitType.Warrior && player.TechTree.CheckIfHasTech(TechType.KaguyaHunting))
unit.AddSkill(SkillType.KAGUYAFRENCHSYNERGY);
if (unit.UnitType == UnitType.Catapult && player.TechTree.CheckIfHasTech(TechType.KaguyaMath))
unit.AddSkill(SkillType.KAGUYAFRENCHSYNERGY);
//处理bambooMove
if (player.TechTree.CheckIfHasTechAtom(TechAtom.KaguyaFrenchBambooMove))
unit.AddSkill(SkillType.BAMBOOMOVE);
//Step #4 处理SkillAdd的生命周期
foreach (var skill in unitInfo.Skills)
if(unit.GetSkill(skill, out var ski))ski.OnSkillAdd(unit,this);
}
// 移除小兵数据
public void RemoveUnitData(uint uid)
{
UnitMap.RemoveUnitData(uid);
if (UnitToGridDict.Remove(uid, out var gid))
{
_gridToUnitDict.Remove(gid);
}
UnitToGridDict.Remove(uid);
UnitToCityDict.Remove(uid);
}
// 获取所有 ID 单位
public List<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;
}
//-------------- 生命周期方法 -------------------//
// 用于网络传输的默认反序列化构造
[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)
{
MapID = 0;
MapConfig = mapCfg;
_idGenerator = new MapIdGenerator();
GridMap = new GridMapData(mapCfg, _idGenerator);
PlayerMap = new PlayerMapData(mapCfg, _idGenerator);
CityMap = new CityMapData();
UnitMap = new UnitMapData();
Net = new NetData();
CityToPlayerDict = new Dictionary<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>();
}
public MapData(MapData copyMap)
{
MapConfig = copyMap.MapConfig;
_idGenerator = copyMap._idGenerator.DeepCopy();
GridMap = new GridMapData(copyMap.GridMap);
PlayerMap = new PlayerMapData(copyMap.PlayerMap);
CityMap = new CityMapData(copyMap.CityMap);
UnitMap = new UnitMapData(copyMap.UnitMap);
Net = new NetData(copyMap.Net);
CityToPlayerDict = new Dictionary<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)
{
MapConfig = copyMap.MapConfig;
_idGenerator.DeepCopy(copyMap._idGenerator);
GridMap.DeepCopy(copyMap.GridMap);
PlayerMap.DeepCopy(copyMap.PlayerMap);
CityMap.DeepCopy(copyMap.CityMap);
UnitMap.DeepCopy(copyMap.UnitMap);
Net.DeepCopy(copyMap.Net);
CityToPlayerDict.Clear();
UnitToCityDict.Clear();
UnitToGridDict.Clear();
CityToGridDict.Clear();
_gridToCityDict.Clear();
_gridToUnitDict.Clear();
foreach (var kv in copyMap.CityToPlayerDict) CityToPlayerDict[kv.Key] = kv.Value;
foreach (var kv in copyMap.UnitToCityDict) UnitToCityDict[kv.Key] = kv.Value;
foreach (var kv in copyMap.UnitToGridDict) UnitToGridDict[kv.Key] = kv.Value;
foreach (var kv in copyMap.CityToGridDict) CityToGridDict[kv.Key] = kv.Value;
foreach (var kv in copyMap._gridToCityDict) _gridToCityDict[kv.Key] = kv.Value;
foreach (var kv in copyMap._gridToUnitDict) _gridToUnitDict[kv.Key] = kv.Value;
}
// MemoryPack 反序列化之后的后处理
[MemoryPackOnDeserialized]
public void OnAfterMemoryPackDeserialize()
{
// 重绑定 ID
Net.RefreshPlayerNet(this);
// 重建缓存字典
_gridToCityDict ??= new Dictionary<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);
}
// 当场上有小兵受伤时
public void OnUnitDamaged(SettlementInfo info)
{
foreach (var idBase in GetAllIdentifierBase())
{
foreach (var skill in idBase.Skills) skill.OnUnitDamaged(idBase, this, info);
}
}
// 当场上有小兵移动时
public void OnOtherUnitMove(UnitData moveUnit, GridData target)
{
foreach (var idBase in GetAllIdentifierBase())
{
foreach (var skill in idBase.Skills) skill.OnOtherUnitMove(idBase, moveUnit, target, this);
}
}
public static void SaveMapData(MapData map)
{
if (map == null) return;
// 改为二进制文件扩展名
string path = Application.persistentDataPath + "/map_archive.dat";
int retryCount = 3;
while (retryCount > 0)
{
try
{
byte[] bytes = MemoryPackSerializer.Serialize(map);
File.WriteAllBytes(path, bytes);
return;
}
catch (IOException ex)
{
retryCount--;
if (retryCount <= 0)
{
LogSystem.LogError($"保存地图数据失败: {ex.Message}");
}
}
}
}
public static MapData GetMapData()
{
string path = Application.persistentDataPath + "/map_archive.dat"; // 改为二进制文件扩展名
if (!File.Exists(path)) return null;
int retryCount = 3;
while (retryCount > 0)
{
try
{
byte[] bytes = File.ReadAllBytes(path);
return MemoryPackSerializer.Deserialize<MapData>(bytes);
}
catch (IOException ex)
{
retryCount--;
if (retryCount <= 0)
{
LogSystem.LogError($"读取地图数据失败: {ex.Message}");
return null;
}
}
}
return null;
}
public GameRecord ExportGameRecord()
{
var gameRecord = new GameRecord();
gameRecord.Mode = MapConfig.GameMode;
gameRecord.AIDiff = MapConfig.AIDiff;
gameRecord.Turn = PlayerMap.SelfPlayerData.Turn;
gameRecord.PlayerCivId = PlayerMap.SelfPlayerData.PlayerCivId;
gameRecord.PlayerForceId = PlayerMap.SelfPlayerData.PlayerForceId;
gameRecord.Score = PlayerMap.SelfPlayerData.PlayerScore;
DateTime now = DateTime.Now;
gameRecord.Time = now.ToString("yyyy.MM.dd HH:mm");
var cityList = new List<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
//判断英雄的真实level
public uint GetGiantRealLv(GiantType giantType, PlayerData player)
{
if (player == null) return 0;
//获取unitTypeInfo,level=0时的信息
if(!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(UnitType.Giant, giantType,0,
out var Lv0Info))
return 0;
//确认玩家最新的hero的unitLevel获得level=realLv的info
var realLv = Table.Instance.GiantExpToLevel(player.giantExp[(uint)Lv0Info.ChessType]);
//判断有没有解锁科技没有的话realLv = 0
var action = new CommonActionId();
action.ActionType = CommonActionType.TrainUnit;
action.UnitType = UnitType.Giant;
action.GiantType = giantType;
if (!player.TechTree.CheckActionCan(action))
realLv = 0;
return (uint)realLv;
}
// 更新回合
public 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 (Net.CurPlayerId != 0 && Time.time - Net.PlayerStartTime >= 60)
// 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;
}
// 存档
SaveMapData(Main.MapData);
PlayerPrefs.SetInt("Archive", 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 = PlayerMap.PlayerDataList[0].Turn;
foreach (var player in PlayerMap.PlayerDataList)
{
// 如果要做复活逻辑,需要处理复活玩家的 Turn
if (!player.Alive) continue;
if (player.Turn >= curTurn) continue;
return player;
}
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 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;
}
// 游戏是否结束
public bool CheckIfGameEnd(out bool isWin)
{
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;
return aliveCount <= 1;
}
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;
}
// 检查两个 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(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}");
}
}
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}");
}
}
}
// 地图 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;
}
}
}