540 lines
19 KiB
C#
540 lines
19 KiB
C#
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;
|
||
} |