diff --git a/game/reconstructed/myomers.cpp b/game/reconstructed/myomers.cpp index a981fc8..27a2985 100644 --- a/game/reconstructed/myomers.cpp +++ b/game/reconstructed/myomers.cpp @@ -745,10 +745,38 @@ void Myomers::MyomersDriveHeat(Scalar time_slice) // accTerm = m |v| |a| dt (acceleration power) Scalar work = mass * (velMag * velMag) * 0.5f; // dot(v,v)*0.5 (_DAT_004b8ee4) - // The three terms, kept separate for diagnosis (#96 "myomers run too hot"): - // two are POWER (x dt), one is an ENERGY added per TICK. + // The three terms (#96 "myomers run too hot"). Two are POWER (x dt); the + // kinetic one is an ENERGY the binary adds once per TICK with NO dt + // (FUN_004b8d18: param_2 multiplies the climb and accel terms only). + // + // FRAME-RATE NORMALISATION. A per-tick energy makes heat/SECOND scale with + // the tick rate, which on a 1995 fixed-frame machine is a constant and on a + // modern PC is not: two players on different hardware, or the same player in + // a heavy scene, get different heat from identical throttle. Measured here + // at ~59Hz the myomers cross their degradation threshold in ~27s of seek-4 + // cruising; at the pod's documented 30Hz board frame that is ~54s. + // + // So the term is rescaled to the reference frame rate: multiply by + // dt * kPodFrameHz, which is 1.0 exactly when dt == 1/30 and makes the heat + // identical on every machine. This is the ONE deliberate divergence from a + // verbatim transcription of @004b8d18, and it exists because the binary's + // value depends on a constant our port does not have. + // + // ⚠ 30Hz is the i860 board frame ([T1], rendering.md) -- the HOST simulation + // rate is not separately established, so treat this as the reference, not as + // proven. BT_MYO_HZ overrides it without a rebuild so the pod veterans can + // calibrate it by feel. + static const Scalar kPodFrameHz = 30.0f; + static Scalar s_hz = -1.0f; + if (s_hz < 0.0f) + { + const char *hv = getenv("BT_MYO_HZ"); + s_hz = (hv && *hv) ? (Scalar)atof(hv) : kPodFrameHz; + if (s_hz <= 0.0f) s_hz = kPodFrameHz; + } + Scalar termClimb = velComplement * vy * mass * gravity * time_slice; - Scalar termKinetic = workComplement * work; // <-- no dt, per the binary + Scalar termKinetic = workComplement * work * (time_slice * s_hz); // rate-normalised Scalar termAccel = velComplement * velMag * accMag * mass * time_slice; Scalar gain = ratio * ratio * damageGain; @@ -757,9 +785,17 @@ void Myomers::MyomersDriveHeat(Scalar time_slice) if (getenv("BT_MYO_LOG")) { static int s_n = 0; + static Scalar s_elapsed = 0.0f; + s_elapsed += time_slice; // wall clock since the sim started if ((s_n++ % 120) == 0) { Scalar sum = termClimb + termKinetic + termAccel; + // T and the clock: how long does seek 4 actually take to cook the + // myomers? (#96 -- the players' claim is "under a minute".) + DEBUG_STREAM << "[myotemp] t=" << s_elapsed + << " T=" << currentTemperature + << " degradeT=" << degradationTemperature + << " failT=" << failureTemperature << "\n" << std::flush; DEBUG_STREAM << "[myoheat] v=" << velMag << " a=" << accMag << " m=" << mass << " g=" << gravity << " ratio=" << ratio << " dmgGain=" << damageGain