Byte-accurate entries for the three water sections (transitional
per-body level rule, provisional salinity, WSRF sentinel/quantization),
updated size math (~576 MiB at 8K with water), the on-the-record
rationale for NOT serializing basins, and README updates for the new
pipeline stage, the 0_water snapshot, and the SkipRoads toggle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three new v2 tagged sections carrying the water-bodies stage output:
WBID (MapSize^2 u16 body ids; 0 none, 1 ocean, 2..N lakes), WBTB
(count-prefixed 20-byte records: id, type, provisional salinity,
transitional flat surface level, pixel count, centroid), WSRF
(MapSize^2 u16 quantized levels; 0 = no-water sentinel, 1 + L*32768
encoding, ~7.7 mm world resolution). Written only when the blueprint
carries water; absent on legacy re-encodes. Parser registers all
three (length + type checks) into new WorldBlueprint members —
consumed by nothing at runtime. Round-trip harness now compares the
water sections whenever the source carries them; v1-source baseline
stays green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IsWaterPixel/IsOceanPixel/IsLakePixel are the single per-pixel water
truth; AssignBiomesAndDraw's water branch now calls them (verbatim
rules — behavior-preserving, proven by the bit-identical-biomes oracle
at acceptance). New IdentifyWaterBodies stage between the masks and
biomes: ocean = body 1, lakes labeled 2..N with CalculateTrueOcean's
4-connectivity in deterministic scan order; one transitional surface
level per body (GetSeaLevel at the body centroid; ocean at map
centre). 0_water snapshot painted from the stage's own outputs.
Priority-flood pit-fill (heap + pit-queue variant) runs as validated
diagnostics only — serializes nothing, asserts filled>=original and
full-map non-ascending drainage (reverse BFS, no sampling), reports
closed-basin statistics.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Config flag SkipRoads (default false): when on, GenerateRoadsAsync and
the 3_roads snapshot are skipped entirely; export proceeds with all
four road sections present but empty (both v2 and legacy v1 permit
zero counts); the console states loudly that the blueprint is a
road-less iteration artifact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Generated by the engine on first run of the project after the v2
envelope landed (terrain-water task 02).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v2 tagged-section container (D-030): raw ISLA magic + u32 version gate,
[u32 tag][u64 length][payload] sections — params (resolved generation
inputs incl. impact centre), heights f32, biomes u8, towns with the
highway-node flag, four road tiers identified by tag not position.
- BlueprintFormat.cs: the single tag/constant registry.
- BlueprintWriter.WriteV2: blueprint-typed, callable outside a
generation run; generator and harness are both just callers.
- ExportMapData dual-writes: v2 under the primary seed name, legacy v1
beside it as _v1.dat (safety net; removal is a future task).
- MapDataParser: LoadMapDataFromPath entry point; v1 parse body
extracted intact as LoadV1 (v2 reader lands in the next commit).
- RoundTripHarness scene: load v1 -> write v2 -> load v2 -> semantic
equality, headless, seconds per cycle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two additions, both observability only — no generation, routing, weight,
terrain or mesher behaviour changes.
1. Every progress print is now prefixed with elapsed time since generation
started, e.g. [+ 142.7s], including a line per pipeline stage and per county
road. The per-stage split (setup / continental loop / branch / county) can
now be read straight off the console. Two A* fix attempts have been reasoned
from structure rather than measurement; this makes the measurement free.
2. The end-of-run PNG capture was extracted into a reusable stage capture that
can be called at any pipeline boundary. Snapshots are now taken after
biomes, after towns, and after roads. The draw proxy renders whatever exists
at the time, so stages that have not run simply do not appear — and a future
water stage will show up automatically once it populates its data.
The existing capture path is reused unchanged: same offscreen SubViewport,
same draw proxy, same frame-yield plus double GPU wait. Those waits are
load-bearing, not superstition, and each staged capture honours them.
Snapshot filenames gain a stage suffix: Map_Seed_<seed>_1_biomes.png,
_2_towns.png, _3_roads.png. Nothing reads these but a human, verified.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A* was searching almost the whole island for every road. Godot's stock
estimate-to-goal is plain straight-line distance, which assumes every step
costs 1 — but a step through mountains costs up to 401 and a step near an
existing road cost 10,001. With the estimate that far below reality, A* cannot
rule anything out, so it degenerates toward Dijkstra.
Two changes, both aimed at that:
1. RoadPathGrid overrides the estimate to scale it by HEURISTIC_WEIGHT (3.0),
i.e. weighted A*. Paths may be up to 3x costlier than the theoretical best
in exchange for exploring far less. This does NOT push roads over mountains:
a ridge costs hundreds of times more than going around, which a 3x bias
nowhere near pays for.
2. ROAD_REPULSION_PENALTY replaces the hardcoded +10000 with 250. The old value
made ground near a road effectively infinite, and it compounded — each road
drawn made the next search slower. 250 still strongly discourages roads from
running alongside each other.
Terrain weights are untouched: the mountain curve (1 + elevation^3 * 400),
beach and wasteland costs are exactly as before, so mountains remain expensive
and roads still avoid them. Routing, tiers, smoothing and grid resolution are
also unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every road path search — highway, branch, county — now goes through one
function instead of three direct GetPointPath calls. The function body is
exactly the call it replaced, so behaviour is unchanged.
This exists so the next commit's pathfinding change can be judged on its own:
if the generated map changes after this point, it was the heuristic, not a
refactor slip.
Routing is untouched: which towns connect, loop order, Prim's daisy-chain,
tier assignment and the abandon protocol all deal only in town positions and
never touch the grid.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every README was read first, checked against the code in its directory, then
rewritten to describe what the code actually does now.
Corrected throughout: map size is config-driven (8192 default) not a fixed
4096; chunks are 24x24x256 not 32x32; road carving is implemented for all four
tiers, not a 'next step'; Data/ and Resources/ are empty, not populated.
Also fixed MATH_MARCHING_CUBES.md, which documented the density sign convention
exactly backwards — it claimed positive was underground, where the code treats
positive as above the surface and counts a corner inside when density < iso.
Getting that backwards inverts every normal, a bug this project has hit before.
The root README now carries accurate run steps: F5 runs the map generator (not
the game), F6 on Scenes/Main.tscn runs the 3D world, and ChunkRadius is a load
radius that should be dropped to 4-8 while iterating.
Docs only. No code changed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>