[版本 V0.7.2c] 发布日期:26.5.31 ---------[bug修复与优化]------------- 1.修复了东风谷早苗掷签范围伤害击杀单位后图像残留的bug 2.修复了铃仙触发幻影齐射并击杀单位时可能产生图像残留的bug 3.修复了隐脉地块可能不显示的bug 4.优化了隐脉生成逻辑,且隐脉将不再会生成于水域或者山脉中 6.修复了探索者发现结界塔时城市经验动画播放错误的bug 8.优化了拖动地图时可能镜头强制移会起始点的bug
1007 lines
40 KiB
C#
1007 lines
40 KiB
C#
/*
|
||
* @Author: 白哉
|
||
* @Description:
|
||
* @Date: 2025年04月03日 星期四 11:04:31
|
||
* @Modify:
|
||
*/
|
||
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using Logic.Skill;
|
||
using MemoryPack;
|
||
using TH1_Logic.Core;
|
||
using TH1Renderer;
|
||
using UnityEngine;
|
||
using UnityEngine.Animations;
|
||
|
||
|
||
namespace RuntimeData
|
||
{
|
||
[MemoryPackable]
|
||
public partial class GridMapData
|
||
{
|
||
public List<GridData> GridList;
|
||
|
||
private Dictionary<uint, GridData> _posToGridDict;
|
||
private Dictionary<uint, GridData> _gridIdToGridDict;
|
||
private Dictionary<uint, int> _gridIdToIndexDict;
|
||
private MapConfig _mapCfg;
|
||
|
||
|
||
[MemoryPackConstructor]
|
||
public GridMapData()
|
||
{
|
||
GridList = new List<GridData>();
|
||
_posToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToIndexDict = new Dictionary<uint, int>();
|
||
}
|
||
|
||
public GridMapData(MapConfig mapCfg, MapIdGenerator idGenerator)
|
||
{
|
||
_mapCfg = mapCfg;
|
||
GridList = new List<GridData>();
|
||
_posToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToIndexDict = new Dictionary<uint, int>();
|
||
|
||
for (int x = 0; x < mapCfg.Width; x++)
|
||
{
|
||
for (int y = 0; y < mapCfg.Height; y++)
|
||
{
|
||
var gridData = new GridData(x, y, idGenerator,mapCfg);
|
||
GridList.Add(gridData);
|
||
_gridIdToGridDict[gridData.Id] = gridData;
|
||
_posToGridDict[gridData.Pos.PosId] = gridData;
|
||
}
|
||
}
|
||
for (int i = 0; i < GridList.Count; i++)
|
||
{
|
||
_gridIdToIndexDict[GridList[i].Id] = i;
|
||
}
|
||
}
|
||
|
||
public GridMapData(GridMapData copyData)
|
||
{
|
||
_mapCfg = copyData._mapCfg;
|
||
GridList = new List<GridData>();
|
||
_posToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict = new Dictionary<uint, GridData>();
|
||
_gridIdToIndexDict = new Dictionary<uint, int>();
|
||
|
||
foreach (var grid in copyData.GridList)
|
||
{
|
||
var gridData = new GridData(grid);
|
||
GridList.Add(gridData);
|
||
_gridIdToGridDict[gridData.Id] = gridData;
|
||
_posToGridDict[gridData.Pos.PosId] = gridData;
|
||
}
|
||
for (int i = 0; i < GridList.Count; i++)
|
||
{
|
||
_gridIdToIndexDict[GridList[i].Id] = i;
|
||
}
|
||
}
|
||
|
||
public void DeepCopy(GridMapData copyData)
|
||
{
|
||
_mapCfg = copyData._mapCfg;
|
||
_posToGridDict.Clear();
|
||
_gridIdToGridDict.Clear();
|
||
_gridIdToIndexDict.Clear();
|
||
|
||
for (int i = 0; i < copyData.GridList.Count; i++)
|
||
{
|
||
var copyGrid = copyData.GridList[i];
|
||
if (i >= GridList.Count)
|
||
{
|
||
var gridData = new GridData(copyGrid);
|
||
GridList.Add(gridData);
|
||
_gridIdToGridDict[gridData.Id] = gridData;
|
||
_posToGridDict[gridData.Pos.PosId] = gridData;
|
||
}
|
||
else
|
||
{
|
||
GridList[i].DeepCopy(copyGrid);
|
||
_gridIdToGridDict[GridList[i].Id] = GridList[i];
|
||
_posToGridDict[GridList[i].Pos.PosId] = GridList[i];
|
||
}
|
||
}
|
||
for (int i = GridList.Count - 1; i >= copyData.GridList.Count; i--)
|
||
{
|
||
_gridIdToGridDict.Remove(GridList[i].Id);
|
||
_posToGridDict.Remove(GridList[i].Pos.PosId);
|
||
GridList.RemoveAt(i);
|
||
}
|
||
for (int i = 0; i < GridList.Count; i++)
|
||
{
|
||
_gridIdToIndexDict[GridList[i].Id] = i;
|
||
}
|
||
}
|
||
|
||
[MemoryPackOnDeserialized]
|
||
public void OnAfterMemoryPackDeserialize()
|
||
{
|
||
_posToGridDict ??= new Dictionary<uint, GridData>();
|
||
_gridIdToGridDict ??= new Dictionary<uint, GridData>();
|
||
_gridIdToIndexDict??= new Dictionary<uint, int>();
|
||
_posToGridDict.Clear();
|
||
_gridIdToGridDict.Clear();
|
||
_gridIdToIndexDict.Clear();
|
||
foreach (var grid in GridList)
|
||
{
|
||
_gridIdToGridDict[grid.Id] = grid;
|
||
_posToGridDict[grid.Pos.PosId] = grid;
|
||
}
|
||
for (int i = 0; i < GridList.Count; i++)
|
||
{
|
||
_gridIdToIndexDict[GridList[i].Id] = i;
|
||
}
|
||
}
|
||
|
||
public void BindMapConfig(MapConfig cfg)
|
||
{
|
||
_mapCfg = cfg;
|
||
}
|
||
|
||
public void OnTurnStart(MapData map)
|
||
{
|
||
foreach (var grid in GridList) grid.OnTurnStart(map);
|
||
}
|
||
|
||
public void OnTurnEnd(MapData map)
|
||
{
|
||
foreach (var grid in GridList) grid.OnTurnEnd(map);
|
||
}
|
||
|
||
public int GetGridIndexByGid(uint gid)
|
||
{
|
||
return _gridIdToIndexDict.GetValueOrDefault(gid, 0);
|
||
}
|
||
|
||
// 通过 gid 获取格子数据
|
||
public bool GetGridDataByGid(uint gid, out GridData data)
|
||
{
|
||
return _gridIdToGridDict.TryGetValue(gid, out data);
|
||
}
|
||
|
||
// 通过坐标获取格子数据
|
||
public bool GetGridDataByPos(int x, int y, out GridData gridData)
|
||
{
|
||
return _posToGridDict.TryGetValue(MapPosition.CalculatePosId(x, y), out gridData);
|
||
}
|
||
|
||
public bool GetGridDataByV2(Vector2 v2, out GridData gridData)
|
||
{
|
||
return GetGridDataByPos((int)v2.x, (int)v2.y, out gridData);
|
||
}
|
||
|
||
public bool GetGridDataByVector3Pos(Vector3 pos, out GridData gridData)
|
||
{
|
||
var p = Table.Instance.WorldToGrid(pos);
|
||
return _posToGridDict.TryGetValue(MapPosition.CalculatePosId(p.x, p.y), out gridData);
|
||
}
|
||
|
||
|
||
// 获取目标周围的所有格子(含center) - Buffer版本,减少GC
|
||
public void GetAroundGridData(int xOffset, int yOffset, GridData gridData, List<GridData> buffer)
|
||
{
|
||
buffer.Clear();
|
||
if (gridData == null) return;
|
||
int radius = Mathf.Max(xOffset, yOffset);
|
||
for (int r = 0; r <= radius; r++)
|
||
{
|
||
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - yOffset; y <= gridData.Pos.Y + yOffset; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (Mathf.Max(Mathf.Abs(x - gridData.Pos.X) , Mathf.Abs(y - gridData.Pos.Y)) != r) continue;
|
||
buffer.Add(aroundGridData);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取目标周围的所有格子(含center) - 兼容版本(会分配新List)
|
||
public List<GridData> GetAroundGridData(int xOffset, int yOffset, GridData gridData)
|
||
{
|
||
var gridDataList = new List<GridData>();
|
||
GetAroundGridData(xOffset, yOffset, gridData, gridDataList);
|
||
return gridDataList;
|
||
}
|
||
|
||
// 获取目标周围的所有格子(不含center) - Buffer版本,减少GC
|
||
public void GetAroundGridDataSet_NOCENTER(int xOffset, int yOffset, GridData gridData, List<GridData> buffer)
|
||
{
|
||
buffer.Clear();
|
||
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - yOffset; y <= gridData.Pos.Y + yOffset; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
buffer.Add(aroundGridData);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取目标周围的所有格子(不含center) - 兼容版本(会分配新HashSet)
|
||
public HashSet<GridData> GetAroundGridDataSet_NOCENTER(int xOffset, int yOffset, GridData gridData)
|
||
{
|
||
HashSet<GridData> gridDataSet = new HashSet<GridData>();
|
||
for (int x = gridData.Pos.X - xOffset; x <= gridData.Pos.X + xOffset; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - yOffset; y <= gridData.Pos.Y + yOffset; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
gridDataSet.Add(aroundGridData);
|
||
}
|
||
}
|
||
|
||
return gridDataSet;
|
||
}
|
||
|
||
// 获取目标周围的所有格子,按照优先2468方向 - Buffer版本,减少GC
|
||
public void GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData, List<GridData> buffer)
|
||
{
|
||
buffer.Clear();
|
||
for (int i = 1; i <= 9; i++)
|
||
{
|
||
int dirForHuman = (i <= 4) ? (2 * i) : (2 * (i - 5) + 1);
|
||
int dirForComputer = dirForHuman - 1;
|
||
int x = gridData.Pos.X + dirForComputer % 3 - 1;
|
||
int y = gridData.Pos.Y + dirForComputer / 3 - 1;
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
buffer.Add(aroundGridData);
|
||
}
|
||
}
|
||
|
||
// 获取目标周围的所有格子,按照优先2468方向 - 兼容版本(会分配新List)
|
||
public List<GridData> GetAroundGridDataSetByOrder(int xOffset, int yOffset, GridData gridData)
|
||
{
|
||
var gridDataList = new List<GridData>();
|
||
GetAroundGridDataSetByOrder(xOffset, yOffset, gridData, gridDataList);
|
||
return gridDataList;
|
||
}
|
||
|
||
// 获取周围格子ID列表 - Buffer版本,减少GC
|
||
public void GetAroundGridIdList(int radius, GridData gridData, List<uint> buffer, bool remainCenter = false)
|
||
{
|
||
buffer.Clear();
|
||
for (int r = 0; r <= radius; r++)
|
||
{
|
||
for (int x = gridData.Pos.X - radius; x <= gridData.Pos.X + radius; x++)
|
||
{
|
||
for (int y = gridData.Pos.Y - radius; y <= gridData.Pos.Y + radius; y++)
|
||
{
|
||
if (!GetGridDataByPos(x, y, out var aroundGridData)) continue;
|
||
if (Mathf.Max(Mathf.Abs(x - gridData.Pos.X) , Mathf.Abs(y - gridData.Pos.Y)) != r) continue;
|
||
if (!remainCenter && x == gridData.Pos.X && y == gridData.Pos.Y) continue;
|
||
buffer.Add(aroundGridData.Id);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 获取周围格子ID列表 - 兼容版本(会分配新List)
|
||
public List<uint> GetAroundGridIdList(int radius, GridData gridData, bool remainCenter = false)
|
||
{
|
||
var gridIdList = new List<uint>();
|
||
GetAroundGridIdList(radius, gridData, gridIdList, remainCenter);
|
||
return gridIdList;
|
||
}
|
||
|
||
// 获取周围某个格子
|
||
public bool GetUpGridData(GridData gridData, int xOffset, int yOffset, out GridData upGridData)
|
||
{
|
||
return GetGridDataByPos(gridData.Pos.X + xOffset, gridData.Pos.Y + yOffset, out upGridData);
|
||
}
|
||
|
||
// 判断是否在左右边界
|
||
public bool IsInLeftOrRightBorder(GridData gridData)
|
||
{
|
||
return gridData.Pos.X == 0 || gridData.Pos.X == _mapCfg.Width - 1;
|
||
}
|
||
|
||
// 判断是否在上下边界
|
||
public bool IsInUpOrDownBorder(GridData gridData)
|
||
{
|
||
return gridData.Pos.Y == 0 || gridData.Pos.Y == _mapCfg.Height - 1;
|
||
}
|
||
|
||
//-------- 计算类方法 --------//
|
||
//计算切比雪夫距离
|
||
public int CalcDistance(GridData gridDataA, GridData gridDataB)
|
||
{
|
||
return Mathf.Max(Mathf.Abs(gridDataA.Pos.X - gridDataB.Pos.X),Mathf.Abs(gridDataA.Pos.Y - gridDataB.Pos.Y));
|
||
}
|
||
//计算曼哈顿距离
|
||
public int CalcManhattanDistance(GridData gridDataA, GridData gridDataB)
|
||
{
|
||
return Mathf.Abs(gridDataA.Pos.X - gridDataB.Pos.X) + Mathf.Abs(gridDataA.Pos.Y - gridDataB.Pos.Y);
|
||
}
|
||
|
||
public void RefreshConnectInfo()
|
||
{
|
||
|
||
}
|
||
|
||
public bool CalcBridgeMirror(GridData gridData)
|
||
{
|
||
var x = gridData.Pos.X;
|
||
var y = gridData.Pos.Y;
|
||
if (!GetGridDataByPos(x, y - 1, out var grid1)) return true;
|
||
if (!GetGridDataByPos(x, y + 1, out var grid2)) return true;
|
||
if (grid1.Terrain != TerrainType.Land) return true;
|
||
if (grid2.Terrain != TerrainType.Land) return true;
|
||
return false;
|
||
}
|
||
|
||
public GridData GetNextGrid(GridData first, GridData second)
|
||
{
|
||
int dx = second.Pos.X - first.Pos.X;
|
||
int dy = second.Pos.Y - first.Pos.Y;
|
||
|
||
// 将方向归一化为 -1, 0, 1(八方向)
|
||
int stepX = dx != 0 ? (dx > 0 ? 1 : -1) : 0;
|
||
int stepY = dy != 0 ? (dy > 0 ? 1 : -1) : 0;
|
||
|
||
int nextX = second.Pos.X + stepX;
|
||
int nextY = second.Pos.Y + stepY;
|
||
|
||
if (GetGridDataByPos(nextX, nextY, out var nextGrid)) return nextGrid;
|
||
return null;
|
||
}
|
||
}
|
||
|
||
public enum GridSpType { None,RemiliaGrid,KaguyaGrid,RemiliaGridDark,LeyLine,Max }
|
||
|
||
// 每个格子包含5个层次的信息
|
||
[MemoryPackable]
|
||
public partial class GridData : IdentifierBase
|
||
{
|
||
//-------- BaseData ---------//
|
||
// 位置信息
|
||
public MapPosition Pos;
|
||
|
||
//public bool CityBuildingRenderMark = false;
|
||
|
||
// 海陆层
|
||
public TerrainType Terrain;
|
||
// 地形层
|
||
public TerrainFeature Feature;
|
||
|
||
// UpdateGeoInfo 复用的HashSet缓冲区
|
||
private static HashSet<GeoSmallClass> _geoSmallSetBuffer;
|
||
// 植被层
|
||
public Vegetation Vegetation;
|
||
// 资源层+建筑层
|
||
public ResourceType Resource;
|
||
public WonderLibrary Wonder;
|
||
public ResourceType ResourceUnderBuilding;
|
||
|
||
// 特殊效果层
|
||
public List<GridSpType> SpTypeList;
|
||
// Geo信息层
|
||
public List<uint> GeoIdList;
|
||
public bool[] WaterRoadForceId;
|
||
// 文明层
|
||
public uint CivId;
|
||
|
||
public CivEnum CivEnum => Table.Instance.TransCivIdToCivEnum(CivId);
|
||
//奇观Id
|
||
|
||
//建筑level-只对部分建筑生效
|
||
public int buildingLevel;
|
||
|
||
//海军基底提供的每回合恢复次数 , 目前天狗酒馆也采用这个次数
|
||
//TODO 整合到gridSkill去
|
||
public int NavalBasePoint;
|
||
//建筑的建立时间,目前只对temple有效
|
||
public uint BuildTime;
|
||
|
||
//通用冷却cd参数,kaguyaFrenchAnimal吸引动物、EgyptianIrrigation生成农田,都会操纵这个参数 //建筑的kaguyaFrenchAnimal吸引量,每回合+1,>=2则可以在周围找个树林吸引一个动物,然后置0
|
||
public uint CommonColdTime;
|
||
//-------- RenderData ---------//
|
||
|
||
//cityborder相关的RenderMark
|
||
[MemoryPackIgnore]
|
||
public bool CityBorderRenderMark = false;
|
||
|
||
|
||
[MemoryPackConstructor]
|
||
public GridData()
|
||
{
|
||
SpTypeList = new List<GridSpType>();
|
||
}
|
||
|
||
public GridData(int x, int y,MapIdGenerator mapIdGenerator,MapConfig mapConfig)
|
||
{
|
||
SpTypeList = new List<GridSpType>();
|
||
Pos = new MapPosition(x,y);
|
||
Id = mapIdGenerator.GeneratorId();
|
||
WaterRoadForceId = new bool[Table.Instance.MaxForceCount];
|
||
}
|
||
|
||
//仅用于AI计算data,不需要render的部分
|
||
public GridData(GridData copyData)
|
||
{
|
||
Pos = new MapPosition(copyData.Pos.X, copyData.Pos.Y);
|
||
Id = copyData.Id;
|
||
Terrain = copyData.Terrain;
|
||
Feature = copyData.Feature;
|
||
Vegetation = copyData.Vegetation;
|
||
Resource = copyData.Resource;
|
||
ResourceUnderBuilding = copyData.ResourceUnderBuilding;
|
||
CivId = copyData.CivId;
|
||
Wonder = copyData.Wonder;
|
||
buildingLevel = copyData.buildingLevel;
|
||
NavalBasePoint = copyData.NavalBasePoint;
|
||
GeoIdList = new List<uint>();
|
||
foreach (var id in copyData.GeoIdList) GeoIdList.Add(id);
|
||
WaterRoadForceId = new bool[copyData.WaterRoadForceId.Length];
|
||
Array.Copy(copyData.WaterRoadForceId, WaterRoadForceId, copyData.WaterRoadForceId.Length);
|
||
foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill());
|
||
//拷贝Grid的SpTypeList
|
||
SpTypeList = new List<GridSpType>();
|
||
SpTypeList.Clear();
|
||
foreach (var SpType in copyData.SpTypeList) SpTypeList.Add(SpType);
|
||
}
|
||
|
||
public void DeepCopy(GridData copyData)
|
||
{
|
||
Pos.X = copyData.Pos.X;
|
||
Pos.Y = copyData.Pos.Y;
|
||
Id = copyData.Id;
|
||
Terrain = copyData.Terrain;
|
||
Feature = copyData.Feature;
|
||
Vegetation = copyData.Vegetation;
|
||
Resource = copyData.Resource;
|
||
ResourceUnderBuilding = copyData.ResourceUnderBuilding;
|
||
CivId = copyData.CivId;
|
||
Wonder = copyData.Wonder;
|
||
buildingLevel = copyData.buildingLevel;
|
||
NavalBasePoint = copyData.NavalBasePoint;
|
||
WaterRoadForceId = new bool[copyData.WaterRoadForceId.Length];
|
||
Array.Copy(copyData.WaterRoadForceId, WaterRoadForceId, copyData.WaterRoadForceId.Length);
|
||
GeoIdList ??= new List<uint>();
|
||
GeoIdList.Clear();
|
||
foreach (var id in copyData.GeoIdList) GeoIdList.Add(id);
|
||
|
||
//拷贝Grid的SpTypeList
|
||
SpTypeList ??= new List<GridSpType>();
|
||
SpTypeList.Clear();
|
||
foreach (var SpType in copyData.SpTypeList) SpTypeList.Add(SpType);
|
||
|
||
Skills.Clear();
|
||
foreach (var skill in copyData.Skills) Skills.Add(skill.GetCopySkill());
|
||
}
|
||
|
||
public void SetGridData(TerrainType ter,TerrainFeature fea,Vegetation veg, ResourceType res,uint civ)
|
||
{
|
||
Terrain = ter;
|
||
Feature = fea;
|
||
Vegetation = veg;
|
||
Resource = res;
|
||
ResourceUnderBuilding = ResourceType.None;
|
||
CivId = civ;
|
||
}
|
||
|
||
public bool IsRoadBridgePort()
|
||
{
|
||
return Feature == TerrainFeature.Road || Resource == ResourceType.Port || Resource == ResourceType.Bridge;
|
||
}
|
||
|
||
public Vector3 V3() => Table.Instance.GridToWorld(this,"");
|
||
|
||
public bool HasBuilding()
|
||
{
|
||
return !(Resource == ResourceType.None ||
|
||
Resource == ResourceType.Fruit ||
|
||
Resource == ResourceType.Crop ||
|
||
Resource == ResourceType.Starfish ||
|
||
Resource == ResourceType.Animal ||
|
||
Resource == ResourceType.Fish ||
|
||
Resource == ResourceType.Metal ||
|
||
Resource == ResourceType.Treasure);
|
||
|
||
}
|
||
|
||
public bool IsMainMap()
|
||
{
|
||
if (!Main.MapData.GridMap.GetGridDataByGid(Id, out var gridData)
|
||
|| gridData != this)
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
public bool InMainSight()
|
||
{
|
||
if (!IsMainMap()) return false;
|
||
return Main.MapData.PlayerMap.SelfPlayerData.Sight.CheckIsInSight(Id);
|
||
}
|
||
|
||
public GridSpType GetTopGridSpType()
|
||
{
|
||
GridSpType ret = GridSpType.None;
|
||
foreach (var SpType in SpTypeList)
|
||
{
|
||
if (SpType == GridSpType.KaguyaGrid) ret = GridSpType.KaguyaGrid;
|
||
if (SpType == GridSpType.RemiliaGrid) ret = GridSpType.RemiliaGrid;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
//返回该格子周围是否存在没有视野的格子
|
||
[MemoryPackIgnore]
|
||
private List<GridData> _aroundBuffer;
|
||
|
||
public bool UnsightNearby(MapData map, PlayerData player)
|
||
{
|
||
_aroundBuffer ??= new List<GridData>();
|
||
map.GridMap.GetAroundGridData(1, 1, this, _aroundBuffer);
|
||
foreach (var grid in _aroundBuffer)
|
||
{
|
||
if (player.Sight.CheckIsInSight(grid.Id)) return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// 全局通知调用
|
||
public void OnTurnStart(MapData map)
|
||
{
|
||
//Step #1 处理海军点 TODO 将来要并入skill去
|
||
NavalBasePoint = 1;
|
||
//Step #2 处理kaguyaFrenchAnimal(必须是该grid玩家的回合)
|
||
if (Resource == ResourceType.KaguyaFrenchYard && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id))
|
||
{
|
||
CommonColdTime++;
|
||
if (CommonColdTime >= 2)
|
||
{
|
||
_aroundBuffer ??= new List<GridData>();
|
||
map.GridMap.GetAroundGridData(1, 1, this, _aroundBuffer);
|
||
|
||
if (map.GetPlayerDataByTerritoryGridId(Id, out var player))
|
||
{
|
||
GridData spawnedAnimalGrid = null;
|
||
foreach (var grid in _aroundBuffer)
|
||
{
|
||
if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) continue;
|
||
if (gridPlayer.Id != player.Id) continue;
|
||
if (grid.Vegetation != Vegetation.Trees) continue;
|
||
if (grid.Resource != ResourceType.None) continue;
|
||
|
||
grid.Resource = ResourceType.Animal;
|
||
grid.Renderer(map)?.InstantUpdateGrid(true);
|
||
|
||
grid.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Treasure));
|
||
CommonColdTime = 0;
|
||
|
||
Main.CityLogic.UpdateGridBuildingData_LogicView(map,this);
|
||
Renderer(map)?.InstantUpdateGrid(true);
|
||
Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
|
||
spawnedAnimalGrid = grid;
|
||
break;
|
||
}
|
||
|
||
//新产出的Animal也可能在其他同玩家KaguyaFrenchYard的周围(跨城市共享一片树林的情况)
|
||
//此时那些KaguyaFrenchYard的buildingLevel也需要刷新,否则CityCoinPerTurn会少算
|
||
if (spawnedAnimalGrid != null)
|
||
{
|
||
_aroundBuffer.Clear();
|
||
map.GridMap.GetAroundGridData(1, 1, spawnedAnimalGrid, _aroundBuffer);
|
||
foreach (var neighbor in _aroundBuffer)
|
||
{
|
||
if (neighbor == this) continue; //自己已经更新过
|
||
if (neighbor.Resource != ResourceType.KaguyaFrenchYard) continue;
|
||
if (!map.GetPlayerDataByTerritoryGridId(neighbor.Id, out var neighborPlayer)) continue;
|
||
if (neighborPlayer.Id != player.Id) continue;
|
||
Main.CityLogic.UpdateGridBuildingData_LogicView(map, neighbor);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//Step #3 处理EgyptianIrrigation(必须是该grid玩家的回合)
|
||
if (Resource == ResourceType.EgyptianIrrigation && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id))
|
||
{
|
||
CommonColdTime++;
|
||
if (CommonColdTime >= 2)
|
||
{
|
||
_aroundBuffer ??= new List<GridData>();
|
||
map.GridMap.GetAroundGridData(1, 1, this, _aroundBuffer);
|
||
|
||
if (map.GetPlayerDataByTerritoryGridId(Id, out var player))
|
||
foreach (var grid in _aroundBuffer)
|
||
{
|
||
if (!map.GetPlayerDataByTerritoryGridId(grid.Id, out var gridPlayer)) continue;
|
||
if (gridPlayer.Id != player.Id) continue;
|
||
if (grid.Terrain != TerrainType.Land) continue;
|
||
if (grid.Vegetation == Vegetation.Trees) continue;
|
||
if (grid.Feature == TerrainFeature.Mountain) continue;
|
||
if (grid.Resource != ResourceType.None) continue;
|
||
|
||
grid.Resource = ResourceType.Crop;
|
||
grid.Renderer(map)?.InstantUpdateGrid(true);
|
||
|
||
grid.Renderer(Main.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Treasure));
|
||
CommonColdTime = 0;
|
||
|
||
//Main.CityLogic.UpdateGridBuildingData(map,this);
|
||
//Renderer(map)?.SetUpdateGrid(true);
|
||
//Renderer(Main.MapData)?.PlayVFX(new GridVFXParams(GridVFXType.Fog));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//Step #4 处理RemiliaMilitary
|
||
if (Resource == ResourceType.RemiliaMilitary && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id))
|
||
{
|
||
if (!HasSpType(GridSpType.RemiliaGrid))
|
||
AddSpType(GridSpType.RemiliaGrid,map,null);
|
||
Renderer(map)?.InstantUpdateGrid();
|
||
|
||
}
|
||
|
||
//Step #5 处理MetalStation
|
||
if (Resource == ResourceType.MetalStation && map.CheckIfGidBelongPid(Id,map.CurPlayer.Id))
|
||
{
|
||
CommonColdTime++;
|
||
if (CommonColdTime > 1)
|
||
{
|
||
Resource = ResourceType.Metal;
|
||
//如果有moriyaRoad科技,更新连通性
|
||
if(map.CurPlayer.TechTree.CheckIfHasTechAtom(TechAtom.MoriyaRoad))
|
||
Main.PlayerLogic.UpdateCityConnect(map,map.CurPlayer);
|
||
//Step #3 播放雾效,更新画面
|
||
if (InMainSight())
|
||
{
|
||
Renderer(map)?.InstantUpdateGrid(true);
|
||
Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
|
||
}
|
||
}
|
||
}
|
||
|
||
//Step #6 处理所有技能
|
||
OnSkillsTurnStart(map);
|
||
}
|
||
|
||
public void OnTurnEnd(MapData map)
|
||
{
|
||
|
||
//Step #2 处理所有技能
|
||
OnSkillsTurnEnd(map);
|
||
}
|
||
|
||
//皮肤层的一些基础属性调用
|
||
public bool HasSpType(GridSpType type)
|
||
{
|
||
foreach(var t in SpTypeList)
|
||
if (t == type)
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
public bool RemoveSpType(GridSpType type,MapData map)
|
||
{
|
||
foreach(var t in SpTypeList)
|
||
if (t == type)
|
||
{
|
||
SpTypeList.Remove(t);
|
||
if (type == GridSpType.RemiliaGrid && RealUnit(map, out var unit))
|
||
{
|
||
unit.RemoveSkill(SkillType.ScarletMistRealTimeVampire,map);
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
public bool AddSpType(GridSpType type,MapData map, UnitData origin)// = null)
|
||
{
|
||
if (HasSpType(type)) return false;
|
||
if (type == GridSpType.RemiliaGrid)
|
||
{
|
||
if(Resource == ResourceType.CityCenter) return false;
|
||
if (Terrain != TerrainType.Land) return false;
|
||
}
|
||
if (type == GridSpType.LeyLine)
|
||
{
|
||
if (Resource == ResourceType.CityCenter) return false;
|
||
if (Terrain != TerrainType.Land) return false;
|
||
if (Feature == TerrainFeature.Mountain) return false;
|
||
}
|
||
SpTypeList.Add(type);
|
||
origin?.HeroTask(map)?.OnAddSpType(map, type);
|
||
//如果是remiliaGrid,且是斯卡雷特帝国的单位,给当前的unit填加skill
|
||
// 注意:多人允许相同 Empire 后,多名 PlayerCivId==0 玩家会同时享有此特权,符合预期保留逻辑。
|
||
if (type == GridSpType.RemiliaGrid && RealUnit(map,out var unit) && unit.Player(map,out var player) && player.PlayerCivId == 0)
|
||
{
|
||
unit.AddSkill_Legacy(SkillType.ScarletMistRealTimeVampire, map,true,-1,false,-1,false,SpecialAddSkillType.Force,0);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public GridSpType GetOnlyOneSpType()
|
||
{
|
||
if (HasSpType(GridSpType.RemiliaGrid))
|
||
return GridSpType.RemiliaGrid;
|
||
if (HasSpType(GridSpType.KaguyaGrid))
|
||
return GridSpType.KaguyaGrid;
|
||
return SpTypeList.Count > 0 ? SpTypeList[0] : GridSpType.None;
|
||
}
|
||
|
||
/*public UnitData Unit(MapData map)
|
||
{
|
||
map.GetUnitDataByGid(Id, out var unit);
|
||
return unit;
|
||
}*/
|
||
|
||
/*public bool Unit(MapData map,out UnitData unit)
|
||
{
|
||
return map.GetUnitDataByGid(Id, out unit);
|
||
}*/
|
||
|
||
public bool RealUnit(MapData map,out UnitData unit)
|
||
{
|
||
return map.GetUnitDataByGid_Core(Id, out unit);
|
||
}
|
||
|
||
//返回Grid上是否存在一个VisibleUnit(非敌方隐身单位)
|
||
public bool VisibleUnit(MapData map,PlayerData player,out UnitData unit)
|
||
{
|
||
return map.GetUnitDataByGid_Core(Id, out unit) && !unit.IsHideAndCantSee(map,player);
|
||
}
|
||
|
||
//返回Grid上是否存在一个VisibleUnit(非隐身单位),但是从只需要考虑selfplayer,且一定是Main.MapData
|
||
public bool MainSelfPlayerVisibleUnit(out UnitData unit)
|
||
{
|
||
return Main.MapData.GetUnitDataByGid_Core(Id, out unit) && !unit.IsHideAndCantSee(Main.MapData,Main.MapData.PlayerMap.SelfPlayerData);
|
||
}
|
||
public CityData BelongButNotOnGridCity(MapData map)
|
||
{
|
||
map.GetCityDataByTerritoryGid(Id, out var city);
|
||
return city;
|
||
}
|
||
|
||
public bool City(MapData map ,out CityData city)
|
||
{
|
||
return map.GetCityDataByTerritoryGid(Id, out city);
|
||
}
|
||
|
||
public CityData CityOnGrid(MapData map)
|
||
{
|
||
map.GetCityDataByGid(Id, out var city);
|
||
return city;
|
||
}
|
||
|
||
public bool CityOnGrid(MapData map,out CityData city)
|
||
{
|
||
return map.GetCityDataByGid(Id, out city);
|
||
}
|
||
|
||
//返回当前grid提供了多少的cityExp
|
||
public int CityExp()
|
||
{
|
||
if (!Table.Instance.GridAndResourceDataAssets.GetResourceInfo(Resource, out var resourceInfo))
|
||
return 0;
|
||
//保护区特判
|
||
if (Resource == ResourceType.Preserve && Vegetation == Vegetation.Trees)
|
||
return buildingLevel;
|
||
|
||
var levelExp = resourceInfo.HasLevel ? resourceInfo.CityExpPerLevel * buildingLevel : 0;
|
||
return levelExp + resourceInfo.Exp;
|
||
}
|
||
|
||
public GridRenderer Renderer(MapData map)
|
||
{
|
||
if (map != Main.MapData) return null;
|
||
MapRenderer.Instance.ROGridMap.TryGetValue(Id, out var renderer);
|
||
return renderer;
|
||
}
|
||
|
||
public PlayerData Player(MapData map)
|
||
{
|
||
map.GetPlayerDataByTerritoryGridId(Id, out var player);
|
||
return player;
|
||
}
|
||
public bool Player(MapData map ,out PlayerData player)
|
||
{
|
||
return map.GetPlayerDataByTerritoryGridId(Id, out player);
|
||
}
|
||
|
||
|
||
public List<uint> GetRealTimeGeoIdList()
|
||
{
|
||
var ret = new List<uint>();
|
||
|
||
foreach (var t in GeoIdList)
|
||
{
|
||
|
||
}
|
||
|
||
foreach(var t in GeoIdList)
|
||
if (Table.Instance.GeoDataAssets.GetGeoInfo(t, out var info))
|
||
{
|
||
if (info.GeoBigClass == GeoBigClass.Forest && Vegetation == Vegetation.Trees) ret.Add(info.Id);
|
||
if (info.GeoBigClass == GeoBigClass.Mountain && Feature == TerrainFeature.Mountain) ret.Add(info.Id);
|
||
//平原要剔除有山的情况,如果已经有山就不加平原信息了
|
||
if (info.GeoBigClass == GeoBigClass.Plain && Feature != TerrainFeature.Mountain) ret.Add(info.Id);
|
||
//水域要剔除有山的情况,如果已经有山就不加平原信息了
|
||
if (info.GeoBigClass == GeoBigClass.Water && Feature != TerrainFeature.Mountain)ret.Add(info.Id);
|
||
if (info.GeoBigClass == GeoBigClass.Building)
|
||
{
|
||
if (info.GeoSmallClass == GeoSmallClass.Bridge && Resource == ResourceType.Bridge) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.Port && Resource == ResourceType.Port) ret.Add(info.Id);
|
||
|
||
if (info.GeoSmallClass == GeoSmallClass.Forge && Resource == ResourceType.Forge) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.Sawmill && Resource == ResourceType.Sawmill) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.Windmill&& Resource == ResourceType.Windmill) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.Market&& Resource == ResourceType.Market) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.Mine&& Resource == ResourceType.Mine) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.NavelBase&& Resource == ResourceType.NavalBase) ret.Add(info.Id);
|
||
if (info.GeoSmallClass == GeoSmallClass.Military&& Resource == ResourceType.Military) ret.Add(info.Id);
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
public void UpdateGeoInfo(MapData map)
|
||
{
|
||
//TODO 后续要将这个改为Action
|
||
if (Main.MapData.Net.Mode == NetMode.Multi) return;
|
||
//Step #1 检查当前GeoList的信息和 grid的实机情况是否匹配
|
||
bool bridge = false;
|
||
bool port = false;
|
||
bool forest = false;
|
||
bool mountain = false;
|
||
bool forge = false;
|
||
bool sawmill = false;
|
||
bool military = false;
|
||
bool navelbase = false;
|
||
bool windmill = false;
|
||
bool market = false;
|
||
bool mine = false;
|
||
foreach(var t in GeoIdList)
|
||
if (Table.Instance.GeoDataAssets.GetGeoInfo(t, out var info))
|
||
{
|
||
if (info.GeoBigClass == GeoBigClass.Forest) forest = true;
|
||
if (info.GeoBigClass == GeoBigClass.Mountain) mountain = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Bridge) bridge = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Port) port = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Forge) forge = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Sawmill) sawmill = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Windmill) windmill = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Market) market = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Mine) mine = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.NavelBase) navelbase = true;
|
||
if (info.GeoSmallClass == GeoSmallClass.Military) military = true;
|
||
}
|
||
|
||
|
||
var smallSet = _geoSmallSetBuffer ??= new HashSet<GeoSmallClass>();
|
||
smallSet.Clear();
|
||
|
||
uint id;
|
||
if (Vegetation == Vegetation.Trees && !forest)
|
||
{
|
||
smallSet.Add(GeoSmallClass.Mangrove);
|
||
smallSet.Add(GeoSmallClass.Deciduous);
|
||
smallSet.Add(GeoSmallClass.Jungle);
|
||
smallSet.Add(GeoSmallClass.Evergreen);
|
||
smallSet.Add(GeoSmallClass.Oasis);
|
||
smallSet.Add(GeoSmallClass.Taiga);
|
||
if (Table.Instance.GeoDataAssets.GetForestGeoId(map, this, out id))
|
||
GeoIdList.Add(id);
|
||
}
|
||
|
||
if (Feature == TerrainFeature.Mountain && !mountain)
|
||
{
|
||
smallSet.Add(GeoSmallClass.Volcano);
|
||
smallSet.Add(GeoSmallClass.Mountain);
|
||
smallSet.Add(GeoSmallClass.Hill);
|
||
if (Table.Instance.GeoDataAssets.GetGeoId(CivEnum, GeoBigClass.Mountain, smallSet, out id))
|
||
GeoIdList.Add(id);
|
||
}
|
||
|
||
if (Resource == ResourceType.Bridge && !bridge) smallSet.Add(GeoSmallClass.Bridge);
|
||
if (Resource == ResourceType.Port && !port) smallSet.Add(GeoSmallClass.Port);
|
||
if (Resource == ResourceType.Forge && !forge) smallSet.Add(GeoSmallClass.Forge);
|
||
if (Resource == ResourceType.Sawmill && !sawmill) smallSet.Add(GeoSmallClass.Sawmill);
|
||
if (Resource == ResourceType.Windmill && !windmill) smallSet.Add(GeoSmallClass.Windmill);
|
||
if (Resource == ResourceType.Market && !market) smallSet.Add(GeoSmallClass.Market);
|
||
if (Resource == ResourceType.Mine && !mine) smallSet.Add(GeoSmallClass.Mine);
|
||
if (Resource == ResourceType.NavalBase && !navelbase) smallSet.Add(GeoSmallClass.NavelBase);
|
||
if (Resource == ResourceType.Military && !military) smallSet.Add(GeoSmallClass.Military);
|
||
|
||
if(smallSet.Count > 0)
|
||
if (Table.Instance.GeoDataAssets.GetGeoId(CivEnum, GeoBigClass.Building, smallSet, out id))
|
||
GeoIdList.Add(id);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
// 位置基类
|
||
[MemoryPackable]
|
||
public partial class MapPosition
|
||
{
|
||
public int X;
|
||
public int Y;
|
||
public uint PosId => CalculatePosId(X, Y);
|
||
|
||
public Vector2Int V2() => new Vector2Int(X, Y);
|
||
public static uint CalculatePosId(int x, int y)
|
||
{
|
||
return (uint)(x * 1000 + y);
|
||
}
|
||
|
||
[MemoryPackConstructor]
|
||
public MapPosition()
|
||
{
|
||
X = 0;
|
||
Y = 0;
|
||
}
|
||
|
||
public MapPosition(int x,int y)
|
||
{
|
||
X = x;
|
||
Y = y;
|
||
}
|
||
|
||
public MapPosition(Vector2 v2)
|
||
{
|
||
X = (int)v2.x;
|
||
Y = (int)v2.y;
|
||
}
|
||
|
||
public void SetPosition(MapPosition pos)
|
||
{
|
||
X = pos.X;
|
||
Y = pos.Y;
|
||
}
|
||
|
||
// 重载 GetHashCode
|
||
public override int GetHashCode()
|
||
{
|
||
return HashCode.Combine(X, Y);
|
||
// 或者更简单的方式:
|
||
// return X.GetHashCode() ^ Y.GetHashCode();
|
||
}
|
||
// 重载 Equals(object)
|
||
public override bool Equals(object obj)
|
||
{
|
||
return obj is MapPosition other && Equals(other);
|
||
}
|
||
// 实现 IEquatable<MapPosition>
|
||
public bool Equals(MapPosition other)
|
||
{
|
||
if (other == null) return false;
|
||
return X == other.X && Y == other.Y;
|
||
}
|
||
// 重载 == 操作符(推荐)
|
||
public static bool operator ==(MapPosition left, MapPosition right)
|
||
{
|
||
if (ReferenceEquals(left, right)) return true;
|
||
if (left is null || right is null) return false;
|
||
return left.Equals(right);
|
||
}
|
||
// 重载 != 操作符(推荐)
|
||
public static bool operator !=(MapPosition left, MapPosition right)
|
||
{
|
||
return !(left == right);
|
||
}
|
||
|
||
}
|
||
|
||
}
|