namespace Theriapolis.Core.World.Generation; /// /// Computes 8-connected land-component IDs and sizes via flood fill. Settlement /// placement uses this to confine settlements to the main landmass — roads /// can't cross ocean, so a settlement on a disconnected island would either be /// unreachable or get a sea-crossing straight-line connector stub from /// EnsureSettlementConnectivity. 8-connected matches A*'s movement /// model so "reachable by road" and "same component" coincide. /// internal static class LandmassMap { private const int W = C.WORLD_WIDTH_TILES; private const int H = C.WORLD_HEIGHT_TILES; private static readonly (int dx, int dy)[] Neighbors = { ( 0,-1), ( 1,-1), ( 1, 0), ( 1, 1), ( 0, 1), (-1, 1), (-1, 0), (-1,-1), }; /// /// Returns a pair of arrays: componentId[x,y] gives the 8-connected /// land component ID at (x,y), or -1 if (x,y) is ocean; componentSize[id] /// gives the tile count of component . /// public static (int[,] componentId, int[] componentSize) Compute(WorldState world) { var compId = new int[W, H]; for (int y = 0; y < H; y++) for (int x = 0; x < W; x++) compId[x, y] = -1; var sizes = new List(); var queue = new Queue<(int x, int y)>(); for (int y = 0; y < H; y++) for (int x = 0; x < W; x++) { if (compId[x, y] != -1) continue; if (world.Tiles[x, y].Biome == BiomeId.Ocean) continue; int id = sizes.Count; int size = 0; compId[x, y] = id; queue.Enqueue((x, y)); while (queue.Count > 0) { var (cx, cy) = queue.Dequeue(); size++; foreach (var (dx, dy) in Neighbors) { int nx = cx + dx, ny = cy + dy; if ((uint)nx >= W || (uint)ny >= H) continue; if (compId[nx, ny] != -1) continue; if (world.Tiles[nx, ny].Biome == BiomeId.Ocean) continue; compId[nx, ny] = id; queue.Enqueue((nx, ny)); } } sizes.Add(size); } return (compId, sizes.ToArray()); } /// /// Returns the component ID of the largest land component, or -1 if the /// world has no land. /// public static int LargestComponentId(int[] componentSizes) { int best = -1; int bestSize = 0; for (int i = 0; i < componentSizes.Length; i++) if (componentSizes[i] > bestSize) { bestSize = componentSizes[i]; best = i; } return best; } }