THE PAYOFF: rivers now carry water. Each carved main river becomes a chain of stepped flat water-body reaches — a new reach every RiverStepDropM (2 m) of bed descent, sitting RiverWaterDepthM (1.2 m) above its bed, strictly descending to the outlet. Reaches are ordinary water bodies (WBID cells + WBTB type 2 'river', fresh; WSRF derives from body levels as ever), so river water renders through the C1 path with the task-15 presence rule at its banks, untouched. Existing bodies are never overwritten — a river MEETS its lake or the sea. Measured on 1280587109: 298 reaches across 6 rivers, 184,620 wet px, levels stepping 278 m down to 39.5 m; in-engine, one valley frame renders surfaces 46.2-114.5 m. Beds widened (RiverWidthScale default 1.75). Lake-enders: the stem's pooling terminal IS a local minimum, so the first extension attempt (blind steepest descent) dead-ended in 0 steps — replaced with the lowground Dijkstra to the nearest classify-water cell; the E-lagoon river now joins its lake (87 px). Smoothing is applied ONLY to lowland routes: smoothing upland stems moved them off their carved valley floors into the walls (max cut 14.6 -> 27.3 m measured; split restores valley-floor fidelity). TYPE_RIVER added to the WBTB registry (parser validation extended; doc updated in the docs commit). Task-22 nits fixed: max-cut is now CUMULATIVE vs the pre-pass surface (the honest number: p95 carve 7.0 m; 339 cells island-wide exceed 20 m — localized outlet-gorge notches where stems cross deposit ridges, deepest 26 m); and 0_height/0_water are re-drawn and re-captured AFTER the river pass so the exported snapshots show beds and water. Guards: flood guard holds (water pixels unchanged around the carve; the water stage touches no heights), BIOME oracle md5-identical to the task-22 baseline, 0_water changed (that IS the river water), island top 457.65 m exact, crater core excluded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| Scripts | ||
| README.md | ||
Core Module
Shared, stateless logic used by both the server and client paths: the .dat parser, the mesher, the
voxel data containers, and the material lookups. No Godot nodes, no scene state. These scripts
define what things are and how to calculate them; they never remember what is currently happening.
Components
MapDataParser.cs — the data bridge
Deserializes the binary .dat blueprint written by /Tools into a WorldBlueprint held in RAM:
map size, the float heightmap, the biome map, town locations, all four tiers of A* road
vectors (Highways, Branch Roads, Rugged Roads, Trails), and — from v2 files — the embedded
generation params (seed, sizes, impact centre, provenance) and each town's highway-node flag.
Two formats are live (byte-accurate contract: Core/Scripts/BLUEPRINT_FORMAT.md). The parser
dispatches on the file's first byte:
- v2 (primary) — raw
ISLAmagic, u32 version gate, then tagged sections[u32 tag][u64 length][payload]. Unknown tags are skipped by length, so future sections are invisible to older readers. Validated on read: version, MapSize bounds, section lengths against the file, biome/tier ordinal ranges. Since the water-bodies stage, v2 files also carry the optionalWBID/WBTB/WSRFwater sections (per-pixel body ids, the body table, quantized surface levels) — parsed intoWorldBlueprintand consumed by nothing at runtime yet. - v1 (legacy) — the positional
"ISLA_V1"format, still written beside v2 as_v1.datand still loadable (with a deprecation warning) until a future removal task.
⚠ Wire-format hazard. Biome and town-tier enums are serialized as their ordinal values
(u8 in v2, i32 in v1). Never reorder or insert members in Enums.cs — append only, at the end.
v2's range checks make drift fail loudly at parse time; legacy v1 has no validation and silently
reinterprets every pixel. (RoadTier is exempt: road tiers are stored in separate file sections —
tagged in v2, positional in v1 — so that enum never hits disk.)
ChunkData.cs + Constants.cs — voxel containers and tuning
- Chunk dimensions:
24 × 24horizontal,256vertical (Constants.cs). - The
+1padding is structural.DensitiesandBlockIDsare one cell larger on every axis —[25, 257, 25]— so the mesher can evaluate the boundary cells shared with the neighbouring chunk and the meshes meet without gaps. - Per-column data for rendering:
SurfaceHeights(the true, unrounded surface),ColumnBiomes, andColumnRoadMaterial(0 = not a road). These let the mesher colour by real height rather than a rounded integer. - Tunables in
Constants.cs:BLEND_BAND_METERS(how far one surface material fades into the next) and the per-tier road block — width, shoulder, grade-smoothing and surface material for each of the four road tiers, plus the derived cull padding.
MarchingCubes.cs — the mesher
Turns a chunk's density field into a Godot ArrayMesh.
- Analytical normals: exact density gradients at each cell corner, interpolated along the edge by the same fraction used for the vertex position — smooth lighting without Godot's normal pass.
- Deterministic vertex welding: shared vertices are keyed by an integer
EdgeKeyordered lowest-to-highest, so neighbouring chunks compute identical keys and no float drift creeps in. - Vertex colouring: each vertex is coloured from its true float depth below the surface, fading between the two materials either side of a boundary rather than switching at an integer depth.
Density sign convention (load-bearing): density is positive above the surface and negative
below it. A corner counts as inside the terrain when density < ISO_LEVEL.
BiomePalette.cs + BlockRegistry.cs + BlockData.cs — materials
-
BlockRegistry: byte-ID lookup for every block (AIR,BEDROCK,STONE,DIRT,SAND, the three grasses,SNOW,WASTELAND_DIRT,ASPHALT). -
BiomePaletteanswers "what material is here?" twice, deliberately:GetVoxelID(...)— the authoritative integer classification by whole-block depth. Decides what is actually stored in each voxel; used for everything non-visual.GetBlendedVoxelIDs(...)— the rendering version. Same question by float depth, returning the two materials either side of the nearest boundary plus how far between them the point is, so the renderer fades instead of snapping.
Both take a road-surface byte, so a trail is surfaced in dirt where a highway is surfaced in asphalt.