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
@@ -475,7 +475,7 @@ default-ON (`'0'` disables).
|
||||
| `BT_AUDIO_SOURCES=<n>` | request `n` OpenAL mono sources instead of the driver default (~256). **Opt-in on purpose** — the cap doubles as a governor, and with EFX reverb live a higher ceiling means more simultaneous voices mixing during heavy combat. Measure frame time. See [[wintesla-port]] |
|
||||
| `BT_SELF_DAMAGE_ZONE=<n>` | aim the BT_SELF_DAMAGE harness at an explicit zone (-1/unset = the lottery) -- ramps one zone deterministically past thresholds (#78 bench) |
|
||||
| `BT_DRIVE_LOG` | the demand-site drive scale: `[drive] n/drive/mm/dmd/mech` ~1Hz (drive = myomers speedEffect x gimp; mm = the gimp level via BTMechGimpLevel) |
|
||||
| `BT_GIMP_SPEED=<0..1>` | override the gimped speed factor (default 0.5 [T3 VGL Lynx]) |
|
||||
| `BT_GIMP_SPEED=<0..1>` | EXTRA gimp demand multiplier (default 1.0 = authentic since 2026-07-30: the real slowdown is the gimp gait machines' clamp to the wg clip speed — see [[locomotion]] §visible limp; the old 0.5 T3 stand-in is retired) |
|
||||
| `BT_PICK_LOG` | #73 aimed-pick diagnostics: `[segpick]` the segment→zone map at tree build (index, name, zone, sphere), `[pickwin]` the winning zone/score/t per pick (score ~0 = threading the part core, ~1 = envelope graze) |
|
||||
| `BT_CRIT_LOG` | #80 crit diagnostics: `[subarmor]` per-subsystem armour/scales/critBonus at ctor (proves the resource keys parsed + the zone got REAL scales), `[critroll]` per landed crit (zone, subsystem, its resulting own-zone level). NB the type-0x1e loader's `[crit]` tag is a different, older log |
|
||||
| `BT_DEVICELOST_TEST=<frame>[,crashrepro]` | #35 bench hook. `<frame>` forces the D3D9 DEVICELOST branch at that render frame (+600/+1200 = 3 cycles), driving the REAL `BTResetLostDevice` recovery. `,crashrepro` runs the field null-teardown shape (double `ParticleEngine::Destroy`) — pre-fix this reproduced the field crash byte-for-byte (`Destroy +0x11`, `target=0x0`); post-fix it must log `SURVIVED`. See [[wintesla-port]] §Device-loss |
|
||||
|
||||
+42
-14
@@ -254,21 +254,49 @@ Check `[shadowobj]` tag lines and `[sync]`/`BT_SYNC_LOG` before touching bias/ti
|
||||
- Detail: `docs/P3_LOCOMOTION.md`. Uses: [[asset-formats]] (SKL/ANI), [[decomp-reference]] (offsets).
|
||||
- Feeds: [[combat-damage]] (collision→damage), [[rendering]] (shadow/visual-conform).
|
||||
|
||||
## Myomer drive + the GIMP (limp) chain — IMPLEMENTED 2026-07-30 (#75/#78) [T2 bench]
|
||||
The speed-demand site (`mechmppr.cpp` InterpretControls) now applies the **drive scale**:
|
||||
`speedDemand *= myomers.speedEffect × gimpFactor`. The myomers factor is the wrapper's live 0..1
|
||||
## Myomer drive + the GIMP (limp) chain — COMPLETE 2026-07-30 (#75/#78)
|
||||
The speed-demand site (`mechmppr.cpp` InterpretControls) applies the **drive scale**:
|
||||
`speedDemand *= myomers.speedEffect`. The myomers factor is the wrapper's live 0..1
|
||||
output (gear/thermal/1−damage, via `BTMyomersDriveOf`); the authentic coupling attached
|
||||
`&speedEffect` into the mover's feed roster, which the 2007 engine lacks — the multiplication at
|
||||
the demand site is the port equivalent. The GIMP chain: `mechdmg` raises **graphicAlarm 3 (left)
|
||||
/ 4 (right)** when a leg zone (the `LegDamageZone`-flagged zones; MadCat: 3/5/8 left, 10/16/19
|
||||
right) crosses `LegHalfStructure` (0.5); while gimped the demand additionally multiplies by
|
||||
**0.5 [T3 — VGL Lynx's "roughly 50%", `BT_GIMP_SPEED` overrides]** and **reverse input is
|
||||
refused** ("reverse disabled", `[gimp]` log; the pod's audio cue rides the alarm's watchers if
|
||||
audio rows exist). Measured composed and exact: `dmd 44.837 → 6.726 = 44.837 × 0.5 × 0.3` (a
|
||||
crit-damaged myomers at 0.3 during the same ramp). ⚠ Cross-TU reads of the gimp level MUST use
|
||||
`BTMechGimpLevel` (mechdmg.cpp) — see [[reconstruction-gotchas]] §23 (the AlarmIndicator typedef
|
||||
split). Open on #78: the **Gimp animation clips** (`Left/RightGimpAnimation` + transitions —
|
||||
authored KEYS in the binary's model-record parser; mech2's enum names states 0x12-0x17 for them
|
||||
but mech3's reverse-fix reassigned those slots to the reverse figures — reconcile before wiring
|
||||
clips) and the audio cue binding. Harness: `BT_SELF_DAMAGE_ZONE=<n>` ramps one zone
|
||||
deterministically; `BT_DRIVE_LOG` prints `[drive] n/drive/mm/dmd/mech`.
|
||||
right) crosses `LegHalfStructure` (0.5), and **reverse input is refused** ("reverse disabled",
|
||||
`[gimp]` log — and the binary agrees: the gimp gait machines have NO standing→reverse entry).
|
||||
⚠ Cross-TU reads of the gimp level MUST use `BTMechGimpLevel` (mechdmg.cpp) — see
|
||||
[[reconstruction-gotchas]] §23 (the AlarmIndicator typedef split). Harness:
|
||||
`BT_SELF_DAMAGE_ZONE=<n>` ramps one zone deterministically; `BT_DRIVE_LOG` prints
|
||||
`[drive] n/drive/mm/dmd/mech`.
|
||||
|
||||
### The VISIBLE limp — the gimp gait machines (reconstructed + bench-verified 2026-07-30) [T2]
|
||||
Bench (madcat, novice, `BT_SELF_DAMAGE=60 BT_SELF_DAMAGE_ZONE=16 BT_SELF_DAMAGE_TICKS=2`,
|
||||
`BT_GOTO` walk): right-leg crossing → alarm 4 → body enters 0x17/wgr FROM A LEFT STEP
|
||||
(phase-correct) → both channels settle in 0x19/ggl and hold it 8k+ frames at the gimp cadence
|
||||
cap (cycle 14.77 = the wgr entry stride, vs 18.5 walk / 22+ run) while raw demand stays 50 —
|
||||
the slowdown IS the gait machine, no demand multiply. Two revive-bugs fixed en route: see
|
||||
[[reconstruction-gotchas]] §24.
|
||||
The binary's limp was hiding behind a **"jump-jet" misread**: the port had `FUN_004a5bf8` /
|
||||
`FUN_004a71f4` reconstructed as `AdvanceBody/LegAnimationAirborne`, gated on
|
||||
`(MovementMode()==3||4) && jumpCapable@0x580` and thought DEAD (mechs never jump). Truth
|
||||
[T1, raw part_012.c]: **mech+0x40 there is the graphicAlarm level (3=left gimp, 4=right)**
|
||||
and **+0x580 is `hasGimpClips`** — set by the conditional loader block that probes `wgl` and
|
||||
loads clip slots 22-27 (`wgl wgr ggr ggl gsl gsr`, +0x624..0x638) plus four measurements:
|
||||
`gimpLeft/RightSpeedMax`@0x53c/0x540 (the wg entry clips' final keyframe strides) and
|
||||
`gimpLeft/RightStrideLength`@0x544/0x548 (MeasureClipStride over the gg cycles). The five
|
||||
`jump*`-named members were renamed accordingly (mech.hpp; there is no jump clip set).
|
||||
Machinery (all four reconstructed in mech2.cpp, gates in mech4.cpp):
|
||||
- **Drivers** `AdvanceBodyAnimationGimp`@004a5bf8 / `AdvanceLegAnimationGimp`@004a71f4 —
|
||||
selected per-frame when `(gimpLevel 3|4) && hasGimpClips`; clamp the demand (body:
|
||||
`bodyTargetSpeed`; leg: the LIVE mapper `speedDemand`, written back) to the gimped side's
|
||||
speed cap and advance the gg cycles at gimp cadence. **This clamp IS the authentic gimp
|
||||
slowdown** — ground speed is clip travel, so the limp cadence bounds it; the earlier T3
|
||||
`×0.5` stand-in in mechmppr is retired (`BT_GIMP_SPEED` remains as an override, default 1.0).
|
||||
- **Finished-callbacks** `GimpBodyClipFinished`@004a6344 / `GimpLegClipFinished`@004a7970 —
|
||||
branched to from the tops of the normal `Body/LegClipFinished`. **Phase-correct entry**:
|
||||
left-gimp enters 0x16/wgl only from a RIGHT step (walk-R cases 5/6/0xe), right-gimp enters
|
||||
0x17/wgr from a LEFT step (7/0xf) — the machine forces one extra normal step if mid-wrong-foot.
|
||||
Cycles: left = 0x18 (ggr clip, stride @0x544), right = 0x19 (ggl, @0x548); exits through the gs
|
||||
transitions 0x1a/0x1b when demand dies. No reverse entry exists while gimped.
|
||||
Open: the load-time per-clip callbacks `PTR_LAB_0050d738/744` on the wg clips (uncarved LABs,
|
||||
inert `Recon` placeholders in the port — likely footstep/cycle events, transitions work without
|
||||
them); the gimp audio cue binding.
|
||||
|
||||
@@ -647,3 +647,20 @@ type, and which member traffic crosses families — is an open work item; the sa
|
||||
explains why `MovementMode()` (the engine `simulationState`) and the KB's "movementMode IS the
|
||||
graphicAlarm level" (task #1) never actually met in the port: they are two different cells, both
|
||||
alive, written by different subsystems.
|
||||
|
||||
## 24. Reviving DEAD reconstructed code replays every port-glue fix it predates (2026-07-30)
|
||||
The gimp gait drivers (`AdvanceBody/LegAnimationGimp`, ex-"Airborne") sat reconstructed-but-dead
|
||||
for weeks while their gate read the wrong cell (gotcha #23's split-brain). The moment the gate
|
||||
was fixed and they ran for the first time, they reproduced — within seconds of engagement — TWO
|
||||
bugs the LIVE drivers had each been cured of long before:
|
||||
1. **the raw `*(this->controlSource)` mapper read** (`controlSource@0x128` is never wired in the
|
||||
port; the live drivers were converted to `MappingMapper()` — the dead one crashed at
|
||||
`AdvanceLegAnimationGimp+0x16`, null deref, first live engagement);
|
||||
2. **the missing alarm→member state re-sync** (`bodyAnimationState = bodyStateAlarm.GetLevel()`;
|
||||
the binary has ONE cell, the recon has two — the ground drivers re-sync at :831/:1181; the
|
||||
dead driver didn't, so the state member froze at its pre-gimp value and the machine pinned in
|
||||
one run state for 8000+ frames).
|
||||
**Rule:** before wiring a long-dead reconstructed function into a live path, diff it against its
|
||||
LIVE sibling for the port-glue idioms (mapper access via `MappingMapper()`, alarm→member
|
||||
re-syncs, null guards, `BTEnvOn` gates). The binary-faithful parts transplant cleanly; it is the
|
||||
RECON-side glue that will be missing, because every glue fix landed only where code was running.
|
||||
|
||||
@@ -45,7 +45,7 @@ be "called on":
|
||||
+ `IntegrateMotion`) in `PerformAndWatch`, retiring the Step-1/2 stand-in translation + free-standing
|
||||
`gBodyAnim`. Gives the authentic locally-simulated + displayed-motion gait.
|
||||
|
||||
**Deferred polish (post-core):** airborne/fall gaits (AdvanceBody/LegAnimationAirborne), torso-twist-to-target
|
||||
**Deferred polish (post-core):** ~~airborne/fall gaits (AdvanceBody/LegAnimationAirborne)~~ — RESOLVED 2026-07-30: those were the GIMP (limp) drivers, now `AdvanceBody/LegAnimationGimp`, live for #78 (see context/locomotion.md §visible limp); torso-twist-to-target
|
||||
(Torso is FULLY reconstructed — TorsoSimulation twist/elevation @004b5cf0 + WriteJoints; needs wiring), gyro sway.
|
||||
|
||||
**First-milestone recommendation:** Steps 1-2 give a visibly correct animation-driven walk/run (real speeds,
|
||||
|
||||
@@ -1017,10 +1017,16 @@ protected:
|
||||
Scalar standSpeed; // @0x530
|
||||
Scalar walkStrideLength; // @0x534
|
||||
Scalar reverseSpeedMax; // @0x538
|
||||
Scalar jumpRunSpeedMax; // @0x53c
|
||||
Scalar jumpWalkSpeedMax; // @0x540
|
||||
Scalar jumpRunStrideLength; // @0x544
|
||||
Scalar jumpWalkStrideLength; // @0x548
|
||||
// GIMP (limp-gait) measurements, filled by the conditional 'wgl' loader
|
||||
// block (FUN_004a80d4 raw :13705-13733). "Left/Right" = WHICH LEG IS
|
||||
// GIMPED (graphicAlarm level 3 = left, 4 = right). Speeds are the wg
|
||||
// entry clips' final keyframe strides; strides are MeasureClipStride
|
||||
// over the gg cycle clips. (These four + hasGimpClips@0x580 were
|
||||
// mislabeled jump* -- there is no jump-jet clip set.)
|
||||
Scalar gimpLeftSpeedMax; // @0x53c wgl final stride (mode-3 speed cap)
|
||||
Scalar gimpRightSpeedMax; // @0x540 wgr final stride (mode-4 speed cap)
|
||||
Scalar gimpLeftStrideLength; // @0x544 ggr cycle stride (mode 3 cycles state 0x18)
|
||||
Scalar gimpRightStrideLength; // @0x548 ggl cycle stride (mode 4 cycles state 0x19)
|
||||
Scalar walkingTurnRate; // @0x574 (WalkingTurnRate, rad/s) -- the master
|
||||
Scalar runningTurnRate; // @0x578 (RunningTurnRate, rad/s) perf turn-rate lerp
|
||||
Scalar groundCycleRate; // @0x5b8
|
||||
@@ -1035,9 +1041,9 @@ protected:
|
||||
Scalar globalTimeScale; // @0x5a8
|
||||
Scalar idleStrideScale; // @0x5ac
|
||||
Scalar gimpCycleRate; // @0x5b0
|
||||
int jumpCapable; // @0x580
|
||||
int hasReverseGimpSet; // @0x584
|
||||
int hasCrashSet; // @0x588
|
||||
int hasGimpClips; // @0x580 set iff the model ships the wg/gg/gs clip set
|
||||
int squatCapable; // @0x584 set iff squ/sqd ship
|
||||
int turnCapable; // @0x588 set iff trn ships
|
||||
int deathAnimationLatched; // @0x650
|
||||
int legResetLatch; // @0x654
|
||||
int bodyResetLatch; // @0x658
|
||||
@@ -1084,8 +1090,17 @@ protected:
|
||||
Scalar LegTransition(int next_state, Scalar adv_time, int move_joints);
|
||||
Scalar AdvanceLegAnimation(Scalar time_slice); // @004a5028
|
||||
Scalar AdvanceBodyAnimation(Scalar time_slice, int loop);
|
||||
Scalar AdvanceBodyAnimationAirborne(Scalar time_slice, int loop);
|
||||
Scalar AdvanceLegAnimationAirborne(Scalar time_slice);
|
||||
Scalar AdvanceBodyAnimationGimp(Scalar time_slice, int loop); // @004a5bf8 (was "Airborne" -- it's the LIMP driver)
|
||||
Scalar AdvanceLegAnimationGimp(Scalar time_slice); // @004a71f4
|
||||
// GIMP finished-callbacks: BodyClipFinished/LegClipFinished tail-branch
|
||||
// into these when (gimpLevel==3||4) && hasGimpClips. Same transition
|
||||
// machine as the normal cbs plus: a top clamp of the demand to the
|
||||
// gimp speed cap, phase-correct wg entries from the walk cases
|
||||
// (left-gimp enters 0x16/wgl from a RIGHT step, right-gimp 0x17/wgr
|
||||
// from a LEFT step -- forcing one extra normal step if mid-wrong-foot),
|
||||
// the gg cycles 0x18/0x19 at gimp cadence, and gs exits 0x1a/0x1b.
|
||||
Scalar GimpBodyClipFinished(Scalar carryover, int move_joints); // FUN_004a6344
|
||||
Scalar GimpLegClipFinished(Scalar carryover); // FUN_004a7970
|
||||
Logical IsDisabled(); // FUN_0049fb54
|
||||
// --- death sequence (the un-exported master-perf death branch, rebuilt
|
||||
// from its exported consumers + the RP VTV::DeathShutdown analog) ---
|
||||
|
||||
+329
-52
@@ -19,8 +19,8 @@
|
||||
//
|
||||
// @004a5028 Mech::AdvanceLegAnimation (1543 bytes)
|
||||
// @004a5678 Mech::AdvanceBodyAnimation (1333 bytes)
|
||||
// @004a5bf8 Mech::AdvanceBodyAnimationAirborne(1792 bytes)
|
||||
// @004a71f4 Mech::AdvanceLegAnimationAirborne (1840 bytes)
|
||||
// @004a5bf8 Mech::AdvanceBodyAnimationGimp(1792 bytes)
|
||||
// @004a71f4 Mech::AdvanceLegAnimationGimp (1840 bytes)
|
||||
//
|
||||
// The embedded assert path on every one of them is
|
||||
// "d:\tesla\bt\bt\MECH2.CPP"
|
||||
@@ -180,10 +180,10 @@ enum MechAnimationState
|
||||
// @0x530 standSpeed "at rest" / minimum move speed threshold
|
||||
// @0x534 walkStrideLength forward walk/run clip length (also speed cap)
|
||||
// @0x538 reverseSpeedMax speed cap while decelerating into Reverse (?)
|
||||
// @0x53c jumpRunSpeedMax airborne speed cap, movementMode==3
|
||||
// @0x540 jumpWalkSpeedMax airborne speed cap, otherwise
|
||||
// @0x544 jumpRunStrideLength airborne clip length, movementMode==3
|
||||
// @0x548 jumpWalkStrideLength airborne clip length, otherwise
|
||||
// @0x53c gimpLeftSpeedMax airborne speed cap, movementMode==3
|
||||
// @0x540 gimpRightSpeedMax airborne speed cap, otherwise
|
||||
// @0x544 gimpLeftStrideLength airborne clip length, movementMode==3
|
||||
// @0x548 gimpRightStrideLength airborne clip length, otherwise
|
||||
// @0x598 motionEventName (string) cleared to "" on fall/reset
|
||||
// @0x5a4 motionEventArmed (int) reset to 0 on fall/reset
|
||||
// @0x5a8 globalTimeScale multiplies every clip increment
|
||||
@@ -313,10 +313,16 @@ Scalar
|
||||
Scalar
|
||||
Mech::BodyClipFinished(Mech *m, unsigned /*a2*/, Scalar carryover, int mj)
|
||||
{
|
||||
// airborne branch (movementMode 3/4 && jumpCapable) -> FUN_004a6344 (deferred; safe
|
||||
// no-op while grounded -- the test mech never jumps).
|
||||
if ((m->MovementMode() == 3 || m->MovementMode() == 4) && m->jumpCapable)
|
||||
return 0.0f;
|
||||
// GIMP branch (FUN_004a6d8c top): a limping mech's body transitions run the
|
||||
// gimp machine instead. Mode = the graphicAlarm level (the binary's real
|
||||
// mech+0x40: 3=left-leg gimp, 4=right) read via the mechdmg bridge --
|
||||
// NEVER graphicAlarm directly here (AlarmIndicator ODR split, gotcha #23).
|
||||
{
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (the TU-safe read)
|
||||
const int gl = BTMechGimpLevel(m);
|
||||
if ((gl == 3 || gl == 4) && m->hasGimpClips)
|
||||
return m->GimpBodyClipFinished(carryover, mj);
|
||||
}
|
||||
|
||||
const Scalar fcr = m->forwardCycleRate; // 0x344
|
||||
const Scalar gts = m->globalTimeScale; // 0x5a8
|
||||
@@ -454,9 +460,14 @@ Scalar
|
||||
Scalar
|
||||
Mech::LegClipFinished(Mech *m, unsigned /*a2*/, Scalar carryover, int mj)
|
||||
{
|
||||
// airborne branch (movementMode 3/4 && jumpCapable) -> FUN_004a7970 (deferred).
|
||||
if ((m->MovementMode() == 3 || m->MovementMode() == 4) && m->jumpCapable)
|
||||
return 0.0f;
|
||||
// GIMP branch (FUN_004a6928 top): route a limping mech's leg transitions
|
||||
// into the gimp machine (mode = graphicAlarm level via bridge, gotcha #23).
|
||||
{
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (the TU-safe read)
|
||||
const int gl = BTMechGimpLevel(m);
|
||||
if ((gl == 3 || gl == 4) && m->hasGimpClips)
|
||||
return m->GimpLegClipFinished(carryover);
|
||||
}
|
||||
|
||||
// The binary reads edx = *(mech+0x128) then [edx]+0x128: subsystemArray[0]
|
||||
// (the roster's ControlsMapper slot 0) -> speedDemand; null (no mapper)
|
||||
@@ -546,6 +557,250 @@ Scalar
|
||||
}
|
||||
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
// GimpBodyClipFinished / GimpLegClipFinished (#78 visible limp)
|
||||
//
|
||||
// @004a6344 (body) / @004a7970 (leg) -- the GIMP transition machines, entered
|
||||
// from the normal finished-callbacks when (gimpLevel 3|4) && hasGimpClips.
|
||||
// Same walk/run/reverse alternation as the normal cbs, PLUS:
|
||||
// - a top clamp of the demand to the gimped leg's speed cap (body: clamps
|
||||
// bodyTargetSpeed@0x6b4; leg: clamps the LIVE mapper speedDemand and
|
||||
// writes it back -- the binary's own "you can't outrun a shot leg");
|
||||
// - PHASE-CORRECT limp entry from the walk cases: left-gimp (mode 3) enters
|
||||
// 0x16/wgl only from a RIGHT step (5/6/0xe) -- the L case forces one more
|
||||
// normal R step first; right-gimp (mode 4) mirrors into 0x17/wgr from the
|
||||
// L case (7/0xf). The limp always starts on the correct foot.
|
||||
// - the gg cycles 0x16/0x18 (left, ggr clip, stride @0x544) and 0x17/0x19
|
||||
// (right, ggl, @0x548), continuing at gimp cadence or exiting through the
|
||||
// gs transitions 0x1a/0x1b when the demand dies;
|
||||
// - NO standing->reverse entry exists in the gimp machines (or drivers):
|
||||
// the binary itself refuses REVERSE while gimped.
|
||||
// Mode is the graphicAlarm level via the mechdmg bridge (gotcha #23).
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
Scalar
|
||||
Mech::GimpBodyClipFinished(Scalar carryover, int mj)
|
||||
{
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (TU-safe)
|
||||
const int mode = BTMechGimpLevel(this); // binary: this+0x40
|
||||
const int state = bodyAnimationState; // 0x728
|
||||
const Scalar fcr = forwardCycleRate; // 0x344
|
||||
const Scalar gts = globalTimeScale; // 0x5a8
|
||||
|
||||
// Top clamp (0x4a6352): while in a moving cycle, cap the body target speed
|
||||
// to the gimped side's entry-clip speed, then floor at zero.
|
||||
if ((unsigned)(state - 6) < 2 || (unsigned)(state - 0x0c) < 2
|
||||
|| (unsigned)(state - 0x12) < 2)
|
||||
{
|
||||
const Scalar cap = (mode == 3) ? gimpLeftSpeedMax : gimpRightSpeedMax; // 0x53c / 0x540
|
||||
if (bodyTargetSpeed > cap) bodyTargetSpeed = cap;
|
||||
if (bodyTargetSpeed < ZeroSpeed) bodyTargetSpeed = ZeroSpeed;
|
||||
}
|
||||
|
||||
const Scalar cyc = bodyCycleSpeed; // 0x6b8
|
||||
const Scalar tgt = bodyTargetSpeed; // 0x6b4 (post-clamp)
|
||||
const Scalar T = carryover * gts; // plain end-tail time
|
||||
int next;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
default: // 0/1 + unmapped: parked
|
||||
return 0.0f;
|
||||
case 2:
|
||||
bodyStateAlarm.SetLevel(1); return 0.0f;
|
||||
case 3: case 4: case 8: case 9: case 0x14: case 0x15:
|
||||
case 0x1a: case 0x1b: case 0x20:
|
||||
bodyStateAlarm.SetLevel(0); return 0.0f;
|
||||
|
||||
// -- walk-R (0x4a6440): states 5,6,0xe -- the left-gimp entry point --
|
||||
case 5: case 6: case 0xe:
|
||||
{
|
||||
bool cont = (tgt >= standSpeed) || ((cyc - fcr * carryover) >= standSpeed);
|
||||
if (!cont) { next = 9; break; } // walk->stand L
|
||||
bool up = (tgt > walkStrideLength) && ((fcr * carryover + cyc) > walkStrideLength);
|
||||
if (up) { next = 0xb; break; } // toward run (dead once clamped)
|
||||
int alt = 7; // normal alternation -> wwl
|
||||
if (mode == 4) alt = 7; // right-gimp: one more normal step
|
||||
if (mode == 3) alt = 0x16; // left-gimp: enter wgl on this R step
|
||||
return BodyTransition(alt, carryover * cyc * gts / walkStrideLength, mj); // LAB_004a6505
|
||||
}
|
||||
// -- walk-L (0x4a63ff): states 7,0xf -- the right-gimp entry point --
|
||||
case 7: case 0xf:
|
||||
{
|
||||
bool cont = (tgt >= standSpeed) || ((cyc - fcr * carryover) >= standSpeed);
|
||||
if (!cont) { next = 8; break; } // walk->stand R
|
||||
bool up = (tgt > walkStrideLength) && ((fcr * carryover + cyc) > walkStrideLength);
|
||||
if (up) { next = 0xa; break; }
|
||||
int alt = 6; // normal alternation -> wwr
|
||||
if (mode == 3) alt = 6; // left-gimp: force back to the R step
|
||||
if (mode == 4) alt = 0x17; // right-gimp: enter wgr on this L step
|
||||
return BodyTransition(alt, carryover * cyc * gts / walkStrideLength, mj);
|
||||
}
|
||||
// -- run cycles (0x4a65f7 / 0x4a6663): 10/12 <-> 11/13 --
|
||||
case 0xa: case 0xc:
|
||||
{
|
||||
bool cont = (tgt >= reverseSpeedMax) || ((cyc - fcr * carryover) >= reverseSpeedMax);
|
||||
if (cont) // LAB_004a6648 run-cadence tail
|
||||
return BodyTransition(0xd, carryover * cyc * gts / reverseStrideLength, mj);
|
||||
next = 0xf; break; // run->walk R
|
||||
}
|
||||
case 0xb: case 0xd:
|
||||
{
|
||||
bool cont = (tgt >= reverseSpeedMax) || ((cyc - fcr * carryover) >= reverseSpeedMax);
|
||||
if (cont)
|
||||
return BodyTransition(0xc, carryover * cyc * gts / reverseStrideLength, mj);
|
||||
next = 0xe; break;
|
||||
}
|
||||
// -- back cycles (0x4a66d5 / 0x4a6754): 0x10/0x12 <-> 0x11/0x13 --
|
||||
case 0x10: case 0x12:
|
||||
{
|
||||
bool up = (tgt > gimpSpeedMax) && ((gimpCycleRate * carryover + cyc) > gimpSpeedMax);
|
||||
if (up) { next = 0x15; break; } // back->stand L
|
||||
Scalar t = carryover * cyc * gts / gimpStrideLength;
|
||||
if (t <= 0.0f) t = -t; // 0x350 stored negative
|
||||
return BodyTransition(0x13, t, mj);
|
||||
}
|
||||
case 0x11: case 0x13:
|
||||
{
|
||||
bool up = (tgt > gimpSpeedMax) && ((gimpCycleRate * carryover + cyc) > gimpSpeedMax);
|
||||
if (up) { next = 0x14; break; }
|
||||
Scalar t = carryover * cyc * gts / gimpStrideLength;
|
||||
if (t <= 0.0f) t = -t;
|
||||
return BodyTransition(0x12, t, mj);
|
||||
}
|
||||
// -- GIMP cycles (0x4a67cf / 0x4a6836): left 0x16/0x18, right 0x17/0x19 --
|
||||
case 0x16: case 0x18:
|
||||
{
|
||||
bool cont = (tgt >= gimpLeftSpeedMax) || ((cyc - fcr * carryover) >= gimpLeftSpeedMax);
|
||||
if (cont) // keep limping (ggr cycle)
|
||||
return BodyTransition(0x18, carryover * cyc * gts / gimpLeftStrideLength, mj);
|
||||
next = 0x1a; break; // demand died -> gsl exit
|
||||
}
|
||||
case 0x17: case 0x19:
|
||||
{
|
||||
bool cont = (tgt >= gimpRightSpeedMax) || ((cyc - fcr * carryover) >= gimpRightSpeedMax);
|
||||
if (cont)
|
||||
return BodyTransition(0x19, carryover * cyc * gts / gimpRightStrideLength, mj);
|
||||
next = 0x1b; break; // -> gsr exit
|
||||
}
|
||||
}
|
||||
return BodyTransition(next, T, mj); // shared plain tail
|
||||
}
|
||||
|
||||
Scalar
|
||||
Mech::GimpLegClipFinished(Scalar carryover)
|
||||
{
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (TU-safe)
|
||||
const int mode = BTMechGimpLevel(this); // binary: this+0x40
|
||||
const int state = legAnimationState; // 0x3b0
|
||||
MechControlsMapper *mppr = MappingMapper(); // **(this+0x128)
|
||||
|
||||
// Top clamp (0x4a7995): cap the LIVE COMMANDED speedDemand itself while in
|
||||
// a moving cycle, writing the clamp back -- this is the binary's authentic
|
||||
// gimp slowdown (the mapper re-derives demand each tick; this cb re-caps
|
||||
// it every clip end, and the gimp cycle cadence below enforces it anyway).
|
||||
if (((unsigned)(state - 6) < 2 || (unsigned)(state - 0x0c) < 2
|
||||
|| (unsigned)(state - 0x12) < 2) && mppr != 0)
|
||||
{
|
||||
const Scalar cap = (mode == 3) ? gimpLeftSpeedMax : gimpRightSpeedMax; // 0x53c / 0x540
|
||||
if (mppr->speedDemand > cap) mppr->speedDemand = cap;
|
||||
if (mppr->speedDemand < ZeroSpeed) mppr->speedDemand = ZeroSpeed;
|
||||
}
|
||||
|
||||
const Scalar spd = (mppr != 0) ? mppr->speedDemand : 0.0f; // (binary derefs; port guards null)
|
||||
const Scalar fcr = forwardCycleRate; // 0x344
|
||||
const Scalar gts = globalTimeScale; // 0x5a8
|
||||
const Scalar cyc = legCycleSpeed; // 0x348
|
||||
const Scalar T = carryover * gts;
|
||||
int next;
|
||||
|
||||
switch (state) // all leg tails mj=1 (binary)
|
||||
{
|
||||
default:
|
||||
return 0.0f;
|
||||
case 2:
|
||||
legStateAlarm.SetLevel(1); return 0.0f;
|
||||
case 3: case 4: case 8: case 9: case 0x14: case 0x15:
|
||||
case 0x1a: case 0x1b: case 0x20:
|
||||
legStateAlarm.SetLevel(0); return 0.0f;
|
||||
|
||||
// -- walk-R (0x4a7a52): states 5,6,0xe -- left-gimp entry --
|
||||
case 5: case 6: case 0xe:
|
||||
{
|
||||
bool cont = (spd >= standSpeed) || ((cyc - fcr * carryover) >= standSpeed);
|
||||
if (!cont) { next = 9; break; }
|
||||
bool up = (spd > walkStrideLength) && ((fcr * carryover + cyc) > walkStrideLength);
|
||||
if (up) { next = 0xb; break; }
|
||||
int alt = 7;
|
||||
if (mode == 4) alt = 7; // right-gimp: one more normal step
|
||||
if (mode == 3) alt = 0x16; // left-gimp: enter wgl on this R step
|
||||
return LegTransition(alt, carryover * cyc * gts / walkStrideLength, 1); // LAB_004a7b38
|
||||
}
|
||||
// -- walk-L (0x4a7bb5): states 7,0xf -- right-gimp entry --
|
||||
case 7: case 0xf:
|
||||
{
|
||||
bool cont = (spd >= standSpeed) || ((cyc - fcr * carryover) >= standSpeed);
|
||||
if (!cont) { next = 8; break; }
|
||||
bool up = (spd > walkStrideLength) && ((fcr * carryover + cyc) > walkStrideLength);
|
||||
if (up) { next = 0xa; break; }
|
||||
int alt = 6;
|
||||
if (mode == 3) alt = 6; // left-gimp: force back to the R step
|
||||
if (mode == 4) alt = 0x17; // right-gimp: enter wgr on this L step
|
||||
return LegTransition(alt, carryover * cyc * gts / walkStrideLength, 1);
|
||||
}
|
||||
// -- run cycles (0x4a7c33 / 0x4a7c93): --
|
||||
case 0xa: case 0xc:
|
||||
{
|
||||
bool cont = (spd >= reverseSpeedMax) || ((cyc - fcr * carryover) >= reverseSpeedMax);
|
||||
if (cont) // LAB_004a7c79
|
||||
return LegTransition(0xd, carryover * cyc * gts / reverseStrideLength, 1);
|
||||
next = 0xf; break;
|
||||
}
|
||||
case 0xb: case 0xd:
|
||||
{
|
||||
bool cont = (spd >= reverseSpeedMax) || ((cyc - fcr * carryover) >= reverseSpeedMax);
|
||||
if (cont)
|
||||
return LegTransition(0xc, carryover * cyc * gts / reverseStrideLength, 1);
|
||||
next = 0xe; break;
|
||||
}
|
||||
// -- back cycles: 0x10/0x12 <-> 0x11/0x13 --
|
||||
case 0x10: case 0x12:
|
||||
{
|
||||
bool up = (spd > gimpSpeedMax) && ((gimpCycleRate * carryover + cyc) > gimpSpeedMax);
|
||||
if (up) { next = 0x15; break; }
|
||||
Scalar t = carryover * cyc * gts / gimpStrideLength;
|
||||
if (t <= 0.0f) t = -t;
|
||||
return LegTransition(0x13, t, 1);
|
||||
}
|
||||
case 0x11: case 0x13:
|
||||
{
|
||||
bool up = (spd > gimpSpeedMax) && ((gimpCycleRate * carryover + cyc) > gimpSpeedMax);
|
||||
if (up) { next = 0x14; break; }
|
||||
Scalar t = carryover * cyc * gts / gimpStrideLength;
|
||||
if (t <= 0.0f) t = -t;
|
||||
return LegTransition(0x12, t, 1);
|
||||
}
|
||||
// -- GIMP cycles: left 0x16/0x18, right 0x17/0x19 --
|
||||
case 0x16: case 0x18:
|
||||
{
|
||||
bool cont = (spd >= gimpLeftSpeedMax) || ((cyc - fcr * carryover) >= gimpLeftSpeedMax);
|
||||
if (cont)
|
||||
return LegTransition(0x18, carryover * cyc * gts / gimpLeftStrideLength, 1);
|
||||
next = 0x1a; break;
|
||||
}
|
||||
case 0x17: case 0x19:
|
||||
{
|
||||
bool cont = (spd >= gimpRightSpeedMax) || ((cyc - fcr * carryover) >= gimpRightSpeedMax);
|
||||
if (cont)
|
||||
return LegTransition(0x19, carryover * cyc * gts / gimpRightStrideLength, 1);
|
||||
next = 0x1b; break;
|
||||
}
|
||||
}
|
||||
return LegTransition(next, T, 1); // shared plain tail (mj=1)
|
||||
}
|
||||
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
// AdvanceLegAnimation (channel A, ground)
|
||||
@@ -664,7 +919,7 @@ Scalar
|
||||
// narrow near-zero entry gate (the pre-#64b accommodation). [T3]
|
||||
const int trnIsRepl = (GetInstance() == ReplicantInstance);
|
||||
const Scalar trnEntryMax = trnIsRepl ? standSpeed * 0.25f : standSpeed;
|
||||
if (hasCrashSet != 0 && mppr != 0
|
||||
if (turnCapable != 0 && mppr != 0
|
||||
&& commandedSpeed >= ZeroSpeed && commandedSpeed <= trnEntryMax
|
||||
&& (mppr->turnDemand > 0.05f
|
||||
|| mppr->turnDemand < -0.05f)
|
||||
@@ -1112,38 +1367,47 @@ Scalar
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
// AdvanceBodyAnimationAirborne (channel B, jump-capable)
|
||||
// AdvanceBodyAnimationGimp (channel B, limping)
|
||||
//
|
||||
// @004a5bf8 (MECH2.CPP:0x2E8)
|
||||
//
|
||||
// Airborne variant of AdvanceBodyAnimation selected by Mech::IntegrateMotion
|
||||
// when jump jets are active. Adds a pre-clamp of bodyTargetSpeed to the jump
|
||||
// speed limits and handles the FallForward / FallBackward jump cycles
|
||||
// (states 0x18/0x19); gimp-to-stand (0x16/0x17) and the lateral falls
|
||||
// (0x1a/0x1b) join the normal advance group here rather than resetting.
|
||||
// GIMP (limp-gait) variant of AdvanceBodyAnimation, selected when
|
||||
// (gimpLevel 3|4) && hasGimpClips ("Airborne"/jump-jet was a misread -- the
|
||||
// clip set is wg/gg/gs, #78). Adds a pre-clamp of bodyTargetSpeed to the
|
||||
// gimped side's speed cap and drives the gg limp cycles (0x18 left / 0x19
|
||||
// right) at gimp cadence; the wg entries (0x16/0x17) and gs exits (0x1a/0x1b)
|
||||
// join the plain advance group. Standing (case 0) only ever enters FORWARD
|
||||
// walk -- the binary refuses reverse while gimped.
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
Scalar
|
||||
Mech::AdvanceBodyAnimationAirborne(Scalar time_slice, int loop)
|
||||
Mech::AdvanceBodyAnimationGimp(Scalar time_slice, int loop)
|
||||
{
|
||||
Scalar distance = 0.0f;
|
||||
|
||||
// RE-SYNC alarm -> state member (the binary's one cell is split in the
|
||||
// recon, same as the ground drivers at :831/:1181). Without this the
|
||||
// member freezes at its pre-gimp value the moment this driver takes over
|
||||
// and the whole machine pins in one state (live-diagnosed 2026-07-30).
|
||||
bodyAnimationState = (int)bodyStateAlarm.GetLevel();
|
||||
|
||||
//
|
||||
// While in any forward/reverse/gimp *moving* cycle, clamp the target to
|
||||
// the jump speed limit for the current gait, then floor at zero.
|
||||
// the gimp speed limit for the current gait, then floor at zero.
|
||||
//
|
||||
{
|
||||
int state = bodyAnimationState; // this+0x728
|
||||
if ((unsigned)(state - 6) < 2 || (unsigned)(state - 0x0c) < 2
|
||||
|| (unsigned)(state - 0x12) < 2)
|
||||
{
|
||||
if (MovementMode() == 3) // run jump
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (gotcha #23)
|
||||
if (BTMechGimpLevel(this) == 3) // left leg gimped
|
||||
{
|
||||
if (bodyTargetSpeed > jumpRunSpeedMax) bodyTargetSpeed = jumpRunSpeedMax; // 0x53c
|
||||
if (bodyTargetSpeed > gimpLeftSpeedMax) bodyTargetSpeed = gimpLeftSpeedMax; // 0x53c
|
||||
}
|
||||
else // walk jump
|
||||
{
|
||||
if (bodyTargetSpeed > jumpWalkSpeedMax) bodyTargetSpeed = jumpWalkSpeedMax; // 0x540
|
||||
if (bodyTargetSpeed > gimpRightSpeedMax) bodyTargetSpeed = gimpRightSpeedMax; // 0x540
|
||||
}
|
||||
if (bodyTargetSpeed < ZeroSpeed) bodyTargetSpeed = ZeroSpeed;
|
||||
}
|
||||
@@ -1259,7 +1523,8 @@ Scalar
|
||||
case 0x18: case 0x19: // FallForward / FallBackward (jump)
|
||||
{
|
||||
Scalar ratio;
|
||||
if (MovementMode() == 3) // run jump: caps 0x53c / 0x544
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (gotcha #23)
|
||||
if (BTMechGimpLevel(this) == 3) // left-gimp cycle: caps 0x53c / 0x544
|
||||
{
|
||||
if (bodyTargetSpeed <= bodyCycleSpeed)
|
||||
{
|
||||
@@ -1267,16 +1532,16 @@ Scalar
|
||||
{
|
||||
bodyCycleSpeed -= forwardCycleRate * time_slice;
|
||||
if (bodyCycleSpeed < bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||
if (bodyCycleSpeed < jumpRunSpeedMax) bodyCycleSpeed = jumpRunSpeedMax; // 0x53c
|
||||
if (bodyCycleSpeed < gimpLeftSpeedMax) bodyCycleSpeed = gimpLeftSpeedMax; // 0x53c
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyCycleSpeed += forwardCycleRate * time_slice;
|
||||
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||
if (bodyCycleSpeed > jumpRunStrideLength) bodyCycleSpeed = jumpRunStrideLength; // 0x544
|
||||
if (bodyCycleSpeed > gimpLeftStrideLength) bodyCycleSpeed = gimpLeftStrideLength; // 0x544
|
||||
}
|
||||
ratio = bodyCycleSpeed / jumpRunStrideLength; // 0x544
|
||||
ratio = bodyCycleSpeed / gimpLeftStrideLength; // 0x544
|
||||
}
|
||||
else // walk jump: caps 0x540 / 0x548
|
||||
{
|
||||
@@ -1286,16 +1551,16 @@ Scalar
|
||||
{
|
||||
bodyCycleSpeed -= forwardCycleRate * time_slice;
|
||||
if (bodyCycleSpeed < bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||
if (bodyCycleSpeed < jumpWalkSpeedMax) bodyCycleSpeed = jumpWalkSpeedMax; // 0x540
|
||||
if (bodyCycleSpeed < gimpRightSpeedMax) bodyCycleSpeed = gimpRightSpeedMax; // 0x540
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyCycleSpeed += forwardCycleRate * time_slice;
|
||||
if (bodyCycleSpeed > bodyTargetSpeed) bodyCycleSpeed = bodyTargetSpeed;
|
||||
if (bodyCycleSpeed > jumpWalkStrideLength) bodyCycleSpeed = jumpWalkStrideLength; // 0x548
|
||||
if (bodyCycleSpeed > gimpRightStrideLength) bodyCycleSpeed = gimpRightStrideLength; // 0x548
|
||||
}
|
||||
ratio = bodyCycleSpeed / jumpWalkStrideLength; // 0x548
|
||||
ratio = bodyCycleSpeed / gimpRightStrideLength; // 0x548
|
||||
}
|
||||
distance = bodyAnimation.Advance(
|
||||
time_slice * ratio * globalTimeScale, loop);
|
||||
@@ -1315,24 +1580,36 @@ Scalar
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
// AdvanceLegAnimationAirborne (channel A, jump-capable)
|
||||
// AdvanceLegAnimationGimp (channel A, limping)
|
||||
//
|
||||
// @004a71f4 (MECH2.CPP:0x672)
|
||||
//
|
||||
// Airborne variant of AdvanceLegAnimation. Like the ground version it reads
|
||||
// the live commanded speed from the controls subsystem, but here it also
|
||||
// CLAMPS that source value to the jump speed limit (writing it back), and
|
||||
// handles the FallForward / FallBackward jump cycles (0x18/0x19). No death
|
||||
// latch / footstep block; gimp-to-stand and lateral falls join the normal
|
||||
// advance group.
|
||||
// GIMP variant of AdvanceLegAnimation (see the body variant's note). Like
|
||||
// the ground version it reads the live commanded speed from the controls
|
||||
// subsystem, but here it also CLAMPS that source value to the gimped side's
|
||||
// speed cap (writing it back -- the authentic "can't outrun a shot leg"),
|
||||
// and drives the gg limp cycles (0x18/0x19). No death latch / footstep
|
||||
// block; wg entries and gs exits join the normal advance group.
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
Scalar
|
||||
Mech::AdvanceLegAnimationAirborne(Scalar time_slice)
|
||||
Mech::AdvanceLegAnimationGimp(Scalar time_slice)
|
||||
{
|
||||
ReconMotionSource *motionSource = *(ReconMotionSource **)(this->controlSource); // **(this+0x128)
|
||||
// Binary: **(this+0x128) + 0x128 = the mapper's live speedDemand. The
|
||||
// port's controlSource@0x128 is NOT wired (null -> the first live gimp
|
||||
// engagement crashed here, 2026-07-30 bench) -- use the roster idiom the
|
||||
// ground driver + LegClipFinished use (MappingMapper), pointing the shim
|
||||
// straight at the speedDemand cell.
|
||||
MechControlsMapper *gimpMppr = MappingMapper();
|
||||
static Scalar s_nullDemand = 0.0f; // no mapper -> demand 0, writes inert
|
||||
ReconMotionSource *motionSource = gimpMppr
|
||||
? (ReconMotionSource *)&gimpMppr->speedDemand
|
||||
: (ReconMotionSource *)&s_nullDemand;
|
||||
Scalar distance = 0.0f;
|
||||
int mode = MovementMode(); // this+0x40 = simulationState
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (gotcha #23)
|
||||
int mode = BTMechGimpLevel(this); // binary this+0x40 = gimp level
|
||||
// RE-SYNC alarm -> state member (see AdvanceBodyAnimationGimp note).
|
||||
legAnimationState = (int)legStateAlarm.GetLevel();
|
||||
int state = legAnimationState; // this+0x3b0
|
||||
|
||||
//
|
||||
@@ -1344,13 +1621,13 @@ Scalar
|
||||
{
|
||||
if (mode == 3)
|
||||
{
|
||||
if (motionSource->commandedSpeed > jumpRunSpeedMax)
|
||||
motionSource->commandedSpeed = jumpRunSpeedMax; // 0x53c
|
||||
if (motionSource->commandedSpeed > gimpLeftSpeedMax)
|
||||
motionSource->commandedSpeed = gimpLeftSpeedMax; // 0x53c
|
||||
}
|
||||
else
|
||||
{
|
||||
if (motionSource->commandedSpeed > jumpWalkSpeedMax)
|
||||
motionSource->commandedSpeed = jumpWalkSpeedMax; // 0x540
|
||||
if (motionSource->commandedSpeed > gimpRightSpeedMax)
|
||||
motionSource->commandedSpeed = gimpRightSpeedMax; // 0x540
|
||||
}
|
||||
if (motionSource->commandedSpeed < ZeroSpeed)
|
||||
motionSource->commandedSpeed = ZeroSpeed;
|
||||
@@ -1477,16 +1754,16 @@ Scalar
|
||||
{
|
||||
legCycleSpeed -= forwardCycleRate * time_slice;
|
||||
if (legCycleSpeed < motionSource->commandedSpeed) legCycleSpeed = motionSource->commandedSpeed;
|
||||
if (legCycleSpeed < jumpRunSpeedMax) legCycleSpeed = jumpRunSpeedMax;
|
||||
if (legCycleSpeed < gimpLeftSpeedMax) legCycleSpeed = gimpLeftSpeedMax;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
legCycleSpeed += forwardCycleRate * time_slice;
|
||||
if (legCycleSpeed > motionSource->commandedSpeed) legCycleSpeed = motionSource->commandedSpeed;
|
||||
if (legCycleSpeed > jumpRunStrideLength) legCycleSpeed = jumpRunStrideLength;
|
||||
if (legCycleSpeed > gimpLeftStrideLength) legCycleSpeed = gimpLeftStrideLength;
|
||||
}
|
||||
ratio = legCycleSpeed / jumpRunStrideLength; // 0x544
|
||||
ratio = legCycleSpeed / gimpLeftStrideLength; // 0x544
|
||||
}
|
||||
else // walk jump
|
||||
{
|
||||
@@ -1496,16 +1773,16 @@ Scalar
|
||||
{
|
||||
legCycleSpeed -= forwardCycleRate * time_slice;
|
||||
if (legCycleSpeed < motionSource->commandedSpeed) legCycleSpeed = motionSource->commandedSpeed;
|
||||
if (legCycleSpeed < jumpWalkSpeedMax) legCycleSpeed = jumpWalkSpeedMax;
|
||||
if (legCycleSpeed < gimpRightSpeedMax) legCycleSpeed = gimpRightSpeedMax;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
legCycleSpeed += forwardCycleRate * time_slice;
|
||||
if (legCycleSpeed > motionSource->commandedSpeed) legCycleSpeed = motionSource->commandedSpeed;
|
||||
if (legCycleSpeed > jumpWalkStrideLength) legCycleSpeed = jumpWalkStrideLength;
|
||||
if (legCycleSpeed > gimpRightStrideLength) legCycleSpeed = gimpRightStrideLength;
|
||||
}
|
||||
ratio = legCycleSpeed / jumpWalkStrideLength; // 0x548
|
||||
ratio = legCycleSpeed / gimpRightStrideLength; // 0x548
|
||||
}
|
||||
distance = legAnimation.Advance(time_slice * ratio * globalTimeScale, 1);
|
||||
}
|
||||
|
||||
@@ -212,13 +212,13 @@ template<class...A> inline void *FUN_00406db4(A&&...) { return 0; } // ResourceF
|
||||
// @0x530 standSpeed = last-keyframe stride of the stand->walk clip
|
||||
// @0x534 walkStrideLength = (s_6+s_7)/(d_6+d_7) (forward walk/run)
|
||||
// @0x538 reverseSpeedMax = last-keyframe stride of the reverse-base clip
|
||||
// @0x53c jumpRunSpeedMax = last-keyframe stride of the run-jump clip
|
||||
// @0x540 jumpWalkSpeedMax = last-keyframe stride of the walk-jump clip
|
||||
// @0x544 jumpRunStrideLength = s/d of fall-forward jump clip (slot 0x18)
|
||||
// @0x548 jumpWalkStrideLength = s/d of fall-backward jump clip (slot 0x19)
|
||||
// @0x580 jumpCapable (int) set 1 iff the model defines the jump clip set
|
||||
// @0x584 hasReverseGimpSet (int)? set 1 iff the second optional clip set exists
|
||||
// @0x588 hasCrashSet (int)? set 1 iff the third optional clip set exists
|
||||
// @0x53c gimpLeftSpeedMax = last-keyframe stride of the run-jump clip
|
||||
// @0x540 gimpRightSpeedMax = last-keyframe stride of the walk-jump clip
|
||||
// @0x544 gimpLeftStrideLength = s/d of fall-forward jump clip (slot 0x18)
|
||||
// @0x548 gimpRightStrideLength = s/d of fall-backward jump clip (slot 0x19)
|
||||
// @0x580 hasGimpClips (int) set 1 iff the model defines the jump clip set
|
||||
// @0x584 squatCapable (int)? set 1 iff the second optional clip set exists
|
||||
// @0x588 turnCapable (int)? set 1 iff the third optional clip set exists
|
||||
// @0x5b8 groundCycleRate forward-cycle slew rate, on-ground (-> 0x344)
|
||||
// @0x5bc airborneCycleRate forward-cycle slew rate, airborne (-> 0x344)
|
||||
// @0x5c4 gyroRumbleTimer (float) reset to 0 at the top of a clip load
|
||||
@@ -319,7 +319,7 @@ void
|
||||
// stride @0x534, reverseSpeedMax @0x538, reverse stride @0x34c, gimpSpeedMax
|
||||
// @0x52c, gimp stride @0x350), then probe for the three OPTIONAL clip sets
|
||||
// (jump / reverse-gimp / crash) and, if their marker resource exists, resolve
|
||||
// those too and set the matching capability flag (jumpCapable @0x580 etc.).
|
||||
// those too and set the matching capability flag (hasGimpClips @0x580 etc.).
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
void
|
||||
@@ -406,10 +406,10 @@ void
|
||||
// ---- OPTIONAL clip set 1: JUMP JETS ----
|
||||
// Probe for the run-jump marker; if present, the model is jump-capable.
|
||||
//
|
||||
jumpCapable = 0; // this+0x580
|
||||
hasGimpClips = 0; // this+0x580
|
||||
if (ResolveAnimationClip(prefix, "wgl") != 0) // FUN_00406ff8 probe (real)
|
||||
{
|
||||
jumpCapable = 1;
|
||||
hasGimpClips = 1;
|
||||
|
||||
// SLOT MAP BINARY-VERIFIED (raw :13787-13812 + .data @0050d921-35):
|
||||
// wgl->0x624 [22], wgr->0x628 [23], ggr->0x62c [24], ggl->0x630 [25],
|
||||
@@ -417,19 +417,19 @@ void
|
||||
// shifted the rest one slot low.)
|
||||
animationClips[22] = *ResolveAnimationClip(prefix, "wgl"); // 0x624 state 0x16
|
||||
legAnimation.SelectSequence(animationClips[22], PTR_LAB_0050d738, DAT_0050d73c, DAT_0050d740);
|
||||
jumpRunSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x53c
|
||||
gimpLeftSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x53c
|
||||
|
||||
animationClips[23] = *ResolveAnimationClip(prefix, "wgr"); // 0x628 state 0x17
|
||||
legAnimation.SelectSequence(animationClips[23], PTR_LAB_0050d744, DAT_0050d748, DAT_0050d74c);
|
||||
jumpWalkSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x540
|
||||
gimpRightSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x540
|
||||
|
||||
animationClips[24] = *ResolveAnimationClip(prefix, "ggr"); // 0x62c state 0x18
|
||||
MeasureClipStride(0x18, &s0, &d0);
|
||||
jumpRunStrideLength = s0 / d0; // 0x544
|
||||
gimpLeftStrideLength = s0 / d0; // 0x544
|
||||
|
||||
animationClips[25] = *ResolveAnimationClip(prefix, "ggl"); // 0x630 state 0x19
|
||||
MeasureClipStride(0x19, &s0, &d0);
|
||||
jumpWalkStrideLength = s0 / d0; // 0x548
|
||||
gimpRightStrideLength = s0 / d0; // 0x548
|
||||
|
||||
animationClips[26] = *ResolveAnimationClip(prefix, "gsl"); // 0x634 state 0x1A
|
||||
animationClips[27] = *ResolveAnimationClip(prefix, "gsr"); // 0x638 state 0x1B
|
||||
@@ -443,10 +443,10 @@ void
|
||||
// suffixes/slots and stored into side members SetLeg/BodyAnimation never
|
||||
// consult -- states 2/3 read animationClips[] directly.)
|
||||
//
|
||||
hasReverseGimpSet = 0; // this+0x584 (squatCapable)
|
||||
squatCapable = 0; // this+0x584 (squatCapable)
|
||||
if (ResolveAnimationClip(prefix, "squ") != 0)
|
||||
{
|
||||
hasReverseGimpSet = 1;
|
||||
squatCapable = 1;
|
||||
animationClips[3] = *ResolveAnimationClip(prefix, "squ"); // 0x5d8
|
||||
animationClips[2] = *ResolveAnimationClip(prefix, "sqd"); // 0x5d4
|
||||
}
|
||||
@@ -457,14 +457,14 @@ void
|
||||
// flag @0x588 (turnCapable); loads trn -> 0x5dc == animationClips[4] --
|
||||
// the state-4 turn-in-place clip (SetLegAnimation(4) arms it).
|
||||
//
|
||||
hasCrashSet = 0; // this+0x588 (turnCapable)
|
||||
turnCapable = 0; // this+0x588 (turnCapable)
|
||||
if (ResolveAnimationClip(prefix, "trn") != 0)
|
||||
{
|
||||
hasCrashSet = 1;
|
||||
turnCapable = 1;
|
||||
animationClips[4] = *ResolveAnimationClip(prefix, "trn"); // 0x5dc
|
||||
}
|
||||
DEBUG_STREAM << "[loadclips] end: fScale=" << forwardThrottleScale
|
||||
<< " gimpSpeedMax=" << gimpSpeedMax << " jumpCapable=" << jumpCapable << "\n" << std::flush;
|
||||
<< " gimpSpeedMax=" << gimpSpeedMax << " hasGimpClips=" << hasGimpClips << "\n" << std::flush;
|
||||
}
|
||||
|
||||
|
||||
@@ -549,25 +549,25 @@ void
|
||||
//
|
||||
// ---- OPTIONAL clip set 1: JUMP JETS (probe "wgli") ----
|
||||
//
|
||||
jumpCapable = 0; // this+0x580
|
||||
hasGimpClips = 0; // this+0x580
|
||||
if (ResolveAnimationClip(prefix, "wgli") != 0)
|
||||
{
|
||||
jumpCapable = 1;
|
||||
hasGimpClips = 1;
|
||||
animationClips[22] = *ResolveAnimationClip(prefix, "wgli"); // 0x624 state 0x16
|
||||
legAnimation.SelectSequence(animationClips[22], PTR_LAB_0050d738, DAT_0050d73c, DAT_0050d740);
|
||||
jumpRunSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x53c
|
||||
gimpLeftSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x53c
|
||||
|
||||
animationClips[23] = *ResolveAnimationClip(prefix, "wgri"); // 0x628 state 0x17
|
||||
legAnimation.SelectSequence(animationClips[23], PTR_LAB_0050d744, DAT_0050d748, DAT_0050d74c);
|
||||
jumpWalkSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x540
|
||||
gimpRightSpeedMax = legAnimation.keyframeData[legAnimation.keyframeCount].stride; // 0x540
|
||||
|
||||
animationClips[24] = *ResolveAnimationClip(prefix, "ggri"); // 0x62c state 0x18
|
||||
MeasureClipStride(0x18, &s0, &d0);
|
||||
jumpRunStrideLength = s0 / d0; // 0x544
|
||||
gimpLeftStrideLength = s0 / d0; // 0x544
|
||||
|
||||
animationClips[25] = *ResolveAnimationClip(prefix, "ggli"); // 0x630 state 0x19
|
||||
MeasureClipStride(0x19, &s0, &d0);
|
||||
jumpWalkStrideLength = s0 / d0; // 0x548
|
||||
gimpRightStrideLength = s0 / d0; // 0x548
|
||||
|
||||
animationClips[26] = *ResolveAnimationClip(prefix, "gsli"); // 0x634 state 0x1A
|
||||
animationClips[27] = *ResolveAnimationClip(prefix, "gsri"); // 0x638 state 0x1B
|
||||
@@ -576,10 +576,10 @@ void
|
||||
//
|
||||
// ---- OPTIONAL clip set 2: SQUAT (probe "squi") ----
|
||||
//
|
||||
hasReverseGimpSet = 0; // this+0x584 (squatCapable)
|
||||
squatCapable = 0; // this+0x584 (squatCapable)
|
||||
if (ResolveAnimationClip(prefix, "squi") != 0)
|
||||
{
|
||||
hasReverseGimpSet = 1;
|
||||
squatCapable = 1;
|
||||
animationClips[3] = *ResolveAnimationClip(prefix, "squi"); // 0x5d8
|
||||
animationClips[2] = *ResolveAnimationClip(prefix, "sqdi"); // 0x5d4
|
||||
}
|
||||
@@ -587,14 +587,14 @@ void
|
||||
//
|
||||
// ---- OPTIONAL clip set 3: TURN-IN-PLACE (probe "trni") ----
|
||||
//
|
||||
hasCrashSet = 0; // this+0x588 (turnCapable)
|
||||
turnCapable = 0; // this+0x588 (turnCapable)
|
||||
if (ResolveAnimationClip(prefix, "trni") != 0)
|
||||
{
|
||||
hasCrashSet = 1;
|
||||
turnCapable = 1;
|
||||
animationClips[4] = *ResolveAnimationClip(prefix, "trni"); // 0x5dc
|
||||
}
|
||||
DEBUG_STREAM << "[loadclips] INTERIOR ('i') set: fScale=" << forwardThrottleScale
|
||||
<< " walkStride=" << walkStrideLength << " jumpCapable=" << jumpCapable
|
||||
<< " walkStride=" << walkStrideLength << " hasGimpClips=" << hasGimpClips
|
||||
<< "\n" << std::flush;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,9 +974,13 @@ void
|
||||
// attached feed into its drive; the 2007 engine's mover has no feed
|
||||
// roster, so the same multiplication is applied to the speed demand here.
|
||||
// speedEffect is the wrapper's live 0..1 output (gear ratio, thermal
|
||||
// curve, 1 - zone damage); a mech with no Myomers drives at 1.0. While
|
||||
// GIMPED (mode 3/4) the demand additionally halves -- [T3: the 0.5 is VGL
|
||||
// Lynx's "roughly 50%" firsthand account, BT_GIMP_SPEED overrides].
|
||||
// curve, 1 - zone damage); a mech with no Myomers drives at 1.0.
|
||||
// GIMP slowdown: the binary's real mechanism is the gimp gait machines
|
||||
// (FUN_004a5bf8/71f4/6344/7970) -- the LIVE demand is clamped to the wg
|
||||
// clip's speed (GimpLegClipFinished writes it back) and the limp cycle
|
||||
// cadence enforces it; an EXTRA multiply here would double-slow. The
|
||||
// earlier T3 0.5 stand-in is therefore retired; BT_GIMP_SPEED remains as
|
||||
// an optional override multiplier (unset = 1.0 = authentic).
|
||||
{
|
||||
extern Scalar BTMyomersDriveOf(void *subsystem);
|
||||
Scalar drive = 1.0f;
|
||||
@@ -998,8 +1002,8 @@ void
|
||||
if (s_gimp < 0.0f)
|
||||
{
|
||||
const char *e = getenv("BT_GIMP_SPEED");
|
||||
s_gimp = (e != 0 && *e != '\0') ? (Scalar)atof(e) : 0.5f;
|
||||
if (s_gimp < 0.0f || s_gimp > 1.0f) s_gimp = 0.5f;
|
||||
s_gimp = (e != 0 && *e != '\0') ? (Scalar)atof(e) : 1.0f; // 1.0 = authentic (clamp lives in the gimp SM)
|
||||
if (s_gimp < 0.0f || s_gimp > 1.0f) s_gimp = 1.0f;
|
||||
}
|
||||
drive *= s_gimp;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user