#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:
co-authored by
Claude Fable 5
parent
c5dfb002f3
commit
b0b7adc4ea
@@ -177,7 +177,11 @@ DamageZonePercentTable *
|
||||
|
||||
if (rotateWithTorso) // binary this[3] != 0
|
||||
{
|
||||
theta += owner->TorsoHeading(); // torso+0x1d8
|
||||
// #124 frame adapter, angular half: theta arrives in the 1995 frame
|
||||
// (ResolveHit's z-reflection), so the twist -- measured in OUR frame
|
||||
// -- enters with the OPPOSITE sign (a reflection reverses angular
|
||||
// direction). Binary form: theta += torso+0x1d8.
|
||||
theta -= owner->TorsoHeading(); // torso+0x1d8 (sign: see adapter)
|
||||
if (theta >= TwoPi) theta -= TwoPi;
|
||||
if (theta < 0.0f) theta += TwoPi;
|
||||
}
|
||||
@@ -277,20 +281,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;
|
||||
// PORT FRAME ADAPTER (#124, corrected 2026-08-03 by the MUZZLE-ANCHOR
|
||||
// probe). The 1995 resolver frame: FORWARD = +Z (the authored ring puts
|
||||
// Front* cells in the +Z arc) and RIGHT = +X (W0/W7 = "Right*") [T1
|
||||
// slice-name data]. Our engine frame, measured with the mech's own
|
||||
// asymmetric geometry as the anchor (BT_ASPECT_TEST muzzle probes -- the
|
||||
// LEFT missile pod LRM15_1 sits at raw local x=-2.32, the RIGHT pod
|
||||
// LRM15_2 at x=+2.32): RIGHT = +X (SAME as 1995) but FORWARD = -Z
|
||||
// (drive-code convention, "forward = -Z at heading 0"). The frames
|
||||
// therefore differ by a Z-NEGATION ONLY -- a REFLECTION, not the pi
|
||||
// rotation first committed (that crossed the pods: left muzzle resolved
|
||||
// rtorso). y (the band axis) untouched.
|
||||
//
|
||||
// A reflection reverses the angular walk direction, so the OTHER angular
|
||||
// input -- the torso-twist term SelectSlice adds -- must flip sign with
|
||||
// it (applied there, same adapter). Twist was 0 in every probe; the
|
||||
// twisted-torso verify is the follow-up on #124.
|
||||
local.z = -local.z;
|
||||
|
||||
// --- height -> layer (penetration depth) ---
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -997,3 +997,13 @@ void BTWeaponSetViewFireEnable(Subsystem *sub, int enable)
|
||||
if (sub != 0 && sub->IsDerivedFrom(MechWeapon::ClassDerivations))
|
||||
((MechWeapon *)sub)->SetViewFireEnable(enable);
|
||||
}
|
||||
|
||||
// #124 aspect probe: a weapon's physical muzzle WORLD position (the pod's own
|
||||
// asymmetric geometry as a left/right anchor). -1-family returns 0.
|
||||
int BTWeaponMuzzleWorld(Subsystem *sub, Point3D *out)
|
||||
{
|
||||
if (sub == 0 || out == 0 || !sub->IsDerivedFrom(MechWeapon::ClassDerivations))
|
||||
return 0;
|
||||
((MechWeapon *)sub)->MuzzlePoint(*out); // the public beam-render alias
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user