myomer damage reaches the wheels, legs gimp at half structure, reverse refuses -- and an ODR trap unmasked (#75/#78)
The speed-demand site (MechControlsMapper::InterpretControls) now applies the
drive scale the mover's feed roster applied in 1995: speedDemand multiplies by
the myomers' live speedEffect (gear ratio, thermal curve, 1 - zone damage, via
the BTMyomersDriveOf bridge) and, while GIMPED, by 0.5 [T3: VGL Lynx's
"roughly 50%", BT_GIMP_SPEED overrides]. The gimp states were already being
raised -- mechdmg sets graphicAlarm 3 (left) / 4 (right) when a LegDamageZone-
flagged zone crosses half structure -- but nothing downstream ever saw them.
While gimped, reverse input is refused ("reverse disabled"), matching the
old-timers' account; the pod's audio cue rides the alarm's watchers.
Bench, one trajectory, arithmetically exact: a deterministic right-leg ramp
(the new BT_SELF_DAMAGE_ZONE harness) crosses 0.5 and the demand goes
44.837 -> 6.726 = 44.837 x 0.5 (gimp) x 0.3 (a crit-chewed myomers from the
same ramp -- the #80 crits composing with #75's scale, unprompted).
The reason "nothing downstream ever saw them" is the real find of the night,
now gotcha #23: AlarmIndicator is typedef'd to DIFFERENT TYPES per header
family -- mech.hpp says ReconAlarm (4 bytes), heat.hpp says GaugeAlarm (0x54)
-- so Mech::graphicAlarm and EVERY member after it sit at different offsets
depending on a TU's include order. mechdmg wrote level 4 and read it back;
mechmppr read 0 from the same object, same expression. No compiler error can
catch it: each TU only ever sees one definition. Until the split is audited,
cross-TU reads of the gimp level go through BTMechGimpLevel (compiled in
mechdmg's TU) -- and the same split-brain explains why the port carries the
binary's ONE movementMode cell as two live members (engine simulationState vs
graphicAlarm level) that never meet.
Also landed en route: the Myomers un-powered self-repair observed healing in
the field logs at exactly 0.011 x the authored Explosive scale per tick --
the 2026-07-29 reversal confirmed live; and Mech message 0x15 "RealMaxSpeed"
raw-decoded (@0x49f604: sets mech+0x7a0 from the message unless the +0x7a4
latch holds -- a console-tunable top speed).
Open on #78, documented in locomotion.md: the Gimp animation clips (authored
keys in the binary's model-record parser; mech2's state enum vs mech3's
reverse-fix disagree about slots 0x12-0x17 -- reconcile before wiring) and
the audio-cue binding.
Diags: BT_SELF_DAMAGE_ZONE, BT_DRIVE_LOG, BT_GIMP_SPEED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8a99972f22
commit
520f6eecd3
@@ -933,6 +933,32 @@ void
|
||||
}
|
||||
}
|
||||
|
||||
// #78: a GIMPED mech cannot back up -- "reverse disabled". mechdmg raises
|
||||
// graphicAlarm level 3 (left leg) / 4 (right leg) when a leg zone passes
|
||||
// half structure, and MovementMode() IS that alarm's level (task #1). The
|
||||
// pod's cue fired through the alarm's audio watchers on the level change;
|
||||
// here the reverse INPUT is refused while limping (VGL Lynx's account of
|
||||
// the 4.10 behavior, night 6).
|
||||
{
|
||||
// NB read the GRAPHIC ALARM, not MovementMode(): the port carries the
|
||||
// binary's one mech+0x40 cell as TWO members (engine simulationState
|
||||
// vs graphicAlarm level) and mechdmg raises the gimp states on the
|
||||
// ALARM. See the split-brain note in combat-damage.md.
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (the TU-safe read)
|
||||
int mm = BTMechGimpLevel(mech);
|
||||
if ((mm == 3 || mm == 4) && reverseThrust >= 1)
|
||||
{
|
||||
reverseThrust = 0;
|
||||
static int s_revWarned = 0;
|
||||
if (!s_revWarned)
|
||||
{
|
||||
s_revWarned = 1;
|
||||
DEBUG_STREAM << "[gimp] REVERSE DISABLED (leg damage, mode "
|
||||
<< mm << ")\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reverseThrust < 1)
|
||||
{
|
||||
speedDemand =
|
||||
@@ -942,7 +968,58 @@ void
|
||||
{
|
||||
speedDemand = -mech->reverseStrideLength * throttlePosition;
|
||||
}
|
||||
|
||||
// #75 + #78: the DRIVE SCALE. The authentic coupling attached the
|
||||
// myomers' speedEffect into the mech's mover, which multiplied every
|
||||
// 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].
|
||||
{
|
||||
extern Scalar BTMyomersDriveOf(void *subsystem);
|
||||
Scalar drive = 1.0f;
|
||||
int n = mech->GetSubsystemCount();
|
||||
for (int i = 2; i < n; ++i) // 0/1 = mapper + voltage bus
|
||||
{
|
||||
Scalar f = BTMyomersDriveOf(mech->GetSubsystem(i));
|
||||
if (f >= 0.0f) // -1 = not a Myomers
|
||||
{
|
||||
drive = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
extern int BTMechGimpLevel(void *mech_v); // mechdmg.cpp (the TU-safe read)
|
||||
int mm = BTMechGimpLevel(mech); // the gimp cell (see above)
|
||||
if (mm == 3 || mm == 4)
|
||||
{
|
||||
static Scalar s_gimp = -1.0f;
|
||||
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;
|
||||
}
|
||||
drive *= s_gimp;
|
||||
}
|
||||
speedDemand *= drive;
|
||||
if (getenv("BT_DRIVE_LOG"))
|
||||
{
|
||||
static float s_dAcc = 0.0f; s_dAcc += time_slice;
|
||||
if (s_dAcc >= 1.0f)
|
||||
{
|
||||
s_dAcc = 0.0f;
|
||||
DEBUG_STREAM << "[drive] n=" << n << " drive=" << drive
|
||||
<< " mm=" << mm << " dmd=" << speedDemand
|
||||
<< " mech=" << (int)mech->GetEntityID()
|
||||
<< " @" << (void *)mech << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
extern int BTMechGimpLevel(void *mech_v);
|
||||
#define BTMechGimpLevelTrace BTMechGimpLevel
|
||||
static int s_cTrace = -1;
|
||||
if (s_cTrace < 0) { const char *e = getenv("BT_MPPR_TRACE"); s_cTrace = (e && *e != '0') ? 1 : 0; }
|
||||
if (s_cTrace)
|
||||
@@ -952,6 +1029,7 @@ void
|
||||
DEBUG_STREAM << "[mppr-c] thr=" << throttlePosition << " rev=" << reverseThrust
|
||||
<< " topSpd=" << mech->reverseStrideLength
|
||||
<< " fScale=" << mech->forwardThrottleScale
|
||||
<< " mm=" << BTMechGimpLevelTrace(mech) // #78: 3/4 = gimp
|
||||
<< " -> dmd=" << speedDemand
|
||||
// glass-regression verification 2026-07-20: the same trace
|
||||
// carries the turn/aim scalars so one gated line proves the
|
||||
|
||||
Reference in New Issue
Block a user