diff --git a/game/reconstructed/mech.cpp b/game/reconstructed/mech.cpp index c2e2519..69c8ce5 100644 --- a/game/reconstructed/mech.cpp +++ b/game/reconstructed/mech.cpp @@ -2118,6 +2118,9 @@ Mech::Mech( ramLastVictim = 0; // ram contact-edge state ramContactLinger = 0.0f; lastInflictingDamage = 0.0f; // task #60: killing-blow magnitude (set on hit) + mapPosture = 0; // binary @0x3f8 (the ctor zero-block @0x4b3d18 zeroes the band) + myomerEffectiveness = 1.0f; // binary @0x79c: 0-init + per-frame MAX; the port holds the + // healthy 1.0 until the Myomers Performance feeder lands [T3] if (GroundReal() && GetCollisionVolumeCount() > 0 && collisionTemplate != 0 && collisionVolume != 0) { diff --git a/game/reconstructed/mech.hpp b/game/reconstructed/mech.hpp index 6544e3a..0260d23 100644 --- a/game/reconstructed/mech.hpp +++ b/game/reconstructed/mech.hpp @@ -1032,6 +1032,16 @@ protected: Scalar gimpStrideLength; // @0x350 AlarmIndicator legStateAlarm; // @0x39c int legAnimationState; // @0x3b0 + int mapPosture; // binary @0x3f8 -- the map-legend/duck posture arbiter + // (0 none / 1 may-duck / 2 holding-squat), computed per + // master frame (FUN_004a9b5c @0x4a9f61-0x4aa007; the + // CROUCH reconstruction, 2026-08-05) + Scalar myomerEffectiveness; // binary @0x79c -- init 0 then MAX over the heatable + // chain's +0x31c each master frame; scales the mapper + // speedDemand (dead myomers cripple the walk) and gates + // the crouch. FEEDER (Myomers Performance @004b8be3, + // not in the export) UNRECONSTRUCTED -- the port holds + // the healthy 1.0 [T3]; see the mech4 posture block. Scalar gimpSpeedMax; // @0x52c Scalar standSpeed; // @0x530 Scalar walkStrideLength; // @0x534 diff --git a/game/reconstructed/mech2.cpp b/game/reconstructed/mech2.cpp index 2df2665..6323e5b 100644 --- a/game/reconstructed/mech2.cpp +++ b/game/reconstructed/mech2.cpp @@ -220,6 +220,16 @@ enum MechAnimationState void Mech::SetLegAnimation(int state) { + // DIAG (BT_DUCK_LOG): every leg re-arm with the caller's return address -- + // the crouch-clobber hunt (who re-arms after the squat parks?). + if (getenv("BT_DUCK_LOG")) + { + char dbuf[96]; + sprintf(dbuf, "[duck] SetLegAnimation(%d) ra=btl4+0x%lx", state, + (unsigned long)_ReturnAddress() + - (unsigned long)GetModuleHandleA(0)); + DEBUG_STREAM << dbuf << std::endl << std::flush; + } legAnimation.SelectSequence( // FUN_004277a8(this+0x65c, ...) animationClips[state], // *(this+0x5cc + state*4) // The real leg finished-callback PTR_LAB_0050d6f0 == FUN_004a6928 diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 1726211..9fe16da 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -4348,6 +4348,95 @@ void // and the Standing case returns adv=0 (no motion). Only the LEGACY inline // clip-select path (BT_GAIT_SM=0) still needs the throttle gate (it would // loop the walk clip forever at idle). + // + // ---- MASTER POSTURE / CROUCH (raw disasm @0x4a9cf0-0x4aa0af, the dark + // master-perf region FUN_004a9b5c; decoded + reconstructed 2026-08-05). + // Binary order preserved: (1) the myomer factor -> speedDemand scale, + // (2) the posture selector (+0x3f8), (3) the DuckRequest consumer. + // + { + // (1) myomerEffectiveness (+0x79c): the binary inits 0 then MAXes + // the heatable chain's +0x31c (healthy myomers publish ~1.0; + // weapons park transient fireImpulse there) and multiplies the + // mapper's speedDemand -- dead muscles = a crawling mech. The + // WRITER (Myomers Performance @004b8be3) is not in the export and + // stays unreconstructed, so the member holds the ctor's healthy + // 1.0 and this multiply is a live no-op until that dig lands + // (neutral-value deferral, the role=NULL pattern). [T3 feeder] + { + MechControlsMapper *postureMppr = MappingMapper(); + if (postureMppr != 0) + postureMppr->speedDemand *= myomerEffectiveness; + } + + // (2) the posture selector (+0x3f8; feeds the map legend + the + // duck arbiter). @0x4a9f61-0x4aa007: any non-zero movementMode + // -> 0; !simLive -> 0 (a NOVICE cannot crouch -- the same +0x25c + // lockout as the crit roll); leg alarm level 0/4 (standing / + // turn-in-place idle) -> myomers alive ? 1 : 0 (|factor| <= 1e-4 + // == dead muscles, const @0x4ab16c); level 1 (parked in the + // squat) -> 2; anything else -> 0. + { + extern int BTPlayerExperienceSimLive(void *owner_mech); + const int postureLegLevel = (int)legStateAlarm.GetLevel(); + // VALUE-SPACE NOTE: the binary tests +0x40 != 0 (its normal + // running state was 0); the task-#1 unification made our + // normal the engine ExistingState == 1 (bench: mode=1 while + // walking around). Same intent -- "no special mode (limbo 2, + // airborne 3/4, dead 9, eject 10)". + const int postureMode = MovementMode(); + if ((postureMode != 0 && postureMode != 1) + || !BTPlayerExperienceSimLive((void *)this)) + mapPosture = 0; + else if (postureLegLevel == 0 || postureLegLevel == 4) + mapPosture = (myomerEffectiveness <= 0.0001f + && myomerEffectiveness >= -0.0001f) ? 0 : 1; + else if (postureLegLevel == 1) + mapPosture = 2; + else + mapPosture = 0; + } + + // (3) the DuckRequest consumer (@0x4aa011-0x4aa0af), gated on the + // latched request AND squatCapable ('squ'/'sqd' clips shipped). + // Duck: SetLegAnimation(2) plays 'sqd'; LegClipFinished case-2 + // parks the pose + sets leg alarm 1. Rise: SetLegAnimation(3) + // plays 'squ'; case-3 restores alarm 0. Both mark the type-3 + // state record dirty (ForceUpdate 8 then 1 -- it ships legState + + // stability, so peers pose the squat for free) and flip the + // stability alarm (ducked = 0, risen = 1). The request is + // consumed whenever both gates passed, hit or miss. + if (duckState != 0 && squatCapable != 0) + { + if (mapPosture == 1) + { + SetLegAnimation(2); // 'sqd' -- squat down + ForceUpdate(8); + ForceUpdate(1); + stabilityAlarm.SetLevel(0); + if (getenv("BT_DUCK_LOG") || getenv("BT_GAIT_LOG")) + DEBUG_STREAM << "[duck] SQUAT (posture 1 -> leg clip 2)\n" << std::flush; + } + else if (mapPosture == 2) + { + SetLegAnimation(3); // 'squ' -- rise + ForceUpdate(8); + ForceUpdate(1); + stabilityAlarm.SetLevel(1); + if (getenv("BT_DUCK_LOG") || getenv("BT_GAIT_LOG")) + DEBUG_STREAM << "[duck] RISE (posture 2 -> leg clip 3)\n" << std::flush; + } + else if (getenv("BT_DUCK_LOG")) + DEBUG_STREAM << "[duck] request consumed, posture=" << mapPosture + << " (mode=" << MovementMode() + << " legLvl=" << (int)legStateAlarm.GetLevel() + << " simLive=" << 1 // re-read below costs a bridge call; posture already folded it + << " myo=" << myomerEffectiveness + << " squat=" << squatCapable << ")\n" << std::flush; + duckState = 0; // consumed (@0x4aa0a9) + } + } + { static const int s_gaitTicksAlways = BTEnvOn("BT_GAIT_SM", 1); if (s_gaitTicksAlways || throttle != 0.0f)