Commit graph

51 commits

Author SHA1 Message Date
0e39538604 feat: tributary water (tapered), significant-lake targeting, finer steps + deeper beds (terrain-water task 24)
All three gate polish items — they interleave in the same files, so they land as
one buildable commit rather than three that do not compile independently.

1. TRIBUTARY WATER. Tributaries carved in task 22/23 but stayed dry: they were
   never registered with the water stage. They now are (as CarvedRiver entries
   named 'trib', kept OUT of the per-river console table so it stays the 6
   mains). RiverTribWaterMinFlow (40k px of along-course flow) sets where water
   starts; RiverTribTaperPx (120 px) makes the wet->dry transition a FADE, not a
   wall — across the taper the wet strip narrows (0.25x -> 1x half-width) AND its
   surface drops toward the bed, so a stream head thins out and vanishes.
   Along-course flow is modelled quadratically from headwater trickle to full
   drainage at the mouth (the plan records drainage per river, not per sample).
   RiverTribWaterMinFlow=0 waters them end to end — the documented fallback.

2. LAKE-ENDER TARGETING. The join routed to the nearest classify-water CELL,
   which a 322-px puddle satisfies; it stopped ~80 px short of the 197k-px
   lagoon beside it. It now routes to a SIGNIFICANT water mask (bodies of at
   least RiverLakeMinTargetPx = 20k cells, built from the water-body table),
   falling back to any classify water only if no significant body is reachable,
   so a seed with genuinely small ponds still connects. Proven by adjacency:
   task 23 had 63 river cells touching the puddle and 0 touching the lagoon;
   task 24 has 49 touching the lagoon and 0 touching the puddle.

3. FINER STEPS + DEEPER BEDS. RiverStepDropM 2.0 -> 0.6 and RiverWaterDepthM
   1.2 -> 2.2, RiverDepthScale 1.0 -> 1.5. Reaches 298 -> 1034; the level gap
   between adjacent reaches drops from median 0.571 m / p95 2.08 m to median
   0.124 m / p95 0.74 m — the pond-staircase reads as a graded descent. Reaches
   stay trivial bodies (median 44 px).

Guards all held: flood guard 0 newly-below-sea and 0 below-sea cells modified,
BIOME oracle md5-identical to the task-22 baseline (output-only unchanged),
island top 457.65 m exact, crater core excluded, lowest carved cell exactly
sea+margin (37.85 m). Cumulative max cut 25.98 m with 339 cells >20 m —
identical to task 23, so deepening the WATER did not deepen the worst cuts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 20:24:38 -04:00
645955d49c docs: stepped river water, WBTB type 2, and the C0b finale in the generator README (terrain-water task 23)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 19:57:25 -04:00
8f5767308a feat: stepped river water + widen + lake-ender join + the task-22 nits (terrain-water task 23, C0b finale)
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>
2026-08-11 19:55:04 -04:00
148602b4c5 feat: lock lowground routing; smooth river courses with the road pass's RDP+Chaikin (terrain-water task 23)
LOWGROUND is the gate verdict and the locked default ('short' stays available
behind the dial for the record). Every river polyline (upland stem + lowland
reach) is now decimated with RDP(4.0) and rounded with 4 Chaikin passes —
numerically mirroring MapGenerator.SmoothPath, since this pass is Godot-free —
before the bed profile is built, so the carved centreline carries none of the
8-connected Dijkstra 45° kinks (task-22 §4 caveat a). The monotone-descent and
sea-clamp constraints are applied to the smoothed course, unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 19:37:29 -04:00
6960c2a2e8 feat: river bed carving with SHORT/LOWGROUND lowland routing A/B (terrain-water task 22, C0b part 2a)
Executes the frozen task-21b plan as real terrain — the first river pass to
modify the render map. NO WATER (part 2b waters the gated routing style).

RiverCarvePass (D-035 numeric): reruns DrainageAnalysis in-pipeline
(deterministic — same seed, same eroded surface, same plan), routes each routed
giant's lowland reach to the nearest ocean by deterministic Dijkstra under two
config-gated cost models — 'short' (distance + uphill penalty; heads direct) vs
'lowground' (cost ~ elevation above sea; follows the lowest ground and
meanders) — then carves every promoted course as a parabolic channel with a
smoothstep shoulder. Width/depth grow downstream with sqrt(drainage); bed
profile forced monotone non-increasing toward the outlet (water must flow) and
clamped to sea + RiverSeaMargin EVERYWHERE, with below-sea cells read-only and
the crater core excluded — the erosion flood-guard discipline, asserted per
generation by the water-pixel A/B (throws on any change).

Pipeline slot: AFTER GenerateTowns (towns read the render map for land/slope/
water checks — carving first would move towns and destabilise every A/B) and
BEFORE roads (a full run's A* should see the beds).

ISLA_SERVER_CONFIG env override added to ConfigManager: batch/A-B tooling loads
an alternate config file and the developer's live ServerConfig.json is never
written by tooling again (the task-19 restore near-miss class is retired).

Measured, seed 1280587109 (both variants): oracle md5-identical to baseline
(BIOM/WBID bitwise equal), 0 newly-below-sea cells, below-sea untouched, island
top 457.65 m exact, lowest carved cell exactly sea+margin (37.85 m), and
Rivers='off' is bitwise identical to the task-19 blueprint. All routed giants
reach the ocean in both styles. Wander (polyline/straight): SHORT 1.02-1.06 vs
LOWGROUND 1.24-1.38; SHORT carves 811k m3 (cuts through rises, max cumulative
cut 14.6 m), LOWGROUND 481k m3 (goes around, 14.3 m). Pass cost ~25 s (plan
21 s, routing 1.4-2.5 s, carve 0.3 s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 19:16:37 -04:00
78e042b805 docs: crater erosion modes and the retired 1.2x exclusion (terrain-water task 19)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 05:50:22 -04:00
f810ff44dc fix: shrink erosion's crater exclusion to the strike core; add FULL/FEATHER modes (terrain-water task 19)
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>
2026-08-10 05:50:22 -04:00
85e7bceb72 docs: erosion hierarchy defaults, EROS v2, and where erosion cannot reach (terrain-water task 18)
Records the four governors and the task-18 defaults, and states the measured
limit plainly: erosion concentrates ~9x on the curve's shelf risers because
that is where sustained slope is, so flat shelves and lowlands are barely
touched and the lowland continuation of a trunk is river promotion's job, not
erosion's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 04:16:33 -04:00
cc6a24d2b9 fix: brush-spread deposition + deposit-cap governor; retune erosion for a drainage hierarchy (terrain-water task 18)
Deposition now spreads over the same cone brush as carving, and the per-cell
erosion ledger becomes a signed NET displacement ledger, so both caps are
measured from the height the pass found. Brush-spreading alone cut the spike
15.49 -> 6.12 m at task-17 dials, but NOT at hierarchy dials (long paths carry
far more sediment; a loaded droplet meeting a rise dumps min(rise, load) at
once -> 25.5 m). So deposition also gets governor 4, ErosionDepositCap
(default 6 m, <= 0 = unbounded), asserted on exit like the carve cap.

Dial defaults retuned for a drainage HIERARCHY: 250k droplets x 384 steps at
inertia 0.35 / evaporation 0.004, erode 0.12, carve cap 15 m. Diagnosis was
that lifetime 48 let a droplet travel at most 48 px on a ~3000 px island
radius, so paths could not overlap into trunks: the deepest task-17 features
were 43x36 px patches. Now 177 channel systems of 200+ cells at the 3 m
threshold, top one 204x279 px with 14 tributary tips; cells past 5 m up 8.6x
(4,948 -> 42,385) while the fine rills are retained. Verified descending, not
contour-locked: 0/155 components have drop/extent < 0.12 (median 1.13).

EROS body version -> 2 (deposit cap inserted after the carve cap); v1 payloads
are skipped whole by the existing rule. HydraulicErosion.VERSION now reads
BlueprintFormat.EROS_VERSION instead of restating it — the local copy had
already drifted and stamped a v2 body as v1, which readers decode with every
float shifted by one field.

Oracle green on both seeds (1280587109, 1512575962): 1_biomes/0_water
md5-identical erosion-ON vs OFF, BIOM/WBID bitwise equal. Flood guard 0 new
water px, 0 below-sea cells touched, crater zone untouched, island top
unchanged, carve field isotropic to 1.4% across the folded 45deg period.
Erosion pass 34 s at these dials.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 04:16:33 -04:00
e1e1fe3227 feat: droplet hydraulic erosion — carve+deposit, three governors, sea clamp (terrain-water task 17, Phase C0)
Erosion: "off"|"v1" gate (default off, pending the developer's gate). Output-only:
applied to the render map after detail, before the crater carve; the classify path
reads pre-erosion heights, so biomes/water stay byte-identical (verified: OFF run
bit-identical to the task-14 reference; ON run BIOM/WBID identical to OFF).
Governors: droplet count / lifetime / per-cell carve cap (ledger-enforced, thrown
on violation). Sea clamp: below-sea cells read-only, land never carved below
sea+margin — rendered coastline provably fixed (asserted per generation). Crater
excluded at 1.2×R. Deterministic from resolvedSeed+9271 (PCG32). New EROS
blueprint section (writer/parser/harness/format doc). Supersedes the reverted D8
approach with the organic droplet model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 02:42:26 -04:00
3b5bc5e5d6 docs: water at rest across the Core/Client/Server READMEs (terrain-water task 13)
Core: BlockRegistry gains WATER, with the reason it is wire-safe (block IDs
are never serialized) AND the reason it is a registry identity only rather
than a voxel the terrain path writes. Corrects the now-false line "Nothing
at runtime consumes the water data yet" -- it does, as of this task.
ChunkData's new per-column water fields and the Constants tunables.

Client: how the water sheet is built and, more importantly, WHY it is a
second mesh instead of a block in the density field -- the trap here is
that writing water into a Marching-Cubes field fuses it into the terrain
instead of laying it on top, and that is not obvious until you have done
it. Notes translucency came free from the separate material.

Server: the per-column water rule, which section is the authority for what
(WBID presence, WSRF level, WBTB fallback), that an unknown body leaves the
column dry rather than guessing, and why depth for shading comes from
blueprint heights rather than the clamped rendered geometry.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 02:33:06 -04:00
9c255a4fc6 feat: render the water we already had (terrain-water task 13, Phase C1)
The blueprint has carried water since task 03 -- WBID per-pixel body id,
WBTB body table, WSRF per-pixel surface level. Nothing ever drew it. Now
the runtime does. No new water data: the blueprint is the authority on
where water is and at what level, and this only reads it.

WHY WATER IS ITS OWN MESH, not a block in the terrain field. The terrain
is a Marching-Cubes iso-surface over ChunkData.Densities. Writing WATER
into that field would not lay a sheet on top of the seabed -- it would
move the iso-surface, fusing the sea into the terrain as if it were solid
ground. A second surface is the only way water can sit at ITS level
independent of the ground under it. So ChunkRenderer builds a water
MeshInstance3D as a child of the chunk's terrain mesh.

TRANSPARENCY IS SHIPPED, NOT DEFERRED (Step 2.4's cheap branch). Because
water is a distinct MeshInstance3D it carries its own StandardMaterial3D,
so alpha is one flag and Godot sorts transparent surfaces after opaque
ones by itself. Zero mesher changes. Alpha runs 0.62 shallow -> 0.97 deep,
so the shelved coast stays readable and open ocean closes up.

WHICH DATA DROVE IT: WSRF for the level (per-pixel, quantised to 1/32768
raw ~ 7.7 mm, far under the 1 m voxel, and no table lookup), WBID for
presence (it is the water stage's own classification output -- the same
set the biome grid's Ocean/Lake pixels form by construction), WBTB as the
fallback when a column is flagged wet but carries the WSRF no-water
sentinel. A body the table does not know leaves the column DRY rather
than guessing a level.

Details worth keeping:
- Water Y uses Constants.HEIGHT_SCALE, the SAME mapping as the terrain
  surface, named so the sheet and the seabed cannot drift apart if the
  vertical band is ever retuned.
- Depth for shading comes from BLUEPRINT heights, not rendered geometry:
  the terrain render clamps its floor at Y=2, so every deep-ocean column
  would otherwise read as one flat ~36 m and the gradient would die
  exactly where the ocean gets interesting.
- A cell is drawn if ANY corner is wet, flat at the highest wet level.
  Drawing onto a partly-dry cell is deliberate -- it carries the sheet
  under the shoreline where the opaque terrain hides it. Only-fully-wet
  cells retreat the waterline a metre and leave a dry gap around every
  coast and lake.
- Non-metallic, roughness 0.35: the scene environment is minimal, and a
  metallic surface reads near-black when there is nothing to reflect.

BlockRegistry gains WATER (id 11). Wire-safe: block IDs are never
serialized -- the blueprint's section table stores heights, biome ordinals
and water data, never block IDs, and chunks are not persisted yet.

MEASURED on seed 1825907253: 241,415 water columns across 454 of 4096
chunks; surface Y 37.6..37.6 m (flat, = 0.15 x 251 -- the flat sea model,
rendered); depth to 32.3 m, matching an independent read of the blueprint
exactly. Both an ocean and a lake fall in the default view.

NO REGRESSION to the 2D pipeline, verified section by section: a
regeneration of the working seed is byte-identical to task 12's run in
TCRV, TDTL, HGTS, BIOM, WBID, WBTB, WSRF, TOWN and all four road
sections; only PRMS differs, and only in its write timestamp. All four
snapshot PNGs md5-identical.

No storms, no waves, no animation, no flow -- water at rest. Those are C2+.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 02:32:04 -04:00
a6d4b870fc fix: IslandAxis defaults to the gate's verdict, 1.15/0.90 (terrain-water task 13)
Task 11 shipped 1.30/0.78 as the default; the developer's gate REJECTED that
elongation. The defaults stayed rejected, so any generation that omitted the
two keys silently produced the rejected island and a different biome baseline
(task 11 measured elongation at 12-15% of biome pixels). The developer's own
ServerConfig.json omits them. Flagged in task 12 §4, fixed here.

Both defaults now name LEGACY_AXIS_X/Y, so the default and the bad-value
restore point at one place instead of drifting apart.

VERIFIED, not assumed: generated seed 1825907253 with IslandAxisX/Y absent
from ServerConfig.json entirely -- the generator reports
"Island falloff: axis 1.15x/0.90y".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 02:20:35 -04:00
5ba868f038 docs: island falloff, coast shelf, offshore islets (terrain-water task 11)
Tools/Scripts/README.md: the falloff dials and what they actually do --
including that IslandAxisX is a weak lever because the island is already
Trench-clamped at ~90% of the map width, so nobody re-derives that the hard
way. The spine crest fix, and an explicit note that the spine's AXIS is
still a straight line down the map centre: known, deferred, its own task.

BLUEPRINT_FORMAT.md config-knobs section: the three new dials, each labelled
with whether it moves biomes. CoastProfile does NOT (it cannot move the
waterline, so HGTS changes while BIOM/WBID/WBTB/WSRF stay bit-identical);
IslandAxis* and OffshoreIslandDensity DO, by design.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 23:31:40 -04:00
6c318e4df0 feat: offshore islands (terrain-water task 11)
Sparse discoverable islets from a low-frequency ocean noise layer, seeded
resolved+7607 at 14 undulations/island (~585 px blobs).

Placed by LERPING the seabed toward a target crest (34 m raw above sea)
rather than adding to it, so an islet can surface at any ambient depth
instead of only where the seafloor happens to be shallow. After the curve's
toe compresses them they stand 5-8 m above the water: low, sandy, ringed by
beach -- which is what the existing biome rules make of that height, with no
biome-rule change.

THE THREE CONSTRAINTS, each by construction rather than by hope:

  Never merges with the mainland. The raise is EXACTLY zero wherever the
  ambient water is shallower than 14 m, so any continuous path from the
  mainland shore to an islet must cross that depth contour, and every pixel
  on it is untouched water. Measured: 0 of 13 islets merged, closest
  approach 78 px.

  Never touches the Trench. A distance mask zeroes the layer by 0.86 of
  half-map, and the ramp starts at 0.90. Measured max islet reach 0.795.
  All four corners stay below sea.

  Never spawns inland. Depth alone is not a test -- a deep lake and the
  carved crater bay are also below sea. The pre-Trench falloff is the honest
  discriminator (mainland coast sits near f = 0.66; islets need f > 0.72),
  and it is axis-invariant, because elongation moves WHERE a given f occurs,
  not the f at which land ends.

Two bugs found and fixed by measuring instead of trusting the first run:

  1. The density dial treated [-1,1] as the noise's actual range. Simplex
     is concentrated well inside it -- this field measures 0.050..0.958 --
     so "top 6%" resolved to a threshold almost nothing cleared: the first
     build raised 171 px on the entire map, none above sea. The threshold is
     now CALIBRATED against the field's own distribution (quantile over a
     1M-sample stride grid, deterministic from the seed), so the dial means
     what it says whatever FastNoiseLite returns.
  2. At 26/island the layer produced 77 islets under 200 px -- scatter, not
     "a handful". Retuned to 14/island at density 0.02: 13 islets of
     200-4792 px.

The pass prints its own islet area, so every run says what it did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 23:30:15 -04:00
566abe9784 feat: submarine coast shelf + elongation dials (terrain-water task 11)
COAST. The task's premise -- "the island edge is the steepest part" -- is
right, and the diagnostic locates it precisely: it is the SEABED, not the
land. HeightCurve.Apply returns early at and below sea level, so tasks
05-09 reshaped the land and never touched the water. Measured on the same
seed, distance-from-shore vs height:

                       land reaches 10 m   seabed reaches 10 m   gradient
  curve OFF                   31 px               31 px       0.254/0.258
  curve ON (ships)           186 px               31 px       0.038/0.258

So the landward side is ALREADY the broad shallow shelf terrain_design.md
asks for -- 54% of the island sits under 14 m -- and flattening it further
would only make it mushy. What is left is a shoreline that shelves gently
on land and then drops 6.7x steeper the moment it goes under.

CoastShelf fixes that side: depth' = depth * (1 - 0.775*exp(-depth/100 m)).
The seabed leaves the waterline at 22.5% of its former gradient and
recovers smoothly, so deep water and the Trench keep their shape. Measured
after: 5 m depth at 43 px (was 15), 10 m at 75 (was 31), 30 m at 150 (was
94) -- a 2.4-2.9x wider shallows.

It is strictly positive for positive depth, so it CANNOT move the waterline
by one pixel. Biomes and water bodies come out bit-identical, which is why
the coast is separable from elongation in the batch rather than tangled
with it -- contrary to the task's expectation that all coast work moves
biomes.

ELONGATION. IslandAxisX/Y replace the 1.15/0.90 literals (the spine shares
AxisX, as it always shared the literal). Sized by replaying candidate
falloffs against real terrain rather than guessing -- the replay reproduces
the shipped extents exactly, bbox and land count.

That replay says the task's suggested 1.30/0.85 buys +2.0% aspect, because
THE ISLAND IS ALREADY TRENCH-CLAMPED IN X: it spans 90.5% of the map width,
and 1.15 -> 1.40 moves the west coast only 393 -> ~360 px. Aspect responds
almost entirely to AxisY, which costs land:

  1.30/0.85  +2.0% aspect  +2.9% land      1.30/0.80  +4.9%  -2.2%
  1.30/0.82  +3.7% aspect  -0.1% land      1.30/0.75 +11.5%  -7.3%

Default 1.30/0.78 -- a visible step (+~8% aspect, measured 1.137 -> 1.200)
at ~4% land. The developer's eye tunes it from the batch; the table above
is the price list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 23:29:49 -04:00
af1a6d912b docs: TDTL v2 body, shelf-edge variation, erosion deferred (terrain-water task 10)
BLUEPRINT_FORMAT.md: TDTL rewritten for the v2 body (30 B) with the
version-skip rule spelled out -- this is the one section whose body is
VERSIONED rather than extended, because v1's layout was retired rather
than grown, and a reader that guesses would mint plausible nonsense.
Records edgeAmpM as APPLIED (post-clamp), so the file always describes
the terrain rather than the request. Config-knobs section updated:
ShelfEdgeVariation is metres of INPUT height -- a boundary displacement,
not an elevation.

Tools/Scripts/README.md: the two detail passes and why neither can breach
the red ceiling or the 420 m cap (K2/K6 do not move). Adds an explicit
"no rivers, no erosion, no flow routing anywhere in this pipeline" note
with the reverted D8 pass and its lesson on the record, so the next
reader does not re-derive it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 20:55:39 -04:00
ed16a7f668 tune: edge warp 5 m @12/island -> 12 m @20/island (terrain-water task 10)
Sized from measurement, not taste-first. |grad raw| at the K3/K4/K5
contours on seed 1375359975 is p50 0.00088 raw/px, so the first try (5 m,
12 undulations/island) displaced the shelf boundary a MEAN of 3.7 px with
a roughness ratio of 1.03 -- real, and invisible at map scale. At 12 m
and 20/island the boundary moves a mean 8.4 px (bench) / 9.1 px
(plateau), roughness ratio 1.09, and the outline grows the notches, coves
and peninsulas the sketch asked for.

Measured cost at 12 m (A off vs B, same seed): lowland and toe/red bands
bit-identical in both pixel count and slope -- the K2 pin holding exactly,
as designed. Foothill riser p50 0.681 -> 0.676 m/px, p90 1.445 -> 1.478,
max 3.09 -> 3.81. Bench p50 0.21 -> 0.232 (micro-relief included), still
buildable. Peaks max identical at 6.55.

Safety bound restated from "half the margin to a fixed knot" to 2/3 of
the smaller adjacent band, which is the constraint that actually matters:
the squeezed band never compresses below a third of its nominal width, so
its slope never more than triples. That puts the ceiling at 16.45 m for
the v5 knots and leaves the 12 m default real headroom if the gate says
"more".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 20:52:08 -04:00
492b56a87c feat: shelf-edge variation -- the red-line scalloping (terrain-water task 10)
PASS B, replacing the reverted incision. The developer's sketch asked for
the shelf/riser BOUNDARY to be organic, not for water: notches, coves and
small peninsulas where a flat shelf meets its riser, instead of the clean
oval contour the curve produces.

Mechanism (the task's preferred "boundary warp", in its most
monotonic-safe form): every shelf/riser boundary is the contour where the
raw height crosses K3, K4 or K5, so the boundary is warped by SLIDING
THOSE THREE KNOTS per column -- edgeShift = simplex(resolvedSeed + 7507,
12 per island width) x ShelfEdgeVariation. The contours then wander in and
out of the terrain instead of tracing an iso-height line. Noise-warped by
construction, so there is no grid direction for an artifact to line up on
-- the failure mode of the thing this replaces.

Why slide knots rather than perturb a weight or the input height:
monotonicity becomes structural instead of conditional. The curve is
strictly monotonic for ANY ordered knot set, so no derivative bound, no
amplitude-vs-feather-width tuning, no way for a dial to invert a column.
MaxEdgeShift keeps the set ordered (half the smallest margin to a fixed
knot = 12.3 m of input height for the v5 knots); the config dial is
clamped to it, loudly. AssertMonotonic now sweeps 24 corners -- the 8
modulation extremes x {-max, 0, +max} shift -- and checks the bound first.

K1, K2 and K6 never move, which buys the guarantees exactly rather than
statistically: below K2 and above K6 a warped column is bit-identical to
an unwarped one, so the red ceiling still floors every shelf edge (storm
ladder safe), the 420 m cap still caps, and the toe and summit spikes are
untouched. The block slides rigidly, so the bench and mid-riser keep their
exact widths -- shelf interiors stay flat, riser interiors keep their
profile, and only the foothill riser and plateau stretch to absorb it.
The micro-relief mask takes the same shift, so pass A's skin follows the
shelf wherever pass B moved its edge.

Dial ShelfEdgeVariation (default 5 m of INPUT height -- a boundary
displacement, not an elevation change) under the existing TerrainDetail
gate; both passes stay one judged unit. TDTL v2 body records relief and
edge amp/frequency/seed-offset plus the applied clamp bound; writer,
parser and harness follow. No blueprint was written between the revert
and this commit, so v2 only ever means relief + edge warp on disk.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 20:31:41 -04:00
3b6d166458 revert: the D8 drainage incision, whole (terrain-water task 10)
The task-10 draft's PASS B shipped and produced the canonical grid
artifact: thousands of straight, disconnected, pooling scratches. Per-
cell steepest descent on a regular grid can only route along its eight
neighbour headings, so at map scale the "channels" read as hatching,
not drainage. Reverted entirely rather than tuned -- no K, p or mask
setting fixes a directional basis. Rivers and erosion move to Phase C
as a hydraulic-erosion pass over FINAL terrain.

Removed: FlowAccumulation / IncisionWeight / EdgeBlend and the INC_*,
SEA_CLAMP, SHELF_INC_WEIGHT and CRATER_* constants; RunIncisionPass and
the pass-2b call; the six incision fields from TDTL (writer, parser,
harness). KEPT, untouched and developer-approved: pass A, the shelf
micro-relief skin, and its ShelfReliefAmp dial.

The crater carve folds back into the single pass-2 loop it came from,
carving two LOCALS written once each. That is what the code did before
the draft split it out, and it makes the aliased double-carve that the
split introduced (fix 1b98fb5) structurally impossible rather than
merely fixed -- there is no longer a read-modify-write to get wrong.

TDTL bodies are now versioned (BlueprintFormat.TDTL_VERSION = 2) and the
parser skips a body version it does not know instead of misreading the
longer v1 layout into plausible nonsense. The only v1 payloads that
exist are in the reverted batch's own tree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 20:30:44 -04:00
21dc3d16ce docs: TDTL section, TerrainDetail gate, v5 winner config notes (terrain-water task 10)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 18:13:58 -04:00
45fe2ce034 feat: shelf micro-relief + drainage incision (terrain-water task 10)
Two paired detail passes on the locked v5 skeleton, output-heights
only (classify path untouched):

PASS A — shelf micro-relief: +-ShelfReliefAmp (default 3 m) Simplex
skin (seed resolved+7409, ~40 undulations/island ~ 200 m features),
weighted by shelf-ness (full mid-shelf, feathered to zero 30% of a
band half-width into the risers) — both shelves get their rolling
texture back; risers and peaks untouched.

PASS B — drainage incision: D8 steepest-descent routing + height-
ordered flow accumulation over the curved+relieved land
(TerrainDetailPass — pure array machinery, named C++ candidate per
D-035); depth = K*accum^p*slope (K=0.78, p=0.45 concave, cap 30 m),
masked full on risers / 30% on shelves / zero on the toe, above the
plateau top, and within 1.2x CraterRadius (feathered to 1.4x); hard
clamp: carved height >= sea + 1 m. Ordering: curve -> relief ->
incision -> crater carve (the carve stays the final authority; carve
moved to its own pass 2c, bit-identical expression on both maps).
The pass prints its own MEASURED depth distribution.

Gate TerrainDetail "off"|"v1" (default v1; both passes one judged
unit; no-op without the curve) + ShelfReliefAmp dial. New TDTL section
(38 B: version + relief/incision params + seed offset) across
writer/parser/harness — blueprints stay self-describing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 18:08:28 -04:00
c71026fd94 feat: task-09 verdict — BALANCED becomes plain "v5", COMPACT retires (terrain-water task 10 prerequisite)
The documented one-liner the task-09 gate triggers: TerrainCurve gate
is now "off"|"v5" (default v5 = the BALANCED preset, id 2);
"v5-compact" retired with a verdict-specific error, "v5-balanced"
redirects to plain "v5". Knots/TCRV encoding unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 18:00:21 -04:00
4797086a13 docs: curve v5 — TCRV preset extension, tri-state gate (terrain-water task 09)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 04:19:58 -04:00
02577872f2 feat: curve v5 — corner fixes + COMPACT/BALANCED taste presets (terrain-water task 09)
The three corner fixes (both presets): riser endpoint slope floors
0.2 -> 0.1 (0.1u + 0.9*smoothstep — climbs decelerate into shelves and
accelerate out); summit-spike base floor 0.1 -> 0.05
(0.05u + 0.95*u^4 — no hard skirt under the peaks); SHELF_SPAN_MIN
2 m -> 6 m (pronounced shelves keep a gentle tilt).

Knots are preset-parameterized (CurveKnots, explicit parameter — pure,
D-035), calibrated from the same pooled batch-04 CDF:
COMPACT  P65/77/84/88/94/97 -> 65/12/7/4/6/3/3 land split
BALANCED P60/73/83/88/96/99 -> 60/13/10/5/8/3/1
Gate: "off"|"v5-compact"|"v5-balanced" (batch-only tri-state;
interim default v5-balanced pending the developer's verdict — the
winner becomes plain "v5" in a follow-up commit); v1-v4 retired
loudly. Assertion runs per preset (8 corners x per-seed spikeMax; the
lowered floors stay strictly positive). TCRV v5 extension (+9 B):
preset id + K5/K6 — the effective curve is unambiguous from the
record; old records parse (version-dispatched); harness compares.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 04:11:53 -04:00
68f7bca8e0 docs: curve v4 — TCRV modulation extension, TerrainCurve v4 default (terrain-water task 08)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 03:31:13 -04:00
4d2e024b2f feat: curve v4 — spatially modulated shelves (terrain-water task 08)
Per the task-07 gate: the terraces work but uniform anchors put a flat
ring at exactly 100 m and 220 m on every mountain. v4 keeps v3's
structure (frozen toe/rise, three risers, per-seed u4 spike to 420 m)
and turns the shelf anchors into smooth spatial fields: bench
100+-12 m and plateau 220+-20 m via two decorrelated very-low-freq
Simplex fields (~3 undulations per island width), plus a strength
field (~5/island) blending each shelf's output span between ~2 m
(pronounced flat) and ~25 m (barely a hint) — the optional strength
modulation shipped, ordering-safe by construction (worst-case bench
top 0.6962 < plateau min 0.9468). Field seeds derive from the RESOLVED
noise seed + fixed offsets (7101/7207/7303) — no config knob. Apply is
per-column with all modulated values as pure parameters (D-035); the
classify path never sees them.

Monotonicity assertion now sweeps all 8 modulation-extreme corners x
per-seed spikeMax. TerrainCurve gate "off"|"v4" (default v4);
v1-v3 retired loudly. TCRV extended (+36 B when version>=4:
amplitudes, span range, frequencies, seed offsets — length-framed
section makes the layout change safe; parser dispatches on the curve
version byte; harness compares the extension).

Note: MapGenerator.cs also carries the developer's editor whitespace
normalization (spaces->tabs, git diff -w empty before this change).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 03:30:27 -04:00
a046486a58 docs: curve v3 — TCRV per-version anchor semantics, TerrainCurve v3 default (terrain-water task 07)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 02:27:30 -04:00
ae7431ac87 feat: curve v3 — terraced ascent, two benches, white plateau at 220 m (terrain-water task 07)
Per the task-06 ground-test verdict (floor and 420 m ceiling frozen;
v2's ascent read flat-then-wall — 87% of the vertical budget in the
last 4% of input): the ascent is rebuilt as a terraced climb. Knots
recalibrated from the same pooled batch-04 land CDF at
P59/72/82/87/95/98 (K=0.509179/0.604081/0.698485/0.767213/0.930304/
1.050720), pooled land fractions exactly 59/13/10/5/8/3/2 (orange/red/
foothill-riser/bench/mid-riser/plateau/spike). Segments: frozen toe
and rise (storm anchors), smoothstep foothill riser to a near-flat
100 m bench, smoothstep mid riser to the near-flat white plateau at
220 m (relocated from 50 m — mountain towns/snow belong there),
per-seed-normalized u4 summit spike to 420 m (both retained from v2),
linear tail. Per-seed effective-curve monotonicity assertion retained.

TerrainCurve gate: "off"|"v3" (default v3); v1/v2 retired with a
loud config error (old blueprints regenerable). TCRV: knot slots carry
K1..K4 (K5/K6 are version constants), PlateauLo/Hi carry the two bench
anchors; version byte selects the semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 02:26:36 -04:00
5720f05a5b docs: curve v2 — TCRV spikeMax semantics, TerrainCurve v2 default, v1 retired (terrain-water task 06)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 00:29:34 -04:00
ac67c27952 feat: curve v2 — per-seed spike normalization, u4 wall, 420 m ceiling (terrain-water task 06)
Per the task-05 hillshade-gate verdict: lowlands frozen (identity/toe/
rise/shoulder/plateau byte-unchanged); the spike now maps [t4,
hMaxSeed] — each seed's own raw pre-curve maximum, computed in a new
pass-1 over GenerateTopography — onto the peak band, so every island
reaches the ceiling (v1's pooled-max domain left mid-range seeds at
110–175 m). Spike stiffened to 0.1u + 0.9u^4; peak cap raised to
420 m above sea (1.82869). Degenerate near-flat guard: spike domain
floored at t4 + 0.01. The curve is now SEED-DEPENDENT: hMaxSeed is a
pure parameter (D-035), recorded in TCRV (field renamed
HMaxCal -> SpikeMax, same byte layout; v1 semantics = pooled max, v2 =
per-seed), and the monotonicity assertion runs per generation against
the effective curve. TerrainCurve gate: "off"|"v2" (default v2);
"v1" retired with a loud config error (task-05 blueprints are
regenerable). Classify path untouched — the biome/water oracle must
hold unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 00:21:25 -04:00
9ebb8296c9 docs: TCRV section entry + TerrainCurve config note + 0_height snapshot (terrain-water task 05)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 15:26:09 -04:00
e7de292b3d feat: 0_height hillshade snapshot + TCRV blueprint section + harness (terrain-water task 05)
0_height: hypsometric storm-ladder tint x Lambert NW hillshade over
the curved heights, captured after topography in both curve modes —
relief is finally visible. Renumbers nothing.

TCRV (50 B, emitted when the curve is on): curve version + input knots
+ output bands, so blueprints are self-describing about the map their
heights went through (H7 spirit). Parsed into
WorldBlueprint.TerrainCurve (null = raw legacy profile); server treats
it as metadata. Round-trip harness compares it when present.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 15:24:33 -04:00
67868638be feat: height-redistribution curve, gated + biome-invariant (terrain-water task 05)
HeightCurve (pure static, D-035): calibrated monotonic piecewise map —
identity at/below sea 0.15; ease-out toe to the orange ceiling 0.206
(75% coverage, knot t1=P75=0.628736 from batch-04's pooled flat-sea
land CDF, 340.6M samples); linear rise to red 0.27 (t2=P90); smooth
shoulder to the 50m plateau shelf 0.3492 (t3=P93); near-flat plateau
step (t4=P96); accelerating spike to the 220m peak cap 1.0265; linear
tail past the calibrated max. Strict monotonicity asserted numerically
at startup, loud throw on violation.

Applied in GenerateTopography AFTER noise+falloff+Trench, BEFORE the
crater carve (carve cuts curved terrain; rim/bowl untouched by the
curve). Biome oracle mechanism: a retained uncurved classify heightmap
(alias of _heightMap when off, zero cost) feeds biome rules, both
flood fills, and the shared water predicates — classification is
curve-invariant by construction. Towns/roads/diagnostics/exported
heights use curved terrain; town positions may legitimately move.

Config gate TerrainCurve: "off"|"v1" (default v1), unknown values
rejected loudly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 15:22:53 -04:00
e435843e9a docs: SeaLevelModel/SeaLevelValue config notes (terrain-water task 04)
Both sea-level modes documented (flat default 0.15 / legacy field
Lerp); noted the blueprint does not record which model produced it —
the WBTB levels are the observable. Batch runner lives in /tmp
(throwaway), not in-repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 06:25:19 -04:00
200adcaf5a feat: config-gated flat-scalar sea level (D-033, terrain-water task 04)
SeaLevelModel: "flat" (one scalar, SeaLevelValue) or "field" (the
legacy latitude Lerp 0.26..0.15). Only GetSeaLevel's body changes —
all nine call sites (crater carve target, both flood fills, biome
split, beach band, Capitol window, town filters, A* zero-point)
inherit the model automatically; no consumer re-tuning. Default:
flat 0.15, pinned in ServerConfig.json.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 06:20:48 -04:00
1f957b3430 docs: water sections (WBID/WBTB/WSRF), no-BSIN rationale, SkipRoads + water stage in READMEs (terrain-water task 03)
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>
2026-08-07 01:40:48 -04:00
3bf822dac6 feat: WBID/WBTB/WSRF blueprint sections + parser + harness extension (terrain-water task 03)
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>
2026-08-07 01:31:50 -04:00
d296ef30c4 feat: shared water predicates + water-bodies stage + priority-flood diagnostics (terrain-water task 03)
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>
2026-08-07 01:29:51 -04:00
81f7030140 feat: SkipRoads iteration toggle (terrain-water task 03)
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>
2026-08-07 01:27:36 -04:00
fb14b0693b chore: Godot .uid files for the new task-02 scripts
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>
2026-08-07 00:17:46 -04:00
a7e6c17422 docs: BLUEPRINT_FORMAT.md + README updates for the v2 container (terrain-water task 02)
Byte-accurate v2 contract (magic/version, tagged sections, registered
FourCC table, validation rules, re-encode sentinels, extension path)
plus the v1 legacy summary — v1 stays live per the safety-net plan.
READMEs updated additively: parser dispatch, dual-write outputs,
harness usage, server params cross-check, wire-format hazard rewording
(u8 on v2 path, range-checked; append-only rule unchanged).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 07:31:03 -04:00
8532d88771 feat: blueprint v2 reader dispatch + validation + params cross-check (terrain-water task 02)
Reader sniffs the first byte (0x49 raw-ISLA v2 vs 0x07 v1 string
prefix) and routes to the v2 tagged-section parser or the intact v1
path (v1 loads log a deprecation warning — fallback stays live).

v2 validation: magic/version gate (loud reject), MapSize sanity bound
[256, 32768] before allocation, every section length checked against
remaining file, section-consumed-exactly check, params-first and
no-duplicate-section rules, biome and town-tier ordinal range checks.
Unknown tags skip by length — the forward-compat property D-030 buys.

ServerChunkManager cross-checks embedded params against config
(seed + MapSize) and logs a prominent desync warning — canon H7's
silent failure becomes loud. ServerConfig.json pins the reference
world: WorldSeed 1409879727, 8K.

Round-trip oracle GREEN: reference v1 (537,054,156 B) -> v2
(335,727,522 B, -192 MB) -> parse -> semantic equality holds
(heights bitwise, biomes, 94 towns, 4/1/37/39 road paths
point-for-point). v1 parse 1.9s, v2 write 1.4s, v2 parse 1.6s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 07:27:42 -04:00
c305fd6565 feat: blueprint v2 envelope — writer, dual-write, round-trip harness (terrain-water task 02)
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>
2026-08-06 07:24:08 -04:00
e070bbc9da fix: restore comment marker on WorldSeed line (build blocker)
Uncommenting the random-seed line left its trailing prose 'if 0 also
randomizes' as code, which stopped the whole project compiling. Only the
comment marker is restored — the choice of random-vs-fixed seed is left as set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 22:43:19 -04:00
783ee8b016 docs: fix MATH_MARCHING_CUBES §4 polarity + dims
Task 17 corrected the density sign convention in §1-§2 but left §4's worked
example on the old inverted convention, so the doc contradicted itself. §4 now
matches: negative is underground, positive is air. Also corrects a stale
16x16x64 chunk reference to 24x24x256.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 21:32:51 -04:00
d72ffc0183 docs: bring all READMEs current with actual code (post-sweep-10 truth pass)
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>
2026-08-05 05:23:58 -04:00
f9ea2da3f4 roads: enable Rugged+Trail carving + per-tier character (D-022)
All four road tiers now carve into the 3D world. Rugged and Trail were
generated, exported and parsed all along, but nothing ever consumed them —
which is why county roads visible on the 2D map did not exist in 3D.

Tier identity is now carried into the carve via a RoadSegment struct, so each
tier gets its own width, shoulder and grade-smoothing from one tunable block in
Constants: highways widest and holding a grade, trails narrow and hugging the
land. Rugged and Trail surface as dirt rather than asphalt.

Because tiers now have different reach, nearest-by-distance was no longer a
sound way to pick the governing road — a nearby footpath could shadow a highway
whose shoulder still covered the column. The carve now considers only roads
whose shoulder actually reaches, and takes the closest of those.

The per-chunk cull pad is derived from the widest shoulder plus a margin, so
widening a road cannot silently truncate it at chunk edges.

Untouched: density field, Marching Cubes interpolation/welding, chunk dims,
the .dat contract, the 2D A* generation, and mesher normals.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 03:53:32 -04:00
0910338a2a F2: continuous road grade — along-segment elevation + joint smoothing (D-021)
Road elevation is now sampled at the point on the road segment nearest the
column being carved, instead of at the segment's midpoint. The old behaviour
gave every column near a segment that segment's single midpoint height, so each
stretch of road was one flat plank and consecutive planks stepped like a
staircase wherever the road crossed a gradient.

Elevation now varies continuously along the segment, and because neighbouring
segments share an end point the height matches exactly at the joins. A new
ROAD_GRADE_SMOOTHING constant dials between holding a straight grade
(cut-and-fill) and hugging the land, per D-021.

Untouched: density field, Marching Cubes, chunk dimensions, the .dat contract,
and the 2D A* road network itself. Only how existing paths are carved into 3D.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 02:20:50 -04:00
ea11258871 F1: soft-fade biome color blending (BLEND_BAND_METERS, geometry untouched)
Vertex colour is now chosen from the true, unrounded surface height and faded
between adjacent materials across a tunable band, instead of switching at one
rounded integer depth. This removes the horizontal contour banding.

Colour path only: density field, Marching Cubes interpolation/welding, chunk
dimensions, the .dat contract and all road logic are untouched. BlockID
classification is unchanged and still drives non-visual use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 01:51:06 -04:00