| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041 |
- 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<MapTile> 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<MapTile> GetExtendedReferenceTiles(MapData mapData, int x, int y, ExpansionDirection direction, ExpansionInfo info)
- {
- List<MapTile> referenceTiles = new List<MapTile>();
- 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<MapTile> 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<MapTile> 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<TerrainType, float> 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<TerrainType, float> AnalyzeBiomeInfluence(List<MapTile> referenceTiles)
- {
- Dictionary<TerrainType, float> influence = new Dictionary<TerrainType, float>();
- 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<TerrainType, float> 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<TerrainType, int> 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<TerrainType, int> GetNeighborTerrain(MapData mapData, int centerX, int centerY, int radius)
- {
- Dictionary<TerrainType, int> terrain = new Dictionary<TerrainType, int>();
- 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<TerrainType, int> 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<Vector2Int> riverEnds = FindRiversAtBoundaries(mapData, info);
- foreach (var riverEnd in riverEnds)
- {
- ExtendRiverNaturally(mapData, riverEnd, info);
- }
- }
- private List<Vector2Int> FindRiversAtBoundaries(MapData mapData, ExpansionInfo info)
- {
- List<Vector2Int> riverPoints = new List<Vector2Int>();
- // 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<Vector2Int> 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;
- }
- }
|