use mercs2_engine::wad;
use mercs2_formats::hash::pandemic_hash_m2 as m2;
const ACTOR_ORIGIN: [f32; 3] = [3750.0, 450.0, -3840.0];
#[test]
fn interior_hardpoint_derives_the_spawn() {
let Some(path) = wad::resolve_vz_wad(None) else {
return eprintln!(
"SKIPPING: no vz.wad discovered. Run `scripts/find-vz-wad.sh --write` or set MERCS2_GAME_DIR."
);
};
let mut w = wad::open(&path).expect("open vz.wad");
for mesh_name in ["pmcoutpost_interior_hq", "proutpost_interior_job"] {
let hash = m2(mesh_name);
let Ok(container) = wad::extract_container(&mut w, hash) else {
println!("{mesh_name} (0x{hash:08X}): container not found");
continue;
};
let hier = mercs2_formats::orchestrator::parse_hier(&container);
println!("\n{mesh_name} (0x{hash:08X}): {} HIER nodes", hier.len());
let hp_enter = m2("hp_playerA_enter");
if let Some(idx) = hier.iter().position(|n| n.hash == hp_enter) {
let root = node_root_matrix(&hier, idx);
let t = [root[12], root[13], root[14]]; let world = [ACTOR_ORIGIN[0] + t[0], ACTOR_ORIGIN[1] + t[1], ACTOR_ORIGIN[2] + t[2]];
println!(
" hp_playerA_enter root-relative=({:.2},{:.2},{:.2}) -> actor+hp = ({:.2},{:.2},{:.2})",
t[0], t[1], t[2], world[0], world[1], world[2]
);
println!(" [vanilla _TeleportHero = 3794.04, 450.75, -3911.03]");
if mesh_name == "pmcoutpost_interior_hq" {
assert!((world[0] - 3794.04).abs() < 2.0, "hp_playerA_enter X derives from mesh data");
}
} else {
println!(" hp_playerA_enter NOT in this mesh's HIER");
}
}
}
fn node_root_matrix(hier: &[mercs2_formats::orchestrator::HierNode], idx: usize) -> [f32; 16] {
let mut m = hier[idx].local;
let mut p = hier[idx].parent;
while let Some(pi) = p {
m = mat4_mul_rowmajor(&m, &hier[pi].local);
p = hier[pi].parent;
}
m
}
fn mat4_mul_rowmajor(a: &[f32; 16], b: &[f32; 16]) -> [f32; 16] {
let mut o = [0.0f32; 16];
for r in 0..4 {
for c in 0..4 {
let mut s = 0.0;
for k in 0..4 {
s += a[r * 4 + k] * b[k * 4 + c];
}
o[r * 4 + c] = s;
}
}
o
}