From eb54acf727e472be094ea657f08710d2562edc62 Mon Sep 17 00:00:00 2001 From: sindoring Date: Sun, 18 Oct 2026 00:50:22 +0400 Subject: [PATCH] Accept negative --at coordinates; probe the pick at given points `--at -8445,320,135` failed: clap read the leading minus of the separate values as a flag. It is now one x,y,z value that may start with a minus. EDITOR_PROBES casts the cursor's walkable-surface ray down at WoW points beside EDITOR_SHOT. Run at 11 Stormwind spawns on WMO floors (the Keep, the Mage Quarter tower's top storey, the Trade District), it hits 0 to 0.21 yd under the z the server stores, on the spawn's own storey. Co-Authored-By: Claude Opus 5.5 --- crates/editor-app/src/main.rs | 66 +++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/crates/editor-app/src/main.rs b/crates/editor-app/src/main.rs index a831d5b..13060ae 100644 --- a/crates/editor-app/src/main.rs +++ b/crates/editor-app/src/main.rs @@ -32,10 +32,21 @@ struct Args { /// Map id; defaults to the first spawn's, else 0 (Eastern Kingdoms). #[arg(long)] map: Option, - /// Where the camera opens, WoW `x,y,z`; defaults to the first spawn on the map, else - /// Northshire. - #[arg(long, value_delimiter = ',', num_args = 3)] - at: Option>, + /// Where the camera opens, WoW `x,y,z` (`--at -8445,320,135`); defaults to the first spawn on + /// the map, else Northshire. + #[arg(long, allow_hyphen_values = true, value_parser = parse_point)] + at: Option<[f32; 3]>, +} + +/// `x,y,z` as three floats. One value, not three: WoW coordinates are mostly negative, and clap +/// reads a leading `-` in a separate value as a flag. +fn parse_point(s: &str) -> Result<[f32; 3], String> { + let v: Vec = s + .split(',') + .map(|p| p.trim().parse::().map_err(|e| format!("`{p}`: {e}"))) + .collect::>()?; + v.try_into() + .map_err(|v: Vec| format!("want x,y,z, got {} value(s)", v.len())) } fn main() -> anyhow::Result<()> { @@ -55,9 +66,9 @@ fn main() -> anyhow::Result<()> { .map .or_else(|| project.creatures.values().next().map(|c| c.map)) .unwrap_or(0); - let start = match args.at.as_deref() { - Some([x, y, z]) => [*x, *y, *z], - _ => project + let start = match args.at { + Some(at) => at, + None => project .creatures .values() .find(|c| c.map == map) @@ -186,6 +197,30 @@ fn shoot_and_exit( benilla_assets::coords::bevy_to_wow(c.translation() + *dir * h.distance) }) }); + // `EDITOR_PROBES=x,y,z;…`: the cursor's cast straight down from 3 yd above each WoW + // point, e.g. a server spawn on a WMO floor, so the pick is measured against the + // height the server stores. + for probe in std::env::var("EDITOR_PROBES") + .unwrap_or_default() + .split(';') + { + let p: Vec = probe + .split(',') + .filter_map(|v| v.trim().parse().ok()) + .collect(); + let [x, y, z] = p[..] else { continue }; + let hit = collision + .ray_body( + benilla_assets::coords::wow_to_bevy([x, y, z + 3.0]), + Dir3::NEG_Y, + 50.0, + ) + .map(|h| z + 3.0 - h.distance); + info!( + "EDITOR_PROBE at=[{x}, {y}, {z}] hit_z={hit:?} dz={:?}", + hit.map(|h| h - z) + ); + } let cam = cam .single() .map(|c| benilla_assets::coords::bevy_to_wow(c.translation())); @@ -208,3 +243,20 @@ fn shoot_and_exit( _ => {} } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn at_takes_negative_coordinates() { + for argv in [ + ["e", "p.json", "--at", "-8445,320,135"], + ["e", "p.json", "--at=-8445,320,135", "--map=0"], + ] { + let args = Args::try_parse_from(argv.iter().filter(|a| !a.is_empty())).unwrap(); + assert_eq!(args.at, Some([-8445.0, 320.0, 135.0])); + } + assert!(Args::try_parse_from(["e", "p.json", "--at", "1,2"]).is_err()); + } +}