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 GeoItemList = new List(); public List SmallClassInfoList; [NonSerialized] private bool _initialized = false; public Dictionary GeoDict = null; [NonSerialized] private List dir4 = new List() { Vector2.up, Vector2.right, Vector2.down, Vector2.left }; [NonSerialized] private List dir8 = new List() { 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 UsedGeoId; public void GeoDictInit() { if (_initialized) return; _initialized = true; GeoDict = new Dictionary(); 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(); } public void OnMatchStart(MapData map) { GeoDictInit(); UsedGeoId = new HashSet(); 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 smallset = new HashSet() { }; 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(); //第一次生成 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 OceanMark = new Dictionary(); 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 queue = new Queue(); 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 oceanMarkSort = new List(); 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.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(); 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 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(); var selected = new List(); 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(); 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 NearbyCity; [MultilingualField] public string GeoName; [MultilingualField] public string GeoDesc; } [Serializable] public class SmallClassInfo { public GeoSmallClass GeoSmallClass; [MultilingualField] public string GeoSmallClassText; }