Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
02a8444
feat(compiler): valley surround core — ridged annulus heightfield, Po…
stellarfeline Aug 4, 2026
b0cbb7f
docs: attribution ledger — Bridson Poisson-disk + Musgrave ridged mul…
stellarfeline Aug 4, 2026
27d5582
feat(compiler): valley crest-band tree line, nav un-climbability proo…
stellarfeline Aug 4, 2026
cb393f1
feat(dsl,compiler): valley + cherry-valley land — surround wired thro…
stellarfeline Aug 4, 2026
3f6fdc1
fix(dsl,compiler,docs): pay the foundation WIP debts so the spec-0026…
stellarfeline Aug 4, 2026
60aed01
fix(docs,dsl): stack sync onto the finished foundation — dedupe DW ro…
stellarfeline Aug 4, 2026
b357ec3
feat(dsl): spec-0026 amendment ranges — vista_radius floor 192 / defa…
stellarfeline Aug 4, 2026
fca407f
fix(compiler): no-collision vegetation class — plants are not floors,…
stellarfeline Aug 4, 2026
2602ed0
feat(compiler): cliff-band degradation, inner-slope blossom, spawn-ey…
stellarfeline Aug 4, 2026
29e9432
feat(compiler): annulus band floor replaces cliff-band degradation (o…
stellarfeline Aug 4, 2026
c32ce83
docs(compiler): purge stale cliff-band wording after band-floor ruling
stellarfeline Aug 4, 2026
7c61455
docs(compiler): DW0141 reserved-base list drops valley/cherry-valley …
stellarfeline Aug 4, 2026
f4517c8
feat(compiler,render): cherry snapshot palette, vista FOV physics, sc…
stellarfeline Aug 4, 2026
649a164
fix(compiler): the valley moat floors elevated footprints (task #157 …
stellarfeline Aug 4, 2026
477f259
fix(compiler,harness): the wave countdown is a liveness census, not a…
stellarfeline Aug 5, 2026
8f88b31
Merge the spec-0026 foundation (with the flood verb) into the valley …
stellarfeline Aug 8, 2026
c314024
Merge the branch's own remote tip; another clean-but-broken merge
stellarfeline Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions crates/compiler/src/assembled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,109 @@ pub fn collision_top_16(name: &str) -> u8 {
if matches!(id, "dirt_path" | "farmland") {
return 15;
}
if is_no_collision_plant(id) {
return 0;
}
FULL_HEIGHT_16
}

/// Vanilla's **no-collision vegetation class**: blocks whose collision shape is
/// EMPTY — a walker passes straight through and stands on whatever is below
/// (they are visual/light-model content only). Found by the hollow-vigil
/// construction round (task #157): the pre-fix fallback modelled these as full
/// cubes, so a `short_grass` tuft on a valley terrace became a phantom
/// standable cell that split a 2-block riser into two climbable 1-block steps
/// — `DW0369` fired on a world that is NOT climbable in vanilla
/// (rejects-valid), and, worse, any walkability proof that ever stood a player
/// ON a tuft/flower cell was unsound (accepts-invalid).
///
/// The list is the **class**, not the three ids the valley generator happens
/// to scatter (fixing only those would be folklore). Sources: Minecraft Java
/// 1.21.11 block shapes — every id here has an empty collision shape.
/// Deliberately excluded because they DO collide (or attach in ways this model
/// does not represent): `azalea`/`flowering_azalea`, `big_dripleaf`, `bamboo`,
/// `cactus`, `chorus_*`, `pointed_dripstone`, `scaffolding`, `sea_pickle`,
/// `cocoa`, lily `pad` (a platform), all leaves, and anything not certainly
/// collision-free — the conservative full-cube default keeps those sound.
pub fn is_no_collision_plant(id: &str) -> bool {
id.ends_with("_sapling")
|| matches!(
id,
// grasses + ground cover
"short_grass"
| "tall_grass"
| "fern"
| "large_fern"
| "dead_bush"
| "bush"
| "firefly_bush"
| "short_dry_grass"
| "tall_dry_grass"
| "seagrass"
| "tall_seagrass"
| "pink_petals"
| "wildflowers"
| "leaf_litter"
| "hanging_roots"
| "mangrove_propagule"
// small + tall flowers
| "dandelion"
| "poppy"
| "blue_orchid"
| "allium"
| "azure_bluet"
| "red_tulip"
| "orange_tulip"
| "white_tulip"
| "pink_tulip"
| "oxeye_daisy"
| "cornflower"
| "lily_of_the_valley"
| "wither_rose"
| "torchflower"
| "sunflower"
| "lilac"
| "rose_bush"
| "peony"
| "pitcher_plant"
// mushrooms + nether flora
| "brown_mushroom"
| "red_mushroom"
| "crimson_fungus"
| "warped_fungus"
| "crimson_roots"
| "warped_roots"
| "nether_sprouts"
| "nether_wart"
// crops
| "wheat"
| "carrots"
| "potatoes"
| "beetroots"
| "melon_stem"
| "pumpkin_stem"
| "attached_melon_stem"
| "attached_pumpkin_stem"
| "torchflower_crop"
| "sweet_berry_bush"
| "sugar_cane"
| "bamboo_sapling"
// climbing / hanging plants
| "vine"
| "glow_lichen"
| "spore_blossom"
| "small_dripleaf"
| "kelp"
| "kelp_plant"
| "cave_vines"
| "cave_vines_plant"
| "twisting_vines"
| "twisting_vines_plant"
| "weeping_vines"
| "weeping_vines_plant"
)
}

/// Whether a block is thin enough to be **walked over rather than onto**
/// ([`THIN_HEIGHT_16`]): its cell is passable and never a floor level of its own,
/// so a walker standing there rests on the block below it. Vanilla agrees — none
Expand Down Expand Up @@ -572,6 +672,29 @@ fn placed_blocks(
}
}
}
// Horizon surround tiles (spec-0026 §5): placed pieces like any other —
// the voxel model, gravity settle, nav and every downstream proof see
// them. Unrotated, sealless, and outside every scene AABB by construction.
if let Some(surround) = &plan.surround {
for piece in &surround.pieces {
let Some(bytes) = structures.get(&piece.structure_file) else {
continue;
};
for (local, name, open) in structure_cells_stateful(bytes) {
let cell = [
piece.pos[0] + local[0],
piece.pos[1] + local[1],
piece.pos[2] + local[2],
];
if is_fence_gate(&name) && open == Some(true) {
open_gates.insert(cell);
} else {
open_gates.remove(&cell);
}
blocks.insert(cell, name);
}
}
}
// Gate thresholds are passable (an open-gate effect fills them with air).
for resolved in plan.anchors.values() {
if let ResolvedAnchor::Gate { from, to, .. } = resolved {
Expand Down Expand Up @@ -1811,6 +1934,74 @@ mod tests {
assert_eq!(collision_top_16("minecraft:oak_stairs[facing=north]"), 16);
}

/// Task #157 (hollow-vigil round finding 1): vanilla no-collision
/// vegetation has an EMPTY collision shape — modelling it as a full cube
/// created phantom standable cells (a tuft split a valley terrace's
/// 2-block riser into two climbable 1-block steps → false `DW0369`) and
/// let walkability proofs stand players on flowers (unsound). The class is
/// pinned here so no future palette id regresses to the full-cube
/// fallback silently.
#[test]
fn no_collision_plants_have_an_empty_collision_shape() {
for id in [
"minecraft:short_grass",
"minecraft:tall_grass",
"minecraft:fern",
"minecraft:large_fern",
"minecraft:pink_petals",
"minecraft:poppy",
"minecraft:oxeye_daisy",
"minecraft:cornflower",
"minecraft:dandelion",
"minecraft:dead_bush",
"minecraft:oak_sapling",
"minecraft:cherry_sapling",
"minecraft:wheat[age=7]",
"minecraft:sweet_berry_bush",
"minecraft:vine",
"minecraft:glow_lichen",
"minecraft:seagrass",
"minecraft:sugar_cane",
] {
assert_eq!(collision_top_16(id), 0, "{id} must have no collision");
assert!(is_thin_decoration(id), "{id} is walked through");
}
// The lookalikes that DO collide stay conservative full cubes.
for id in [
"minecraft:azalea",
"minecraft:big_dripleaf",
"minecraft:bamboo",
"minecraft:cactus",
"minecraft:pointed_dripstone",
"minecraft:oak_leaves[persistent=true]",
"minecraft:sea_pickle",
] {
assert_eq!(collision_top_16(id), 16, "{id} must keep collision");
}
}

/// The occupancy consequence of the class: a tuft/flower/petal cell is
/// passable air for the walker — the standable surface is the block BELOW
/// it, never the plant itself.
#[test]
fn plants_are_not_floors_and_not_obstacles() {
let mut b = floor(63, 0, 2, 0, 0);
b.insert([0, 64, 0], "minecraft:short_grass".to_string());
b.insert([1, 64, 0], "minecraft:pink_petals".to_string());
b.insert([2, 64, 0], "minecraft:fern".to_string());
let occ = occupancy_of(b, &BTreeSet::new());
for c in [[0, 64, 0], [1, 64, 0], [2, 64, 0]] {
assert!(
!occ.solid.contains(&c) && !occ.tall.contains(&c),
"{c:?} must be passable — a plant is not an obstacle"
);
assert!(
!occ.partial.contains_key(&c),
"{c:?} must not be a floor level of its own"
);
}
}

#[test]
fn partial_floors_are_solid_and_thin_decoration_is_passable() {
let mut b = floor(63, 0, 3, 0, 0);
Expand Down
8 changes: 4 additions & 4 deletions crates/compiler/src/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,7 @@ fn coalesce_commands(writes: &BTreeMap<[i32; 3], String>) -> Vec<String> {
const DEFAULT_NOISE_SCALE: f64 = 0.35;

/// splitmix64 finalizer (the generators' `mix64`).
fn mix64(mut z: u64) -> u64 {
pub(crate) fn mix64(mut z: u64) -> u64 {
z = z.wrapping_add(0x9E37_79B9_7F4A_7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
Expand All @@ -2092,7 +2092,7 @@ fn mix64(mut z: u64) -> u64 {

/// White noise in `[0, 1)` at an integer lattice point (the generators'
/// `hash01`, verbatim).
fn hash01(seed: u64, x: i32, y: i32, z: i32, salt: u64) -> f64 {
pub(crate) fn hash01(seed: u64, x: i32, y: i32, z: i32, salt: u64) -> f64 {
let mut h = seed ^ salt.wrapping_mul(0x9E37_79B9_7F4A_7C15);
h = mix64(h ^ (x as i64 as u64).wrapping_mul(0x0000_0100_0000_01B3));
h = mix64(h ^ (y as i64 as u64).wrapping_mul(0xFF51_AFD7_ED55_8CCD));
Expand All @@ -2101,7 +2101,7 @@ fn hash01(seed: u64, x: i32, y: i32, z: i32, salt: u64) -> f64 {
}

/// Smoothstep fade.
fn fade(t: f64) -> f64 {
pub(crate) fn fade(t: f64) -> f64 {
t * t * (3.0 - 2.0 * t)
}

Expand All @@ -2112,7 +2112,7 @@ fn lerp(a: f64, b: f64, t: f64) -> f64 {

/// Trilinearly-interpolated value noise in `[0, 1]` — smooth, so palette picks
/// cluster into patches (the generators' `value_noise`, verbatim).
fn value_noise(seed: u64, x: i32, y: i32, z: i32, freq: f64, salt: u64) -> f64 {
pub(crate) fn value_noise(seed: u64, x: i32, y: i32, z: i32, freq: f64, salt: u64) -> f64 {
let (fx, fy, fz) = (x as f64 * freq, y as f64 * freq, z as f64 * freq);
let (x0, y0, z0) = (fx.floor(), fy.floor(), fz.floor());
let (tx, ty, tz) = (fade(fx - x0), fade(fy - y0), fade(fz - z0));
Expand Down
Loading
Loading