#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:
co-authored by
Claude Fable 5
parent
37dd7f9663
commit
1570983fde
@@ -161,6 +161,16 @@ register. ⚠ The audit also flags the damage-economy item as SELF-CONTRADICTOR
|
||||
(task #8 "landed" vs the kShotDamage=12 bring-up residue) — audit before reworking.
|
||||
|
||||
## Deferred subsystems / feeds (authentic path scoped, marked in code)
|
||||
- **Environment gravity unwired — the myomer CLIMB-heat term is inert (found 2026-07-31, #85).**
|
||||
`Mover::localEnvironment` (MOVER.h:271) is declared and never populated anywhere in the port;
|
||||
`GetEnvironment()->gravityConstant` is the named analog of the 1995 Mover's gravity POINTER at
|
||||
`+0x250` (`FUN_00421e2c` does `vy -= **(+0x250)` per tick), which the myomer drive-heat
|
||||
integrator uses for its `m·g·|vy|·dt` climb-work term. The bridge (`BTMechMyomerMotionSample`)
|
||||
null-guards it, so climbing currently generates no myomer heat (flat maps: moot; cavern slopes:
|
||||
a missing cost). EnvironmentZone resources (RES type 23) exist in BTL4.RES — wiring them (or at
|
||||
least a constant-gravity Environment on mover birth) revives the term. Also relevant to any
|
||||
future airborne/jump physics (`ApplyAirResistanceAndGravity` reads the same member).
|
||||
[[subsystems]]
|
||||
- **Revolving-door Phase 2 (true mid-mission drop-in) — PLANNED, gated on a spike**
|
||||
(`docs/REVOLVING_DOOR_PLAN.md`, 2026-07-24). Open questions before any engine work:
|
||||
(a) can an `Entity::MakeMessage` be REGENERATED post-birth (serializing current state), or
|
||||
|
||||
+23
-1
@@ -66,7 +66,29 @@ 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; structural un-stub is INERT — no live mover/heat coupling).
|
||||
- **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).
|
||||
**✅ 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
|
||||
any seek (Oracle's night-7 panel-confirmed report). Operands re-grounded from the raw disasm +
|
||||
the decomp and mapped to named members via the complete-TU bridge `BTMechMyomerMotionSample`
|
||||
(mech4.cpp): `+0x1C4` vec = `localVelocity.linearMotion`; `+0x82C` vec =
|
||||
`localAcceleration.linearMotion` (the authentic 15-ring smoothed AccelerationLastFrame);
|
||||
`+0x20C` = `moverMass` (the collision divert's cell — the old "motion gain" label was wrong);
|
||||
`*(+0x250)` = the environment **gravity pointer** (`FUN_00421e2c` does `vy -= **(+0x250)`/tick).
|
||||
Physics: `heat += ratio²·(1+dmg)·[0.005·½mv² + 0.2·m·g·|vy|·dt + 0.2·m|v||a|·dt]` — kinetic work
|
||||
+ climb power + acceleration power. The binary `fabs`es v.y (the port's old draft didn't).
|
||||
**Authored tuning extracted from BTL4.RES** (all 18 mech variants share one record):
|
||||
VelocityEfficiency=0.995, AccelerationEfficiency=0.8, seek gears 0.3/0.5/0.7/0.9999 rec idx 2;
|
||||
myomer thermal profile startT=77 / degradation=1000 / failure=2000 / conductance=1.9e5 /
|
||||
thermalMass=2.5e5. Live-verified (solo arena, BT_MYO_LOG): standstill ≈ 0 generation, hard
|
||||
circling drove T 77→1225 (past degradation) and the coolant loop pulled it back to ~520
|
||||
equilibrium on slowing. ⚠ Two carried caveats: (1) the kinetic work term has **no dt** in the
|
||||
binary (per-tick accumulate, a 1995 fixed-frame assumption) → heat/s scales mildly with frame
|
||||
rate; kept verbatim, field-calibrate against pod veterans. (2) the climb term is wired but
|
||||
inert: `Mover::localEnvironment` is never populated in the port (EnvironmentZone res type 23
|
||||
unwired), so g reads 0 — see [[open-questions]].
|
||||
⚠ **The registered Performance is the WRAPPER @004b8b9c, not the inner integrator @004b8d18**
|
||||
[T1, fixed 2026-07-28]. The ctor @004b8fec stores `[0x511620]` into activePerformance, and that
|
||||
pointer resolves to `0x4b8b9c`, whose first instruction is `call 0x4b0bd0` =
|
||||
|
||||
@@ -1606,6 +1606,36 @@ static void
|
||||
}
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// BTMechMyomerMotionSample -- the five Mech motion operands of the myomer
|
||||
// drive-heat integrator @004b8d18 (issue #85), read here in a complete-Mech
|
||||
// TU and mapped to NAMED engine members per the databinding rule:
|
||||
// binary mech+0x1C4 vec -> localVelocity.linearMotion (|v|, v.y --
|
||||
// the same vector the crash block's iv2 squares, operand-confirmed)
|
||||
// binary mech+0x82C vec -> localAcceleration.linearMotion (the published
|
||||
// "AccelerationLastFrame" attribute, decomp-reference entity table)
|
||||
// binary mech+0x20C -> moverMass (same cell the collision divert
|
||||
// divides by -- NOT a "motion gain")
|
||||
// binary *(mech+0x250) -> GetEnvironment()->gravityConstant (the 1995
|
||||
// Mover holds a POINTER to the environment gravity: FUN_00421e2c does
|
||||
// vy -= **(+0x250) per tick. So the integrator's velTerm is m*g*|vy|
|
||||
// = climb work, and the heat physics reads: kinetic energy + climb
|
||||
// power + acceleration power, scaled by the authored inefficiencies.)
|
||||
//###########################################################################
|
||||
void BTMechMyomerMotionSample(void *mech_v,
|
||||
float *vel_mag, float *acc_mag, float *vel_y, float *mass, float *gravity)
|
||||
{
|
||||
Mech *m = (Mech *)mech_v;
|
||||
const Vector3D &v = m->localVelocity.linearMotion;
|
||||
const Vector3D &a = m->localAcceleration.linearMotion;
|
||||
*vel_mag = (float)Sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
*acc_mag = (float)Sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
|
||||
*vel_y = (float)v.y;
|
||||
*mass = (float)m->moverMass;
|
||||
Environment *env = m->GetEnvironment();
|
||||
*gravity = (env != 0) ? (float)env->gravityConstant : 0.0f;
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
// MechDeathHandler effect-spawn bridge (mechdmg.cpp calls this)
|
||||
//
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -331,11 +331,10 @@ class Mech;
|
||||
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)
|
||||
Scalar OwnerVelocityMag() const { return 0.0f; } // |Mech velocity|
|
||||
Scalar OwnerDriveMag() const { return 0.0f; } // |Mech drive/accel|
|
||||
Scalar OwnerVelocityY() const { return 0.0f; } // Mech velocity.y
|
||||
Scalar OwnerMotionGain() const { return 0.0f; } // Mech +0x20C
|
||||
Scalar OwnerMass() const { return 0.0f; } // *Mech +0x250
|
||||
// (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.)
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user