#96: instrument the myomer heat model -- per-mech profile CONFIRMED, overheat cause LOCATED
Two player claims, both now answered from the decomp + measurement rather than
inference.
THE EQUATION (FUN_004b8d18, constants read from the image: _DAT_004b8ee4=0.5,
_DAT_004b8ee8=0.0 (the fabs), _DAT_004b8eec=1.0):
heat += ratio^2 * (1+damageLevel) *
[ (1-accEff)*|vy|*m*g*dt climb POWER
+ (1-velEff)*(0.5*m*|v|^2) kinetic ENERGY -- NO dt
+ (1-accEff)*|v|*|a|*m*dt ] accel POWER
Our implementation already reproduces this verbatim, dt-less term included.
CLAIM 1 -- "each mech has a unique heating profile". TRUE, and working, but NOT
by the mechanism the player described. Measured across thr1/own1/mad1/vul1:
* the myomer record is IDENTICAL on every chassis
(velEff 0.995, accEff 0.8, gears 3000/5000/7000/9999, rec 2,
degradeT 1000, failT 2000, thermalMass 250000)
* the heat-family COUNT is identical too -- 6 Condensers, 1 HeatSinkBank,
1 Reservoir, 4 Generators on all four
* every cooling parameter is byte-identical Thor vs Owens (condenser
conductance 315000 / mass 420000, bank 231000 / 1.39e6, reservoir
190000 / 3.42e6)
So there is NO authored per-chassis cooling variation. The profile emerges from
the equation instead: heat ~ m*v^2, and light mechs are faster. Measured at
seek 4, flat out:
thr1 mass 70000 |v| 11.34 kinetic/tick 49026
own1 mass 35000 |v| 17.22 kinetic/tick 57350
The Owens is HALF the mass and generates 17% MORE drive heat, because v^2 beats
m. That reproduces the player's OUTCOME (a Thor sustains seek 4, a light
chicken-walker cannot) via speed, not heatsink count.
CLAIM 2 -- "it runs too hot". The kinetic term carries NO dt: it adds an ENERGY
every TICK, so its contribution per SECOND scales with the tick rate. Measured
dt here is ~0.017 (~59Hz) and variable. The other two terms are power terms and
are rate-independent. On flat ground the climb term is additionally dead --
gravity reads 0 (the carried "environment gravity unwired" open), so hills do not
heat at all right now.
NOT yet established: the 1995 tick rate the dt-less term was calibrated against.
Until that is pinned the OVERHEAT FACTOR is unquantified -- flagged, not guessed.
Adds three diagnostics, all under BT_MYO_LOG:
[myoheat] now splits climb/kinetic/accel + dt + the kinetic share
[myoparm] one line per myomer: efficiencies, gears, thermal thresholds
[hsparm] one line per heat subsystem: conductance + thermal mass
and three benches (myoheat/myoparm/myocmp) that produced the tables above.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
56f15b569a
commit
9dd7d48b41
@@ -546,6 +546,19 @@ HeatSink::HeatSink(
|
||||
|
||||
pendingHeat = 0.0f;
|
||||
|
||||
// #96 -- "each mech has a unique heating profile". The heat-family COUNT is
|
||||
// identical on every chassis (6 condensers, 1 bank, 1 reservoir), so if
|
||||
// per-mech cooling variation exists it must live in these authored values.
|
||||
// Dump them so that is a measured fact rather than an assumption.
|
||||
if (getenv("BT_MYO_LOG"))
|
||||
DEBUG_STREAM << "[hsparm] " << (GetName() ? GetName() : "?")
|
||||
<< " startT=" << startingTemperature
|
||||
<< " degradeT=" << degradationTemperature
|
||||
<< " failT=" << failureTemperature
|
||||
<< " conductance=" << thermalConductance
|
||||
<< " thermalMass=" << thermalMass
|
||||
<< "\n" << std::flush;
|
||||
|
||||
//
|
||||
// A "master" heat sink (segment flagged 0x100, not a sub-/damaged copy)
|
||||
// drives the per-frame thermal simulation.
|
||||
|
||||
@@ -229,6 +229,25 @@ Myomers::Myomers(
|
||||
recommendedSeekVoltageIndex = resource->seekVoltageRecommendedIndex; // @0x324 res+0x1AC
|
||||
currentSeekVoltageIndex = recommendedSeekVoltageIndex; // @0x320
|
||||
minSeekVoltageIndex = 0; // @0x328
|
||||
|
||||
// #96 -- "each mech has a unique heating profile". Dump what THIS mech's
|
||||
// myomer record actually streamed, so per-chassis variation is a measured
|
||||
// fact rather than an assumption. One line per myomer built.
|
||||
if (getenv("BT_MYO_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[myoparm] " << (GetName() ? GetName() : "?")
|
||||
<< " velEff=" << velocityEfficiency
|
||||
<< " accEff=" << accelerationEfficiency
|
||||
<< " gears=[";
|
||||
for (int g = 0; g <= maxSeekVoltageIndex && g < 5; ++g)
|
||||
DEBUG_STREAM << (g ? "," : "") << seekVoltage[g];
|
||||
DEBUG_STREAM << "] rec=" << recommendedSeekVoltageIndex
|
||||
<< " max=" << maxSeekVoltageIndex
|
||||
<< " degradeT=" << degradationTemperature
|
||||
<< " failT=" << failureTemperature
|
||||
<< " thermalMass=" << thermalMass
|
||||
<< "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -726,20 +745,31 @@ 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)
|
||||
|
||||
pendingHeat /* @0x1C8 */ +=
|
||||
ratio * ratio * damageGain *
|
||||
( velComplement * vy * mass * gravity * time_slice
|
||||
+ workComplement * work
|
||||
+ velComplement * velMag * accMag * mass * time_slice );
|
||||
// The three terms, kept separate for diagnosis (#96 "myomers run too hot"):
|
||||
// two are POWER (x dt), one is an ENERGY added per TICK.
|
||||
Scalar termClimb = velComplement * vy * mass * gravity * time_slice;
|
||||
Scalar termKinetic = workComplement * work; // <-- no dt, per the binary
|
||||
Scalar termAccel = velComplement * velMag * accMag * mass * time_slice;
|
||||
Scalar gain = ratio * ratio * damageGain;
|
||||
|
||||
pendingHeat /* @0x1C8 */ += gain * (termClimb + termKinetic + termAccel);
|
||||
|
||||
if (getenv("BT_MYO_LOG"))
|
||||
{
|
||||
static int s_n = 0;
|
||||
if ((s_n++ % 120) == 0)
|
||||
{
|
||||
Scalar sum = termClimb + termKinetic + termAccel;
|
||||
DEBUG_STREAM << "[myoheat] v=" << velMag << " a=" << accMag
|
||||
<< " m=" << mass << " g=" << gravity
|
||||
<< " ratio=" << ratio << " dmgGain=" << damageGain
|
||||
<< " dt=" << time_slice
|
||||
<< " | climb=" << (gain * termClimb)
|
||||
<< " kinetic=" << (gain * termKinetic)
|
||||
<< " accel=" << (gain * termAccel)
|
||||
<< " kineticShare=" << (sum != 0.0f ? (termKinetic / sum) : 0.0f)
|
||||
<< " pending=" << pendingHeat << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# #96: compare myomer heat GENERATION between chassis at seek 4, flat out.
|
||||
# Cooling capacity is identical across chassis (measured), and heat ~ m*v^2, so
|
||||
# a light fast mech may out-heat a heavy slow one despite half the mass.
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
V="$1"
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
sed "s/^map=.*/map=grass/; s/^time=.*/time=day/; 0,/^vehicle=.*/s//vehicle=$V/" MP.EGG > MYOC.EGG
|
||||
LOG=myocmp_$V.log
|
||||
rm -f "$LOG"
|
||||
export BT_MYO_LOG=1 BT_FORCE_SEEK=3
|
||||
export BT_GOTO="1500 1500"
|
||||
bt_launch "$LOG" MYOC.EGG 0x03
|
||||
sleep 100
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# #96: WHY do the myomers run too hot? Split the heat into its three terms.
|
||||
#
|
||||
# The binary's integrator (FUN_004b8d18, constants 0.5 / 0.0 / 1.0 verified):
|
||||
# heat += ratio^2 * (1+damage) * [ (1-effA)*|vy|*m*g*dt climb POWER
|
||||
# + (1-effV)*(0.5*m*|v|^2) kinetic ENERGY, NO dt
|
||||
# + (1-effA)*|v|*|a|*m*dt ] accel POWER
|
||||
# Two terms are power (x dt); the kinetic one is an ENERGY added PER TICK, so its
|
||||
# contribution per SECOND scales with the tick rate. On flat ground at steady
|
||||
# speed vy~0 and |a|~0, which should leave the dt-less term carrying nearly all
|
||||
# of the heat. BT_MYO_LOG now prints the split.
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
sed "s/^map=.*/map=grass/; s/^time=.*/time=day/" MP.EGG > MYO.EGG
|
||||
LOG=myoheat_${1:-seek4}.log
|
||||
rm -f "$LOG"
|
||||
# Run flat out in the top gear, straight line, no combat.
|
||||
export BT_MYO_LOG=1 BT_DRIVE_LOG=1 BT_FORCE_SEEK=${2:-3}
|
||||
export BT_SPAWN_ENEMY=1 # a SECOND chassis -> one run dumps two myomer records
|
||||
export BT_GOTO="1500 1500"
|
||||
bt_launch "$LOG" MYO.EGG 0x03
|
||||
sleep 90
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
echo "=== heat term split ==="
|
||||
grep -oE "\[myoheat\][^\n]*" "$LOG" | tail -6
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# #96 Q1: "each mech has a unique heating profile" -- is the myomer record
|
||||
# per-chassis, or shared? Build one mech per run and dump what it streamed.
|
||||
# Oracle's example is the pair thr1 (Thor, sustains seek 4) vs own1 (Owens,
|
||||
# a light chicken-walker that cannot).
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
V="$1"
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
# swap the FIRST pilot's vehicle -- that is the one solo flies
|
||||
sed "s/^map=.*/map=grass/; s/^time=.*/time=day/; 0,/^vehicle=.*/s//vehicle=$V/" MP.EGG > MYOP.EGG
|
||||
LOG=myoparm_$V.log
|
||||
rm -f "$LOG"
|
||||
export BT_MYO_LOG=1 BT_ROSTER=1
|
||||
bt_launch "$LOG" MYOP.EGG 0x03
|
||||
sleep 70
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
Reference in New Issue
Block a user