- The main menu bar's two empty recesses fill. The micro menu — the eight arched buttons: character, spellbook, talents, quest log, socials, world map, main menu, help — sits in the dark panel between the page arrows and the bags, transcribed from the reference's own layout: the character button wears your portrait, a button holds itself down while its panel is up, and the talent button appears at level 10. The latency meter fills the one slot the bar art leaves transparent for exactly it, tinted green/yellow/red on the reference's 300/600 ms thresholds and drawn UNDER the bar art, seen through its window, the way 1.12 actually does it. The socials and main-menu buttons find their panels one snapshot later; help still waits. - The UI explains itself on hover: 1.12 ships its detailed two-line tooltips ON — a FrameXML default, not the opt-in extra it is often remembered as — so the micro buttons, your own portrait, the XP bar, the five bag buttons and the ping meter all answer a hover with the reference's own explanations. - The PvP flag, end to end: crossed swords on the player, target and party frames, driven by the two descriptor bits the wire actually carries, and `/pvp` speaks — the yellow toast plus the system chat line, the OFF sentence being the one that warns the flag lingers for five minutes. It really does linger: turning it off waits out the server's timer, which looks like a bug and is the reference. - Quest markers stopped freezing. The grey `!` over a questgiver above your level goes gold the moment you ding, an NPC re-entering view is asked afresh instead of wearing a session-old answer, and the marker drops when the questgiver bit goes off — the re-ask triggers mirror the reference's own set, plus one named addition (your own level changing). - Water and heights: swimming downhill no longer bounces between swim and fall ten times a second — the surface line is a two-sided constraint, so the swimmer settles with the river instead of being left behind by it. And collision height is per-unit now, read from the unit's own model: a gnome used to swim with a third of a yard of water over her head because five depth lines were quoting a human's number. - A round of pixel honesty in text and chat: the newest chat line no longer wears a wider drop shadow than every line above it (shadows move in whole pixels now), keeps its descenders, and hover boxes stop painting over the chat body. The chat input's history browse survives programmatic edits, a failed script chunk warns instead of vanishing, and recall deepens to 64 lines. Idle animation got honest too: a model's idle is Stand resolved through its own lookup table — never file order, which had the duel flag bobbing on its spawn clip — and an armed idle is culled on the box its animation authors, not the bind pose, so the flag stops vanishing at most camera angles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
69 lines
3.1 KiB
Rust
69 lines
3.1 KiB
Rust
//! What is actually standing at a world coordinate:
|
|
//! `cargo run -p benilla-formats --example what_is_here -- <map> <x> <y> [radius_yd]`
|
|
//! e.g. `what_is_here Kalimdor 295.72 -3689.61 60`.
|
|
//!
|
|
//! A bug report names a *place* — a `.go xyz` pin — and every scene-render diagnosis starts by
|
|
//! turning that pin into the **placements** behind it: which WMOs and which M2 doodads, with their
|
|
//! distance from the pin. The distinction is load-bearing for draw-order defects: two batches of one
|
|
//! WMO share the authored MOBA order the pipeline biases by, while a WMO and a nearby M2 doodad are
|
|
//! separate lanes with no ordering relationship at all.
|
|
//!
|
|
//! Output is Blizzard data — pipe it to the scratchpad, never into the repo.
|
|
|
|
fn main() -> anyhow::Result<()> {
|
|
let mut args = std::env::args().skip(1);
|
|
let usage = || anyhow::anyhow!("usage: what_is_here <map> <x> <y> [radius_yd]");
|
|
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: f32 = args.next().map_or(Ok(60.0), |r| r.parse())?;
|
|
|
|
let data = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../WoW/Data");
|
|
let mut chain = benilla_formats::open_chain(&data)?;
|
|
let tiles = benilla_formats::MapTiles::load(&mut chain, &map)?;
|
|
let (tx, ty) = tiles.tile_at(x, y);
|
|
println!("{map}: pin ({x}, {y}) is tile {tx}_{ty}; scanning r={radius} yd across 3x3");
|
|
|
|
// The pin can sit near a tile seam and the building it names be authored in the neighbour, so
|
|
// sweep the 3x3 — a placement is deduped by uniqueId, which is stable across the tiles it spans.
|
|
let mut seen_wmo = std::collections::HashSet::new();
|
|
let mut seen_doodad = std::collections::HashSet::new();
|
|
let mut wmos: Vec<(f32, String, [f32; 3], u32)> = Vec::new();
|
|
let mut doodads: Vec<(f32, String, [f32; 3], f32, u32)> = Vec::new();
|
|
let dist = |p: [f32; 3]| ((p[0] - x).powi(2) + (p[1] - y).powi(2)).sqrt();
|
|
|
|
for (tx, ty) in tiles.existing_in_radius(x, y, 1) {
|
|
let tile = benilla_formats::load_tile_mesh(&mut chain, &map, tx, ty)?;
|
|
for w in &tile.wmos {
|
|
let d = dist(w.position);
|
|
if d <= radius && seen_wmo.insert(w.unique_id) {
|
|
wmos.push((d, w.model.clone(), w.position, w.unique_id));
|
|
}
|
|
}
|
|
for m in &tile.doodads {
|
|
let d = dist(m.position);
|
|
if d <= radius && seen_doodad.insert(m.unique_id) {
|
|
doodads.push((d, m.model.clone(), m.position, m.scale, m.unique_id));
|
|
}
|
|
}
|
|
}
|
|
wmos.sort_by(|a, b| a.0.total_cmp(&b.0));
|
|
doodads.sort_by(|a, b| a.0.total_cmp(&b.0));
|
|
|
|
println!("\n=== WMOs ({}) ===", wmos.len());
|
|
for (d, model, p, id) in &wmos {
|
|
println!(
|
|
"{d:7.1} yd #{id:<10} ({:.2}, {:.2}, {:.2}) {model}",
|
|
p[0], p[1], p[2]
|
|
);
|
|
}
|
|
println!("\n=== doodads ({}) ===", doodads.len());
|
|
for (d, model, p, scale, id) in &doodads {
|
|
println!(
|
|
"{d:7.1} yd #{id:<10} x{scale:.2} ({:.2}, {:.2}, {:.2}) {model}",
|
|
p[0], p[1], p[2]
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|