Two sources of the sawtooth silhouette on island edges: - the per-cell 0.7 trim coin left random full cubes standing inside bevel runs. Trim is now an ISLAND-level trait (rng child "trim", 0.75): the whole rim bevels or the island stays blocky — never speckled. - a cell open on two OPPOSITE sides (1-wide ridge) got a wedge whose high edge leaned on nothing, and adjacent ridge cells could slope opposite ways. Support law: a wedge must lean against mass, so opposite-open cells keep the full cube (only single-open sides and adjacent-open corners trim).
- commit
- 28d443810e581761da9fb38b07aa116e5afafdde
- parent
- 73a6248131e4bc224e272b91681acf8067405237
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 23 | ++++++++++++++++------- |
@@ -268,6 +268,12 @@ export function buildIsland(
268268 dropAt(c, dx, dz) >= 0.99;269269 const shaped = new Set<string>(); // no caps/decor on these tops270270 271+ // Shoulder trim is an ISLAND-level trait (WFC coherence rule, USER RULE:272+ // triangles must never read jagged): either the whole rim bevels or the273+ // island stays blocky. The old per-cell 0.7 coin left random full cubes274+ // standing inside bevel runs — a sawtooth silhouette.275+ const trimIsland = rng.child("trim").chance(0.75);276+271277 // Waterfall pick (per island): one qualifying edge column.272278 let waterfallCell: string | null = null;273279 {@@ -345,10 +351,15 @@ export function buildIsland(
345351 346352 // Waterfall column: waterfallTop at the crest, waterfall faces below.347353 const isFall = k === waterfallCell && open.length > 0;348- // Shoulder trim on plain edge tops (never on the waterfall).349- const trim = !isFall && !c.onRun && c.dist >= 1 && c.depth >= 1 &&350- open.length >= 1 && open.length <= 2 &&351- cellRng.chance(0.7);354+ // Shoulder trim on plain edge tops (never on the waterfall). Support355+ // law: a wedge leans against mass, so its high side must be CLOSED —356+ // a cell open on two OPPOSITE sides (1-wide ridge) keeps the full357+ // cube instead of a wedge with a floating high edge.358+ const adjacentOpen = open.length === 2 &&359+ open[0][0] !== open[1][0] && open[0][1] !== open[1][1];360+ const trim = trimIsland && !isFall && !c.onRun && c.dist >= 1 &&361+ c.depth >= 1 &&362+ (open.length === 1 || adjacentOpen);352363 353364 // One face per waterfall column — water can't zigzag around the354365 // corner course to course (the old per-course re-pick did).@@ -384,9 +395,7 @@ export function buildIsland(
384395 // open too (else the cut face jams into that block's corner), and385396 // with two open sides the slope descends toward the LOWER one.386397 const dirs = open.map(([dx, dz]) => dirOf(dx, dz));387- const adjacent = open.length === 2 &&388- (open[0][0] !== open[1][0] && open[0][1] !== open[1][1]);389- const corner = adjacent &&398+ const corner = adjacentOpen &&390399 dropAt(c, open[0][0] + open[1][0], open[0][1] + open[1][1]) >= 0.99;391400 const slopeDir = open.length === 1392401 ? dirs[0]