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 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 00:50:22 +04:00
parent 148692f796
commit eb54acf727
+59 -7
View File
@@ -32,10 +32,21 @@ struct Args {
/// Map id; defaults to the first spawn's, else 0 (Eastern Kingdoms).
#[arg(long)]
map: Option<u32>,
/// 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<Vec<f32>>,
/// 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<f32> = s
.split(',')
.map(|p| p.trim().parse::<f32>().map_err(|e| format!("`{p}`: {e}")))
.collect::<Result<_, _>>()?;
v.try_into()
.map_err(|v: Vec<f32>| 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<f32> = 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());
}
}