540 lines
19 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.

using System;
using System.Collections.Generic;
using Logic.CrashSight;
using UnityEngine;
using RuntimeData;
using Logic.Multilingual;
using Random = UnityEngine.Random;
[Serializable]
[CreateAssetMenu(fileName = "GeoDataAssets", menuName = "TH1 Game Data/Geo Data Asset")]
public class GeoDataAssets : ScriptableObject
{
public List<GeoItem> GeoItemList = new List<GeoItem>();
public List<SmallClassInfo> SmallClassInfoList;
[NonSerialized]
private bool _initialized = false;
public Dictionary<uint, GeoItem> GeoDict = null;
[NonSerialized]
private List<Vector2> dir4 = new List<Vector2>() { Vector2.up, Vector2.right, Vector2.down, Vector2.left };
[NonSerialized]
private List<Vector2> dir8 = new List<Vector2>() { Vector2.down , Vector2.right , Vector2.left , Vector2.up ,Vector2.left+Vector2.up , Vector2.up+Vector2.right, Vector2.down+Vector2.right,Vector2.down+Vector2.left };
public HashSet<uint> UsedGeoId;
public void GeoDictInit()
{
if (_initialized) return;
_initialized = true;
GeoDict = new Dictionary<uint, GeoItem>();
foreach(var t in Table.Instance.GeoDataAssets.GeoItemList)
GeoDict.Add(t.Id,t);
}
public bool GetGeoInfo(uint geoid,out GeoItem item )
{
if (!_initialized) GeoDictInit();
return GeoDict.TryGetValue(geoid, out item);
}
public void OnMapGenerator()
{
GeoDictInit();
UsedGeoId = new HashSet<uint>();
}
public void OnMatchStart(MapData map)
{
GeoDictInit();
UsedGeoId = new HashSet<uint>();
foreach(var grid in map.GridMap.GridList)
foreach(var geo in grid.GeoIdList)
UsedGeoId.Add(geo);
}
public void InitGeoData(MapData mapData)
{
var _width = mapData.MapConfig.Width;
var _height = mapData.MapConfig.Width;
//Step #3 生成Geo信息
for (int x = 0; x < _width; x++)
{
for (int y = 0; y < _height; y++)
{
if (!mapData.GridMap.GetGridDataByPos(x, y, out GridData grid)) continue;
Vector2 curV2 = new Vector2(x, y);
//生成Geo信息如果是深海先跳过
if (grid.Terrain == TerrainType.DeepSea) continue;
GeoBigClass big = GeoBigClass.Plain;
HashSet<GeoSmallClass> smallset = new HashSet<GeoSmallClass>() { };
bool firstAdd = false;
if (grid.Terrain != TerrainType.Land)
{
firstAdd = true;
big = GeoBigClass.Water;
if (CheckGeoLake(mapData, grid))
smallset.Add(GeoSmallClass.Lake);
if (CheckGeoRiver(mapData, grid))
smallset.Add(GeoSmallClass.River);
if (CheckGeoFjord(mapData, grid))
smallset.Add(GeoSmallClass.Fjord);
if (smallset.Count == 0)
smallset.Add(GeoSmallClass.Sea);
}
if (grid.Feature == TerrainFeature.Mountain)
{
firstAdd = true;
big = GeoBigClass.Mountain;
smallset.Add(GeoSmallClass.Hill);
smallset.Add(GeoSmallClass.Mountain);
smallset.Add(GeoSmallClass.Volcano);
}
if (grid.Vegetation == Vegetation.Trees)
{
firstAdd = true;
big = GeoBigClass.Forest;
if (CheckGeoMangrove(mapData, grid))
smallset.Add(GeoSmallClass.Mangrove);
if (CheckGeoOasis(mapData, grid))
smallset.Add(GeoSmallClass.Oasis);
if (CheckGeoSavanna(mapData, grid))
smallset.Add(GeoSmallClass.Savanna);
if (CheckGeoJungle(mapData, grid))
smallset.Add(GeoSmallClass.Jungle);
if (smallset.Count == 0)
{
smallset.Add(GeoSmallClass.Deciduous);
smallset.Add(GeoSmallClass.Evergreen);
smallset.Add(GeoSmallClass.Taiga);
}
}
grid.GeoIdList = new List<uint>();
//第一次生成
if (firstAdd && GetGeoId(grid.CivEnum, big, smallset, out var id))
{
grid.GeoIdList.Add(id);
UsedGeoId.Add(id);
}
if (grid.Terrain == TerrainType.Land && grid.Feature != TerrainFeature.Mountain)
{
smallset.Clear();
big = GeoBigClass.Plain;
if (CheckGeoWetland(mapData, grid))
smallset.Add(GeoSmallClass.Wetland);
if (CheckGeoMarsh(mapData, grid))
smallset.Add(GeoSmallClass.Marsh);
if (CheckGeoFloodplain(mapData, grid))
smallset.Add(GeoSmallClass.Floodplain);
if (CheckGeoPlain(mapData, grid))
smallset.Add(GeoSmallClass.Plain);
if (smallset.Count == 0)
{
smallset.Add(GeoSmallClass.Grassland);
smallset.Add(GeoSmallClass.Permafrost);
smallset.Add(GeoSmallClass.Tundra);
smallset.Add(GeoSmallClass.Plain);
}
if (CheckGeoDesert(mapData, grid))
smallset.Add(GeoSmallClass.Desert);
//第二次生成
if (GetGeoId(grid.CivEnum, big, smallset, out id))
{
grid.GeoIdList.Add(id);
UsedGeoId.Add(id);
}
}
}
}
//Step #4 整体处理Geo中的大洋部分
uint OceanCount = 0;
Dictionary<Vector2,uint> OceanMark = new Dictionary<Vector2,uint>();
for (int x = 0; x < _width; x++)
for (int y = 0; y < _height; y++)
if (mapData.GridMap.GetGridDataByPos(x, y, out var _grid)
&& _grid.Terrain != TerrainType.DeepSea)
OceanMark.Add(new Vector2(x,y),9999);
for (int x = 0; x < _width; x++)
for (int y = 0; y < _height; y++)
{
if (!OceanMark.TryGetValue(new Vector2(x, y), out var tmark))
{
Queue<Vector2> queue = new Queue<Vector2>();
OceanMark.Add(new Vector2(x, y),OceanCount);
queue.Enqueue(new Vector2(x, y));
var tmptest = 0;
while (queue.Count > 0 && tmptest < 10000)
{
tmptest++;
var now = queue.Dequeue();
foreach(var dir in dir8)
if (mapData.GridMap.GetGridDataByV2(now + dir,out var _g)
&& !OceanMark.TryGetValue(now + dir, out var _))
{
OceanMark.Add(now + dir,OceanCount);
queue.Enqueue(now + dir);
}
}
OceanCount++;
}
}
//OceanMarkSort 按照海域大小,记录了每一片成片相连的海域
List<Vector2> oceanMarkSort = new List<Vector2>();
for (int i = 0; i < OceanCount; i++)
{
int count = 0;
for (int x = 0; x < _width; x++)
for (int y = 0; y < _height; y++)
if (OceanMark.TryGetValue(new Vector2(x, y), out var tmark) && tmark == i)
count++;
oceanMarkSort.Add(new Vector2(count,i));
}
oceanMarkSort.Sort((a, b) => a.x.CompareTo(b.x));
int oceanId = 1;
var smallsea = new HashSet<GeoSmallClass>() { GeoSmallClass.Sea };
//遍历每一片海域赋予geoId
for (int t = oceanMarkSort.Count - 1; t >= 0; t--)
{
//tmpId 就是本轮要设置的大洋/大海 , 前6大海域走大洋体系
uint geoId = (uint)oceanId;
if (oceanId > 6) //剩下的走大海体系从所有sea里面挑选就行
GetGeoId(CivEnum.Common, GeoBigClass.Water, smallsea, out geoId);
for (int x = 0; x < _width; x++)
for (int y = 0; y < _height; y++)
{
if (!mapData.GridMap.GetGridDataByPos(x, y, out GridData g)) continue;
if (OceanMark.TryGetValue(new Vector2(x, y), out var tmark) &&
tmark == (uint)oceanMarkSort[t].y)
{
g.GeoIdList = new List<uint>();
g.GeoIdList.Add(geoId);
}
}
oceanId++;
}
}
public string GetSmallClassName(GeoSmallClass smallClass)
{
foreach(var t in SmallClassInfoList)
if (t.GeoSmallClass == smallClass)
return MultilingualManager.Instance.GetMultilingualTextSafe(t.GeoSmallClassText);
return "";
}
public bool GetGeoId(CivEnum civ, GeoBigClass bigType, HashSet<GeoSmallClass> smallTypeSet, out uint id)
{
id = 0;
// 添加保护:确保 GeoDict 已初始化
if (GeoDict == null || GeoDict.Count == 0)
{
LogSystem.LogError("GeoDict 未初始化或为空,无法获取 GeoId");
return false;
}
// 添加保护:确保 smallTypeSet 不为空
if (smallTypeSet == null || smallTypeSet.Count == 0)
{
LogSystem.LogError("smallTypeSet 为空,无法获取 GeoId");
return false;
}
// 添加保护:确保 UsedGeoId 已初始化
if (UsedGeoId == null) UsedGeoId = new HashSet<uint>();
var selected = new List<uint>();
foreach (var t in GeoDict)
{
if (t.Value == null) continue; // 添加保护
if (civ != CivEnum.Common && t.Value.CivEnum != civ) continue;
if (t.Value.GeoBigClass != bigType) continue;
if (!smallTypeSet.Contains(t.Value.GeoSmallClass)) continue;
if (UsedGeoId.Contains(t.Key)) continue;
selected.Add(t.Key);
}
if (selected.Count == 0)
{
foreach (var t in GeoDict)
{
if (t.Value == null) continue; // 添加保护
if (civ != CivEnum.Common && t.Value.CivEnum != civ) continue;
if (t.Value.GeoBigClass != bigType) continue;
// 水体的话,只能从 sea 里选择
if (bigType == GeoBigClass.Water && t.Value.GeoSmallClass != GeoSmallClass.Sea) continue;
if (UsedGeoId.Contains(t.Key)) continue;
selected.Add(t.Key);
}
// 如果第二轮还是找不到
if (selected.Count == 0)
{
LogSystem.LogWarning($"未找到匹配的 GeoId: Civ={civ}, BigType={bigType}");
return false;
}
}
id = selected[Random.Range(0, selected.Count)];
return true;
}
public bool GetForestGeoId(MapData mapData,GridData grid,out uint retId)
{
var big = GeoBigClass.Forest;
var smallset = new HashSet<GeoSmallClass>();
retId = 0;
if (grid.Vegetation != Vegetation.Trees) return false;
if (CheckGeoMangrove(mapData,grid))
smallset.Add(GeoSmallClass.Mangrove);
if (CheckGeoOasis(mapData,grid))
smallset.Add(GeoSmallClass.Oasis);
if (CheckGeoSavanna(mapData,grid))
smallset.Add(GeoSmallClass.Savanna);
if (CheckGeoJungle(mapData,grid))
smallset.Add(GeoSmallClass.Jungle);
if (smallset.Count == 0)
{
smallset.Add(GeoSmallClass.Deciduous);
smallset.Add(GeoSmallClass.Evergreen);
smallset.Add(GeoSmallClass.Taiga);
}
return GetGeoId(grid.CivEnum, big, smallset, out retId);
}
private bool CheckGeoLake(MapData map, GridData grid)
{
//dir4必须都是land
if (grid.Terrain != TerrainType.ShallowSea) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir4)
if (!map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
|| tgrid.Terrain != TerrainType.Land) return false;
return true;
}
private bool CheckGeoRiver(MapData map, GridData grid)
{
//dir4必须有相对两块是land
if (grid.Terrain != TerrainType.ShallowSea) return false;
var cur = grid.Pos.V2();
if (!map.GridMap.GetGridDataByV2(cur + dir4[0], out var tgrid0))return false;
if (!map.GridMap.GetGridDataByV2(cur + dir4[1], out var tgrid1))return false;
if (!map.GridMap.GetGridDataByV2(cur + dir4[2], out var tgrid2))return false;
if (!map.GridMap.GetGridDataByV2(cur + dir4[3], out var tgrid3))return false;
if (tgrid0.Terrain == TerrainType.Land && tgrid2.Terrain == TerrainType.Land) return true;
if (tgrid1.Terrain == TerrainType.Land && tgrid3.Terrain == TerrainType.Land) return true;
return false;
}
private bool CheckGeoFjord(MapData map, GridData grid)
{
//dir8必须有1格山
if (grid.Terrain != TerrainType.ShallowSea) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir8)
if (map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
&& tgrid.Feature == TerrainFeature.Mountain) return true;
return false;
}
private bool CheckGeoWetland(MapData map, GridData grid)
{
//dir4必须有1格水
if (grid.Terrain != TerrainType.Land) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir4)
if (map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
&& tgrid.Terrain != TerrainType.Land) return true;
return false;
}
private bool CheckGeoMarsh(MapData map, GridData grid)
{
//dir4必须有1格水
if (grid.Terrain != TerrainType.Land) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir4)
if (map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
&& tgrid.Terrain != TerrainType.Land) return true;
return false;
}
private bool CheckGeoOasis(MapData map, GridData grid)
{
//dir8不能有水
if (grid.Vegetation != Vegetation.Trees) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir8)
if (!map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
|| tgrid.Terrain != TerrainType.Land) return false;
return true;
}
private bool CheckGeoMangrove(MapData map, GridData grid)
{
//dir4必须有2格水
if (grid.Vegetation != Vegetation.Trees) return false;
var cur = grid.Pos.V2();
int water = 0;
foreach (var dir in dir4)
if (map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
&& tgrid.Terrain != TerrainType.Land) water ++;
return water > 1;
}
private bool CheckGeoFloodplain(MapData map, GridData grid)
{
//dir4必须有2格水
if (grid.Terrain != TerrainType.Land) return false;
var cur = grid.Pos.V2();
int water = 0;
foreach (var dir in dir4)
if (map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
&& tgrid.Terrain != TerrainType.Land) water ++;
return water > 1;
}
private bool CheckGeoDesert(MapData map, GridData grid)
{
//dir8必须无水自身无田
if (grid.Terrain != TerrainType.Land) return false;
if (grid.Resource == ResourceType.Crop) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir8)
if (!map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
|| tgrid.Terrain != TerrainType.Land)
return false;
return true;
}
private bool CheckGeoSavanna(MapData map, GridData grid)
{
//dir8必须无林
if (grid.Vegetation != Vegetation.Trees) return false;
var cur = grid.Pos.V2();
foreach (var dir in dir8)
if (!map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
|| tgrid.Vegetation != Vegetation.Trees)
return false;
return true;
}
private bool CheckGeoJungle(MapData map, GridData grid)
{
//dir8必须>=3林
if (grid.Vegetation != Vegetation.Trees) return false;
var cur = grid.Pos.V2();
var tree = 0;
foreach (var dir in dir8)
if (map.GridMap.GetGridDataByV2(cur + dir, out var tgrid)
&& tgrid.Vegetation == Vegetation.Trees)
tree++;
return tree > 2;
}
private bool CheckGeoPlain(MapData map, GridData grid)
{
//必须是crop
if (grid.Terrain != TerrainType.Land) return false;
if (grid.Resource != ResourceType.Crop) return false;
return true;
}
}
public enum GeoBigClass {Building, Mountain, Plain, Forest, Water }
public enum GeoSmallClass {
NavelBase,
Military,
Windmill,
Market,
Hill,
Mountain,
Volcano,
Plain,
Floodplain,
Desert,
Savanna,
Sawmill,
Mangrove,
Deciduous,
Jungle,
Ocean,
Sea,
River,
Lake,
Port,
Mine,
Forge,
Oasis,
Marsh,
Grassland,
Evergreen,
Taiga,
Wetland,
Permafrost,
Tundra,
Fjord,
Bridge,
Island,
Peninsula
}
[Serializable]
public class GeoItem
{
public uint Id;
public GeoBigClass GeoBigClass;
public GeoSmallClass GeoSmallClass;
public CivEnum CivEnum;
public List<CityLibrary> NearbyCity;
[MultilingualField]
public string GeoName;
[MultilingualField]
public string GeoDesc;
}
[Serializable]
public class SmallClassInfo
{
public GeoSmallClass GeoSmallClass;
[MultilingualField]
public string GeoSmallClassText;
}