#124 CORRECTION: the frame adapter is a Z-REFLECTION, not a pi rotation --

proven by the mech's own pods

The first fix (c5dfb00) negated x AND z. The operator challenged it:
'they couldn't hit rear panels AT ALL -- a 180 rotation couldn't explain
that' -- and a rotation would also have relocated rear damage, not
erased it, while the bench premise (the dummy faces its attacker) was
assumed, never measured.

The zero-premise probe (BT_ASPECT_TEST): four self-impacts at known
WORLD cardinals + one at each weapon's PHYSICAL MUZZLE position -- the
mech's own asymmetric geometry as the left/right anchor. Measured:

* WorldToLocal is a clean R(yaw), no hidden terms (yaws -30/150/40 deg
  all consistent);
* our frame: RIGHT = +X -- SAME as the 1995 ring (W0/W7 = Right*) --
  and FORWARD = -Z, flipped vs the ring's Front*-in-+Z-arc;
* under the pi rotation the pods CROSSED (left muzzle -> rtorso);
  under z-negation every anchor lands its own side.

So: negate local z only, and -- since a reflection reverses angular
direction -- the SelectSlice torso-twist term flips sign with it
(twist was 0 in all probes; twisted-torso verify tracked on #124).

Anchor acceptance (fresh exe, third yaw): LRM15_1 left pod -> ltorso,
LRM15_2 -> right wedges, left/right arm muzzles -> larm/rarm, nose ->
front cells, tail -> rear cells, flank cardinals -> rgun / left wedges.

The BT_ASPECT_TEST probe + BTWeaponMuzzleWorld bridge stay as bench
scalpels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-08-03 09:05:35 -05:00
co-authored by Claude Fable 5
parent c5dfb002f3
commit b0b7adc4ea
4 changed files with 114 additions and 24 deletions
+68
View File
@@ -6218,6 +6218,74 @@ void
}
}
// #124 PROBE (BT_ASPECT_TEST=1): the zero-premise frame probe. At frame
// ~700, dispatch four self TakeDamage messages whose impact points sit
// at KNOWN WORLD BEARINGS (+X/-X/+Z/-Z, 8 u out, torso height) around
// our OWN mech, logging our yaw + each resolve. With the mech's
// heading known and torso twist zero, the theta each bearing produces
// measures the WorldToLocal frame convention directly -- no facing
// premises, no shooter geometry.
if ((Entity *)this == application->GetViewpointEntity()
&& getenv("BT_ASPECT_TEST"))
{
static int s_apFrame = 0;
if (++s_apFrame == 700)
{
YawPitchRoll ypr; ypr = localOrigin.angularPosition;
DEBUG_STREAM << "[aspect] mech yaw=" << (float)ypr.yaw
<< " twist=" << (float)TorsoHeading()
<< " pos=(" << localOrigin.linearPosition.x << ","
<< localOrigin.linearPosition.z << ")\n" << std::flush;
static const struct { const char *tag; float dx, dz; } kProbe[4] =
{ {"+X", 8, 0}, {"-X", -8, 0}, {"+Z", 0, 8}, {"-Z", 0, -8} };
for (int pi = 0; pi < 4; ++pi)
{
Damage dmg;
dmg.damageType = (Enumeration)3; // Laser
dmg.damageAmount = 0.01f; // negligible
dmg.burstCount = 1;
dmg.impactPoint = localOrigin.linearPosition;
dmg.impactPoint.x += kProbe[pi].dx;
dmg.impactPoint.y += 7.0f; // torso height
dmg.impactPoint.z += kProbe[pi].dz;
DEBUG_STREAM << "[aspect] probe " << kProbe[pi].tag
<< " ->\n" << std::flush;
Entity::TakeDamageMessage td(
Entity::TakeDamageMessageID, sizeof(Entity::TakeDamageMessage),
GetEntityID(), -1, dmg, -1);
Dispatch(&td);
}
// SELF-ANCHORED L/R probe: impact each weapon's PHYSICAL muzzle
// position -- the pod's own asymmetric geometry is the anchor.
// A right-side launcher's muzzle impact must resolve a
// right-family zone; which flip (z-only reflection vs full pi
// rotation) achieves that decides the adapter's x term.
{
extern int BTWeaponMuzzleWorld(Subsystem *w, Point3D *out); // mechweap TU
for (int wi = 2; wi < GetSubsystemCount(); ++wi)
{
Subsystem *w = GetSubsystem(wi);
Point3D mzp;
if (w == 0 || !BTWeaponMuzzleWorld(w, &mzp))
continue;
Damage dmg;
dmg.damageType = (Enumeration)3;
dmg.damageAmount = 0.01f;
dmg.burstCount = 1;
dmg.impactPoint = mzp;
DEBUG_STREAM << "[aspect] muzzle probe '"
<< (w->GetName() ? w->GetName() : "?")
<< "' at (" << mzp.x << "," << mzp.y << "," << mzp.z
<< ") ->\n" << std::flush;
Entity::TakeDamageMessage td(
Entity::TakeDamageMessageID, sizeof(Entity::TakeDamageMessage),
GetEntityID(), -1, dmg, -1);
Dispatch(&td);
}
}
}
}
// #86 BENCH (BT_KILL_SUBSYS=<name>): force the AUTHENTIC destruction of
// one named subsystem -- MechSubsystem::ForceCriticalFailure, the exact
// call the zone crit-cascade (SendSubsystemDamage) makes, and the thing