1153 lines
42 KiB
C#
1153 lines
42 KiB
C#
/*
|
||
* @Author: 白哉
|
||
* @Description:
|
||
* @Date: 2025年04月03日 星期四 11:04:31
|
||
* @Modify:
|
||
*/
|
||
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using Logic;
|
||
using Logic.Action;
|
||
using Logic.AI;
|
||
using Logic.CrashSight;
|
||
using MemoryPack;
|
||
using ParadoxNotion;
|
||
using TH1_Logic.Core;
|
||
using TH1_Logic.HeroTask;
|
||
using UnityEngine;
|
||
|
||
|
||
namespace RuntimeData
|
||
{
|
||
|
||
public enum GameMode
|
||
{
|
||
PERFECT,
|
||
DOMINATION,
|
||
CREATIVE
|
||
}
|
||
|
||
// 一场游戏的设置数据
|
||
[MemoryPackable]
|
||
public partial class MapConfig
|
||
{
|
||
public uint Width;
|
||
public uint Height;
|
||
public uint PlayerCount;
|
||
public AIDifficult AIDiff;
|
||
public GameMode GameMode;
|
||
|
||
public uint selfCivId;
|
||
public uint selfForceId;
|
||
|
||
[MemoryPackConstructor]
|
||
public MapConfig()
|
||
{
|
||
|
||
}
|
||
|
||
public MapConfig(uint width, uint height, uint playerCount,uint civId, uint forceId, AIDifficult aiDiff = AIDifficult.LUNATIC)
|
||
{
|
||
Width = width;
|
||
Height = height;
|
||
PlayerCount = playerCount;
|
||
AIDiff = aiDiff;
|
||
selfCivId = civId;
|
||
selfForceId = forceId;
|
||
}
|
||
}
|
||
|
||
|
||
// 一场游戏的地图数据
|
||
[MemoryPackable]
|
||
public partial class MapData
|
||
{
|
||
//--------------- 基础核心数据 ---------------//
|
||
// 地图 ID
|
||
public uint MapID;
|
||
// 地图配置数据
|
||
public MapConfig MapConfig;
|
||
// 地块数据
|
||
public GridMapData GridMap;
|
||
// 玩家数据
|
||
public PlayerMapData PlayerMap;
|
||
// 城市数据
|
||
public CityMapData CityMap;
|
||
// 小兵数据
|
||
public UnitMapData UnitMap;
|
||
// 网络数据
|
||
public NetData Net;
|
||
|
||
// 城市 -> 玩家, 将引用关系转化为ID关系
|
||
public Dictionary<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.RenderMark = true;
|
||
|
||
}
|
||
|
||
//如果这次这个城市是newPlayer的原始首都,那么new Player要更换首都
|
||
if (newPlayerData.CradleCityId == cid)
|
||
{
|
||
city.IsCapital = true;
|
||
newPlayerCapitalCityData.IsCapital = false;
|
||
}
|
||
|
||
//正式更换城市归属
|
||
CityToPlayerDict[cid] = pid;
|
||
if (CityMap.GetCityById(cid, out var cityData))
|
||
{
|
||
cityData.RenderMark = true;
|
||
if (GetGridDataByCityId(cid, out var gridData))
|
||
gridData.CityBuildingRenderMark = true;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
//改变小兵的位置
|
||
public void SetUnitIdToGridId(uint uid, uint gid)
|
||
{
|
||
if (UnitToGridDict.TryGetValue(uid, out var oldGid))
|
||
{
|
||
_gridToUnitDict.Remove(oldGid);
|
||
}
|
||
|
||
UnitToGridDict[uid] = gid;
|
||
_gridToUnitDict[gid] = uid;
|
||
if (UnitMap.GetUnitDataByUnitId(uid, out var unitData))
|
||
unitData.RenderMark = true;
|
||
}
|
||
|
||
public void SetUnitDataDie(UnitData unitData)
|
||
{
|
||
unitData.Alive = false;
|
||
|
||
// 标记原city的rendermark
|
||
if (GetCityDataByUnitId(unitData.Id, out var cityData))
|
||
cityData.CityInfoRenderMark = true;
|
||
|
||
// 清除数据层绑定
|
||
RemoveUnitData(unitData.Id);
|
||
if (GetGridIdByUnitId(unitData.Id, out var gridId))
|
||
_gridToUnitDict.Remove(gridId);
|
||
|
||
UnitMap.UnitMapRenderMark = true;
|
||
}
|
||
|
||
//改变小兵到城市的所属关系
|
||
public void SetUnitIdToCityId(uint uid, uint cid)
|
||
{
|
||
UnitToCityDict[uid] = cid;
|
||
//if (CityMap.GetCityById(cid, out var cityData)) cityData.RenderMark = true;
|
||
//if (PlayerMap.GetPlayerDataByPlayerID(pid, out var playerData)) playerData.RenderMark = true;
|
||
}
|
||
|
||
|
||
//获得一个玩家有多少个城市
|
||
public int GetCityCount(uint pid)
|
||
{
|
||
int ret = 0;
|
||
foreach (var cityData in CityMap.CityList)
|
||
if (CityToPlayerDict.ContainsKey(cityData.Id) && CityToPlayerDict[cityData.Id] == pid)
|
||
ret++;
|
||
|
||
return ret;
|
||
}
|
||
|
||
//获得一共城市有多少单位
|
||
public int GetUnitCount(uint cid)
|
||
{
|
||
int ret = 0;
|
||
foreach (var unit in UnitMap.UnitList)
|
||
if (UnitToCityDict.ContainsKey(unit.Id) && UnitToCityDict[unit.Id] == cid)
|
||
ret+= unit.GetPopulation();
|
||
return ret;
|
||
}
|
||
|
||
//-------------- 读写小兵方法 -------------------//
|
||
// 通过格子 gid 找小兵 uid
|
||
public bool GetUnitIdByGid(uint gid, out uint uid)
|
||
{
|
||
return _gridToUnitDict.TryGetValue(gid, out uid);
|
||
}
|
||
|
||
// 通过格子 gid 找小兵 data
|
||
public bool GetUnitDataByGid(uint gid, out UnitData unitData)
|
||
{
|
||
unitData = null;
|
||
|
||
/*foreach(var tt in UnitMap.UnitList)
|
||
if (GetGridDataByUnitId(tt.Id,out var gridData))
|
||
if (gridData.Id == gid)
|
||
{
|
||
unitData = tt;
|
||
return true;
|
||
}
|
||
|
||
return false;*/
|
||
//TODO _gridToUnitDict这个缓存有错,和unitToGrid没有对上。 暂时用了上面的办法,之后需要修改这里的缓存
|
||
if (_gridToUnitDict.TryGetValue(gid, out var uid))
|
||
{
|
||
return UnitMap.GetUnitDataByUnitId(uid, out unitData);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 通过城市 ID 找小兵列表
|
||
public void GetUnitDataListByCityId(uint cid, List<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);
|
||
}
|
||
|
||
|
||
//-------------- 读写玩家方法 -------------------//
|
||
// 通过城市找玩家 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);
|
||
}
|
||
|
||
// 通过城市 cid 找格子数据 data
|
||
public bool GetGridDataByCityId(uint cid, out GridData data)
|
||
{
|
||
if (CityToGridDict.TryGetValue(cid, out var gid))
|
||
{
|
||
return GridMap.GetGridDataByGid(gid, out data);
|
||
}
|
||
|
||
data = null;
|
||
return false;
|
||
}
|
||
|
||
|
||
//------------- 计算地图信息方法 -----------------//
|
||
public bool CheckIfNearByGridSamePlayer(GridData gridData, int dir)
|
||
{
|
||
dir--;
|
||
int x = gridData.Pos.X + dir % 3 - 1, y = gridData.Pos.Y + dir / 3 - 1;
|
||
if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height)
|
||
return false;
|
||
if (GridMap.GetGridDataByPos(x, y, out GridData gridData2))
|
||
{
|
||
GetPlayerDataByTerritoryGridId(gridData.Id,out var p1);
|
||
GetPlayerDataByTerritoryGridId(gridData2.Id,out var p2);
|
||
return p1 == p2;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
public bool CheckIfNearByGridSameCity(GridData gridData, int dir)
|
||
{
|
||
dir--;
|
||
int x = gridData.Pos.X + dir % 3 - 1, y = gridData.Pos.Y + dir / 3 - 1;
|
||
if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height)
|
||
return false;
|
||
if (GridMap.GetGridDataByPos(x, y, out GridData gridData2))
|
||
{
|
||
GetCityDataByTerritoryGid(gridData.Id,out var c1);
|
||
GetCityDataByTerritoryGid(gridData2.Id,out var c2);
|
||
return c1 == c2;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//返回在gid周围1格内,dir方向的那格是否是freeland
|
||
public bool CheckIfNearByGridFreeland(GridData gridData, int dir,out GridData gridData2)
|
||
{
|
||
gridData2 = null;
|
||
dir--;
|
||
int x = (int)gridData.Pos.X + dir % 3 - 1, y = (int)gridData.Pos.Y + dir / 3 - 1;
|
||
if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height)
|
||
return false;
|
||
if(GridMap.GetGridDataByPos(x, y, out gridData2))
|
||
return !GetPlayerDataByTerritoryGridId(gridData2.Id,out var playerData);
|
||
return false;
|
||
}
|
||
|
||
//判断grid的dir方向是不是道路或者海路联通的。注意要区分 陆地港口桥梁 和 港口海域 两种情况,港口是两种情况都出现的
|
||
public bool CheckIfNearByGridRoadCanConnenct(GridData gridData, int dir)
|
||
{
|
||
int x = gridData.Pos.X + dir % 3 - 1, y = gridData.Pos.Y + dir / 3 - 1;
|
||
if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height) return false;
|
||
if (!GridMap.GetGridDataByPos(x, y, out var gridData2)) return false;
|
||
|
||
//如果有一个没有road return
|
||
if (gridData.Feature != TerrainFeature.Road || gridData2.Feature != TerrainFeature.Road) return false;
|
||
|
||
//判断主权情况 均有主权且不是一个同盟要return false
|
||
if (GetPlayerDataByTerritoryGridId(gridData.Id, out var playerData1) &&
|
||
GetPlayerDataByTerritoryGridId(gridData2.Id, out var playerData2) &&
|
||
!SameUnion(playerData1.Id, playerData2.Id))
|
||
return false;
|
||
|
||
//排除一个是非港口海域,一个是陆地的情况
|
||
if (gridData.Terrain != TerrainType.Land && gridData.Resource != ResourceType.Port &&
|
||
gridData2.Terrain == TerrainType.Land)
|
||
{
|
||
//bridge的情况特判
|
||
if (gridData.Resource == ResourceType.Bridge) return true;
|
||
return false;
|
||
}
|
||
|
||
if (gridData2.Terrain != TerrainType.Land && gridData2.Resource != ResourceType.Port &&
|
||
gridData.Terrain == TerrainType.Land)
|
||
{
|
||
//bridge的情况特判
|
||
if (gridData2.Resource == ResourceType.Bridge) return true;
|
||
return false;
|
||
}
|
||
|
||
//剩下情况都是true
|
||
return true;
|
||
}
|
||
|
||
//返回在gid周围1个内,dir方向的那格是不是land
|
||
public bool CheckIfNearByGridIsLand(GridData gridData, int dir,out GridData gridData2)
|
||
{
|
||
gridData2 = null;
|
||
dir--;
|
||
int x = (int)gridData.Pos.X + dir % 3 - 1, y = (int)gridData.Pos.Y + dir / 3 - 1;
|
||
if(x < 0 || x >= MapConfig.Width || y < 0 || y >= MapConfig.Height)
|
||
return false;
|
||
GridMap.GetGridDataByPos(x, y, out gridData2);
|
||
return gridData2.Terrain == TerrainType.Land;
|
||
}
|
||
|
||
public List<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)
|
||
{
|
||
uint civId = PlayerMap.GetPlayerData(pid).PlayerCivId;
|
||
|
||
|
||
int cityRk = Table.Instance.cityNameLibraryHat[civId];
|
||
//如果是真Map
|
||
if (this == Main.MapData)
|
||
Table.Instance.cityNameLibraryHat[civId]++;
|
||
|
||
Table.Instance.CivDataAssets.GetCivInfo(civId, out var civData);
|
||
|
||
//避免越界 保险mod一下
|
||
if (cityRk >= civData.CityInfoList.Count)
|
||
{
|
||
LogSystem.LogError("MapData_AddCityData_CityInfoList_Array_OverFlow");
|
||
cityRk %= civData.CityInfoList.Count;
|
||
}
|
||
|
||
|
||
|
||
CityLibrary nameEnum = civData.CityInfoList[cityRk].CityNameEnum;
|
||
CityData cityData = CityMap.AddCityData(GetNearby1RadiusFreelandGidList(gid), nameEnum, _idGenerator);
|
||
CityToPlayerDict[cityData.Id] = pid;
|
||
CityToGridDict[cityData.Id] = gid;
|
||
_gridToCityDict[gid] = cityData.Id;
|
||
//建立city会在改格子上自动建立road
|
||
if (GridMap.GetGridDataByGid(gid, out var gridData))
|
||
gridData.Feature = TerrainFeature.Road;
|
||
|
||
//处理rendermark
|
||
var gridIdSet = GetPlayerTerritoryGridIdSet(pid);
|
||
foreach(var gridId in gridIdSet)
|
||
if (GridMap.GetGridDataByGid(gridId, out var gridDataTerr))
|
||
gridDataTerr.RenderMark = true;
|
||
CityMap.CityMapRenderMark = true;
|
||
cityData.RenderMark = true;
|
||
cityData.CityInfoRenderMark = true;
|
||
return cityData;
|
||
}
|
||
|
||
// 新建小兵数据
|
||
public UnitData AddUnitData(uint gid, uint cid, UnitType unitType,GiantType giantType = GiantType.None,uint unitLevel = 0)
|
||
{
|
||
UnitData unitData = UnitMap.AddUnitData(unitType,giantType,unitLevel, _idGenerator);
|
||
UnitToCityDict[unitData.Id] = cid;
|
||
UnitToGridDict[unitData.Id] = gid;
|
||
_gridToUnitDict[gid] = unitData.Id;
|
||
UnitMap.UnitMapRenderMark = true;
|
||
unitData.RenderMark = true;
|
||
|
||
// 加任务
|
||
unitData.TaskContent = HeroTaskManager.Instance.GetHeroTaskContent(unitData);
|
||
|
||
//给角色新增skill
|
||
if (!Table.Instance.UnitTypeDataAssets.GetUnitTypeInfo(unitType, giantType, unitLevel,out var unitInfo))
|
||
return unitData;
|
||
|
||
foreach (var skill in unitInfo.Skills)
|
||
unitData.AddSkill(skill);
|
||
if (!GetPlayerDataByUnitId(unitData.Id, out var playerData))
|
||
return unitData;
|
||
|
||
//处理常规科技树拥有然后带给unit的技能 ,包括Watermove waterdefence等等
|
||
var dict = ActionLogicFactory.GetActionLogicDict();
|
||
foreach (var action in dict.Keys)
|
||
if(action.ActionType == CommonActionType.UnitSkill
|
||
&& playerData.TechTree.CheckActionCan(action))
|
||
unitData.AddSkill(action.SkillType);
|
||
|
||
//处理
|
||
return unitData;
|
||
}
|
||
|
||
// 移除小兵数据
|
||
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()
|
||
{
|
||
// 重建缓存字典
|
||
_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;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
// 地图 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;
|
||
}
|
||
}
|
||
}
|
||
|