using UnityEngine; using System.Collections.Generic; public class ExpansionManager { private MapMaker2 mapMaker; private NoiseGenerator noiseGenerator; private System.Random random; public ExpansionManager(MapMaker2 mapMaker) { this.mapMaker = mapMaker; this.noiseGenerator = new NoiseGenerator(); this.random = new System.Random(mapMaker.seed); } public bool ShouldExpand(Vector2 playerPosition, MapData mapData) { float distanceThreshold = mapMaker.playerDistanceThreshold; // Check distance to each edge float distToLeft = playerPosition.x; float distToRight = mapData.Width - playerPosition.x; float distToTop = mapData.Height - playerPosition.y; float distToBottom = playerPosition.y; bool shouldExpand = distToLeft < distanceThreshold || distToRight < distanceThreshold || distToTop < distanceThreshold || distToBottom < distanceThreshold; if (shouldExpand) { Debug.Log($"Expansion needed! Threshold: {distanceThreshold}"); Debug.Log($"Distances - Left: {distToLeft}, Right: {distToRight}, Top: {distToTop}, Bottom: {distToBottom}"); } return shouldExpand; } public void ExpandMap(MapData mapData, Vector2 playerPosition, int expansionSize) { float distanceThreshold = mapMaker.playerDistanceThreshold; // Determine expansion directions bool expandLeft = playerPosition.x < distanceThreshold; bool expandRight = mapData.Width - playerPosition.x < distanceThreshold; bool expandUp = mapData.Height - playerPosition.y < distanceThreshold; bool expandDown = playerPosition.y < distanceThreshold; // Calculate new dimensions int leftExpansion = expandLeft ? expansionSize : 0; int rightExpansion = expandRight ? expansionSize : 0; int upExpansion = expandUp ? expansionSize : 0; int downExpansion = expandDown ? expansionSize : 0; int newWidth = mapData.Width + leftExpansion + rightExpansion; int newHeight = mapData.Height + upExpansion + downExpansion; // Store old dimensions and expansion info ExpansionInfo expansionInfo = new ExpansionInfo { oldWidth = mapData.Width, oldHeight = mapData.Height, newWidth = newWidth, newHeight = newHeight, leftExpansion = leftExpansion, rightExpansion = rightExpansion, upExpansion = upExpansion, downExpansion = downExpansion }; // Expand the map with offset handling ExpandMapWithOffset(mapData, expansionInfo); // Generate logical terrain and features for new areas using enhanced system GenerateEnhancedExpansion(mapData, expansionInfo); Debug.Log($"Map expanded from {expansionInfo.oldWidth}x{expansionInfo.oldHeight} to {newWidth}x{newHeight}"); Debug.Log($"Expansion directions - Left: {leftExpansion}, Right: {rightExpansion}, Up: {upExpansion}, Down: {downExpansion}"); } private struct ExpansionInfo { public int oldWidth, oldHeight; public int newWidth, newHeight; public int leftExpansion, rightExpansion; public int upExpansion, downExpansion; } private void ExpandMapWithOffset(MapData mapData, ExpansionInfo info) { mapData.ExpandMapWithOffset(info.newWidth, info.newHeight, info.leftExpansion, info.downExpansion); } private void GenerateEnhancedExpansion(MapData mapData, ExpansionInfo info) { // Enhanced multi-pass terrain generation for natural-looking results GenerateNaturalTerrain(mapData, info); SmoothTerrainTransitions(mapData, info); GenerateNaturalFeatures(mapData, info); GenerateSettlementsAndRoads(mapData, info); } private void GenerateNaturalTerrain(MapData mapData, ExpansionInfo info) { // Generate each expansion region with proper biome continuity if (info.leftExpansion > 0) GenerateTerrainRegion(mapData, 0, 0, info.leftExpansion, info.newHeight, ExpansionDirection.Left, info); if (info.rightExpansion > 0) { int startX = info.leftExpansion + info.oldWidth; GenerateTerrainRegion(mapData, startX, 0, info.rightExpansion, info.newHeight, ExpansionDirection.Right, info); } if (info.downExpansion > 0) GenerateTerrainRegion(mapData, info.leftExpansion, 0, info.oldWidth, info.downExpansion, ExpansionDirection.Down, info); if (info.upExpansion > 0) { int startY = info.downExpansion + info.oldHeight; GenerateTerrainRegion(mapData, info.leftExpansion, startY, info.oldWidth, info.upExpansion, ExpansionDirection.Up, info); } } private enum ExpansionDirection { Left, Right, Up, Down } private void GenerateTerrainRegion(MapData mapData, int startX, int startY, int width, int height, ExpansionDirection direction, ExpansionInfo info) { for (int x = startX; x < startX + width; x++) { for (int y = startY; y < startY + height; y++) { GenerateEnhancedTerrain(mapData, x, y, direction, info); } } } private void GenerateEnhancedTerrain(MapData mapData, int x, int y, ExpansionDirection direction, ExpansionInfo info) { MapTile tile = mapData.GetTile(x, y); if (tile == null) return; // Get reference context from existing terrain List referenceTiles = GetExtendedReferenceTiles(mapData, x, y, direction, info); // Generate realistic height with proper continuity float height = GenerateRealisticHeight(x, y, referenceTiles, direction, info); tile.height = height; // Determine terrain with natural biome rules TerrainType terrainType = DetermineRealisticTerrain(referenceTiles, height, x, y, direction, info); tile.terrainType = terrainType; tile.isWalkable = terrainType != TerrainType.Mountain && terrainType != TerrainType.Ocean; } private List GetExtendedReferenceTiles(MapData mapData, int x, int y, ExpansionDirection direction, ExpansionInfo info) { List referenceTiles = new List(); int range = 4; // Wider reference for better continuity switch (direction) { case ExpansionDirection.Left: for (int refY = y - range; refY <= y + range; refY++) { int refX = info.leftExpansion; if (mapData.IsValidPosition(refX, refY)) referenceTiles.Add(mapData.GetTile(refX, refY)); } break; case ExpansionDirection.Right: for (int refY = y - range; refY <= y + range; refY++) { int refX = info.leftExpansion + info.oldWidth - 1; if (mapData.IsValidPosition(refX, refY)) referenceTiles.Add(mapData.GetTile(refX, refY)); } break; case ExpansionDirection.Up: for (int refX = x - range; refX <= x + range; refX++) { int refY = info.downExpansion + info.oldHeight - 1; if (mapData.IsValidPosition(refX, refY)) referenceTiles.Add(mapData.GetTile(refX, refY)); } break; case ExpansionDirection.Down: for (int refX = x - range; refX <= x + range; refX++) { int refY = info.downExpansion; if (mapData.IsValidPosition(refX, refY)) referenceTiles.Add(mapData.GetTile(refX, refY)); } break; } return referenceTiles; } private float GenerateRealisticHeight(int x, int y, List referenceTiles, ExpansionDirection direction, ExpansionInfo info) { float distanceFromBoundary = GetDistanceFromBoundary(x, y, direction, info); // For very close tiles, copy heights directly if (distanceFromBoundary < 6f) { Vector2Int sourcePos = GetCorrespondingOriginalTile(x, y, direction, info); if (IsInOriginalArea(sourcePos.x, sourcePos.y, info)) { MapTile sourceTile = mapMaker.GetMapData().GetTile(sourcePos.x, sourcePos.y); if (sourceTile != null) { // Add very small noise to avoid perfect repetition float noise = noiseGenerator.GetNoise(x, y, 0.1f) * 0.05f; return sourceTile.height + noise; } } } // For farther tiles, use blended approach float height = 0f; height += noiseGenerator.GetNoise(x, y, 0.02f) * 0.6f; // Large landforms height += noiseGenerator.GetNoise(x, y, 0.06f) * 0.25f; // Medium features height += noiseGenerator.GetNoise(x, y, 0.12f) * 0.1f; // Small details height += noiseGenerator.GetNoise(x, y, 0.25f) * 0.05f; // Fine noise // Strong blending with reference heights if (referenceTiles.Count > 0) { float refAverage = 0f; foreach (var tile in referenceTiles) refAverage += tile.height; refAverage /= referenceTiles.Count; float blendFactor = Mathf.Lerp(0.8f, 0.2f, Mathf.Clamp01(distanceFromBoundary / 15f)); height = Mathf.Lerp(height, refAverage, blendFactor); } return height; } private float GetDistanceFromBoundary(int x, int y, ExpansionDirection direction, ExpansionInfo info) { switch (direction) { case ExpansionDirection.Left: return x; case ExpansionDirection.Right: return (info.newWidth - 1) - x; case ExpansionDirection.Down: return y; case ExpansionDirection.Up: return (info.newHeight - 1) - y; default: return 10f; } } private TerrainType DetermineRealisticTerrain(List referenceTiles, float height, int x, int y, ExpansionDirection direction, ExpansionInfo info) { // Calculate distance from boundary for continuity strength float distanceFromBoundary = GetDistanceFromBoundary(x, y, direction, info); // For the first 8 tiles from boundary, use direct terrain copying if (distanceFromBoundary < 8f) { return CopyNearestTerrain(x, y, direction, info); } else { // For further tiles, still use heavy continuity Dictionary biomeInfluence = AnalyzeBiomeInfluence(referenceTiles); TerrainType dominantBiome = GetDominantBiome(biomeInfluence); return GenerateContinuousTerrain(dominantBiome, height, x, y); } } private TerrainType CopyNearestTerrain(int x, int y, ExpansionDirection direction, ExpansionInfo info) { // Find the exact corresponding tile in the original map and copy its terrain Vector2Int sourcePos = GetCorrespondingOriginalTile(x, y, direction, info); if (IsInOriginalArea(sourcePos.x, sourcePos.y, info)) { MapTile sourceTile = mapMaker.GetMapData().GetTile(sourcePos.x, sourcePos.y); if (sourceTile != null) { return sourceTile.terrainType; } } // Fallback to nearest boundary tile return GetNearestBoundaryTerrain(x, y, direction, info); } private Vector2Int GetCorrespondingOriginalTile(int x, int y, ExpansionDirection direction, ExpansionInfo info) { switch (direction) { case ExpansionDirection.Left: // Mirror from the left boundary int offsetFromLeft = x; return new Vector2Int(info.leftExpansion + offsetFromLeft, y); case ExpansionDirection.Right: // Mirror from the right boundary int offsetFromRight = x - (info.leftExpansion + info.oldWidth); return new Vector2Int(info.leftExpansion + info.oldWidth - 1 - offsetFromRight, y); case ExpansionDirection.Down: // Mirror from the bottom boundary int offsetFromBottom = y; return new Vector2Int(x, info.downExpansion + offsetFromBottom); case ExpansionDirection.Up: // Mirror from the top boundary int offsetFromTop = y - (info.downExpansion + info.oldHeight); return new Vector2Int(x, info.downExpansion + info.oldHeight - 1 - offsetFromTop); default: return new Vector2Int(x, y); } } private TerrainType GetNearestBoundaryTerrain(int x, int y, ExpansionDirection direction, ExpansionInfo info) { Vector2Int boundaryPos = Vector2Int.zero; switch (direction) { case ExpansionDirection.Left: boundaryPos = new Vector2Int(info.leftExpansion, y); break; case ExpansionDirection.Right: boundaryPos = new Vector2Int(info.leftExpansion + info.oldWidth - 1, y); break; case ExpansionDirection.Down: boundaryPos = new Vector2Int(x, info.downExpansion); break; case ExpansionDirection.Up: boundaryPos = new Vector2Int(x, info.downExpansion + info.oldHeight - 1); break; } if (IsInOriginalArea(boundaryPos.x, boundaryPos.y, info)) { MapTile boundaryTile = mapMaker.GetMapData().GetTile(boundaryPos.x, boundaryPos.y); if (boundaryTile != null) return boundaryTile.terrainType; } return TerrainType.Plains; } private Dictionary AnalyzeBiomeInfluence(List referenceTiles) { Dictionary influence = new Dictionary(); foreach (var tile in referenceTiles) { float weight = GetBiomeWeight(tile.terrainType); if (influence.ContainsKey(tile.terrainType)) influence[tile.terrainType] += weight; else influence[tile.terrainType] = weight; } return influence; } private float GetBiomeWeight(TerrainType terrain) { // Some terrain types are more "influential" for expansion switch (terrain) { case TerrainType.Ocean: return 4.0f; // Much stronger ocean influence case TerrainType.Mountain: return 2.5f; case TerrainType.Lake: return 2.0f; // Stronger water influence case TerrainType.Forest: return 1.5f; case TerrainType.Shore: return 1.5f; // Stronger shore influence case TerrainType.River: return 1.2f; default: return 0.8f; } } private TerrainType GetDominantBiome(Dictionary influence) { if (influence.Count == 0) return TerrainType.Plains; TerrainType dominant = TerrainType.Plains; float maxInfluence = 0f; foreach (var kvp in influence) { if (kvp.Value > maxInfluence) { maxInfluence = kvp.Value; dominant = kvp.Key; } } return dominant; } private TerrainType GenerateContinuousTerrain(TerrainType dominantBiome, float height, int x, int y) { switch (dominantBiome) { case TerrainType.Mountain: return TransitionMountain(height, x, y); case TerrainType.Forest: return TransitionForest(height, x, y); case TerrainType.Ocean: return TransitionOcean(height, x, y); case TerrainType.Lake: return TransitionLake(height, x, y); case TerrainType.Shore: return TransitionShore(height, x, y); case TerrainType.River: return TransitionRiver(height, x, y); default: return TerrainType.Plains; } } private TerrainType TransitionMountain(float height, int x, int y) { // Mountains are very conservative if (height > 0.3f) // Lower threshold for mountain continuation return TerrainType.Mountain; else if (height > 0.0f) // Wide foothill zone return TerrainType.Forest; // Mountain foothills else return TerrainType.Plains; } private TerrainType TransitionForest(float height, int x, int y) { // Conservative forest generation if (height > 0.2f) return TerrainType.Mountain; // Higher areas become mountains else if (height > -0.1f) // Wide forest zone return TerrainType.Forest; else return TerrainType.Plains; } private TerrainType TransitionOcean(float height, int x, int y) { // Ocean is extremely conservative - almost always continues as ocean if (height < 0.0f) // Much more lenient for ocean continuation return TerrainType.Ocean; else if (height < 0.2f) // Very wide shore zone return TerrainType.Shore; else if (height < 0.4f) return TerrainType.Plains; // Coastal plains else return TerrainType.Forest; } private TerrainType TransitionLake(float height, int x, int y) { // Lakes are very conservative if (height < 0.0f) // Much more lenient for lake continuation return TerrainType.Lake; else if (height < 0.2f) // Wide shore zone return TerrainType.Shore; else return TerrainType.Plains; } private TerrainType TransitionShore(float height, int x, int y) { if (height < -0.1f) // More lenient transition to water return TerrainType.Ocean; else if (height < 0.4f) // Much wider shore zone return TerrainType.Shore; else return TerrainType.Plains; } private TerrainType TransitionRiver(float height, int x, int y) { // Rivers are conservative if (height < 0.0f) return TerrainType.River; else if (height < 0.1f) return TerrainType.Plains; // River plains else return TerrainType.Forest; } private TerrainType GenerateNaturalTerrainFromHeight(float height, int x, int y) { // Pure height-based generation without noise variation if (height > 0.6f) return TerrainType.Mountain; else if (height > 0.2f) return TerrainType.Forest; else if (height < -0.5f) return TerrainType.Ocean; else if (height < -0.2f) return TerrainType.Lake; else if (height < 0.0f) return TerrainType.Shore; else return TerrainType.Plains; } private void SmoothTerrainTransitions(MapData mapData, ExpansionInfo info) { // Much more aggressive smoothing for natural blending int smoothRadius = 8; // Increased from 5 // Process each expansion boundary multiple times for (int pass = 0; pass < 3; pass++) // Multiple smoothing passes { if (info.leftExpansion > 0) SmoothBoundaryRegion(mapData, info.leftExpansion - smoothRadius, 0, smoothRadius * 2, info.newHeight); if (info.rightExpansion > 0) SmoothBoundaryRegion(mapData, info.leftExpansion + info.oldWidth - smoothRadius, 0, smoothRadius * 2, info.newHeight); if (info.upExpansion > 0) SmoothBoundaryRegion(mapData, 0, info.downExpansion + info.oldHeight - smoothRadius, info.newWidth, smoothRadius * 2); if (info.downExpansion > 0) SmoothBoundaryRegion(mapData, 0, info.downExpansion - smoothRadius, info.newWidth, smoothRadius * 2); } } private void SmoothBoundaryRegion(MapData mapData, int startX, int startY, int width, int height) { for (int x = startX; x < startX + width; x++) { for (int y = startY; y < startY + height; y++) { if (!mapData.IsValidPosition(x, y)) continue; SmoothTileWithNeighbors(mapData, x, y); } } } private void SmoothTileWithNeighbors(MapData mapData, int x, int y) { MapTile centerTile = mapData.GetTile(x, y); Dictionary neighborTerrain = GetNeighborTerrain(mapData, x, y, 3); // Larger radius // If this tile is very isolated, consider changing it if (!neighborTerrain.ContainsKey(centerTile.terrainType) || neighborTerrain[centerTile.terrainType] < 3) // Stricter isolation check { TerrainType newTerrain = GetMostCommonNeighborTerrain(neighborTerrain); if (IsValidTerrainTransition(centerTile.terrainType, newTerrain)) { centerTile.terrainType = newTerrain; // Also smooth the height towards neighbors float avgHeight = GetAverageNeighborHeight(mapData, x, y, 2); centerTile.height = Mathf.Lerp(centerTile.height, avgHeight, 0.5f); } } } private Dictionary GetNeighborTerrain(MapData mapData, int centerX, int centerY, int radius) { Dictionary terrain = new Dictionary(); for (int x = centerX - radius; x <= centerX + radius; x++) { for (int y = centerY - radius; y <= centerY + radius; y++) { if (x == centerX && y == centerY) continue; if (!mapData.IsValidPosition(x, y)) continue; TerrainType terrainType = mapData.GetTile(x, y).terrainType; terrain[terrainType] = terrain.ContainsKey(terrainType) ? terrain[terrainType] + 1 : 1; } } return terrain; } private float GetAverageNeighborHeight(MapData mapData, int centerX, int centerY, int radius) { float totalHeight = 0f; int count = 0; for (int x = centerX - radius; x <= centerX + radius; x++) { for (int y = centerY - radius; y <= centerY + radius; y++) { if (x == centerX && y == centerY) continue; if (!mapData.IsValidPosition(x, y)) continue; totalHeight += mapData.GetTile(x, y).height; count++; } } return count > 0 ? totalHeight / count : 0f; } private TerrainType GetMostCommonNeighborTerrain(Dictionary terrain) { TerrainType mostCommon = TerrainType.Plains; int maxCount = 0; foreach (var kvp in terrain) { if (kvp.Value > maxCount) { maxCount = kvp.Value; mostCommon = kvp.Key; } } return mostCommon; } private bool IsValidTerrainTransition(TerrainType from, TerrainType to) { // Some terrain transitions don't make sense (e.g., ocean to mountain directly) if (from == TerrainType.Ocean && to == TerrainType.Mountain) return false; if (from == TerrainType.Mountain && to == TerrainType.Ocean) return false; return true; } private void GenerateNaturalFeatures(MapData mapData, ExpansionInfo info) { // Extend rivers naturally ExtendRiversIntoExpansion(mapData, info); // Create natural water bodies GenerateNaturalLakes(mapData, info); } private void ExtendRiversIntoExpansion(MapData mapData, ExpansionInfo info) { // Find rivers at boundaries and extend them naturally List riverEnds = FindRiversAtBoundaries(mapData, info); foreach (var riverEnd in riverEnds) { ExtendRiverNaturally(mapData, riverEnd, info); } } private List FindRiversAtBoundaries(MapData mapData, ExpansionInfo info) { List riverPoints = new List(); // Check boundaries for rivers CheckBoundaryForRivers(mapData, info.leftExpansion, 0, 1, info.newHeight, riverPoints); CheckBoundaryForRivers(mapData, info.leftExpansion + info.oldWidth - 1, 0, 1, info.newHeight, riverPoints); return riverPoints; } private void CheckBoundaryForRivers(MapData mapData, int startX, int startY, int width, int height, List riverPoints) { for (int x = startX; x < startX + width; x++) { for (int y = startY; y < startY + height; y++) { if (mapData.IsValidPosition(x, y)) { MapTile tile = mapData.GetTile(x, y); if (tile.terrainType == TerrainType.River) { riverPoints.Add(new Vector2Int(x, y)); } } } } } private void ExtendRiverNaturally(MapData mapData, Vector2Int start, ExpansionInfo info) { Vector2Int current = start; int maxLength = 25; for (int i = 0; i < maxLength; i++) { Vector2Int next = FindBestRiverDirection(mapData, current); if (next == current) break; MapTile nextTile = mapData.GetTile(next.x, next.y); if (nextTile.terrainType == TerrainType.Plains || nextTile.terrainType == TerrainType.Shore) { nextTile.terrainType = TerrainType.River; current = next; } else { break; } } } private Vector2Int FindBestRiverDirection(MapData mapData, Vector2Int current) { Vector2Int[] directions = { new Vector2Int(0, 1), new Vector2Int(1, 0), new Vector2Int(0, -1), new Vector2Int(-1, 0), new Vector2Int(1, 1), new Vector2Int(-1, 1), new Vector2Int(1, -1), new Vector2Int(-1, -1) }; Vector2Int best = current; float bestScore = float.MinValue; foreach (var dir in directions) { Vector2Int candidate = current + dir; if (mapData.IsValidPosition(candidate.x, candidate.y)) { float score = EvaluateRiverDirection(mapData, candidate); if (score > bestScore) { bestScore = score; best = candidate; } } } return best; } private float EvaluateRiverDirection(MapData mapData, Vector2Int position) { MapTile tile = mapData.GetTile(position.x, position.y); // Rivers prefer lower elevations and suitable terrain float score = -tile.height; // Lower is better if (tile.terrainType == TerrainType.Plains) score += 2f; else if (tile.terrainType == TerrainType.Shore) score += 1f; else if (tile.terrainType == TerrainType.Forest) score += 0.5f; else score -= 10f; // Avoid mountains, existing water, etc. return score; } private void GenerateNaturalLakes(MapData mapData, ExpansionInfo info) { // Create 1-2 small lakes in suitable locations for (int i = 0; i < 2; i++) { Vector2Int lakeCenter = FindSuitableLakeLocation(mapData, info); if (lakeCenter != Vector2Int.zero) { CreateNaturalLake(mapData, lakeCenter); } } } private Vector2Int FindSuitableLakeLocation(MapData mapData, ExpansionInfo info) { for (int attempts = 0; attempts < 30; attempts++) { int x = random.Next(0, info.newWidth); int y = random.Next(0, info.newHeight); // Only in expansion areas if (IsInOriginalArea(x, y, info)) continue; if (mapData.IsValidPosition(x, y)) { MapTile tile = mapData.GetTile(x, y); if (tile.height < -0.1f && tile.terrainType == TerrainType.Plains) { return new Vector2Int(x, y); } } } return Vector2Int.zero; } private bool IsInOriginalArea(int x, int y, ExpansionInfo info) { return x >= info.leftExpansion && x < info.leftExpansion + info.oldWidth && y >= info.downExpansion && y < info.downExpansion + info.oldHeight; } private void CreateNaturalLake(MapData mapData, Vector2Int center) { int radius = random.Next(3, 6); for (int x = center.x - radius; x <= center.x + radius; x++) { for (int y = center.y - radius; y <= center.y + radius; y++) { if (mapData.IsValidPosition(x, y)) { float distance = Vector2Int.Distance(center, new Vector2Int(x, y)); if (distance <= radius) { MapTile tile = mapData.GetTile(x, y); if (distance < radius - 1) { tile.terrainType = TerrainType.Lake; } else if (distance < radius) { tile.terrainType = TerrainType.Shore; } } } } } } private void GenerateSettlementsAndRoads(MapData mapData, ExpansionInfo info) { GenerateExpansionSettlements(mapData, info); ExtendRoadsToExpansion(mapData, info); GenerateExpansionHarbours(mapData, info); } // Settlement and road generation methods (enhanced versions) private void GenerateExpansionSettlements(MapData mapData, ExpansionInfo info) { int totalNewArea = (info.newWidth * info.newHeight) - (info.oldWidth * info.oldHeight); int newSettlementCount = Mathf.Max(1, totalNewArea / 3000); // Fewer, better placed settlements Debug.Log($"Generating {newSettlementCount} new settlements for {totalNewArea} new tiles"); for (int i = 0; i < newSettlementCount; i++) { Vector2Int position = FindSuitableSettlementPosition(mapData, info); if (position != Vector2Int.zero) { CreateNewSettlement(mapData, position); Debug.Log($"Created new settlement at {position}"); } } } private Vector2Int FindSuitableSettlementPosition(MapData mapData, ExpansionInfo info) { for (int attempts = 0; attempts < 100; attempts++) { int x = random.Next(0, info.newWidth); int y = random.Next(0, info.newHeight); if (IsInOriginalArea(x, y, info)) continue; MapTile tile = mapData.GetTile(x, y); if (tile != null && tile.terrainType == TerrainType.Plains && tile.featureType == FeatureType.None) { // Check distance from other settlements bool validLocation = true; var existingSettlements = mapData.GetTowns(); existingSettlements.AddRange(mapData.GetVillages()); foreach (var settlement in existingSettlements) { float distance = Vector2Int.Distance(new Vector2Int(x, y), settlement.position); if (distance < 25) // Increased minimum distance for better distribution { validLocation = false; break; } } if (validLocation) return new Vector2Int(x, y); } } return Vector2Int.zero; } private void CreateNewSettlement(MapData mapData, Vector2Int position) { MapTile tile = mapData.GetTile(position.x, position.y); SettlementType type = random.NextDouble() < 0.25 ? SettlementType.Town : SettlementType.Village; tile.featureType = type == SettlementType.Town ? FeatureType.Town : FeatureType.Village; string settlementName = GenerateSettlementName(type); tile.name = settlementName; Settlement settlement = new Settlement(settlementName, type, position); mapData.AddSettlement(settlement); } private string GenerateSettlementName(SettlementType type) { string[] townNames = { "Newborough", "Westhold", "Eastport", "Northgate", "Southhaven", "Riverside", "Hillcrest", "Stoneburg" }; string[] villageNames = { "Millbrook", "Fairfield", "Greendale", "Oakwood", "Rosehaven", "Brightwater", "Meadowbrook", "Willowbend" }; string[] names = type == SettlementType.Town ? townNames : villageNames; return names[random.Next(names.Length)]; } private void ExtendRoadsToExpansion(MapData mapData, ExpansionInfo info) { var settlements = mapData.GetTowns(); settlements.AddRange(mapData.GetVillages()); foreach (var settlement in settlements) { bool nearExpansionEdge = IsNearExpansionEdge(settlement.position, info); if (nearExpansionEdge) { var nearestNewSettlement = FindNearestNewSettlement(mapData, settlement.position, info); if (nearestNewSettlement != Vector2Int.zero) { CreateRoadBetween(mapData, settlement.position, nearestNewSettlement); } } } } private bool IsNearExpansionEdge(Vector2Int position, ExpansionInfo info) { int edgeDistance = 20; bool nearLeft = info.leftExpansion > 0 && position.x < info.leftExpansion + edgeDistance; bool nearRight = info.rightExpansion > 0 && position.x > info.leftExpansion + info.oldWidth - edgeDistance; bool nearBottom = info.downExpansion > 0 && position.y < info.downExpansion + edgeDistance; bool nearTop = info.upExpansion > 0 && position.y > info.downExpansion + info.oldHeight - edgeDistance; return nearLeft || nearRight || nearBottom || nearTop; } private Vector2Int FindNearestNewSettlement(MapData mapData, Vector2Int fromPosition, ExpansionInfo info) { Vector2Int nearest = Vector2Int.zero; float nearestDistance = float.MaxValue; var settlements = mapData.GetTowns(); settlements.AddRange(mapData.GetVillages()); foreach (var settlement in settlements) { bool inNewArea = !IsInOriginalArea(settlement.position.x, settlement.position.y, info); if (inNewArea) { float distance = Vector2Int.Distance(fromPosition, settlement.position); if (distance < nearestDistance) { nearestDistance = distance; nearest = settlement.position; } } } return nearest; } private void CreateRoadBetween(MapData mapData, Vector2Int start, Vector2Int end) { Vector2Int current = start; while (current != end) { if (current.x < end.x) current.x++; else if (current.x > end.x) current.x--; else if (current.y < end.y) current.y++; else if (current.y > end.y) current.y--; if (mapData.IsValidPosition(current.x, current.y)) { MapTile tile = mapData.GetTile(current.x, current.y); if (tile.featureType == FeatureType.None && tile.isWalkable) { tile.featureType = FeatureType.Road; } } } } private void GenerateExpansionHarbours(MapData mapData, ExpansionInfo info) { var settlements = mapData.GetTowns(); settlements.AddRange(mapData.GetVillages()); foreach (var settlement in settlements) { if (!IsInOriginalArea(settlement.position.x, settlement.position.y, info)) { if (IsNearWater(mapData, settlement.position.x, settlement.position.y, 5)) { Vector2Int harbourPos = FindHarbourPosition(mapData, settlement.position); if (harbourPos != Vector2Int.zero) { mapData.GetTile(harbourPos.x, harbourPos.y).featureType = FeatureType.Harbour; } } } } } private bool IsNearWater(MapData mapData, int x, int y, int range) { for (int dx = -range; dx <= range; dx++) { for (int dy = -range; dy <= range; dy++) { int checkX = x + dx; int checkY = y + dy; if (mapData.IsValidPosition(checkX, checkY)) { MapTile tile = mapData.GetTile(checkX, checkY); if (tile.IsWater()) return true; } } } return false; } private Vector2Int FindHarbourPosition(MapData mapData, Vector2Int settlementPos) { for (int range = 1; range <= 5; range++) { for (int dx = -range; dx <= range; dx++) { for (int dy = -range; dy <= range; dy++) { int x = settlementPos.x + dx; int y = settlementPos.y + dy; if (mapData.IsValidPosition(x, y)) { MapTile tile = mapData.GetTile(x, y); if (tile.terrainType == TerrainType.Shore && tile.featureType == FeatureType.None) { return new Vector2Int(x, y); } } } } } return Vector2Int.zero; } }