using UnityEngine; using System.Collections.Generic; using System.Linq; public class GeographicFeatureGenerator { private static readonly string[] forestNames = { "Whispering Woods", "Ancient Grove", "Darkwood Forest", "Silverleaf Woods", "Thornwick Forest", "Emerald Thicket", "Shadowmere Woods", "Moonlight Grove", "Iron Bark Forest", "Mystic Woodland", "Deeproot Forest", "Starfall Woods", "Bramblewood", "Goldleaf Grove", "Stormwind Forest", "Twilight Woods" }; private static readonly string[] lakeNames = { "Crystal Lake", "Mirror Waters", "Dragon's Rest Lake", "Silver Mirror", "Moonbeam Lake", "Clearwater Basin", "Starfall Lake", "Azure Waters", "Misty Lake", "Tranquil Waters", "Golden Pond", "Serene Lake", "Echo Lake", "Shimmer Waters", "Peaceful Basin", "Reflection Lake" }; private static readonly string[] plainNames = { "Golden Plains", "Emerald Fields", "Windswept Meadows", "Sunset Plains", "Harmony Fields", "Verdant Expanse", "Rolling Hills", "Peaceful Meadows", "Sunlit Plains", "Gentle Fields", "Open Grasslands", "Green Expanse", "Fertile Plains", "Wide Meadows", "Bright Fields", "Vast Grasslands" }; private static readonly string[] mountainNames = { "Frostpeak Mountains", "Dragon's Spine", "Stormhaven Peaks", "Iron Crags", "Skyreach Mountains", "Cloudtop Peaks", "Granite Heights", "Thunder Ridge", "Snowcap Mountains", "Windbreak Peaks", "Stone Sentinels", "Frost Crown" }; private static readonly string[] riverNames = { "Swift Current", "Silver Stream", "Moonflow River", "Crystal Brook", "Whisper Creek", "Golden Waters", "Peaceful Stream", "Starlight River", "Gentle Current", "Clear Stream", "Harmony River", "Serene Waters" }; private HashSet processedTiles = new HashSet(); public List GenerateFeatures(MapData mapData) { var features = new List(); processedTiles.Clear(); // Generate forest features features.AddRange(GenerateForestFeatures(mapData)); // Generate lake features features.AddRange(GenerateLakeFeatures(mapData)); // Generate plain features (larger areas only) features.AddRange(GeneratePlainFeatures(mapData)); // Generate mountain features features.AddRange(GenerateMountainFeatures(mapData)); // Generate river features features.AddRange(GenerateRiverFeatures(mapData)); return features; } private List GenerateForestFeatures(MapData mapData) { var features = new List(); var forestClusters = FindConnectedAreas(mapData, TerrainType.Forest, 6); // Minimum 6 tiles foreach (var cluster in forestClusters) { if (!IsClusterProcessed(cluster)) { string name = GetRandomName(forestNames); var center = GetClusterCenter(cluster); var feature = new GeographicFeature(name, GeographicFeatureType.Forest, center); foreach (var tile in cluster) { feature.AddTile(tile); processedTiles.Add(tile); } features.Add(feature); } } return features; } private List GenerateLakeFeatures(MapData mapData) { var features = new List(); var lakeClusters = FindConnectedAreas(mapData, TerrainType.Lake, 4); // Minimum 4 tiles foreach (var cluster in lakeClusters) { if (!IsClusterProcessed(cluster)) { string name = GetRandomName(lakeNames); var center = GetClusterCenter(cluster); var feature = new GeographicFeature(name, GeographicFeatureType.Lake, center); foreach (var tile in cluster) { feature.AddTile(tile); processedTiles.Add(tile); } features.Add(feature); } } return features; } private List GeneratePlainFeatures(MapData mapData) { var features = new List(); var plainClusters = FindConnectedAreas(mapData, TerrainType.Plains, 25); // Minimum 25 tiles for named plains foreach (var cluster in plainClusters) { if (!IsClusterProcessed(cluster)) { // Check if this plain area is far enough from settlements var center = GetClusterCenter(cluster); if (IsFarFromSettlements(mapData, center, 8)) { string name = GetRandomName(plainNames); var feature = new GeographicFeature(name, GeographicFeatureType.Plain, center); foreach (var tile in cluster) { feature.AddTile(tile); processedTiles.Add(tile); } features.Add(feature); } } } return features; } private List GenerateMountainFeatures(MapData mapData) { var features = new List(); var mountainClusters = FindConnectedAreas(mapData, TerrainType.Mountain, 6); // Minimum 6 tiles foreach (var cluster in mountainClusters) { if (!IsClusterProcessed(cluster)) { string name = GetRandomName(mountainNames); var center = GetClusterCenter(cluster); var feature = new GeographicFeature(name, GeographicFeatureType.Mountain, center); foreach (var tile in cluster) { feature.AddTile(tile); processedTiles.Add(tile); } features.Add(feature); } } return features; } private List GenerateRiverFeatures(MapData mapData) { var features = new List(); var riverClusters = FindConnectedAreas(mapData, TerrainType.River, 8); // Minimum 8 tiles foreach (var cluster in riverClusters) { if (!IsClusterProcessed(cluster)) { string name = GetRandomName(riverNames); var center = GetClusterCenter(cluster); var feature = new GeographicFeature(name, GeographicFeatureType.River, center); foreach (var tile in cluster) { feature.AddTile(tile); processedTiles.Add(tile); } features.Add(feature); } } return features; } private List> FindConnectedAreas(MapData mapData, TerrainType terrainType, int minSize) { var visited = new HashSet(); var clusters = new List>(); for (int x = 0; x < mapData.Width; x++) { for (int y = 0; y < mapData.Height; y++) { var pos = new Vector2Int(x, y); if (!visited.Contains(pos) && mapData.GetTile(x, y)?.terrainType == terrainType) { var cluster = FloodFill(mapData, pos, terrainType, visited); if (cluster.Count >= minSize) { clusters.Add(cluster); } } } } return clusters; } private List FloodFill(MapData mapData, Vector2Int start, TerrainType targetType, HashSet visited) { var cluster = new List(); var stack = new Stack(); stack.Push(start); while (stack.Count > 0) { var current = stack.Pop(); if (visited.Contains(current) || !mapData.IsValidPosition(current.x, current.y)) continue; var tile = mapData.GetTile(current.x, current.y); if (tile?.terrainType != targetType) continue; visited.Add(current); cluster.Add(current); // Add neighbors for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; var neighbor = new Vector2Int(current.x + dx, current.y + dy); if (!visited.Contains(neighbor)) stack.Push(neighbor); } } } return cluster; } private Vector2Int GetClusterCenter(List cluster) { if (cluster.Count == 0) return Vector2Int.zero; Vector2 sum = Vector2.zero; foreach (var tile in cluster) { sum += new Vector2(tile.x, tile.y); } Vector2 center = sum / cluster.Count; return new Vector2Int(Mathf.RoundToInt(center.x), Mathf.RoundToInt(center.y)); } private bool IsClusterProcessed(List cluster) { return cluster.Any(tile => processedTiles.Contains(tile)); } private bool IsFarFromSettlements(MapData mapData, Vector2Int position, float minDistance) { var settlements = mapData.GetAllSettlements(); foreach (var settlement in settlements) { float distance = Vector2.Distance(position, settlement.position); if (distance < minDistance) return false; } return true; } private string GetRandomName(string[] nameArray) { return nameArray[Random.Range(0, nameArray.Length)]; } }