# /Tools/Scripts — generation logic C# backend for the offline developer tools. Decoupled from the live server and client. ## `MapGenerator.cs` Generates the entire 2D blueprint. Roughly in order: 1. **Config** — reads `ServerConfig.json`, which sets `MapSize` (8192 by default) and the crater radius. The `MapSize = 4096` field initialiser is a fallback that is immediately overwritten. 2. **Topography** — FastNoiseLite base height plus a mountain spine, minus a squircle distance falloff, giving a guaranteed island. Noise frequency is divided by `scaleFactor` (`MapSize / 1024f`) so terrain features stay the same real-world size at any map profile. When `TerrainCurve: "v4"` (the default), the calibrated height-redistribution curve (`HeightCurve.cs` — the terraced ascent with spatially modulated shelves: flat farmable lowlands, a walkable foothill bench at 100±12 m, the white mountain-town plateau at 220±20 m — shelf heights and strength drift across the island so no two flanks wear the same ring — and a per-seed-normalized summit spike to the 420 m cap) reshapes above-sea terrain after noise/falloff/Trench and before the crater carve; biome classification reads a retained uncurved map, so biomes are identical either way. Drops the `0_height` hillshade snapshot (hypsometric bands × NW hillshade) in both modes. 3. **Sea level and water** — sea level per the configured model (`SeaLevelModel`: `"flat"` scalar — the default, `SeaLevelValue` 0.15 — or the legacy `"field"` latitude Lerp); flood fill separates true ocean from inland lakes; a mainland fill guarantees one contiguous landmass. 4. **The crater** — placed along the northern coast and carved to below sea level, but only out to **80 % of its radius**, which guarantees a landbridge rather than severing the island. 5. **Water bodies** — promotes the classified water into explicit data: the ocean as body 1, each lake connected-component labeled (same 4-connectivity as the true-ocean fill), one transitional surface level per body. Classification comes from the shared `IsOceanPixel`/`IsLakePixel` predicates that the biome stage also uses, so the two can never disagree. A priority-flood pit-fill runs here as validated diagnostics (basin statistics to console; serializes nothing). Drops the `0_water` snapshot. 6. **Biomes and towns** — biome zoning by height and temperature; tiered town placement (Capitol, Hubs, Villages, Outposts, POIs) filtered by slope, water proximity and spacing. 7. **Roads** — see below. Skipped entirely when the `SkipRoads` config toggle is on (the ~25-min A\* pass is the bottleneck; a skip run exports a road-less iteration blueprint in ~80 s with a loud console banner). 8. **Export** — writes the `.dat` blueprint (dual-write: the v2 tagged container under the primary seed name, plus the legacy v1 format beside it as `_v1.dat` — see `Core/Scripts/BLUEPRINT_FORMAT.md`), then renders the PNG snapshot. The v2 file embeds the resolved generation params (seed, MapSize, crater radius, density, impact centre, provenance). ### The road network `AStarGrid2D` over the whole map at one node per pixel. Mountains are made expensive rather than impassable (`1 + elevation³ × 400`), water and the crater are marked solid. - **Continental loop** — highway nodes sorted by radial angle and connected in a ring. - **Mountain branch** — a spur from the loop to the snow hub. - **County roads** — Prim's algorithm daisy-chains remaining towns onto the network. Villages become Rugged roads, everything else Trails. - **Abandon protocol** — a town further than 8 % of the map from the network is skipped rather than pathed to, so one unreachable outpost cannot hang generation. - **Smoothing** — every path is decimated with Ramer–Douglas–Peucker (tolerance 4.0) then smoothed with 4 Chaikin passes. ⚠ **This is the project's dominant performance problem.** At 8K the grid is 67 million nodes, and the weight-setup pass touches every one before the first path is requested. The `await` yields between stages cannot interrupt a single engine-side `GetPointPath` call, so a long path still blocks. Full regenerations frequently get abandoned. ### The PNG snapshot `SaveMapSnapshot()` builds an offscreen `SubViewport` in code rather than relying on the scene's layout, because Godot's UI layout engine will otherwise crush a 4096+ render down to the editor window size. A `MapDrawProxy` control re-issues the `_Draw()` calls into that viewport — `GetImage()` captures only the base texture and would otherwise miss the roads and towns entirely — and the code awaits two `RenderingServer.FramePostDraw` signals so the GPU has actually painted before the image is read back. Road colours on the snapshot, useful for identifying a road: **red** = Highway, **black** = Branch, **dark brown** = Rugged, **light brown** = Trail. ### Outputs `MapData_Seed_.dat` (v2), `MapData_Seed__v1.dat` (legacy dual-write), and the staged snapshots `Map_Seed__{0_height,0_water,1_biomes,2_towns,3_roads}.png`, all to `user://`. (`0_height` is the hypsometric hillshade of the curved terrain; `0_water` is painted from the water stage's own outputs — ocean deep blue, lakes lighter blue, land neutral; `3_roads` is absent on a SkipRoads run.) ## `RoundTripHarness.cs` The blueprint format regression test (scene: `Tools/Scenes/RoundTripHarness.tscn`). Loads a known-good blueprint through the real parser, re-writes it as v2 through the real writer, re-loads it, and asserts semantic equality (heights bitwise, biomes, towns, every road point). Headless, seconds per cycle, no generation. Exit code 0 = pass. ## Rules 1. **No magic numbers.** Distances, radii and thresholds derive from `MapSize` or `scaleFactor`, so the generator behaves identically at 4K and 10K. 2. **Respect the GPU.** Anything exporting visual data must `await` the appropriate frame signals before reading pixels back.