TH1/Unity/Assets/Scripts/TH1_Logic/Map/MapGenerator.cs
2025-12-06 01:38:53 +08:00

1112 lines
47 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.

/*
* @Author: 白哉
* @Description: 地图生成器
* @Date: 2025年04月01日 星期二 11:04:43
* @Modify:
*/
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using RuntimeData;
using TH1_Logic.Core;
using Unity.VisualScripting;
using Random = UnityEngine.Random;
namespace Logic
{
public class MapGenerator
{
private Main _main;
private MapData _mapData;
//海陆层参数,柏林噪音
private const float Scale = 20f; // 噪声的缩放因子
private float _landThreshold; // 陆地的阈值
private float _mountainThreshold = 0.7f; // 山地的阈值
private float[,] _heightMap;
private const int SmoothIterations = 3; //平滑次数
private uint _width, _height;
private List<MapPosition> _tribes;
//用来避免treasure连着生成
private List<MapPosition> _treasure;
private List<MapPosition> _innerCityGrid;
private List<MapPosition> _outerCityGrid;
public List<MapPosition> PlayerCivOri;
public Dictionary<uint, GeoItem> GeoDict;
public HashSet<uint> UsedGeoId;
List<Vector2> dir4 = new List<Vector2>() { Vector2.up, Vector2.right, Vector2.down, Vector2.left };
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 MapGenerator(Main main,MapData mapData)
{
_main = main;
_mapData = mapData;
GeoDict = new Dictionary<uint, GeoItem>();
UsedGeoId = new HashSet<uint>();
foreach(var t in Table.Instance.GeoDataAssets.GeoItemList)
GeoDict.Add(t.Id,t);
}
public bool GetGeoId(CivEnum civ, GeoBigClass bigType, HashSet<GeoSmallClass> smallTypeSet,out uint id)
{
var selected = new List<uint>();
foreach (var t in GeoDict)
{
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 (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;
//这一轮 放下smallType的限制
//if(!smallTypeSet.Contains(t.Value.GeoSmallClass )) continue;
if(UsedGeoId.Contains(t.Key)) continue;
selected.Add(t.Key);
}
//如果第二轮还是找不到
if (selected.Count == 0)
{
id = 0;
return false;
}
}
id = selected[Random.Range(0, selected.Count)];
return true;
}
public void GenerateMap(MapData mapData)
{
_width = mapData.MapConfig.Width;
_height = mapData.MapConfig.Height;
_heightMap = new float[_width, _height];
if (DebugCenter.Instance.DebugLandThreshold < 0)
_landThreshold = new System.Random().Next(200, 400) / 1000f;
else
_landThreshold = DebugCenter.Instance.DebugLandThreshold;
_tribes = new List<MapPosition>();
_treasure = new List<MapPosition>();
_innerCityGrid = new List<MapPosition>();
_outerCityGrid = new List<MapPosition>();
//用柏林噪音生成地图高度
GenerateHeightMap();
//平滑地图
SmoothHeightMap();
//归一化处理地图,均匀分布0~1范围
NormalizeHeightMap();
//在连续的大片海洋上生成岛屿
GenerateIsland();
//在连续的陆地上生成湖泊
GenerateLakes();
//生成所有tribe
GenerateTribes();
//创建文明摇篮城市(每个玩家的原始首都)
GenerateCradleCity(mapData);
//初始化所有Grid信息
InitGridMapData(mapData);
}
//在MapRenderer初始化之后再进行一些mapgenerator的其他操作
public void GenerateMapAfterMapRenderer(MapData mapData)
{
//初始化所有玩家的初始视野信息
InitAllPlayerSight(mapData);
//初始化Debug的参数
InitDebugInfo(mapData);
//初始化所有玩家的特殊领地技能(目前主要是kaguya)
InitPlayerSpecialSkill(mapData);
}
//海陆层使用的地形高度数据
private void GenerateHeightMap()
{
for (var x = 0; x < _width; x++)
{
for (var y = 0; y < _height; y++)
{
float seda = new System.Random().Next(1, 30);
float sedb = new System.Random().Next(2, 40);
// 使用种子值来改变噪声的生成,保证每次生成的噪声不同
var xCoord = (float)x / _width * Scale / seda;
var yCoord = (float)y / _height * Scale / sedb;
_heightMap[x, y] = Mathf.PerlinNoise(xCoord, yCoord);
//Debug.Log($"sed = {seda}\\{sedb} xC = {xCoord} yC = {yCoord} height = {heightMap[x, y]}");
}
}
}
//海陆层使用的地形平滑工具
private void SmoothHeightMap()
{
for (var iteration = 0; iteration < SmoothIterations; iteration++)
{
var newHeightMap = new float[_width, _height];
for (int x = 0; x < _width; x++)
{
for (int y = 0; y < _height; y++)
{
// 计算该位置的邻域平均值
var average = GetAverageHeight(x, y);
newHeightMap[x, y] = average;
}
}
_heightMap = newHeightMap;
}
}
// 获取该位置的邻域平均高度值
private float GetAverageHeight(int x, int y)
{
var total = 0f;
var count = 0;
// 获取周围的邻域高度值
for (var dx = -1; dx <= 1; dx++)
{
for (var dy = -1; dy <= 1; dy++)
{
var nx = x + dx;
var ny = y + dy;
if (nx < 0 || nx >= _width || ny < 0 || ny >= _height)
continue;
total += _heightMap[nx, ny];
count++;
// 确保在有效范围内
}
}
return total / count;
}
//归一化处理高度
private void NormalizeHeightMap()
{
var minHeight = Mathf.Infinity;
var maxHeight = -Mathf.Infinity;
// 计算最小值和最大值
for (var x = 0; x < _width; x++)
{
for (int y = 0; y < _height; y++)
{
if (_heightMap[x, y] < minHeight)
minHeight = _heightMap[x, y];
if (_heightMap[x, y] > maxHeight)
maxHeight = _heightMap[x, y];
}
}
// 将高度图归一化到 [0, 1] 范围
for (var x = 0; x < _width; x++)
{
for (var y = 0; y < _height; y++)
{
_heightMap[x, y] = (_heightMap[x, y] - minHeight) / (maxHeight - minHeight);
}
}
}
//生成湖泊
private void GenerateLakes()
{
//Step #1 每次寻找一个最高的山,开始制造河流,每次流往附近最低的格子,直到格子周围没有比自己更低的格子,停下。
//每次找的山头周围2格内不能有水流
List<(Vector2Int pos, float height)> sorth = GetSortedTilesByHeight(_heightMap);
//目前先暂定river最多占掉0.05的陆地格子
int riverCount = (int)(sorth.Count * 0.05);
for (int i = sorth.Count - 1; i >= 0; i--)
if(sorth[i].height <= _landThreshold)
{
riverCount = (int)((sorth.Count - i) * 0.05);
if (i * 2 < _width * _height)
{
//30%的概率是干旱图70%的概率是多河图
if (Random.Range(0f, 1f) < 0.7)
{
riverCount = (int)((sorth.Count - i) * 0.2);
//Debug.Log("More River!!");
}
}
break;
}
for(int i = sorth.Count - 1;i >= 0 && riverCount > 0; i-- )
{
//寻找下一个河流源头
if (!CheckGridRiverHead(sorth[i].pos.x, sorth[i].pos.y, 2)) continue;
var x = sorth[i].pos.x;
var y = sorth[i].pos.y;
for (;riverCount > 0;)//p用来保底
{
//当前水流x,y,找四个方向最低的
int tx = x, ty = y;
if (x - 1 >= 0 && _heightMap[x - 1, y] < _heightMap[tx, ty] && _heightMap[x - 1, y] > _landThreshold) {tx = x - 1; ty = y; }
if (x + 1 < _width && _heightMap[x + 1, y] < _heightMap[tx, ty] && _heightMap[x + 1, y] > _landThreshold) {tx = x + 1; ty = y; }
if (y - 1 >= 0 && _heightMap[x, y - 1] < _heightMap[tx, ty]&& _heightMap[x, y - 1] > _landThreshold) {tx = x; ty = y - 1; }
if (y + 1 < _height && _heightMap[x, y + 1] < _heightMap[tx, ty] && _heightMap[x, y + 1] > _landThreshold) {tx = x; ty = y + 1; }
//让当前格子变为水流
_heightMap[x, y] = 0;
riverCount--;
//如果不能再流
if (tx == x && ty == y) break;
x = tx;
y = ty;
}
}
}
//给生成湖泊用的函数 给格子排序高度
public static List<(Vector2Int pos, float height)> GetSortedTilesByHeight(float[,] heights)
{
int width = heights.GetLength(0);
int height = heights.GetLength(1);
// 使用 LINQ 将二维数组展平、转换、排序并生成列表,一行搞定。
return Enumerable.Range(0, width)
.SelectMany(x => Enumerable.Range(0, height)
.Select(y => (pos: new Vector2Int(x, y), height: heights[x, y])))
.OrderBy(tile => tile.height)
.ToList();
}
//给生成湖泊用的函数 确认一个点周围x范围内有没有水
public bool CheckGridRiverHead(int X,int Y,int r)
{
for (var x = X - r; x < X + r; x++)
for (var y = Y - r; y < Y + r; y++)
{
if(x < 0 || x >= _width || y < 0 || y >= _height) return false;
if (_heightMap[x, y] <= _landThreshold) return false;
}
return true;
}
//在连续大片海洋中生成岛屿
private void GenerateIsland()
{
for (var x = 1; x < _width - 1; x++)
for (var y = 1; y < _height - 1; y++)
{
var t = GenerateTerrain(x, y);
var canBeIsland = false;
if (t == TerrainType.DeepSea)
{
canBeIsland = true;
var landNearby = 0;
for (var i = -2; i <= 2; i++)
for (var j = -2; j <= 2; j++)
if (x + i >= 0 && x + i < _width && y + j >= 0 && y + j < _height &&
GenerateTerrain(x + i, y + j) != TerrainType.DeepSea)
landNearby++;
if (landNearby > 9)
canBeIsland = false;
}
if (canBeIsland && UnityEngine.Random.Range(0, 100) < 50)
_heightMap[x, y] = _landThreshold + 1;
}
}
//生成所有tirbes 以及对应的innergrids
private void GenerateTribes()
{
var cant = new bool[_width, _height];
var count = _width * _height; //剩余多少格子能用
var cityNum = (int)(count / 12);
Array.Clear(cant, 0, cant.Length);
for (var x = 0; x < _width; x++)
for (var y = 0; y < _height; y++)
if (_heightMap[x, y] <= _landThreshold || x == 0 || x == _width - 1 || y == 0 || y == _height - 1)
{
cant[x, y] = true;
count--;
}
for (int c = 0; count > 0 && c <= cityNum; c++)
{
var choose = (int)UnityEngine.Random.Range(1, count); //每次随机一个可用的格子(序号)
//Debug.Log("this time " + count + " choose : " + choose);
var tmp = 0;
for (int x = 0; x < _width; x++)
{
for (int y = 0; y < _height; y++)
{
if (cant[x, y])
continue;
tmp++;
if (tmp != choose)
continue;
_tribes.Add(new MapPosition(x,y));
for (var xx = -2; xx <= 2; xx++)
for (var yy = -2; yy <= 2; yy++)
{
var nx = x + xx;
var ny = y + yy;
if (nx < 0 || nx >= _width || ny < 0 || ny >= _height || cant[nx, ny])
continue;
cant[nx, ny] = true; // 把新城市方圆2距离的格子都ban了
count--;
}
break;
}
if (tmp == choose) break;
}
if (count <= 0) break;
}
foreach (var cent in _tribes)
{
for(int i = cent.X - 1; i<= cent.X + 1 ; i++)
for(int j = cent.Y - 1; j<= cent.Y + 1 ; j++)
if(i >= 0 && i < _width && j >= 0 && j < _height)
_innerCityGrid.Add(new MapPosition(i,j));
}
foreach (var cent in _tribes)
{
for(int i = cent.X - 2; i<= cent.X + 2 ; i++)
for(int j = cent.Y - 2; j<= cent.Y + 2 ; j++)
if(i >= 0 && i < _width && j >= 0 && j < _height)
_outerCityGrid.Add(new MapPosition(i,j));
}
//List<Vector2Int> LandCenters = new List<Vector2Int>();
//_map.cityCount = map.cityCenters.Count;
}
private void InitGridMapData(MapData mapData)
{
//标记innerCity和OnterCity
for (int x = 0; x < _width; x++)
{
for (int y = 0; y < _height; y++)
{
Vector2 curV2 = new Vector2(x, y);
// 生成文明层
var civId = GenerateCivilization(mapData,x, y);
CivEnum civEnum = (CivEnum)(civId + 1);
InnerOuterCity innerOuterCity = InnerOuterCity.Edge;
if(_innerCityGrid.Contains(new MapPosition(x, y)))
innerOuterCity = InnerOuterCity.InnerCity;
else if(_outerCityGrid.Contains(new MapPosition(x, y)))
innerOuterCity = InnerOuterCity.OuterCity;
// 生成海陆层(陆地、浅海、深海)
var terrain = GenerateTerrain(x, y,civEnum);
// 生成地形层(是否有山)
var feature = (terrain == TerrainType.Land) ? GenerateFeature(x, y,civEnum) : TerrainFeature.None;
//如果是摇篮城市terrain设置为road
if (mapData.GridMap.GetGridDataByPos(x, y, out var gg)
&& mapData.GetCityDataByGid(gg.Id,out var cc))
feature = TerrainFeature.Road;
// 生成植被层(是否有树木)
var vegetation = (feature == TerrainFeature.None && terrain == TerrainType.Land)
? GenerateVegetation(x, y,civEnum)
: Vegetation.None;
// 生成资源层
var resource = GenerateResource(x, y, terrain, feature, vegetation,civEnum,innerOuterCity);
//生成四个角的塔
if (HasTower(x, y))
{
feature = TerrainFeature.None;
resource = ResourceType.Tower;
vegetation = Vegetation.None;
}
// 设置这个地块的所有层信息
//Debug.Log(map.grid[x,y]);
if (mapData.GridMap.GetGridDataByPos(x, y, out GridData g))
g.SetGridData(terrain,feature,vegetation,resource,civId);
}
}
//Step #2 生成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 #3 整体处理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++;
}
}
private bool HasTower(int x, int y)
{
if (x == 0 && y == 0) return true;
if (x == 0 && y == _height - 1) return true;
if (x == _width - 1 && y == 0) return true;
if (x == _width - 1 && y == _height - 1) return true;
return false;
}
//生成文明摇篮城市(每个玩家的原始首都)
private void GenerateCradleCity(MapData mapData)
{
//制作文明摇篮地点候选列表
var cityIndices = new System.Collections.Generic.List<MapPosition>();
foreach (var c in _tribes)
{
var nearbyLand = 0;
var cx = (int)c.PosId / 1000;
var cy = (int)c.PosId % 1000;
for (int x = cx - 1; x <= cx + 1; x++)
for (int y = cy - 1; y <= cy + 1; y++)
if (x >= 0 && x < _width && y >= 0 && y < _height && _heightMap[x, y] > _landThreshold)
nearbyLand++;
if (nearbyLand > 0)
cityIndices.Add(new MapPosition(cx,cy));
}
if (cityIndices.Count < mapData.MapConfig.PlayerCount)
mapData.MapConfig.PlayerCount = (uint)cityIndices.Count;
// 从制作列表随机排序并取前m个
PlayerCivOri = cityIndices
.OrderBy(x => Guid.NewGuid()) // 使用 Guid 确保随机性
.Take((int)mapData.MapConfig.PlayerCount)
.ToList();
//建立玩家档案,建立具体城市,建立初始单位
int rk = 0;
foreach (var p in mapData.PlayerMap.PlayerDataList)
{
GridData g;
mapData.GridMap.GetGridDataByPos(PlayerCivOri[rk].X, PlayerCivOri[rk].Y, out g);
//建设一个新城市
CityData c = mapData.AddCityData(g.Id, p.Id);
//设置该城市为首都
Main.CityLogic.SetCapital(mapData, c.Id);
//设置该玩家的文明摇篮城市为该城市
p.CradleCityId = c.Id;
//Main.CityLogic.SetCradle(mapData, c.Id);
//TODO grid还没初始化好这里不应该先建立unit的应该在grid初始化结束后建单位
//建立文明的开国单位,并额外赋予初始行动点
if(mapData.AddUnitData(g.Id,c.Id,new UnitFullType(UnitType.Warrior,GiantType.None,0),out var newUnit))
newUnit.SetFullAPCPMP();
rk++;
}
}
//返回x,y位置的海陆层信息
private TerrainType GenerateTerrain(int x, int y,CivEnum civ = CivEnum.Common,InnerOuterCity innerOuterCity = InnerOuterCity.All)
{
// 走柏林噪音判断
var heightValue = _heightMap[x, y];
if (heightValue > _landThreshold)
return TerrainType.Land;
if (x + 1 < _width && _heightMap[x + 1, y] > _landThreshold)
return TerrainType.ShallowSea;
if (x - 1 > 0 && _heightMap[x - 1, y] > _landThreshold)
return TerrainType.ShallowSea;
if (y + 1 < _height && _heightMap[x, y + 1] > _landThreshold)
return TerrainType.ShallowSea;
if (y - 1 > 0 && _heightMap[x, y - 1] > _landThreshold)
return TerrainType.ShallowSea;
return TerrainType.DeepSea;
}
//返回x,y位置的地形层信息
private TerrainFeature GenerateFeature(int x, int y,CivEnum civ = CivEnum.Common,InnerOuterCity innerOuterCity = InnerOuterCity.All)
{
if (_tribes.Contains(new MapPosition(x, y)))
return TerrainFeature.None;//如果是城市中心那必没有山
var table = Table.Instance.PlayerDataAssets;
return UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(TerrainFeature.Mountain, civ) ? TerrainFeature.Mountain : TerrainFeature.None;
}
//返回x,y位置的植被层信息
private Vegetation GenerateVegetation(int x, int y,CivEnum civ = CivEnum.Common,InnerOuterCity innerOuterCity = InnerOuterCity.All)
{
if (_tribes.Contains(new MapPosition(x,y)))
return Vegetation.None;//如果是城市中心那必没有树
var table = Table.Instance.PlayerDataAssets;
var a = UnityEngine.Random.Range(0f, 1f);
var b = table.GetLandformRate(Vegetation.Trees, civ);
//Debug.Log($"{x},{y},{civ},{innerOuterCity} : {a}/{b}, Tree");
return a < b ? Vegetation.Trees : Vegetation.None;
}
//返回x,y位置的资源层信息
private ResourceType GenerateResource(int x, int y, TerrainType terrain, TerrainFeature feature, Vegetation vegetation,CivEnum civ = CivEnum.Common,InnerOuterCity innerOuterCity = InnerOuterCity.All)
{
if (_tribes.Contains(new MapPosition(x,y))) return ResourceType.CityCenter;
var table = Table.Instance.PlayerDataAssets;
//Step #1 不再二环以内的,只能考虑遗迹和海星,且不能有其他资源
if (innerOuterCity == InnerOuterCity.Edge)
{
//先考虑遗迹
if (UnityEngine.Random.Range(0f, 1f) <
table.GetLandformRate(ResourceType.Treasure, civ, innerOuterCity))
{
//如果遗迹附近有别的遗迹,不连着生成
if (_treasure.Any(P => Math.Abs(P.X - x) + Math.Abs(P.Y - y) <= 2))
return ResourceType.None;
_treasure.Add(new MapPosition(x,y));
return ResourceType.Treasure;
}
//再考虑鲸鱼
if (terrain != TerrainType.Land)
{
if (UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Starfish, civ, innerOuterCity))
return ResourceType.Starfish;
}
return ResourceType.None;
}
//Step #2 如果是海洋
if (terrain != TerrainType.Land)
{
//先考虑渔业再考虑鲸鱼最后考虑Treasure
if (UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Fish, civ, innerOuterCity))
return ResourceType.Fish;
if (UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Starfish, civ, innerOuterCity))
return ResourceType.Starfish;
if (UnityEngine.Random.Range(0f, 1f) <
table.GetLandformRate(ResourceType.Treasure, civ, innerOuterCity))
{
//如果遗迹附近有别的遗迹,不连着生成
if (_treasure.Any(P => Math.Abs(P.X - x) + Math.Abs(P.Y - y) <= 2))
return ResourceType.None;
_treasure.Add(new MapPosition(x,y));
return ResourceType.Treasure;
}
return ResourceType.None;
}
//Step #3 如果是陆地先处理山再处理树最后处理Fruit和Crop
if (feature == TerrainFeature.Mountain)
{
var a = UnityEngine.Random.Range(0f, 1f);
var b = table.GetLandformRate(ResourceType.Metal, civ, innerOuterCity);
//Debug.Log($"{x},{y},{a}/{b},Metal");
//return UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Metal, civ, innerOuterCity)
return a < b
? ResourceType.Metal : ResourceType.None;
}
if (vegetation == Vegetation.Trees)
{
var a = UnityEngine.Random.Range(0f, 1f);
var b = table.GetLandformRate(ResourceType.Animal, civ, innerOuterCity);
//Debug.Log($"{x},{y},{a}/{b},Animal");
return a < b
? ResourceType.Animal : ResourceType.None;
}
if (UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Fruit, civ, innerOuterCity))
return ResourceType.Fruit;
if (UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Crop, civ, innerOuterCity))
return ResourceType.Crop;
if (UnityEngine.Random.Range(0f, 1f) < table.GetLandformRate(ResourceType.Treasure, civ, innerOuterCity))
{
//如果遗迹附近有别的遗迹,不连着生成
if (_treasure.Any(P => Math.Abs(P.X - x) + Math.Abs(P.Y - y) <= 2))
return ResourceType.None;
_treasure.Add(new MapPosition(x,y));
return ResourceType.Treasure;
}
return ResourceType.None;
}
//返回x,y位置的文化层信息
private uint GenerateCivilization(MapData mapData,int x, int y)
{
// 简单的随机分配一个文明,或者使用更复杂的算法进行分布
var minDis = 2139062143;
var civSeed = -1;
var tt = 0;
//Debug.Log(PlayerCivOri.Count);
foreach (var p in PlayerCivOri)
{
if (Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) < minDis)
{
minDis = (int)(Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y));
civSeed = (int)_mapData.PlayerMap.PlayerDataList[tt].PlayerCivId;
}
else if ((Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) == minDis) && UnityEngine.Random.Range(0, 2) == 1)
{
minDis = (int)(Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y));
civSeed = (int)_mapData.PlayerMap.PlayerDataList[tt].PlayerCivId;
}
else if ((Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y) == minDis + 1) && UnityEngine.Random.Range(0, 3) == 1)
{
minDis = (int)(Mathf.Abs(p.X - x) + Mathf.Abs(p.Y - y));
civSeed = (int)_mapData.PlayerMap.PlayerDataList[tt].PlayerCivId;
}
tt++;
}
if (civSeed == -1)
return (uint)UnityEngine.Random.Range(1, mapData.MapConfig.PlayerCount + 1);
return (uint)civSeed;
}
//初始化所有玩家的视野信息
private void InitAllPlayerSight(MapData mapData)
{
foreach (var cityData in mapData.CityMap.CityList)
{
mapData.GetPlayerDataByCityId(cityData.Id, out var playerData);
mapData.GetGridDataByCityId(cityData.Id, out var gridData);
Main.PlayerLogic.UpdateSight_LogicView(mapData, playerData, mapData.GridMap.GetAroundGridIdList(2,gridData,true),false,0.01f);
}
}
private void InitDebugInfo(MapData mapData)
{
if (DebugCenter.Instance.DebugSelfPlayerAllSight)
{
System.Collections.Generic.List<uint> tt = new System.Collections.Generic.List<uint>();
foreach (var gridData in mapData.GridMap.GridList)
tt.Add(gridData.Id);
Main.PlayerLogic.UpdateSight_LogicView(mapData,mapData.PlayerMap.SelfPlayerData,tt);
}
if (DebugCenter.Instance.DebugAIAllTech)
{
foreach (var playerData in mapData.PlayerMap.PlayerDataList)
{
if (playerData == mapData.PlayerMap.SelfPlayerData)
continue;
playerData.TechTree.LearnTech(TechType.Fishing);
playerData.TechTree.LearnTech(TechType.Organization);
playerData.TechTree.LearnTech(TechType.Farming);
playerData.TechTree.LearnTech(TechType.Mining);
playerData.TechTree.LearnTech(TechType.Forestry);
playerData.TechTree.LearnTech(TechType.Hunting);
playerData.TechTree.LearnTech(TechType.Smithery);
playerData.TechTree.LearnTech(TechType.Mathematics);
playerData.TechTree.LearnTech(TechType.FreeSpirit);
playerData.TechTree.LearnTech(TechType.Chivalry);
playerData.TechTree.LearnTech(TechType.Sailing);
playerData.TechTree.LearnTech(TechType.Navigation);
playerData.TechTree.LearnTech(TechType.Ramming);
playerData.TechTree.LearnTech(TechType.Riding);
playerData.TechTree.LearnTech(TechType.Archery);
playerData.TechTree.LearnTech(TechType.Spiritualism);
playerData.TechTree.LearnTech(TechType.Construction);
playerData.TechTree.LearnTech(TechType.Meditation);
playerData.TechTree.LearnTech(TechType.Aquatism);
}
}
if (DebugCenter.Instance.DebugAIMoreMoney)
{
foreach (var playerData in mapData.PlayerMap.PlayerDataList)
{
if (playerData == mapData.PlayerMap.SelfPlayerData)
continue;
playerData.AddCoin(10);
}
}
}
//初始化所有玩家的特殊领地技能(目前主要是kaguya)
private void InitPlayerSpecialSkill(MapData mapData)
{
//遍历每一个玩家处理每一种可能得specialSkill
foreach (var player in mapData.PlayerMap.PlayerDataList)
{
//首先通过Tech 赋予玩家技能
//处理kaguya专属skill
if (player.TechTree.CheckIfHasTechAtom(TechAtom.KaguyaFrenchNapoleonicCode))
{
var cityList = new List<CityData>();
mapData.GetCityDataListByPlayerId(player.Id,cityList);
foreach(var city in cityList)
Main.PlayerLogic.SetCityTerritoryGridSp(mapData,city,GridSpType.KaguyaGrid);
}
}
}
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;
}
}
}