diff --git a/context/combat-damage.md b/context/combat-damage.md index 1edf23c..13e40d9 100644 --- a/context/combat-damage.md +++ b/context/combat-damage.md @@ -1012,6 +1012,16 @@ code involved; grammar = the binary ctors @0x49ea48/@0x49e5e4). Results: "Right*", W1/W2 = "Front*" -- with theta = atan2(z,x) from +X toward +Z, the mech-local FRONT is the **+Z arc (theta 45-135 deg)** and dead-ahead is the W1|W2 SEAM. (Corrects the earlier #92 comment that put frontal hits in W6.) +* **⚠ PORT FRAME ADAPTER (#124, fixed 2026-08-03)**: that +Z-forward is the + **1995 resolver frame**. OUR engine's entity frame carries forward along + **−Z** (bench-measured: a victim facing its attacker took frontal beams at + local z=−4..−10, θ≈270°, resolving the REAR-named cells — which was the whole + night-10 field table: rear panels unhittable, Owens flanks CROSSED). The + frames differ by a **π rotation about Y** (rotation, not reflection — the + flank observations cross rather than persist), so `ResolveHit` negates + local x and z after `WorldToLocal` before the band/wedge math. A/B verified: + frontal fire θ≈1.5 → utorso/dtorso/ltorso/rtorso, zero rear zones. The + separate "one band LOW" Y-signature is #16 (pick-ray parallax), not this. Frontal unaimed foot-band hits therefore straddle the RightFoot/LeftFoot cells by impact-x sign, and the authored cross-bleed (20% opposite foot, 10+10% opposite leg) is a data-true source of "hit the opposite leg" reports. diff --git a/game/reconstructed/dmgtable.cpp b/game/reconstructed/dmgtable.cpp index fe637e8..f8ce4b0 100644 --- a/game/reconstructed/dmgtable.cpp +++ b/game/reconstructed/dmgtable.cpp @@ -277,6 +277,22 @@ int Scalar heightRef = owner->CylinderReferenceHeight(); // owner+0x2ec[+0xc] if (heightRef <= 0.0f) return -1; + // PORT FRAME ADAPTER (#124, measured 2026-08-03). The 1995 resolver frame + // carries the mech's FORWARD along +Z: the authored wedge ring puts the + // FrontChest-family cells in the +Z arc (slice-name audit [T1]) -- frontal + // hits MUST read z > 0 for the shipped tables to mean what they say. Our + // engine's entity frame carries forward along -Z (bench 2026-08-03: the + // dummy facing its attacker takes frontal beams at local z = -4..-10, + // theta ~270 deg, resolving the REAR-named cells; the night-10 field table + // -- rear panels unhittable, Owens left torso -> RIGHT pod -- is this same + // pi ROTATION at every aspect: rotation, not reflection, because the + // left/right flanks CROSS rather than persist). Express the impact in the + // cylinder's frame: rotate pi about Y. y (the band axis) is untouched, + // and the frames share handedness, so the torso-twist add in SelectSlice + // keeps its sign. + local.x = -local.x; + local.z = -local.z; + // --- height -> layer (penetration depth) --- Scalar depth = local.y; // binary local_2c int layerIndex = (int)floorf((Scalar)layerCount * (depth / heightRef)); @@ -305,7 +321,9 @@ int { static int s_rh = 0; if (s_rh++ < 400) - DEBUG_STREAM << "[dmgresolve] localY=" << local.y + DEBUG_STREAM << "[dmgresolve] impact=(" << impact.x << "," + << impact.y << "," << impact.z << ")" + << " local=(" << local.x << "," << local.y << "," << local.z << ")" << " heightRef=" << heightRef << " frac=" << (depth / heightRef) << " layer=" << layerIndex << "/" << layerCount