Skip to content

World generation overhaul: biomes, terrain, coasts & blending#5

Merged
MichaelFisher1997 merged 4 commits into
masterfrom
opencode/clever-panda
Jun 14, 2026
Merged

World generation overhaul: biomes, terrain, coasts & blending#5
MichaelFisher1997 merged 4 commits into
masterfrom
opencode/clever-panda

Conversation

@MichaelFisher1997

Copy link
Copy Markdown
Contributor

Modular, deterministic world-gen pipeline replacing the single-file generator. Closes the gap to Minetest/Luanti world richness.

What changed

  • Modular pipeline (src/game/gen/): Climate, Biomes, TerrainNoise, Caves, Ores, Surface, Trees, Decorations, WorldgenStats — declarative stages orchestrated by a thin TerrainGenerator. Deterministic per seed (byte-identical, seamless chunk borders), ~3.6ms/chunk.
  • Biome system: 12 biomes via Voronoi climate selection + altitude chill. Fixed a climate-map DC-bias bug that had frozen half the map to freezing; all biomes now appear across seeds.
  • Terrain: continents + ridged mountains + rivers + detail; caves (worm tunnels + caverns); depth-gated ores (coal -> iron -> copper).
  • Coastlines (tuned over several passes): beaches are a proximity effect with a patch-based presence mask (per-biome tendency) so lakes get sandy coves + earthen banks instead of a uniform ring; elevation-aware rocky threshold removes grey stone rings; 3D-wobble damping near sea fixes invisible-water gaps; underwater shelves grade sand -> gravel -> dirt.
  • Biome blending: continuous temperature-driven snow mask tapers Snow -> Snowy Grass -> Grass (no hard biome/snow lines) + biome-edge mottling.
  • Blocks: +8 (dead bush, fern, papyrus, cornflower, birch wood/leaves, spruce/snowy leaves) + 9 atlas tiles.
  • Debug: WorldgenOverlay (G, H cycle) with live minimap (biome/heat/humidity/height/slope/shore/snow) + target readout; gen timing in F3 perf overlay; __voxl.worldgen() console API.
  • Spawn search finds dry land near origin.

Verification

  • tsc --noEmit clean; vite build passes; 3/3 test suites pass.
  • Headless checks: determinism (identical seed -> identical chunks), 0-discontinuity chunk borders, full biome palette across seeds, coastal surface audits (beach-band sand ~4% pockets, 0% stone), gradual snow transects.
  • (No screenshots — this sandbox lacks Chromium; visual verification left to the manual checklist below.)

Manual checklist

Grassland lake (sandy coves + grass banks) · forest lake (earthen banks) · desert/snowy/mountain coasts · steep shore (no sand) · shallow shelves · chunk-boundary shoreline · walk a beach · G -> shore/snow overlay to inspect block reasons.

Notes / TODOs

  • Tree types still snap at biome boundaries (ground surface blends; trees don't yet).
  • Water-body classification is a local debug estimate; generation uses biome tendency + patch noise.
  • dist/ was env-locked in the worktree; build verified to a temp outDir.

Modular world-gen pipeline (src/game/gen/) replacing the single-file
generator with declarative, deterministic stages: Climate (heat/humidity
+ altitude chill), Biomes (Voronoi climate selection, 12 biomes),
TerrainNoise (continents + ridged mountains + rivers + detail), Caves
(worm tunnels + caverns), Ores (depth-gated veins), Surface (coastal +
snow + blend painting), Trees (6 species w/ variants), Decorations
(clustered ground cover), WorldgenStats.

Generation is deterministic per seed (byte-identical, seamless chunk
borders) and budgeted (~3.6ms/chunk).

Biome system: 12 biomes selected by nearest climate point with altitude
chill; fixed a climate-map DC-bias bug that had frozen half the map to
freezing. All biomes appear across seeds.

Coastlines (multi-pass tuning):
- Beaches are a proximity effect, not an elevation biome; sand requires
  real water adjacency so low inland plains stay grassy.
- Beach PRESENCE is a low-frequency patch mask gated by per-biome
  tendency (desert always sandy; forest/snow/mountain mostly earthen
  banks w/ sand pockets) — breaks the uniform sand ring around lakes.
- Beach width noise-modulated + slope-aware; steep shores never sandy.
- Elevation-aware rocky threshold near sea kills grey stone rings.
- 3D solidity wobble damped near sea so dressed floor tracks the
  heightmap (fixes invisible-water/beach gaps).
- Underwater shelves grade sand -> gravel -> dirt by depth.

Biome blending: continuous temperature-driven snow mask (tapers
Snow -> Snowy Grass -> Grass, biome-independent) + biome-edge
mottling, removing hard biome/snow boundary lines.

Blocks: +8 (dead bush, fern, papyrus, cornflower, birch wood/leaves,
spruce leaves, snowy leaves) + 9 atlas tiles.

Debug: WorldgenOverlay (G toggle, H cycle) w/ live minimap
(biome/heat/humidity/height/slope/shore/snow modes) + target-column
readout (biome, surface, snow, beach strength/presence, water extent,
shore distance, slope). Gen timing in the F3 perf overlay.
__voxl.worldgen()/worldgenMode()/worldgenInfo() console API.

Spawn search now finds dry land near origin.

Typecheck clean; build passes; 3/3 test suites pass.
@github-actions

Copy link
Copy Markdown

📋 Summary

No linked issues (e.g., “Fixes #123”) were found in the PR description. The PR title/body position this as a feature overhaul rather than a bug-fix tied to a tracked issue.

This PR replaces the monolithic TerrainGenerator with a modular, deterministic world-generation pipeline (src/game/gen/*), adds 12 biomes with climate-space selection, new coastline/snow-blending logic, 8 new blocks + 9 atlas tiles, a debug overlay, and spawn search. All builds (vite build, astro build) and all 3 test suites pass. The code follows existing patterns, keeps block ids stable, preserves flipY=false atlas semantics, and correctly marks new plants as shape: "plantlike".

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

None identified.

⚠️ High Priority Issues (Should Fix)

[HIGH] src/game/gen/Caves.ts:30-34 — Caves carve air pockets below sea level under land columns
Confidence: Medium
Description: isCarved() only skips caves beneath shallow/ocean columns (height <= sea + 1 && y < sea). For a coastal hill (height > sea + 1), caves are still carved at y < sea, and SurfacePainter.fillWater() only fills air from topY+1 upward, so those sub-sea cave cells remain dry air pockets underwater.
Impact: Visible dry-air pockets under coasts/hills; contradicts the stated intent of keeping caves out of sub-sea columns because the liquid sim has no pressure model.
Suggested Fix: Treat any column whose surface is above sea level as “dry crust” for the entire sub-sea volume:

// After the existing shallow/ocean guard:
if (height > sea && y < sea) return false;

This preserves deep ocean caverns (height <= sea) while keeping land-column caves dry.

💡 Medium Priority Issues (Nice to Fix)

[MEDIUM] src/game/gen/TerrainNoise.ts:90-96 — River carve can raise the ocean floor
Confidence: Medium
Description: riverOffset() computes delta = (target - approx) * s * 0.9 with target = sea - 3. When approx is below target (deep ocean), delta becomes positive, raising the seabed inside river bands instead of leaving it alone. This works against the tuned underwater shelf grading.
Impact: Subtle ocean-floor ridges / shelf artifacts where river bands cross deep water.
Suggested Fix: Clamp the carve to never raise terrain:

const delta = Math.min(0, (target - approx) * s * 0.9);
return delta < -52 ? -52 : delta;

[MEDIUM] src/ui/WorldgenOverlay.ts:88 — Overlay hint text is wrong
Confidence: High
Description: The hint reads "G: mode", but Game.ts wires KeyG to toggle the overlay and KeyH (while open) to cycle modes.
Impact: Players see misleading on-screen help.
Suggested Fix: Change the hint to "G: toggle · H: cycle" or split into two hints.

[MEDIUM] src/ui/WorldgenOverlay.ts:173-182 — Minimap render is very expensive
Confidence: High
Description: renderMap() calls gen.debugAt() once per pixel (112×112 = 12,544 calls). Each debugAt() performs an 81-cell water-extent scan, so one minimap refresh evaluates the heightmap ~1 million times. It is throttled to movement, but opening the overlay or crossing the threshold still causes a multi-frame hitch.
Impact: Debug overlay can stall the main thread.
Suggested Fix: Add a fast?: boolean option to debugAt() that skips waterExtent/shoreDist, and use it in renderMap(); only compute the full debug readout for the single target column.

[MEDIUM] src/game/gen/Trees.ts:63-65 — Redundant layerR branch in birch canopy
Confidence: High
Description: const layerR = t > 0.75 ? 0 : t > 0.4 ? 2 : 2; always evaluates to 0 or 2; the second 2 branch is dead code.
Impact: Code clarity / minor confusion.
Suggested Fix: Collapse to const layerR = t > 0.75 ? 0 : 2;.

ℹ️ Low Priority Suggestions (Optional)

[LOW] src/game/gen/Biomes.ts:47-61beach biome is defined but never selected
Confidence: High
Description: selectBiome() never returns "beach" (by design, per the file comment), so the beach entry in BIOME_DEFS is unused except as a lookup target that never occurs.
Impact: Dead data; minor confusion for future maintainers.
Suggested Fix: Either remove the entry or add a comment explaining it is a reserved palette entry.

[LOW] src/game/gen/TerrainNoise.ts:90-96riverOffset() recomputes continent/hills noise already evaluated in height()
Confidence: Medium
Description: riverOffset() re-runs the 4-octave continent and 3-octave hills fbm that height() already computed, roughly doubling the heightmap noise cost per column.
Impact: Extra ~1.7k noise evaluations per chunk; small but unnecessary.
Suggested Fix: Refactor height() to compute a base approx first and pass it to riverOffset(), or accept a precomputed baseHeight parameter.

[LOW] src/game/TerrainGenerator.ts:505-506 — Re-exports are unused
Confidence: High
Description: export { landBiome, landBlend, type BiomeId }; is not imported anywhere.
Impact: Dead re-exports.
Suggested Fix: Remove them or document why they are exposed.

[LOW] src/game/Game.ts:319-337findSpawnColumn() can be expensive for ocean-start seeds
Confidence: Medium
Description: Worst-case search spans radius 6→384 in 6-block steps (~16k checks), each evaluating the heightmap and biome.
Impact: One-time startup hitch if the origin is ocean and land is far.
Suggested Fix: Cache columnHeight/biomeAt results during the spiral, or start with a cheaper 2D-only climate check.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 8 Generator is cleanly split into Climate, Biomes, TerrainNoise, Caves, Ores, Surface, Trees, Decorations, Stats.
Open/Closed 7 New biomes require editing BIOME_DEFS and possibly TreeGenerator; could be more plugin-like.
Liskov Substitution 8 No deep inheritance; small data-driven types behave consistently.
Interface Segregation 8 Focused types (SetBlock, DecoColumn, SurfaceCtx) keep call sites narrow.
Dependency Inversion 7 Modules depend directly on the concrete Noise class, but that matches the existing codebase.
Average 7.6

🎯 Final Assessment

Overall Confidence Score: 78%

Confidence Breakdown:

  • Code Quality: 85% (clean modular structure, minor dead code / redundant branches)
  • Completeness: 90% (delivers all stated world-gen features and debug tooling)
  • Risk Level: 60% (large change to core generation; a few terrain/perf artifacts possible)
  • Test Coverage: 70% (existing suites pass, but no new automated tests for determinism/biome distribution)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing

Verdict:

MERGE WITH FIXES

The PR is well-architected, deterministic, and passes all build/test gates, but address the sub-sea cave air pockets and the river-offset ocean-floor artifact before merging.

New%20session%20-%202026-06-14T22%3A43%3A43.769Z
opencode session  |  github run

[HIGH] Caves: never carve below sea level. Previously, worm tunnels carved
at y < sea under land columns (height > sea+1), leaving dry air pockets
underwater (fillWater only floods columns whose surface is below sea).
Now all sub-sea crust stays solid; caves live only above sea under land.
Verified: 0 sub-sea air cells, 55k+ above-sea cave cells remain. Removed
the now-unreachable deep-cavern branch.

[MED] River carve clamped to never raise terrain (was ridging the deep
ocean floor where bands crossed water). Also refactored height() to pass
the continent+hills base to riverOffset() instead of recomputing two fbm
octaves per column (~1.7k fewer noise evals/chunk; gen dropped 3.6->3.4ms).

[MED] WorldgenOverlay hint text corrected ('G toggle · H cycle').

[MED] Minimap perf: debugAt() gains a fast flag that skips the radius-8
water-extent scan and radius-6 shore distance for the per-pixel minimap
render (~600 fewer noise evals/pixel); full readout still used for the
single target column.

[MED] Trees: collapsed redundant birch canopy layerR branch.

[LOW] Biomes: documented 'beach' as a reserved palette entry (selectBiome
never returns it by design).

[LOW] Removed dead re-exports (landBiome/landBlend/BiomeId) and the now-
unused landBiome import from TerrainGenerator.

[LOW] findSpawnColumn spiral step 6->8 to halve worst-case ocean-start cost.

Typecheck clean; build passes; 3/3 tests pass.
@github-actions

Copy link
Copy Markdown

📋 Summary

No linked issues (e.g., “Fixes #123”) were found in the PR description. The PR is a feature overhaul of world generation.

This PR replaces the monolithic TerrainGenerator with a modular, deterministic pipeline (src/game/gen/*), adds 12 biomes, new coastline/snow-blending logic, 8 new blocks + 9 atlas tiles, a debug overlay, and spawn search. All builds (vite build, astro build) and all 3 test suites pass. The code follows existing patterns, keeps block ids stable, preserves invertY=false atlas semantics, and correctly marks new plants as shape: "plantlike".

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

None identified.

⚠️ High Priority Issues (Should Fix)

None identified.

💡 Medium Priority Issues (Nice to Fix)

[FIXED] Previous issue: src/game/gen/TerrainNoise.ts:90-96 — River carve could raise the ocean floor
Note: riverOffset() now clamps delta with Math.min(0, ...) and receives a precomputed baseCH, so it neither ridges the seabed nor recomputes continent/hills noise.

[FIXED] Previous issue: src/ui/WorldgenOverlay.ts:88 — Overlay hint text was wrong
Note: Hint now reads "G toggle · H cycle".

[FIXED] Previous issue: src/ui/WorldgenOverlay.ts:173-182 — Minimap render was very expensive
Note: debugAt() now accepts a fast flag that skips the water-extent and shore-distance scans; renderMap() uses it.

[FIXED] Previous issue: src/game/gen/Trees.ts:63-65 — Redundant layerR branch in birch canopy
Note: Collapsed to const layerR = t > 0.75 ? 0 : 2;.

[MEDIUM] src/game/gen/Biomes.ts:343landBlend() returns maximum edge value at biome centers
Confidence: High
Description: The edge metric is documented as “≈ 1 when the two nearest biomes are nearly equidistant,” but the special case bestD < 1e-6 ? 1 returns 1 at the exact center of a biome (where the nearest biome is closest and the second-nearest is farthest). This causes applySnowAndBlend() to mottle the surface toward the neighbour biome throughout the small region around each biome’s climate point, producing incorrect center-mottling instead of boundary-mottling.
Impact: Small patches of surface blocks from the wrong biome at the centers of climate regions; contradicts the intended biome-edge blending.
Suggested Fix: Return 0 when bestD is negligible, or remove the unnecessary guard (the existing denominator sqrt(bestD) + 0.001 already prevents division by zero):

const edge = bestD < 1e-6 ? 0 : Math.max(0, 1 - Math.sqrt(secondD) / (Math.sqrt(bestD) + 0.001));

[MEDIUM] src/game/Game.ts:220,850KeyH has conflicting handlers
Confidence: High
Description: Pressing H while the worldgen overlay is open calls worldgenOverlay.cycleMode() and then falls through to handleLightingDebugKey(), which toggles shadows. The same keystroke therefore changes two unrelated debug toggles at once.
Impact: Confusing control behavior for anyone using the worldgen overlay and lighting debug keys.
Suggested Fix: Return early after handling the worldgen cycle key:

if (code === "KeyH" && this.worldgenOverlay.isOpen) {
  this.worldgenOverlay.cycleMode();
  return;
}

ℹ️ Low Priority Suggestions (Optional)

[FIXED] Previous issue: src/game/gen/Biomes.ts:47-61beach biome defined but never selected
Note: A comment now explains that "beach" is a reserved palette entry.

[FIXED] Previous issue: src/game/gen/TerrainNoise.ts:90-96riverOffset() recomputed continent/hills noise
Note: height() now passes baseCH into riverOffset().

[FIXED] Previous issue: src/game/TerrainGenerator.ts:505-506 — Re-exports were unused
Note: The dead export { landBiome, landBlend, type BiomeId }; re-exports have been removed.

[LOW] src/game/Game.ts:376-396findSpawnColumn() can still be expensive for ocean-start seeds
Confidence: Medium
Description: The search now steps by 8 blocks, but worst-case still evaluates columnHeight() + biomeAt() for ~9,400 columns on an ocean-start seed, each re-evaluating the heightmap and climate noise with no caching.
Impact: A one-time startup/regeneration hitch when the origin is ocean and land is far away.
Suggested Fix: Cache columnHeight/biomeAt results during the spiral, or pre-screen with a cheaper 2D-only climate/height check.

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 8 Generator cleanly split into Climate, Biomes, TerrainNoise, Caves, Ores, Surface, Trees, Decorations, Stats.
Open/Closed 7 New biomes require editing BIOME_DEFS and tendencyFor; could be more plugin-like.
Liskov Substitution 8 No deep inheritance; small data-driven types behave consistently.
Interface Segregation 8 Focused types (SetBlock, DecoColumn, SurfaceCtx) keep call sites narrow.
Dependency Inversion 7 Modules depend directly on the concrete Noise class, matching existing codebase style.
Average 7.6

🎯 Final Assessment

Overall Confidence Score: 78%

Confidence Breakdown:

  • Code Quality: 85% (clean modular structure, minor control/surface-blend issues)
  • Completeness: 90% (delivers all stated world-gen features and debug tooling)
  • Risk Level: 60% (large change to core generation; a couple of terrain/UX artifacts possible)
  • Test Coverage: 70% (existing suites pass, but no new automated tests for determinism/biome distribution)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing

Verdict:

MERGE WITH FIXES

The PR is well-architected, deterministic, and passes all build/test gates. Address the biome-center blending bug in landBlend() and the KeyH control conflict before merging.

New%20session%20-%202026-06-14T23%3A00%3A17.791Z
opencode session  |  github run

[MED] Biomes.landBlend(): the edge metric was broken — the formula
1 - sqrt(secondD)/(sqrt(bestD)+eps) evaluated to ~0 everywhere (center
AND boundary), and the bestD<1e-6 ? 1 special case returned max edge at
biome CENTERS (backwards). Replaced with a correct fixed-width band:
edge = 1 - (dSecond - dBest)/0.05, so it's 0 deep inside a biome and 1
on a boundary. Biome-edge mottling now actually fires near climate
boundaries (was effectively dead). Verified: max edge 1.0, ~22% of
columns in the mottle band.

[MED] Game.ts: KeyH fell through from the worldgen mode-cycle into
handleLightingDebugKey (which toggles shadows), so one keypress changed
two unrelated toggles. Added an early return after cycling.

[LOW] findSpawnColumn: the biomeAt(... !== 'ocean') check was redundant
with columnHeight > SEA (selectBiome returns ocean iff height <= sea),
so it duplicated the heightmap + climate work. Dropped it — check is now
a single columnHeight() call, roughly halving worst-case ocean-start cost.

Typecheck clean; build passes; 3/3 tests pass; determinism holds.
@github-actions

Copy link
Copy Markdown

📋 Summary

No linked issues (e.g., “Fixes #123”) were found in the PR description or title. The PR is a feature overhaul of world generation.

This PR replaces the monolithic TerrainGenerator with a modular, deterministic pipeline (src/game/gen/*), adds 12 biomes, new coastline/snow-blending logic, 8 new blocks + 9 atlas tiles, a debug overlay, and spawn search. All builds (vite build, astro build) and all 3 test suites pass. The code follows existing patterns, keeps block ids stable, preserves invertY=false atlas semantics, and correctly marks new plants as shape: "plantlike".

📌 Review Metadata

🔴 Critical Issues (Must Fix - Blocks Merge)

None identified.

✅ All previously reported critical/high issues have been resolved (sub-sea cave air pockets fixed).

⚠️ High Priority Issues (Should Fix)

None identified.

💡 Medium Priority Issues (Nice to Fix)

[FIXED] Previous issue: src/game/gen/Caves.ts:30-34 — Caves carved dry air pockets below sea level under land columns.
Note: isCarved() now returns false for y < sea, so sub-sea crust stays solid.

[FIXED] Previous issue: src/game/gen/TerrainNoise.ts:95-105 — River carve could ridge the deep ocean floor.
Note: riverOffset() clamps with Math.min(0, ...) and receives a precomputed baseCH.

[FIXED] Previous issue: src/ui/WorldgenOverlay.ts:80 — Overlay hint text was wrong.
Note: Hint now reads "G toggle · H cycle".

[FIXED] Previous issue: src/ui/WorldgenOverlay.ts:173-182 — Minimap render was very expensive.
Note: debugAt() has a fast flag that skips the water-extent and shore-distance scans; renderMap() uses it.

[FIXED] Previous issue: src/game/gen/Trees.ts:58 — Redundant birch canopy layerR branch.
Note: Collapsed to const layerR = t > 0.75 ? 0 : 2;.

[FIXED] Previous issue: src/game/gen/Biomes.ts:84-87beach biome was dead data.
Note: Comment now documents it as a reserved palette entry.

[FIXED] Previous issue: src/game/gen/TerrainNoise.ts:95-105riverOffset() recomputed continent/hills noise.
Note: height() now passes baseCH into riverOffset().

[FIXED] Previous issue: src/game/TerrainGenerator.ts — Dead re-exports of landBiome, landBlend, BiomeId.
Note: Re-exports removed.

[FIXED] Previous issue: src/game/gen/Biomes.ts:342-350landBlend() returned maximum edge at biome centers.
Note: Replaced with a fixed-width boundary band: 0 deep inside a biome, 1 on the boundary.

[FIXED] Previous issue: src/game/Game.ts:220-223KeyH toggled shadows while cycling the worldgen overlay.
Note: Added early return after cycling the overlay mode.

[FIXED] Previous issue: src/game/Game.ts:377-396findSpawnColumn() duplicated biome/height work.
Note: Removed the redundant biomeAt() check, roughly halving worst-case ocean-start cost.

[MEDIUM] src/game/Items.ts:133 — New plantlike blocks break too slowly in survival
Confidence: High
Description: The new decorations Dead Bush (30), Fern (31), Papyrus (32), and Cornflower (33) are not in the HARDNESS instant map. They therefore default to "medium" (0.9 s), while existing plantlike blocks such as Tall Grass, Flower, Dandelion, and Mushroom break instantly.
Impact: Survival players experience inconsistent, slower gathering of the new decorative flora.
Suggested Fix: Add the new plantlike ids to the instant group:

20: "instant", 21: "instant", 22: "instant", [MUSHROOM_BLOCK]: "instant",
30: "instant", 31: "instant", 32: "instant", 33: "instant", // plantlike

[MEDIUM] src/game/Items.ts:114-120 — New leaf blocks drop themselves while oak/jungle leaves drop nothing
Confidence: High
Description: DROP_TABLE maps the existing Leaves (6) and Jungle Leaves (26) to null, but the newly added Birch Leaves (35), Spruce Leaves (36), and Snowy Leaves (37) are absent, so they drop placeable leaf items. This creates a survival/creative inconsistency with the older leaf types.
Impact: New leaf types behave differently from existing ones for no gameplay reason.
Suggested Fix: Add the new leaf ids to DROP_TABLE:

const DROP_TABLE: Record<number, ItemId | null> = {
  1: "b2",
  6: null,
  10: "b2",
  11: null,
  26: null,
  35: null,
  36: null,
  37: null,
};

ℹ️ Low Priority Suggestions (Optional)

[LOW] src/game/Items.ts:84-92 — Creative palette omits the new blocks
Confidence: High
Description: CREATIVE_PALETTE does not include b30b37, so the new dead bush, fern, papyrus, cornflower, birch wood/leaves, spruce leaves, and snowy leaves cannot be picked from the creative palette.
Impact: New blocks are only obtainable by breaking them in survival or via console.
Suggested Fix: Append the new item ids to CREATIVE_PALETTE (and the starter hotbar if desired).

[LOW] src/ui/WorldgenOverlay.ts:192-193 — Humidity-mode colour comment is misleading
Confidence: High
Description: The comment says // dry(brown) → wet(blue), but the implementation uses heatColor(1 - d.humidity), which renders dry as red and wet as blue.
Impact: Comment contradicts the actual minimap colours.
Suggested Fix: Either update the comment to // dry(red) → wet(blue) or add a dedicated humidity colour ramp.

[LOW] src/game/gen/Trees.ts:145-148placeCactus() is dead code
Confidence: High
Description: Cacti are placed by DecorationGenerator, not TreeGenerator; placeCactus() is never called.
Impact: Unused method adds maintenance surface.
Suggested Fix: Remove placeCactus() from TreeGenerator, or route desert cactus placement through it.

[LOW] src/game/gen/Surface.ts:132-140decideSurface() declares an unused height parameter
Confidence: High
Description: The function signature includes height: number, but the body immediately runs void height;.
Impact: Minor clarity/cleanliness issue.
Suggested Fix: Remove the parameter (and the void height; line) from the signature and its callers.

[LOW] src/game/Game.ts:377-396findSpawnColumn() can still stall on ocean-start seeds
Confidence: Medium
Description: The redundant biomeAt() check was removed, but the spiral still evaluates columnHeight() for up to ~9,400 grid points in the worst case with no caching.
Impact: One-time startup/regeneration hitch when the origin is ocean and land is far away.
Suggested Fix: Cache columnHeight() results during the spiral, or pre-screen candidate rings with a cheaper 2D-only height estimate.

[LOW] src/ui/WorldgenOverlay.ts:105-108 — Toggling the overlay off/on can show a stale minimap
Confidence: Medium
Description: setVisible(false) does not invalidate lastRenderX/lastRenderZ/lastRenderMode. If the overlay is reopened without the player having moved, the cached canvas from the previous session is displayed.
Impact: Brief stale readout after reopening the overlay.
Suggested Fix: Reset the render cache when hiding or when showing:

setVisible(v: boolean): void {
  this.visible = v;
  this.root.hidden = !v;
  if (!v) {
    this.lastRenderX = Number.NaN;
    this.lastRenderZ = Number.NaN;
    this.lastRenderMode = null;
  }
}

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 8 Generator is cleanly split into Climate, Biomes, TerrainNoise, Caves, Ores, Surface, Trees, Decorations, Stats.
Open/Closed 7 New biomes require editing BIOME_DEFS and tendencyFor; could be more plugin-like.
Liskov Substitution 8 No deep inheritance; small data-driven types behave consistently.
Interface Segregation 8 Focused types (SetBlock, DecoColumn, SurfaceCtx) keep call sites narrow.
Dependency Inversion 7 Modules depend directly on the concrete Noise class, matching existing codebase style.
Average 7.6

🎯 Final Assessment

Overall Confidence Score: 85%

Confidence Breakdown:

  • Code Quality: 85% (clean modular structure; minor survival-parity oversights)
  • Completeness: 90% (delivers all stated world-gen features and debug tooling)
  • Risk Level: 70% (large change to core generation, but deterministic and well-tested)
  • Test Coverage: 70% (existing suites pass, but no new automated tests for determinism/biome distribution)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing

Verdict:

MERGE WITH FIXES

The PR is well-architected, deterministic, and passes all build/test gates. Address the survival-parity inconsistencies in Items.ts (plantlike dig time and leaf drops) before merging; the remaining items are optional cleanups.

New%20session%20-%202026-06-14T23%3A13%3A40.973Z
opencode session  |  github run

[MED] Items: new plantlike blocks (dead bush 30, fern 31, papyrus 32,
cornflower 33) now break instantly in survival like the existing plantlike
blocks, instead of defaulting to 0.9s.

[MED] Items: new leaf blocks (birch 35, spruce 36, snowy 37) now drop
nothing, matching oak (6) and jungle (26) leaves.

[LOW] Items: added the new blocks (b30-b37) to the creative palette so
they're pickable without console/survival gathering.

[LOW] WorldgenOverlay: fixed misleading humidity-mode colour comment
(dry=red, not brown); invalidate the minimap render cache on hide so
reopening forces a fresh render instead of a stale canvas.

[LOW] Trees: removed dead placeCactus() (cacti are placed by the
decoration generator).

[LOW] Surface: removed the unused height parameter from decideSurface()
(and the void height; placeholder) plus its callers + SurfaceCtx field.

Typecheck clean; build passes; 3/3 tests pass; determinism holds.
@github-actions

Copy link
Copy Markdown

📋 Summary

No linked issues (e.g., “Fixes #123”) were found in the PR description or title. This is a feature overhaul, not a bug-fix tied to a tracked issue.

This PR replaces the monolithic TerrainGenerator with a modular, deterministic pipeline (src/game/gen/*), adds 12 biomes with climate-space selection, new coastline/snow-blending logic, 8 new blocks + 9 atlas tiles, a debug overlay, and spawn search. All builds (vite build, astro build) and all 3 test suites pass. Block ids remain stable, flipY=false atlas semantics are preserved, and new plant blocks are correctly marked shape: "plantlike".

📌 Review Metadata

The PR fully implements its stated scope: modular generation, 12 biomes, coast/beach tuning, continuous snow blending, new blocks, debug overlay, and dry-land spawn search. Code quality is high, determinism is maintained, and the survival/creative parity issues from earlier review rounds are resolved.

🔴 Critical Issues (Must Fix - Blocks Merge)

None identified.

✅ All previously reported critical issues have been resolved.

⚠️ High Priority Issues (Should Fix)

None identified.

✅ All previously reported high-priority issues have been resolved.

💡 Medium Priority Issues (Nice to Fix)

None identified.

[FIXED] Previous medium issues from prior reviews:

  • src/game/gen/Caves.ts — sub-sea cave air pockets under land columns.
  • src/game/gen/TerrainNoise.ts — river carve ridging the deep ocean floor and recomputing continent/hills noise.
  • src/ui/WorldgenOverlay.ts — misleading hint text and expensive per-pixel minimap render.
  • src/game/gen/Trees.ts — redundant birch canopy layerR branch.
  • src/game/gen/Biomes.tslandBlend() returning maximum edge at biome centers.
  • src/game/Game.tsKeyH cycling the worldgen overlay and toggling shadows simultaneously.
  • src/game/Items.ts — new plantlike blocks breaking too slowly and new leaf blocks dropping placeable items.

ℹ️ Low Priority Suggestions (Optional)

[FIXED] Previous low issues from prior reviews:

  • beach biome documented as a reserved palette entry.
  • Dead re-exports of landBiome/landBlend/BiomeId removed.
  • New blocks added to the creative palette.
  • Humidity-mode colour comment corrected.
  • Dead placeCactus() removed from TreeGenerator.
  • Unused height parameter removed from decideSurface().
  • Minimap render cache invalidated when the overlay is hidden.

[LOW] src/game/Game.ts:377-396findSpawnColumn() can still stall on ocean-start seeds
Confidence: Medium
Description: The redundant biomeAt() check was removed and the step is now 8 blocks, but the spiral still evaluates columnHeight() for up to ~9,400 grid points in the worst case with no caching.
Impact: One-time startup/regeneration hitch when the origin is ocean and dry land is far away.
Suggested Fix: Cache columnHeight() results during the spiral, or pre-screen candidate rings with a cheaper 2D-only height estimate:

const cache = new Map<string, boolean>();
const check = (x: number, z: number): boolean => {
  const key = `${x},${z}`;
  if (cache.has(key)) return cache.get(key)!;
  const ok = gen.columnHeight(x, z) > SEA_LEVEL;
  cache.set(key, ok);
  return ok;
};

📊 SOLID Principles Score

Principle Score Notes
Single Responsibility 8 Generator cleanly split into Climate, Biomes, TerrainNoise, Caves, Ores, Surface, Trees, Decorations, Stats.
Open/Closed 7 New biomes require editing BIOME_DEFS and tendencyFor; could be more plugin-like.
Liskov Substitution 8 No deep inheritance; small data-driven types behave consistently.
Interface Segregation 8 Focused types (SetBlock, DecoColumn, SurfaceCtx) keep call sites narrow.
Dependency Inversion 7 Modules depend directly on the concrete Noise class, matching existing codebase style.
Average 7.6

🎯 Final Assessment

Overall Confidence Score: 88%

Confidence Breakdown:

  • Code Quality: 90% (clean modular structure; minor dead-data/perf nits only)
  • Completeness: 95% (delivers all stated world-gen features and debug tooling)
  • Risk Level: 65% (large change to core generation, but deterministic and well-tested)
  • Test Coverage: 70% (existing suites pass, but no new automated tests for determinism/biome distribution)

Merge Readiness:

  • All critical issues resolved
  • SOLID average score >= 6.0
  • Overall confidence >= 60%
  • No security concerns
  • Tests present and passing

Verdict:

MERGE

The PR is well-architected, deterministic, and passes all build/test gates. All previously reported correctness and survival-parity issues are fixed; the remaining spawn-search cost is a minor optional optimization.

New%20session%20-%202026-06-14T23%3A51%3A30.384Z
opencode session  |  github run

@MichaelFisher1997 MichaelFisher1997 merged commit 6388150 into master Jun 14, 2026
1 check passed
@MichaelFisher1997 MichaelFisher1997 deleted the opencode/clever-panda branch June 14, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant