the visible limp (#78): there was never a jump-jet clip set -- the 'Airborne' drivers ARE the gimp gait machines
The port had FUN_004a5bf8/71f4 fully reconstructed as AdvanceBody/Leg-
AnimationAirborne, gated on (MovementMode()==3||4) && jumpCapable@0x580 and
believed dead ("the test mech never jumps"). The binary says otherwise:
mech+0x40 in that gate is the graphicAlarm LEVEL (3=left-leg gimp, 4=right)
and +0x580 is hasGimpClips, set by the conditional loader block that probes
'wgl' and fills clip slots 22-27 (wgl/wgr/ggr/ggl/gsl/gsr) plus the four
measurements at 0x53c-0x548 (wg entry strides = speed caps, gg cycle strides).
Renamed the five jump* members + both drivers accordingly.
New: GimpBodyClipFinished @004a6344 / GimpLegClipFinished @004a7970 -- the
gimp transition machines, branched from the normal finished-callbacks. Phase-
correct limp entry (left-gimp enters 0x16/wgl only from a RIGHT step, right-
gimp 0x17/wgr from a LEFT step), gg cycles at gimp cadence, gs exits, and the
demand clamp to the gimped side's speed cap (leg cb writes it back into the
mapper -- the binary's authentic slowdown; the T3 x0.5 stand-in in mechmppr is
retired, BT_GIMP_SPEED now defaults 1.0). The binary's gimp machines have no
reverse entry -- the "reverse disabled" behavior is now binary-proven.
Reviving the dead drivers replayed two port-glue bugs (gotcha #24): the raw
*(controlSource) mapper read (null -> crash at first engagement) and the
missing alarm->member state re-sync (machine pinned in one run state). Both
fixed; bench harness gained BT_SELF_DAMAGE_TICKS=<n> to hold a zone past
LegHalfStructure without destroying it.
Bench-verified (madcat, novice, zone 16): crossing -> alarm 4 -> wgr entry
from a left step -> 8k+ frames stable in the ggl limp cycle at cadence 14.77
(vs 18.5 walk / 22+ run) with raw demand still 50. [T2]
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
520f6eecd3
commit
36f68718c2
@@ -67,7 +67,7 @@
|
||||
// Helper / engine routine name mapping used below:
|
||||
// FUN_0049fb54 Mech::IsDisabled() -> Logical
|
||||
// FUN_004a5678 Mech::AdvanceBodyAnimation(dt, loop) [mech2.cpp]
|
||||
// FUN_004a5bf8 Mech::AdvanceBodyAnimationAirborne(dt, loop) [mech2.cpp]
|
||||
// FUN_004a5bf8 Mech::AdvanceBodyAnimationGimp(dt, loop) [mech2.cpp]
|
||||
// FUN_004ab188 Mech::DeadReckonPose(dt)
|
||||
// FUN_004ab1c8 Mech::IntegrateMotion(dt, loop) -> Logical (disabled?)
|
||||
// FUN_004086ac Vector::Scale(out, in, scalar)
|
||||
@@ -239,7 +239,7 @@ struct MechBaseLayoutCheck
|
||||
// @0x4b8 templateBottomLift (mech.hpp) ctor: 0.05 x volume X width
|
||||
// @0x518 standingTemplateMaxY (mech.hpp) CORRECTED (was "heatLevel")
|
||||
// @0x51c duckedTemplateMaxY (mech.hpp) CORRECTED (was "heatCapacity"); 0.6 x standing
|
||||
// @0x580 jumpCapable (mech3)
|
||||
// @0x580 hasGimpClips (mech3)
|
||||
// @0x598 motionEventName (mech2)
|
||||
// @0x5a4 motionEventArmed (mech2)
|
||||
// @0x5b8 groundCycleRate (mech3)
|
||||
@@ -285,7 +285,7 @@ void
|
||||
//
|
||||
// Advance the *displayed-motion* (channel-B) body gait one frame and integrate
|
||||
// the resulting cycle distance into the world transform. Picks the airborne
|
||||
// body updater when (movementMode==3||4) && jumpCapable, else the ground one.
|
||||
// body updater when (movementMode==3||4) && hasGimpClips, else the ground one.
|
||||
// Also fires the queued footstep / motion event when the mech "arrives".
|
||||
// Returns the IsDisabled() result captured at entry (the caller branches on it).
|
||||
//###########################################################################
|
||||
@@ -337,12 +337,19 @@ Logical
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the body gait (airborne flavour while jumping).
|
||||
// Advance the body gait (GIMP flavour while limping, #78). The binary's
|
||||
// mech+0x40 here is the graphicAlarm level (3=left gimp, 4=right) -- read
|
||||
// via the mechdmg bridge, never MovementMode()/graphicAlarm directly
|
||||
// (simulationState split-brain + AlarmIndicator ODR, gotcha #23).
|
||||
Scalar cycleDistance;
|
||||
if ((MovementMode() == 3 || MovementMode() == 4) && jumpCapable) // 0x40, 0x580
|
||||
cycleDistance = AdvanceBodyAnimationAirborne(time_slice, loop); // FUN_004a5bf8
|
||||
else
|
||||
cycleDistance = AdvanceBodyAnimation(time_slice, loop); // FUN_004a5678
|
||||
{
|
||||
extern int BTMechGimpLevel(void *mech_v);
|
||||
const int gl = BTMechGimpLevel(this);
|
||||
if ((gl == 3 || gl == 4) && hasGimpClips) // 0x40, 0x580
|
||||
cycleDistance = AdvanceBodyAnimationGimp(time_slice, loop); // FUN_004a5bf8
|
||||
else
|
||||
cycleDistance = AdvanceBodyAnimation(time_slice, loop); // FUN_004a5678
|
||||
}
|
||||
|
||||
// The raw velocity vector (0x298) = {0, 0, -cycleDistance/dt}; the world-step
|
||||
// FUN_00408744 reads all three components, so 0x2a0 (== angularAccum[2]) MUST be
|
||||
@@ -2427,9 +2434,16 @@ void
|
||||
else if (!turning && bs == 4) // turn stopped -> back to stand
|
||||
SetBodyAnimation(0);
|
||||
}
|
||||
// GIMP routing (#78): a limping PEER runs the gimp machines too
|
||||
// (the graphicAlarm level replicates with the damage state).
|
||||
extern int BTMechGimpLevel(void *mech_v);
|
||||
const int replGl = BTMechGimpLevel(this);
|
||||
const bool replGimp = (replGl == 3 || replGl == 4) && hasGimpClips;
|
||||
const Scalar replLegAdv = s_peerLegCh
|
||||
? AdvanceLegAnimation(dt) // old: re-derived leg SM
|
||||
: AdvanceBodyAnimation(dt, 1); // authentic: body channel, mj=1 poses skeleton
|
||||
? (replGimp ? AdvanceLegAnimationGimp(dt) // FUN_004a71f4
|
||||
: AdvanceLegAnimation(dt)) // old: re-derived leg SM
|
||||
: (replGimp ? AdvanceBodyAnimationGimp(dt, 1) // FUN_004a5bf8
|
||||
: AdvanceBodyAnimation(dt, 1)); // authentic: body channel, mj=1 poses skeleton
|
||||
// AUTHENTIC coupled position (mirror of the master's world-step at
|
||||
// mech4.cpp:3325-3336 == Mech::IntegrateMotion tail @004ab1c8): between
|
||||
// records the body advances by the CLIP'S OWN travel rotated by the
|
||||
@@ -3897,13 +3911,25 @@ void
|
||||
// INVISIBLE in the binary" (locomotion.md) -- now it is here too.
|
||||
// BT_BODY_MJ=1 restores the old double-writer for A/B.
|
||||
static const int s_bodyMj = BTEnvOn("BT_BODY_MJ", 0);
|
||||
adv = AdvanceBodyAnimation(dt, s_bodyMj); // channel B: replication projection (mj=0)
|
||||
// GIMP routing (#78): route BOTH channels through the gimp
|
||||
// drivers while limping -- (gimpLevel 3|4) && hasGimpClips,
|
||||
// level via the mechdmg bridge (gotcha #23).
|
||||
extern int BTMechGimpLevel(void *mech_v);
|
||||
const int gl2 = BTMechGimpLevel(this);
|
||||
const bool gimpy = (gl2 == 3 || gl2 == 4) && hasGimpClips;
|
||||
adv = gimpy ? AdvanceBodyAnimationGimp(dt, s_bodyMj) // FUN_004a5bf8
|
||||
: AdvanceBodyAnimation(dt, s_bodyMj); // channel B: replication projection (mj=0)
|
||||
mirrorBodyAdv = adv; // stash for the send-mirror gait advance
|
||||
legAdv = AdvanceLegAnimation(dt); // channel A: local sim -- pose + travel
|
||||
legAdv = gimpy ? AdvanceLegAnimationGimp(dt) // FUN_004a71f4
|
||||
: AdvanceLegAnimation(dt); // channel A: local sim -- pose + travel
|
||||
}
|
||||
else
|
||||
{
|
||||
adv = AdvanceBodyAnimation(dt, 1); // single-channel: body does both
|
||||
extern int BTMechGimpLevel(void *mech_v);
|
||||
const int gl2 = BTMechGimpLevel(this);
|
||||
const bool gimpy = (gl2 == 3 || gl2 == 4) && hasGimpClips;
|
||||
adv = gimpy ? AdvanceBodyAnimationGimp(dt, 1)
|
||||
: AdvanceBodyAnimation(dt, 1); // single-channel: body does both
|
||||
}
|
||||
// [syncF] per-frame divergence probe (task #49): log every frame
|
||||
// where the two channels return different distances or either
|
||||
@@ -5608,10 +5634,21 @@ void
|
||||
&& !IsMechDestroyed() && damageZoneCount > 0)
|
||||
{
|
||||
static float s_sdAccum = 0.0f;
|
||||
static int s_sdTicks = 0;
|
||||
// BT_SELF_DAMAGE_TICKS=<n>: stop after n hits (bench: ramp a leg
|
||||
// PAST LegHalfStructure then hold, so the limp window is unbounded
|
||||
// instead of the 1 s between half and destroyed at high dps).
|
||||
static int s_sdMaxTicks = -1;
|
||||
if (s_sdMaxTicks < 0)
|
||||
{
|
||||
const char *te = getenv("BT_SELF_DAMAGE_TICKS");
|
||||
s_sdMaxTicks = (te != 0 && *te != '\0') ? atoi(te) : 0; // 0 = unlimited
|
||||
}
|
||||
s_sdAccum += dt;
|
||||
if (s_sdAccum >= 1.0f)
|
||||
if (s_sdAccum >= 1.0f && (s_sdMaxTicks <= 0 || s_sdTicks < s_sdMaxTicks))
|
||||
{
|
||||
s_sdAccum = 0.0f;
|
||||
++s_sdTicks;
|
||||
Damage dmg;
|
||||
dmg.damageType = Damage::ExplosiveDamageType;
|
||||
float want = (float)atof(getenv("BT_SELF_DAMAGE"));
|
||||
@@ -5634,7 +5671,8 @@ void
|
||||
sizeof(Entity::TakeDamageMessage),
|
||||
GetEntityID(), sdZone /*-1 -> cylinder lottery*/, dmg);
|
||||
DEBUG_STREAM << "[selfdmg] " << dmg.damageAmount
|
||||
<< " to own mech (unaimed)" << std::endl << std::flush;
|
||||
<< " to own mech (zone " << sdZone << ", -1=lottery)"
|
||||
<< std::endl << std::flush;
|
||||
Dispatch(&td);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user