Files
benilla-octo/crates/benilla-formats/examples/impass_here.rs
T
Sam SungandClaude Opus 5.5 8da634afa9 Comments in benilla-formats: one line of fact each, no history, no record numbers (#130)
Part of #41: the comments of `benilla-formats` (the library, its extract tools, examples and tests) are rewritten to the rule in `docs/METHOD.md`, which is what the code does and the reference fact behind it, in a line. 19,654 comment lines become 7,264.

Fixes #105: the WMO collision comments now name walking's box-query leaf `0x6bca50` and the camera and line-of-sight segment-query leaf `0x6bc700`, in `models/collision.rs`, `examples/wmo_bsp.rs` and `tests/chain.rs`.

- **The mechanical strip.** The citations that were only a private record number go first, and each edit is shown to be a pure removal.
- **Wrong facts corrected.** The rewrite corrects the comments that stated wrong facts. Among them:
  - A placed doodad's material alpha reads file slot 0 on its spawn clock, while the reference reads bone 0's armed sequence, re-rolled every play window. The old text said doodads are armed once at load.
  - The fog end is stored ×36 and scaled once at load (`0x6d6090`). It is not raw.
  - The MCSH bit picks a map doodad's sun scale, 1.0 lit or 0.5 shadowed. 2.5 belongs to an entity's light node.
  - `SpellRec` is the row as stored, column × 4, not a translated copy.
  - A global sequence's clock is anchored at the instance's attach time (`0x714352`).
  - Particle twinkle is a gated size multiplier (`0x7b2adc`).
  - The blob shadow sizes from the Stand box.
  - Data claims are checked against the install: the weapon-trail kits' parameters and alphas, the beam rows' noise, and the maps' `field_16`.
  - Dozens more turned up on the way: renamed or deleted items, docs sitting on the wrong item, stale counts and links.
- **Deviation labels.** "Deviation:" now marks only a choice with a reason. An unbuilt behaviour says so plainly.
- **Verification.** Every file strips to identical code, and `cargo fmt --check` is clean. Four second readers compared every file old against new, restored the facts the rewrite had dropped, and checked each new citation against the reference, the code and the shipped data.

String literals are untouched; they are #92.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 19:38:53 +07:00

121 lines
4.6 KiB
Rust

//! A top-down map of the MCNK impassable flag (header flag bit 1) around a pin: north up, one
//! glyph per 33.333 yd chunk, across tile seams, since a band crosses them. `what_is_here` covers
//! models. Output is Blizzard data: never commit it.
//! `cargo run -p benilla-formats --example impass_here -- <map> <x> <y> [radius_chunks]`
use std::collections::HashMap;
use benilla_formats::{impassable_at, world_to_tile, TileMesh, CHUNK_SIZE, TILE_SIZE};
/// The map's NW corner: world x and y fall from 32 tiles as the grid runs south and east.
const MAP_CENTER: f32 = 32.0 * TILE_SIZE;
/// Global chunk index of a world coordinate on either axis, row from x and column from y, falling
/// from `MAP_CENTER` as the client's per-chunk lookups do (`0x69b350`).
fn chunk_index(coord: f32) -> i32 {
((MAP_CENTER - coord) / CHUNK_SIZE).floor() as i32
}
fn chunk_centre(index: i32) -> f32 {
MAP_CENTER - (index as f32 + 0.5) * CHUNK_SIZE
}
fn main() -> anyhow::Result<()> {
let mut args = std::env::args().skip(1);
let usage = || anyhow::anyhow!("usage: impass_here <map> <x> <y> [radius_chunks]");
let map = args.next().ok_or_else(usage)?;
let x: f32 = args.next().ok_or_else(usage)?.parse()?;
let y: f32 = args.next().ok_or_else(usage)?.parse()?;
let radius: i32 = args.next().map_or(Ok(8), |r| r.parse())?;
let data = benilla_formats::wow_data().expect("no WoW install found (set $WOW_DATA)");
let mut chain = benilla_formats::open_chain(&data)?;
let mut tiles: HashMap<(u32, u32), Option<TileMesh>> = HashMap::new();
let mut flag_at = |chain: &mut _, sx: f32, sy: f32| -> Option<bool> {
let (tx, ty) = world_to_tile(sx, sy);
let tile = tiles
.entry((tx, ty))
.or_insert_with(|| benilla_formats::load_tile_mesh(chain, &map, tx, ty).ok());
impassable_at(&tile.as_ref()?.chunks, [sx, sy, 0.0])
};
let (pin_row, pin_col) = (chunk_index(x), chunk_index(y));
let (ptx, pty) = world_to_tile(x, y);
println!(
"{map}: pin ({x}, {y}) is tile {ptx}_{pty}, chunk (col {}, row {}) of that tile",
pin_col.rem_euclid(16),
pin_row.rem_euclid(16),
);
println!("global chunk (row {pin_row}, col {pin_col}); {CHUNK_SIZE:.3} yd per glyph\n");
// North up (rows step south = falling x), west left (columns step east = falling y).
println!(" '#' impassable · '.' passable · '@' the pin's chunk · ' ' no tile");
print!(" ");
for dc in -radius..=radius {
// A tile seam every 16 chunks.
print!(
"{}",
if (pin_col + dc).rem_euclid(16) == 0 {
'|'
} else {
' '
}
);
}
println!();
for dr in -radius..=radius {
let row = pin_row + dr;
print!("{:5} ", row);
for dc in -radius..=radius {
let col = pin_col + dc;
let glyph = match flag_at(&mut chain, chunk_centre(row), chunk_centre(col)) {
_ if (row, col) == (pin_row, pin_col) => '@',
Some(true) => '#',
Some(false) => '.',
None => ' ',
};
print!("{glyph}");
}
println!();
}
// The pin's own chunk, and the distance to the wall in each cardinal direction.
let pin_flag = flag_at(&mut chain, x, y);
println!(
"\npin chunk: {}",
match pin_flag {
Some(true) => "IMPASSABLE (the pin is inside the band)",
Some(false) => "passable",
None => "no tile loaded here",
}
);
for (name, dx, dy) in [
("north (+x)", 1.0, 0.0),
("south (-x)", -1.0, 0.0),
("west (+y)", 0.0, 1.0),
("east (-y)", 0.0, -1.0),
] {
// Step chunk by chunk until the flag flips; a mover stops at that chunk's near boundary.
let hit = (1..=radius).find(|n| {
let (sx, sy) = (
x + dx * *n as f32 * CHUNK_SIZE,
y + dy * *n as f32 * CHUNK_SIZE,
);
flag_at(&mut chain, sx, sy) == Some(true)
});
match hit {
Some(n) => {
let index =
chunk_index(if dx != 0.0 { x } else { y }) + if dx + dy > 0.0 { -n } else { n };
let near_edge = MAP_CENTER
- (if dx + dy > 0.0 { index + 1 } else { index }) as f32 * CHUNK_SIZE;
let d = (near_edge - if dx != 0.0 { x } else { y }).abs();
println!(" {name}: impassable chunk {n} away, wall at {d:.2} yd");
}
None => println!(" {name}: clear for {radius} chunks"),
}
}
Ok(())
}