myomer system COMPLETE -- the 'WAVE 6 mover cutover' was a phantom built on

a misattribution; the authentic couplings are all live

THE CORRECTION [T1]: @004b9550/@004b95b8 ('ConnectToMover'/'Disconnect
FromMover') are MechWeapon::ConfigureMappables/ChooseButton -- their +0x31C
is the weapon TriggerState and **(mech+0x128) is the CONTROLS MAPPER's
button roster (the MECHWEAP.CPP assert string + the trigger-edge detector
@004b9608 sit adjacent in the image).  The binary has NO dynamic myomer->
speed feed: AvailableOutput has exactly TWO callers in the whole image --
RegisterMaxOutput (assembly) and the SeekVoltageGraph sampler -- and
speedEffect@0x31C is a published GAUGE attribute.

The authentic myomer system (now fully live):
1. drive heat (#85, this morning);
2. VITAL death -- myomers are the only authored vital=1 subsystem
   (BTL4.RES res+0x48 scan): destroying them kills the mech via the #80
   vital-crit path (this is also the ram-death mechanism -- myomers are a
   0.35-weight collision-rattle target);
3. the assembly-time gait cap: RegisterMaxOutput @004b8ef0 raises
   mech+0x7A0 to AvailableOutput(top gear) = base x ~1.4284 -- now called
   (idempotent max, per tick; the 0x358 layout lock leaves no latch room)
   with real OwnerBaseSpeed/OwnerMaxSpeed bridges; the old per-frame
   reverseSpeedMax2 heal that would have clobbered it is garbage-guarded;
4. the ENG-page power curve at correct absolute scale (real base speed);
5. electrical sourcing (low gears run on a browned-out generator).

REMOVED (inventions): the mechmppr `speedDemand *= speedEffect` demand
multiplier + BTMyomersDriveOf + the Myomers ConnectToMover/MoverAttach
family.  Damaged-but-alive myomers heat faster and drop the power curve
but do NOT slow the mech (gitea #75's premise is falsified by the binary;
the real damage slowdown is the LEG-damage gimp gait).  Seek gears change
heat/sourcing/graph -- never speed.

Verified live: a mech at myomer dmg=0.234 (speedEffect attr 0.766)
beelines at full commanded speed with dmgGain=1.234 heat; 0 faults.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Zh7PTkFy4KwTzVighLR9J
This commit is contained in:
Joe DiPrima
2026-07-31 09:17:31 -05:00
co-authored by Claude Fable 5
parent 31552c0444
commit cf8618bcb9
6 changed files with 142 additions and 126 deletions
+29 -2
View File
@@ -66,8 +66,35 @@ Making a base byte-exact GROWS every subclass — they must be re-based TOGETHER
change needs its own verification pass).
- **WAVE 5** — Torso (aim twist/elevation; joint-I/O reconstructed; the BLH record disables torso →
faithfully no visible twist).
- **WAVE 6** — Myomers (mover-coupled; the mover feed is still inert — WAVE 6 cutover — but **DRIVE
HEAT IS LIVE**, #85 fix 2026-07-31, see below).
- **WAVE 6 — ✅ MYOMER SYSTEM COMPLETE (2026-07-31). The "mover cutover" concept is DEAD — it
was built on a misattribution.** `@004b9550/@004b95b8` (the "ConnectToMover/DisconnectFromMover"
bodies) are **MechWeapon::ConfigureMappables/ChooseButton** — their `+0x31C` is the weapon
TriggerState and `**(mech+0x128)` is the CONTROLS MAPPER's button roster (proven by the
MECHWEAP.CPP assert string + the trigger-edge detector @004b9608 adjacent). **The binary has NO
dynamic myomer→speed feed**: `AvailableOutput` has exactly TWO callers in the whole image —
`RegisterMaxOutput` (assembly) and the SeekVoltageGraph sampler — and `speedEffect@0x31C` is a
published GAUGE attribute. The authentic myomer system, all live in the port now:
1. **Heat** — the drive-heat integrator (#85, below).
2. **Vital death** — myomers are the ONLY `vital=1` subsystem in the authored records
(BTL4.RES `res+0x48` scan): destroying them KILLS the mech (the #80 vital-crit path). This
is also the collision ram-death mechanism (myomers are a 0.35-weight rattle target).
3. **The assembly cap** — `RegisterMaxOutput @004b8ef0`: `mech+0x7A0 = max(cap,
AvailableOutput(topGear))` = base × ~1.4284, the gait rise-clamp both channels bound
against (part_012:12103/12334). Port: each myomer registers per tick (idempotent max();
the 0x358 layout lock leaves no latch room) — state-identical to once-at-assembly. The
old per-frame `reverseSpeedMax2 = reverseStrideLength` heal (which would have clobbered
it) is garbage-guarded only now. `Mech+0x34C` (base) = port `reverseStrideLength`,
`Mech+0x7A0` (cap) = port `reverseSpeedMax2` — both documented misnomers.
4. **The ENG-page power curve** — SeekVoltageResponse, now at the correct absolute scale
(OwnerBaseSpeed reads the real base instead of the 1.0 stub).
5. **Electrical sourcing** — low seek gears keep the myomers runnable on a browned-out
generator (HasVoltage: `gear voltage <= measured`).
**Deliberately REMOVED (was an invention): the `speedDemand *= speedEffect` demand multiplier**
(mechmppr.cpp) and `BTMyomersDriveOf`. Damaged-but-alive myomers heat faster and drop the
power curve but do NOT slow the mech — gitea #75's premise is falsified by the binary; the
mech's damage slowdown is the LEG-damage gimp gait, a separate live mechanism. Seek gears do
NOT change speed (heat + sourcing + graph only). Verified live: a mech with dmg=0.234
(speedEffect attr 0.766) beelines at full commanded speed with dmgGain=1.234 heat.
**✅ MYOMER DRIVE HEAT LIVE (#85, 2026-07-31) [T2].** The five Mech motion operands of the
drive-heat integrator `@004b8d18` were `return 0.0f` cross-family shims, so the (fully
reconstructed) integrator accumulated `ratio² · damageGain · 0` every tick — myomers ice cold at
+2
View File
@@ -108,6 +108,8 @@ The whole roster sits on the BT base chain `engine Subsystem → MechSubsystem
dampingConstant@0x1F4, ...; recon mislabels + leaves accumulators uninit). Reverted to the stub (no NaN to the root
joint). Remaining = a full ctor+integrator reconstruction from @004b3778 (bigger than the torso). Full detail: CLAUDE.md
§10d "WAVE-5 GYROSCOPE". Also: the mech.cpp gyro↔torso cross-link write is broken (SubProxy::linkTarget→gyro+4) — commented out.
- **WAVE 6 — ✅ COMPLETE (2026-07-31): the "mover cutover" was a phantom.** @004b9550/@004b95b8 are MechWeapon button-mapping functions (misattribution); the binary has NO dynamic myomer→speed feed (AvailableOutput's only callers: RegisterMaxOutput at assembly + the SeekVoltageGraph sampler; speedEffect@0x31C is a gauge attribute). Landed: real drive-heat operands (#85), RegisterMaxOutput → mech+0x7A0 gait cap (idempotent per-tick), real OwnerBaseSpeed (graph scale), the invented `speedDemand *= speedEffect` multiplier REMOVED, ConnectToMover/BTMyomersDriveOf deleted. Myomer damage does not slow a live mech; destruction kills (the only authored vital=1 subsystem). Full authority: context/subsystems.md WAVE 6.
(Historical entry below kept for the record:)
- **WAVE 6 — Mover-coupled: ◐ STRUCTURAL un-stub DONE + WIRED (gated `BT_MYOMERS` default ON; verified INERT, no locomotion/combat regression).** Myomers (0xBC6) constructs + ticks its real Performance via `CreateMyomersSubsystem`, but is INERT: `MyomersSimulation` early-returns on `OwnerAdvancedDamage()==False` (real gate = messmgr 0xBD3), and the mover feed (`MoverAttach`) + `SetOwnerMaxSpeed` are no-op stubs so the live JointedMover is never touched. De-shim dropped the `owner*`/`segmentFlags` shim fields (accessors return neutral defaults) to fit the exact-0x358 alloc (`sizeof` lock). ⚠ AUTHENTIC COUPLING DEFERRED: routing `SpeedEffect@0x31C` into the mover + real owner-motion accessors + the advanced-damage heat gate — this DRIVES THE LIVE MOVER, so reconcile with the gait cutover FIRST. BLH tick 26→27; `BT_MYOMERS=0` → Actuator stub. See CLAUDE.md §10d "WAVE 6".
- **WAVE 7 — Projectile/missile weapons:** ProjectileWeapon (0xBCD), MissileLauncher (0xBD0). **PARTIALLY BLOCKED:** case 0xBCE calls `@4bdcb4`, which has no CLASSMAP entry — decompile it first and resolve the 0xBCD/0xBCE/0xBD0 label tangle (Risk 1) before swapping these.
- **WAVE 8 — Message hub:** SubsystemMessageManager (0xBD3). Real, but **verify the cache-slot/tick-bypass interaction** (Risk 4) before enabling its tick.
+31 -4
View File
@@ -1636,6 +1636,30 @@ void BTMechMyomerMotionSample(void *mech_v,
*gravity = (env != 0) ? (float)env->gravityConstant : 0.0f;
}
// Myomer owner speed-cell bridges (myomer-system completion, 2026-07-31):
// Mech+0x34C = the run-speed base the mapper multiplies demand by (port
// member `reverseStrideLength` -- documented misnomer), the
// base AvailableOutput scales by;
// Mech+0x7A0 = the gait rise-CLAMP (port member `reverseSpeedMax2` --
// misnomer) that Myomers::RegisterMaxOutput raises to
// AvailableOutput(top gear) = base x ~1.4284.
float BTMechRunSpeedBase(void *mech_v)
{
Mech *m = (Mech *)mech_v;
return (m != 0) ? (float)m->reverseStrideLength : 0.0f;
}
float BTMechTopSpeedGet(void *mech_v)
{
Mech *m = (Mech *)mech_v;
return (m != 0) ? (float)m->reverseSpeedMax2 : 0.0f;
}
void BTMechTopSpeedSet(void *mech_v, float v)
{
Mech *m = (Mech *)mech_v;
if (m != 0)
m->reverseSpeedMax2 = (Scalar)v;
}
//###########################################################################
// MechDeathHandler effect-spawn bridge (mechdmg.cpp calls this)
//
@@ -3944,10 +3968,13 @@ void
// reverseSpeedMax2@0x7a0 is the run-cycle bodyCycleSpeed CLAMP (AdvanceBody
// Animation case 12/13); LoadLocomotionClips does not set it -> it reads
// 0xCDCDCDCD (-4.3e8) and the clamp clobbers bodyCycleSpeed -> the run cycle
// explodes. Bring-up: clamp the run cadence to the run stride (reverseStride
// Length, the case-12 divisor) so bodyCycleSpeed/reverseStrideLength ~ 1, like
// walk. (The authentic value comes from the model/LoadLocomotionClips.)
reverseSpeedMax2 = reverseStrideLength;
// explodes. GARBAGE-GUARDED only (myomer completion 2026-07-31): the
// authentic writer is Myomers::RegisterMaxOutput -- ctor zeroes it, each
// myomer raises it to AvailableOutput(top gear) = base x ~1.4284 -- and the
// old unconditional per-frame heal CLOBBERED that registration back to
// base x 1.0 every frame. Heal only a value that is clearly uninit junk.
if (reverseSpeedMax2 < 0.001f || reverseSpeedMax2 > 500.0f)
reverseSpeedMax2 = reverseStrideLength;
// Gait SELECTION by throttle: walk aims for walkStrideLength; run aims for
// reverseSpeedMax (the run/reverse cap). Exceeding walkStrideLength makes the
// walk handler transition "up" to the run cycle (state 11 -> 12/13) via the real
+14 -28
View File
@@ -969,31 +969,19 @@ 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.
// 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).
// (The myomer `speedDemand *= speedEffect` DRIVE SCALE is GONE -- myomer-
// system completion, 2026-07-31. It was an invention built on the
// ConnectToMover misattribution: the binary has NO dynamic myomer->speed
// coupling. AvailableOutput's only callers are RegisterMaxOutput (the
// assembly-time gait cap, now live via MyomersSimulation) and the ENG-page
// graph sampler; damaged-but-alive myomers heat faster and drop the power
// curve but do NOT slow the mech, and DESTROYED myomers KILL it (the
// authored vital flag + the #80 vital-crit death). GIMP slowdown: the
// binary's real mechanism is the gimp gait machines -- the LIVE demand is
// clamped to the wg clip's speed and the limp cadence enforces it;
// BT_GIMP_SPEED remains as an optional override multiplier (unset = 1.0 =
// authentic).)
{
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)
@@ -1005,17 +993,15 @@ void
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;
speedDemand *= 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
DEBUG_STREAM << "[drive] mm=" << mm << " dmd=" << speedDemand
<< " mech=" << (int)mech->GetEntityID()
<< " @" << (void *)mech << "\n" << std::flush;
}
+29 -68
View File
@@ -414,28 +414,13 @@ Scalar Myomers::SeekVoltageResponse(Scalar input_voltage)
// (see emitter.cpp BTSeekVoltageSample). Guarded: a non-Myomers subsystem
// samples 0 (only emitter/myomer pages own a graph in the shipped config).
//***************************************************************************
//***************************************************************************
// #75 bridge -- the myomers' live drive fraction for the mech's speed demand.
//
// The authentic coupling: ConnectToMover attached &speedEffect into the
// mech's mover, which multiplied every attached feed into its drive. The
// 2007 engine's mover has no such roster, so the port applies the same
// multiplication at the speed-demand site (mechmppr.cpp): demand *= this.
// speedEffect@0x31C is the wrapper's live 0..1 output -- gear ratio, thermal
// curve and (1 - zone damage) -- already republished every tick. A mech
// with no Myomers (or no roster yet) drives at 1.0; a DESTROYED myomers
// drives 0.0 (no drive -- the subsystem that moves the mech is gone).
//***************************************************************************
Scalar BTMyomersDriveOf(void *subsystem)
{
Entity *entity = (Entity *)subsystem;
if (entity == 0 || !entity->IsDerivedFrom(Myomers::ClassDerivations))
return -1.0f; // not a Myomers
Scalar f = ((Myomers *)entity)->SpeedEffect();
if (f < 0.0f) f = 0.0f;
if (f > 1.0f) f = 1.0f;
return f;
}
// (BTMyomersDriveOf is DELETED -- myomer-system completion, 2026-07-31. The
// demand-side `speedDemand *= speedEffect` coupling it fed (mechmppr.cpp) was
// an invention built on the ConnectToMover misattribution. The binary's only
// runtime AvailableOutput consumers are RegisterMaxOutput (assembly) and the
// SeekVoltageGraph sampler; myomer damage does NOT slow a live mech -- a
// DESTROYED myomers KILLS it, via the authored vital flag (the only vital=1
// subsystem in the RES) + the #80 vital-crit death path.)
Scalar BTMyomersSeekSample(void *subsystem, Scalar voltage)
{
@@ -505,6 +490,20 @@ static const Scalar MyomerRecoveryPerTick = -0.010999999940395355f; // 0xbc34395
//
void Myomers::MyomersSimulation(Scalar time_slice)
{
// 0. RegisterMaxOutput (@004b8ef0) -- the assembly-time top-speed cap.
// The binary calls it from the mech-assembly loop over the myomer chain
// (part_012.c:10322), after the model record is streamed:
// mech[0x7A0] = max(mech[0x7A0], AvailableOutput(seekVoltage[maxIdx]))
// = base x (topGear/recGear) = base x ~1.4284 on a healthy assembly --
// the gait rise-clamp both channels bound against (part_012:12103/12334).
// The port's capability-chain fill is the SubProxy stub (same situation as
// the flush's chain walk), so each myomer registers ITSELF here. Called
// per tick instead of once (the 0x358 layout lock leaves no room for a
// latch member): RegisterMaxOutput is an idempotent max() -- best falls as
// heat/damage degrade AvailableOutput, and max() never lowers the cap --
// so the resulting state is identical to the binary's once-at-assembly.
RegisterMaxOutput(); // @004b8ef0
// 1. @004b8bab -- the base electrical state machine. MUST come first, and
// must precede any heat-model gate: OwnerAdvancedDamage() is off below
// veteran, so gating ahead of this denied the machine to most pilots.
@@ -739,52 +738,14 @@ void Myomers::ResetToInitialState(Logical powered)
//***************************************************************************
// Myomers::ConnectToMover (mislabeled)
//***************************************************************************
//
// ⚠ MISATTRIBUTION (task #5 correction): @004b9550/@004b95b8 are NOT Myomers
// functions -- the weapon message-table @0x511860 proves they are
// MechWeapon::ConfigureMappables (id 9) / ChooseButton (id 10), the weapon->
// button grouping handlers (the +0x31C coincidence: Myomers speedEffect vs
// MechWeapon TriggerState). The bodies below are retained as the port's
// Myomers stand-ins but carry no binary address.
//
// Attaches the live SpeedEffect output (@0x31C) into the Mech's JointedMover
// roster (the mover at **(Mech[0xD0]+0x128)). Driven by a connect message
// whose payload index is at message+0xC:
// index = abs(message->value) - 1;
// if (message->value < 1) { mover->Detach(index); this[0x110] = -1; }
// else { this[0x110] = 0; mover->Attach(index, &speedEffect, this, 10, 9, 0); }
// (mover vtable +0x38 = Attach, +0x3C = Detach.)
//
void Myomers::ConnectToMover(SubsystemMessage &message)
{
int index = abs(message.value) - 1; // message+0xC
if (message.value < 1) {
MoverDetach(index); // owner mover vtable +0x3C
/* base+0x110 = -1 (detached) -- inert, no base accessor yet */
} else {
/* base+0x110 = 0 (attached) -- inert, no base accessor yet */
MoverAttach(index, &speedEffect); // owner mover vtable +0x38, feed @0x31C
// (original args: index,&speedEffect,this,10,9,0)
}
}
//***************************************************************************
// Myomers::DisconnectFromMover (stand-in; no binary address)
//***************************************************************************
// Counterpart of ConnectToMover: detaches the SpeedEffect feed.
// if (message->value > 0)
// mover->Release(message->value, &speedEffect); // mover vtable +0x44
void Myomers::DisconnectFromMover(SubsystemMessage &message)
{
if (message.value > 0) { // message+0xC
MoverRelease(message.value, &speedEffect); // owner mover vtable +0x44, feed @0x31C
}
}
// (ConnectToMover / DisconnectFromMover are DELETED -- myomer-system
// completion, 2026-07-31. Their source functions @004b9550/@004b95b8 are
// MechWeapon::ConfigureMappables / ChooseButton (the +0x31C there is the
// weapon TriggerState and **(mech+0x128) is the CONTROLS MAPPER's button
// roster -- proven by the MECHWEAP.CPP assert string and the trigger-edge
// detector @004b9608 adjacent in the image). The binary has NO dynamic
// myomer->mover feed; see the RegisterMaxOutput latch in MyomersSimulation
// for the real (assembly-time cap) locomotion coupling.)
//***************************************************************************
// Myomers::ToggleSeekVoltageMessageHandler @004b8a48
//***************************************************************************
+37 -24
View File
@@ -274,16 +274,19 @@ class Mech;
HasVoltage(Subsystem *source = 0); // slot 16, @004b8f3c (overrides PoweredSubsystem::HasVoltage; Gitea #62 rename)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Mover-connection message handlers (bound via the simulation message map;
// route the live SpeedEffect @0x31C into / out of the Mech's JointedMover
// roster at owner Mech[0x128]).
// (The "mover-connection" handlers are GONE -- myomer-system completion,
// 2026-07-31. @004b9550/@004b95b8 are MechWeapon::ConfigureMappables /
// ChooseButton (the +0x31C there is TriggerState, and **(mech+0x128) is
// the CONTROLS MAPPER's button roster, not a mover) -- confirmed by the
// decomp neighborhood (the MECHWEAP.CPP assert string + the trigger-edge
// detector @004b9608 immediately after). The binary has NO dynamic
// myomer->speed feed: AvailableOutput has exactly two callers in the whole
// image (RegisterMaxOutput at assembly + the SeekVoltageGraph sampler),
// and speedEffect@0x31C is a published GAUGE attribute. The authentic
// locomotion couplings are the assembly-time top-speed cap (Mech+0x7A0)
// and the VITAL flag (myomers destroyed = mech dead -- the only vital
// subsystem in the authored records).)
//
protected:
void
ConnectToMover(SubsystemMessage &message); // @004b9550 (attach SpeedEffect feed)
void
DisconnectFromMover(SubsystemMessage &message); // @004b95b8 (detach SpeedEffect feed)
public:
// ToggleSeekVoltage message handler @004b8a48 (Myomers handler table
// @0x51158C: {9, "ToggleSeekVoltage"}). Issue #19: was an EMPTY stub +
@@ -328,13 +331,31 @@ class Mech;
// mech actually slows when damaged/overheated and motion generates heat --
// that path DOES drive the live mover, so reconcile it with the gait cutover
// FIRST (both would feed the mover).
Scalar OwnerBaseSpeed() const { return 1.0f; } // Mech +0x34C (neutral)
Scalar OwnerMaxSpeed() const { return 0.0f; } // Mech +0x7A0
void SetOwnerMaxSpeed(Scalar) { } // no-op (real: write Mech top speed)
// Myomer-system completion (2026-07-31): the owner speed cells, via
// complete-Mech-TU bridges (mech4.cpp), per the databinding rule:
// Mech+0x34C = the run-speed base (port member reverseStrideLength --
// documented misnomer; the mapper's demand multiplier)
// Mech+0x7A0 = the gait rise-CLAMP RegisterMaxOutput raises at
// assembly (port member reverseSpeedMax2 -- misnomer)
Scalar OwnerBaseSpeed() const
{
extern float BTMechRunSpeedBase(void *mech);
Scalar v = (Scalar)BTMechRunSpeedBase(owner);
return (v > 0.0f) ? v : 1.0f; // pre-stream guard (record not read yet)
}
Scalar OwnerMaxSpeed() const
{
extern float BTMechTopSpeedGet(void *mech);
return (Scalar)BTMechTopSpeedGet(owner);
}
void SetOwnerMaxSpeed(Scalar v)
{
extern void BTMechTopSpeedSet(void *mech, float v);
BTMechTopSpeedSet(owner, (float)v);
}
// (The five drive-heat motion shims are GONE -- #85 fix 2026-07-31:
// MyomersDriveHeat samples the real Mech via the complete-TU bridge
// BTMechMyomerMotionSample (mech4.cpp). The mover-feed trio below is
// still the WAVE-6 remainder.)
// BTMechMyomerMotionSample (mech4.cpp).)
// FUN_004ad7d4 [T1] -- the SAME player+0x260 heat-model experience gate
// as HeatSink::HeatModelActive() (the "advanced damage" name was a
// misread; the old `return False` stub kept movement heat off for
@@ -355,20 +376,12 @@ class Mech;
// which sensor.cpp:312 already reads the same way.
Scalar DamageStructureLevel() const{ return GetSubsystemDamageLevel(); } // DamageZone +0x158
void MoverAttach(int index, Scalar *feed) { (void)index; (void)feed; } // JointedMover vtable +0x38 (no-op)
void MoverDetach(int index) { (void)index; } // JointedMover vtable +0x3C (no-op)
void MoverRelease(int handle, Scalar *feed) { (void)handle; (void)feed; } // JointedMover vtable +0x44 (no-op)
// Live-master gate constants (the ctor reads owner->simulationFlags).
enum {
SegmentCopyMask = 0x0C, // (flags & 0xC): 0 == master, 4 == damaged copy
MasterHeatSinkFlag = 0x0100 // live-master segment bit
};
// The mover attachment handle lives at BASE+0x110 (a MechSubsystem slot), NOT a
// Myomers own field -- declaring it here appended a 4-byte tail that overflowed the
// 0x358 alloc once the PoweredSubsystem base became byte-exact. The coupling is
// inert (ConnectToMover -> no-op MoverAttach), so the handle is write-only today;
// when the real mover feed is revived, write base+0x110 via a base accessor.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local data. Offsets are byte offsets into the shipped object.
@@ -376,8 +389,8 @@ class Mech;
protected:
Scalar speedEffect; // @0x31C attr 0x12; ctor 1.0f -- live drive fed to the mover
public:
// #75: the live drive fraction for the speed-demand coupling
// (BTMyomersDriveOf -> mechmppr's demand scale).
// speedEffect is a published GAUGE attribute (attr 0x12) -- the demand
// coupling that briefly read it was an invention (removed 2026-07-31).
Scalar SpeedEffect() const { return speedEffect; }
protected:
int currentSeekVoltageIndex; // @0x320 attr 0x13; ctor = recommended -- selected drive "gear"