GeographicFeatureGenerator.cs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. public class GeographicFeatureGenerator
  5. {
  6. private static readonly string[] forestNames = {
  7. "Whispering Woods", "Ancient Grove", "Darkwood Forest", "Silverleaf Woods",
  8. "Thornwick Forest", "Emerald Thicket", "Shadowmere Woods", "Moonlight Grove",
  9. "Iron Bark Forest", "Mystic Woodland", "Deeproot Forest", "Starfall Woods",
  10. "Bramblewood", "Goldleaf Grove", "Stormwind Forest", "Twilight Woods"
  11. };
  12. private static readonly string[] lakeNames = {
  13. "Crystal Lake", "Mirror Waters", "Dragon's Rest Lake", "Silver Mirror",
  14. "Moonbeam Lake", "Clearwater Basin", "Starfall Lake", "Azure Waters",
  15. "Misty Lake", "Tranquil Waters", "Golden Pond", "Serene Lake",
  16. "Echo Lake", "Shimmer Waters", "Peaceful Basin", "Reflection Lake"
  17. };
  18. private static readonly string[] plainNames = {
  19. "Golden Plains", "Emerald Fields", "Windswept Meadows", "Sunset Plains",
  20. "Harmony Fields", "Verdant Expanse", "Rolling Hills", "Peaceful Meadows",
  21. "Sunlit Plains", "Gentle Fields", "Open Grasslands", "Green Expanse",
  22. "Fertile Plains", "Wide Meadows", "Bright Fields", "Vast Grasslands"
  23. };
  24. private static readonly string[] mountainNames = {
  25. "Frostpeak Mountains", "Dragon's Spine", "Stormhaven Peaks", "Iron Crags",
  26. "Skyreach Mountains", "Cloudtop Peaks", "Granite Heights", "Thunder Ridge",
  27. "Snowcap Mountains", "Windbreak Peaks", "Stone Sentinels", "Frost Crown"
  28. };
  29. private static readonly string[] riverNames = {
  30. "Swift Current", "Silver Stream", "Moonflow River", "Crystal Brook",
  31. "Whisper Creek", "Golden Waters", "Peaceful Stream", "Starlight River",
  32. "Gentle Current", "Clear Stream", "Harmony River", "Serene Waters"
  33. };
  34. private HashSet<Vector2Int> processedTiles = new HashSet<Vector2Int>();
  35. public List<GeographicFeature> GenerateFeatures(MapData mapData)
  36. {
  37. var features = new List<GeographicFeature>();
  38. processedTiles.Clear();
  39. // Generate forest features
  40. features.AddRange(GenerateForestFeatures(mapData));
  41. // Generate lake features
  42. features.AddRange(GenerateLakeFeatures(mapData));
  43. // Generate plain features (larger areas only)
  44. features.AddRange(GeneratePlainFeatures(mapData));
  45. // Generate mountain features
  46. features.AddRange(GenerateMountainFeatures(mapData));
  47. // Generate river features
  48. features.AddRange(GenerateRiverFeatures(mapData));
  49. return features;
  50. }
  51. private List<GeographicFeature> GenerateForestFeatures(MapData mapData)
  52. {
  53. var features = new List<GeographicFeature>();
  54. var forestClusters = FindConnectedAreas(mapData, TerrainType.Forest, 6); // Minimum 6 tiles
  55. foreach (var cluster in forestClusters)
  56. {
  57. if (!IsClusterProcessed(cluster))
  58. {
  59. string name = GetRandomName(forestNames);
  60. var center = GetClusterCenter(cluster);
  61. var feature = new GeographicFeature(name, GeographicFeatureType.Forest, center);
  62. foreach (var tile in cluster)
  63. {
  64. feature.AddTile(tile);
  65. processedTiles.Add(tile);
  66. }
  67. features.Add(feature);
  68. }
  69. }
  70. return features;
  71. }
  72. private List<GeographicFeature> GenerateLakeFeatures(MapData mapData)
  73. {
  74. var features = new List<GeographicFeature>();
  75. var lakeClusters = FindConnectedAreas(mapData, TerrainType.Lake, 4); // Minimum 4 tiles
  76. foreach (var cluster in lakeClusters)
  77. {
  78. if (!IsClusterProcessed(cluster))
  79. {
  80. string name = GetRandomName(lakeNames);
  81. var center = GetClusterCenter(cluster);
  82. var feature = new GeographicFeature(name, GeographicFeatureType.Lake, center);
  83. foreach (var tile in cluster)
  84. {
  85. feature.AddTile(tile);
  86. processedTiles.Add(tile);
  87. }
  88. features.Add(feature);
  89. }
  90. }
  91. return features;
  92. }
  93. private List<GeographicFeature> GeneratePlainFeatures(MapData mapData)
  94. {
  95. var features = new List<GeographicFeature>();
  96. var plainClusters = FindConnectedAreas(mapData, TerrainType.Plains, 25); // Minimum 25 tiles for named plains
  97. foreach (var cluster in plainClusters)
  98. {
  99. if (!IsClusterProcessed(cluster))
  100. {
  101. // Check if this plain area is far enough from settlements
  102. var center = GetClusterCenter(cluster);
  103. if (IsFarFromSettlements(mapData, center, 8))
  104. {
  105. string name = GetRandomName(plainNames);
  106. var feature = new GeographicFeature(name, GeographicFeatureType.Plain, center);
  107. foreach (var tile in cluster)
  108. {
  109. feature.AddTile(tile);
  110. processedTiles.Add(tile);
  111. }
  112. features.Add(feature);
  113. }
  114. }
  115. }
  116. return features;
  117. }
  118. private List<GeographicFeature> GenerateMountainFeatures(MapData mapData)
  119. {
  120. var features = new List<GeographicFeature>();
  121. var mountainClusters = FindConnectedAreas(mapData, TerrainType.Mountain, 6); // Minimum 6 tiles
  122. foreach (var cluster in mountainClusters)
  123. {
  124. if (!IsClusterProcessed(cluster))
  125. {
  126. string name = GetRandomName(mountainNames);
  127. var center = GetClusterCenter(cluster);
  128. var feature = new GeographicFeature(name, GeographicFeatureType.Mountain, center);
  129. foreach (var tile in cluster)
  130. {
  131. feature.AddTile(tile);
  132. processedTiles.Add(tile);
  133. }
  134. features.Add(feature);
  135. }
  136. }
  137. return features;
  138. }
  139. private List<GeographicFeature> GenerateRiverFeatures(MapData mapData)
  140. {
  141. var features = new List<GeographicFeature>();
  142. var riverClusters = FindConnectedAreas(mapData, TerrainType.River, 8); // Minimum 8 tiles
  143. foreach (var cluster in riverClusters)
  144. {
  145. if (!IsClusterProcessed(cluster))
  146. {
  147. string name = GetRandomName(riverNames);
  148. var center = GetClusterCenter(cluster);
  149. var feature = new GeographicFeature(name, GeographicFeatureType.River, center);
  150. foreach (var tile in cluster)
  151. {
  152. feature.AddTile(tile);
  153. processedTiles.Add(tile);
  154. }
  155. features.Add(feature);
  156. }
  157. }
  158. return features;
  159. }
  160. private List<List<Vector2Int>> FindConnectedAreas(MapData mapData, TerrainType terrainType, int minSize)
  161. {
  162. var visited = new HashSet<Vector2Int>();
  163. var clusters = new List<List<Vector2Int>>();
  164. for (int x = 0; x < mapData.Width; x++)
  165. {
  166. for (int y = 0; y < mapData.Height; y++)
  167. {
  168. var pos = new Vector2Int(x, y);
  169. if (!visited.Contains(pos) && mapData.GetTile(x, y)?.terrainType == terrainType)
  170. {
  171. var cluster = FloodFill(mapData, pos, terrainType, visited);
  172. if (cluster.Count >= minSize)
  173. {
  174. clusters.Add(cluster);
  175. }
  176. }
  177. }
  178. }
  179. return clusters;
  180. }
  181. private List<Vector2Int> FloodFill(MapData mapData, Vector2Int start, TerrainType targetType, HashSet<Vector2Int> visited)
  182. {
  183. var cluster = new List<Vector2Int>();
  184. var stack = new Stack<Vector2Int>();
  185. stack.Push(start);
  186. while (stack.Count > 0)
  187. {
  188. var current = stack.Pop();
  189. if (visited.Contains(current) || !mapData.IsValidPosition(current.x, current.y))
  190. continue;
  191. var tile = mapData.GetTile(current.x, current.y);
  192. if (tile?.terrainType != targetType)
  193. continue;
  194. visited.Add(current);
  195. cluster.Add(current);
  196. // Add neighbors
  197. for (int dx = -1; dx <= 1; dx++)
  198. {
  199. for (int dy = -1; dy <= 1; dy++)
  200. {
  201. if (dx == 0 && dy == 0) continue;
  202. var neighbor = new Vector2Int(current.x + dx, current.y + dy);
  203. if (!visited.Contains(neighbor))
  204. stack.Push(neighbor);
  205. }
  206. }
  207. }
  208. return cluster;
  209. }
  210. private Vector2Int GetClusterCenter(List<Vector2Int> cluster)
  211. {
  212. if (cluster.Count == 0)
  213. return Vector2Int.zero;
  214. Vector2 sum = Vector2.zero;
  215. foreach (var tile in cluster)
  216. {
  217. sum += new Vector2(tile.x, tile.y);
  218. }
  219. Vector2 center = sum / cluster.Count;
  220. return new Vector2Int(Mathf.RoundToInt(center.x), Mathf.RoundToInt(center.y));
  221. }
  222. private bool IsClusterProcessed(List<Vector2Int> cluster)
  223. {
  224. return cluster.Any(tile => processedTiles.Contains(tile));
  225. }
  226. private bool IsFarFromSettlements(MapData mapData, Vector2Int position, float minDistance)
  227. {
  228. var settlements = mapData.GetAllSettlements();
  229. foreach (var settlement in settlements)
  230. {
  231. float distance = Vector2.Distance(position, settlement.position);
  232. if (distance < minDistance)
  233. return false;
  234. }
  235. return true;
  236. }
  237. private string GetRandomName(string[] nameArray)
  238. {
  239. return nameArray[Random.Range(0, nameArray.Length)];
  240. }
  241. }