#85: myomer drive heat LIVE -- the five Mech motion operands were return-0

shims, so the reconstructed integrator @004b8d18 accumulated zero forever

The integrator, its gates and the heat plumbing were all reconstructed and
running -- but OwnerVelocityMag/OwnerDriveMag/OwnerVelocityY/OwnerMotionGain/
OwnerMass were cross-family compile shims returning 0.0f, so the myomers
priced ratio^2 * damageGain * 0 every tick: ice cold at any seek (Oracle's
night-7 panel-confirmed report).

Operands re-grounded against the raw disasm + decomp and mapped to NAMED
members via a complete-Mech-TU bridge (BTMechMyomerMotionSample, mech4.cpp),
per the databinding rule:
  +0x1C4 vec  -> localVelocity.linearMotion
  +0x82C vec  -> localAcceleration.linearMotion (the authentic 15-ring
                 smoothed AccelerationLastFrame the port already maintains)
  +0x20C      -> moverMass (the collision divert's cell; the old "motion
                 gain" label was a misread)
  *(+0x250)   -> environment gravity (FUN_00421e2c: vy -= **(+0x250)/tick),
                 via GetEnvironment()->gravityConstant
So the heat physics reads: 0.005 * 1/2 m v^2 (kinetic work, per-tick/no-dt
in the binary -- kept verbatim, frame-rate note documented) + 0.2 * m g
|vy| dt (climb power) + 0.2 * m |v||a| dt (acceleration power), all scaled
by ratio^2 and (1 + zone damage).  Restored the binary's fabs(v.y) (the
draft had signed vy -- descending would have COOLED).

Authored tuning extracted from BTL4.RES (18 mech records, one shared
tuning): VelocityEfficiency=0.995, AccelerationEfficiency=0.8, gears
0.3/0.5/0.7/0.9999 rec idx 2; myomer thermal profile 77/1000/2000,
conductance 1.9e5, thermalMass 2.5e5.

Live-verified (solo arena, BT_MYO_LOG + BT_HEAT_LOG): standstill generates
~0; hard circling drove Myomers T 77 -> 1225 (past the 1000 degradation
line) and the coolant loop conducted it back to ~520 equilibrium on
slowing; damageGain live at 1.048 from grind rattle.

Known-inert remainder (filed in open-questions): the climb term reads g=0
because Mover::localEnvironment is never populated in the port
(EnvironmentZone res type 23 unwired); the mover feed (speedEffect ->
locomotion) stays WAVE 6.

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 08:14:05 -05:00
co-authored by Claude Fable 5
parent 37dd7f9663
commit 1570983fde
5 changed files with 116 additions and 21 deletions
+49 -15
View File
@@ -659,30 +659,62 @@ void Myomers::MyomersSimulation(Scalar time_slice)
//
void Myomers::MyomersDriveHeat(Scalar time_slice)
{
if (!OwnerAdvancedDamage()) // FUN_004ad7d4 gate
return;
if (!OwnerAdvancedDamage()) // FUN_004ad7d4 gate (binary
return; // gates the ACCUMULATE; same outcome)
Scalar velMag = OwnerVelocityMag(); // |Mech velocity| (+0x1C4/0x1C8/0x1CC)
Scalar accMag = OwnerDriveMag(); // |Mech drive vector| (+0x82C/0x830/0x834)
Scalar vy = OwnerVelocityY(); // Mech +0x1C8
Scalar k = OwnerMotionGain(); // Mech +0x20C
Scalar m = OwnerMass(); // *Mech[0x250]
// #85 fix (2026-07-31): the five Mech motion operands were `return 0.0f`
// cross-family shims, so this integrator faithfully accumulated ratio^2 *
// damageGain * ZERO every tick -- the myomers stayed ice cold at any seek
// (Oracle's night-7 panel-confirmed report). Now sampled for real via the
// complete-Mech-TU bridge (mech4.cpp), operands re-grounded against the
// raw disasm of @004b8d18 + the authored resource (BTL4.RES 0x0BC6 records:
// VelocityEfficiency=0.995, AccelerationEfficiency=0.8, gears
// 0.3/0.5/0.7/0.9999 rec idx 2 -- one shared tuning across all 18 mechs).
float velMag, accMag, vy, mass, gravity;
{
extern void BTMechMyomerMotionSample(void *mech,
float *vel_mag, float *acc_mag, float *vel_y,
float *mass, float *gravity);
BTMechMyomerMotionSample(owner, &velMag, &accMag, &vy, &mass, &gravity);
}
Scalar velComplement = 1.0f - accelerationEfficiency; // @0x354
Scalar workComplement = 1.0f - velocityEfficiency; // @0x350
Scalar velComplement = 1.0f - accelerationEfficiency; // @0x354 (= 0.2 authored)
Scalar workComplement = 1.0f - velocityEfficiency; // @0x350 (= 0.005 authored)
Scalar damageGain = 1.0f + DamageStructureLevel(); // this[0xE0]+0x158
Scalar ratio = seekVoltage[currentSeekVoltageIndex] // @0x330[@0x320]
/ seekVoltage[recommendedSeekVoltageIndex];// @0x330[@0x324]
if (ratio < 1.0f) ratio = 1.0f;
Scalar work = k * (velMag * velMag) * 0.5f; // dot(v,v)*0.5 == |v|^2*0.5 (_DAT_004b8ee4)
// The binary fabs()es all three motion operands (the jbe/fchs pairs
// @4b8dd5/@4b8df0/@4b8e16); |v| and |a| are sqrt-positive already, but
// v.y is signed -- descending must heat like climbing, not cool.
if (vy < 0.0f) vy = -vy;
// Physics (operands nailed 2026-07-31): mass@0x20C, gravity=**(0x250) --
// work = 1/2 m v^2 (kinetic energy; NO dt in the
// binary -- the accumulate is per-TICK, an authentic 1995
// fixed-frame assumption, so heat/s scales with frame rate;
// kept verbatim, field-calibrate if the pod veterans object)
// velTerm = m g |vy| dt (climb power)
// accTerm = m |v| |a| dt (acceleration power)
Scalar work = mass * (velMag * velMag) * 0.5f; // dot(v,v)*0.5 (_DAT_004b8ee4)
pendingHeat /* @0x1C8 */ +=
ratio * ratio * damageGain *
( velComplement * vy * k * m * time_slice
( velComplement * vy * mass * gravity * time_slice
+ workComplement * work
+ velComplement * velMag * accMag * k * time_slice );
+ velComplement * velMag * accMag * mass * time_slice );
if (getenv("BT_MYO_LOG"))
{
static int s_n = 0;
if ((s_n++ % 120) == 0)
DEBUG_STREAM << "[myoheat] v=" << velMag << " a=" << accMag
<< " m=" << mass << " g=" << gravity
<< " ratio=" << ratio << " dmgGain=" << damageGain
<< " pending=" << pendingHeat << "\n" << std::flush;
}
}
@@ -807,9 +839,11 @@ struct MyomersLayoutCheck
// The real class at 0xBC6 (ctor @004b8fec) is Myomers (the mech's artificial-
// muscle drive); the factory built an Actuator RECON_SUBSYS stub. alloc 0x358.
// The subsystem constructs (resolves its Generator + builds the seek-voltage
// gear table) and ticks its real Performance, but is currently INERT w.r.t.
// locomotion/heat (no-op mover feed + the OwnerAdvancedDamage()==False sim gate)
// -- so it cannot regress the gait/drive. Authentic coupling = a follow-up.
// gear table) and ticks its real Performance. DRIVE HEAT IS LIVE (#85 fix
// 2026-07-31: the motion operands are sampled from the real Mech). Still
// inert: the LOCOMOTION coupling (no-op mover feed -- speedEffect is computed
// but nothing consumes it; WAVE 6 cutover) and the climb heat term (the
// environment gravity is unwired in the port, so g reads 0).
//===========================================================================//
Subsystem *CreateMyomersSubsystem(Mech *owner, int id, void *seg)
{