Task 17's hard 1.2 x CraterRadius cutoff left a visible un-eroded disc with a hard edge. Measured (radius dump, seed 1280587109): the carve writes only inside 0.80 x (640 px) and its displacement is EXACTLY 0 beyond that, so the 640-960 px annulus was 620,811 LAND cells of ordinary terrain held smooth for no geometric reason. That annulus is now eroded — 98% of those cells are touched. The protected core is the carve's own extent (CraterErosionCore, 0.80 x). CraterErosionMode selects the transition: "full" applies full strength at the core boundary (older-crater look), "feather" ramps 0->full out to CraterErosionFeather (1.05 x, mirroring the detail pass) for a younger-crater look with no seam. Implemented as a WEIGHT that scales carve and deposit amounts, not a skip, which is what makes feather a one-liner. Default "feather" pending the gate. Core = the carve radius is load-bearing, not tidy: the carve runs AFTER erosion and scales height toward the sea target, so it amplifies any erosion delta inside its radius. Measured at a 0.50 core: 79 cells newly below the rendered sea and 113,310 below-sea cells disturbed — the in-pass flood guard cannot see this because it measures before the carve. At 0.80 the two passes touch disjoint cells and the guarantee is exact again (0/0/0). A narrower core also reclaims nothing extra, since the over-protected annulus lies entirely outside the carve. MapGenerator now owns CRATER_CARVE_FACTOR as the single source of that 0.80 and warns loudly if the configured core is narrower. Verified both modes, seed 1280587109: 1_biomes/0_water md5-identical to erosion-OFF, BIOM/WBID bitwise equal, 0 newly below/above sea, 0 below-sea cells modified, 0 cells modified inside the core, island top 457.65 m unchanged, isotropy 1.005/1.007. Bay-to-ocean connection demonstrated explicitly by flood fill through rendered water to the map's north border (2,122,935 cells), not merely argued from the sea clamp. EROS body version -> 3: craterExclFactor replaced by craterCoreFactor + craterFeatherFactor + craterMode. Round-trip harness PASS with a real v3 payload. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| Scenes | ||
| Scripts | ||
| README.md | ||
/Tools
Offline developer utilities. Nothing here ships. These generate the source data that /Server
and /Client later consume, which keeps world-generation logic out of the shipped client.
The 2D world generator
Script: Tools/Scripts/MapGenerator.cs · Scenes: Tools/Scenes/MapPreview.tscn and
Scenes/MapCaptureTool.tscn
Builds the topographical and logistical foundation of the island before any 3D exists.
- Resolution is config-driven, from
ServerConfig.json'sMapProfile:4K/6K/8K/10K→ 4096 / 6144 / 8192 / 10240. Shipped default is 8K. 1 pixel = 1 in-game metre = 1 voxel footprint. (TheMapSizefield initialiser in the script says 4096, but it is overwritten from config before use.) - Topography: FastNoiseLite plus distance-falloff maths for a guaranteed mainland island, a mountain spine, dynamic temperature-driven sea level, and an impact crater pushed into the northern coast (carved at 80 % of its radius so a landbridge always survives).
- Water bodies: the classified water promoted into explicit blueprint data (ocean + labeled
lakes with transitional per-body levels —
WBID/WBTB/WSRFsections), plus priority-flood basin diagnostics. TheSkipRoadsconfig toggle turns a full ~26-min generation into a ~80 s road-less iteration cycle. - Logistics:
AStarGrid2Dpathfinding producing a looping continental highway, a branch to the mountain hub, and county roads daisy-chained outward with Prim's algorithm. Paths are decimated (Ramer–Douglas–Peucker) then smoothed (4 Chaikin passes). - Zoning: biome mapping and tiered town/POI placement by slope, temperature and proximity.
⚠️ A* is the known bottleneck
The grid is one node per pixel — 67 million nodes at 8K — and the weight-setup pass walks every
one of them. A full generation can stall long enough that runs get abandoned. await yields are
sprinkled through the road stage, but they cannot interrupt a single engine-side GetPointPath call,
so they only help between paths. Expect a regeneration to take minutes, or to need abandoning.
Outputs — written to user://, not into the project
| File | What it is |
|---|---|
MapData_Seed_<seed>.dat |
The binary blueprint the server reads — v2 tagged container (~336 MB at 8K; contract in Core/Scripts/BLUEPRINT_FORMAT.md) |
MapData_Seed_<seed>_v1.dat |
The same content in the legacy v1 format (~512 MB), dual-written as a safety net until a future removal task |
Map_Seed_<seed>.png |
Visual snapshot of the map, for reviewing and picking seeds |
On Linux: ~/.local/share/godot/app_userdata/islaApocolypse/.
Running it
Open Tools/Scenes/MapPreview.tscn and press F6. It is also the project's main scene, so
pressing F5 anywhere runs this — which is why F5 regenerates your world instead of starting the
game. Use Scenes/MapCaptureTool.tscn when you specifically want the full-resolution PNG.
Rules
- No client dependencies. Nothing here may reference UI, player controllers or shaders in
/Client. - Data out, nothing in. Tools emit
.dat/.png/.json; they never write to a live server save. - No magic numbers. Distances and radii scale off
MapSizeor the derivedscaleFactor, so the generator behaves the same at every profile.