#183 FIXED: the ENG-page BAY FIRE icon could never light -- the gauge cluster's ammo-bin link was a RAW-OFFSET walk that resolved to NULL. Oracle reported no bay-fire icon where the jam icon shows. Everything upstream was already correct and #47-verified: the AmmoBin arms cookOffArmed on the authentic gate (heat alarm FAILURE + rounds remaining), counts a fixed 10s fuse and detonates; the widget exists (btefire.pcc, TwoState) and its coordinates are BYTE-FAITHFUL to the binary (jam 0xa0,0xb2 / fire 0x121,0xb6, part_014.c:6510-6522 -- note the machine puts them in DIFFERENT places, so the report's 'same place as the jam icon' premise is wrong). The break was BallisticWeaponCluster::Execute resolving the bin with ResolveLink(subsystem + 0x43c) -- the BINARY's layout applied to our compiled ProjectileWeapon, i.e. the databinding trap the conventions forbid. It returned NULL, so BTAmmoBinCookOffArmed(NULL) read 0 forever and the icon had no input. The typed bridge already existed: BTWeaponAmmoBin (projweap.cpp) was written in July for this exact trap when the ammo DIGITS failed to bind the same way -- these two call sites were simply missed. Both now use it; TestInstance's raw bin+0x180 round-count read replaced with a new typed BTAmmoBinRoundCount. Rig added (BT_BAYFIRE_TEST=<seconds> drives the AUTHENTIC arming condition, same hook family as BT_VALVE/BT_FLUSH_TEST) plus an edge-only [gau-fire] receipt. Measured: before, bin=00000000 cookOffArmed=0 on every cluster; after, each cluster binds a real bin and the burning rack alone reports cookOffArmed 0->1->0 across arm and detonation while its neighbours stay 0. NOTE #47's fix was correct but only 'data path rig-verified' with the pixels left to the field -- this is why that gap mattered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joe DiPrima
2026-08-15 11:04:02 -05:00
co-authored by Claude Opus 5
parent 4457a8503c
commit 86c1f54644
4 changed files with 97 additions and 3 deletions
+33
View File
@@ -250,6 +250,13 @@ int BTAmmoBinCookOffTime(void *bin)
{
return (bin != 0) ? ((AmmoBin *)bin)->cookOffTime : 0;
}
// #183: the round count, for the gauge cluster's TestInstance gate. Same
// databinding rule as the two above -- the caller used to raw-read bin+0x180,
// which is the BINARY's offset and garbage on our compiled AmmoBin.
int BTAmmoBinRoundCount(void *bin)
{
return (bin != 0) ? ((AmmoBin *)bin)->GetAmmoCount() : 0;
}
// The round's GameModel resource ID (ammoModelFile @0x1E8, word 0x7A). In the
// arcade the MissileLauncher seeds each spawned Missile's model from AmmoBin+0x1e8
@@ -353,6 +360,32 @@ void AmmoBin::AmmoBinSimulation(Scalar time_slice)
{
WatchSimulation(time_slice); // FUN_004aeac4(this) -- drive this+0x140
// #183 VERIFICATION HOOK (BT_BAYFIRE_TEST=<seconds>, off by default).
// #47 fixed the fire icon's DATA PATH and asked the field to "confirm the
// pixels"; Oracle reports no icon, so the pixels need a rig rather than a
// playtest. Forcing a real bay fire in play takes sustained ballistic heat
// on a specific bin -- this drives the AUTHENTIC arming condition instead
// (heat alarm -> FAILURE on a non-empty bin) at a scripted time, so
// everything downstream (cookOffArmed, the countdown, the icon, the
// detonation) runs exactly as it does in a match. Same hook family as
// BT_VALVE / BT_POWER_DETACH_TEST / BT_FLUSH_TEST.
{
static const char *s_bfEnv = getenv("BT_BAYFIRE_TEST");
if (s_bfEnv != 0 && *s_bfEnv != '\0' && ammoCount > 0)
{
static Scalar s_bfClock = 0.0f;
static int s_bfFired = 0;
s_bfClock += time_slice;
if (!s_bfFired && s_bfClock >= (Scalar)atof(s_bfEnv))
{
s_bfFired = 1;
heatAlarm.SetLevel(2); // FAILURE -- the authentic arm gate
DEBUG_STREAM << "[bayfire-test] forced heat FAILURE on " << GetName()
<< " (" << ammoCount << " rounds)\n" << std::flush;
}
}
}
if (simulationState == 1) // this+0x40 (destroyed)
ammoAlarm.SetLevel(Empty);
+32 -3
View File
@@ -1831,6 +1831,7 @@ WeaponCluster::WeaponCluster(
// weapon's name so the Execute probe can attribute its readings; tag the
// recharge arc with the same name for its own [arc] probe.
diagWeaponName = (subsystem_in != 0) ? subsystem_in->GetName() : "?";
lastLoggedBayFire = -1; // #183 diagnostic edge tracker
((SegmentArc270 *)rechargeArc)->diagName = diagWeaponName;
}
@@ -2081,11 +2082,37 @@ void BallisticWeaponCluster::BecameActive()
void BallisticWeaponCluster::Execute()
{
jammed = (*(int *)((char *)subsystem + 0x364) == 5); // BEST-EFFORT raw (weaponAlarm==Jammed)
void *ammoBin = ResolveLink((char *)subsystem + 0x43c); // FUN_00417ab4
// #183 DATABINDING FIX (gotcha: never raw-read a compiled object's offsets).
// The hand-rolled walk `ResolveLink(subsystem + 0x43c)` is the BINARY's
// layout; on our compiled ProjectileWeapon it resolved to NULL, so
// cookOffArmed always read 0 and the bay-fire icon could never light --
// which is exactly the "no icon" the field reports (#183), even though #47
// had correctly fixed the flag this reads. The typed bridge already
// existed: BTWeaponAmmoBin (projweap.cpp) was written in July for this same
// trap, when the ammo DIGITS failed to bind for the same reason; these two
// call sites were missed.
extern void *BTWeaponAmmoBin(void *weapon); // projweap.cpp (typed link)
void *ammoBin = BTWeaponAmmoBin(subsystem);
extern int BTAmmoBinCookOffArmed(void *bin); // bin+0x18C (complete-type bridge)
extern int BTAmmoBinCookOffTime(void *bin); // bin+0x190
int bayFire = BTAmmoBinCookOffArmed(ammoBin);
firing = bayFire; // the btefire TwoState input
// #183 probe: #47 fixed this data path but the pixels were never confirmed
// and the field reports no icon. Print what the CLUSTER sees, so a rig can
// separate "flag never arrives" from "flag arrives, lamp does not draw".
if (getenv("BT_BAYFIRE_LOG"))
{
// edge-only per cluster: a bay fire burns for 10s at gauge rate, and
// a per-frame line buries the transition we care about.
if (bayFire != lastLoggedBayFire)
{
lastLoggedBayFire = bayFire;
DEBUG_STREAM << "[gau-fire] cluster '"
<< (diagWeaponName ? diagWeaponName : "?")
<< "' bin=" << ammoBin << " cookOffArmed=" << bayFire
<< " fireLamp=" << (void *)fireLamp << "\n" << std::flush;
}
}
if (bayFire == 0)
{
reloading = 0;
@@ -2127,8 +2154,10 @@ void BallisticWeaponCluster::DrawWarningLamp(int on)
//
Logical BallisticWeaponCluster::TestInstance() const
{
void *ammoBin = ResolveLink((char *)subsystem + 0x43c);
if (ammoBin == NULL || *(int *)((char *)ammoBin + 0x180) == 0)
extern void *BTWeaponAmmoBin(void *weapon); // projweap.cpp (typed link)
extern int BTAmmoBinRoundCount(void *bin); // ammobin.cpp (typed count)
void *ammoBin = BTWeaponAmmoBin(subsystem);
if (ammoBin == NULL || BTAmmoBinRoundCount(ammoBin) == 0)
return True;
if (jammed != 0)
return True;
+1
View File
@@ -509,6 +509,7 @@
int warningCenterY; // @0xE0 this[0x38]
int warningState; // @0xE4 this[0x39]
const char *diagWeaponName; // PORT diagnostic (appended; clusters are plain-new)
int lastLoggedBayFire; // PORT diagnostic (#183): edge-only [gau-fire] logging
};
class EnergyWeaponCluster : // @004c93b0
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# =========================================================================
# #183 -- does the ENG-page BAY FIRE icon light?
# #47 fixed the data path and asked the field to "confirm the pixels";
# Oracle reports no icon. Force a real bay fire with BT_BAYFIRE_TEST and
# watch the whole chain:
# [bayfire-test] forced heat FAILURE ... the rig armed it
# [ammo] ... BAY FIRE: cook-off armed ... the AUTHENTIC arm fired
# [gau-fire] ... what the ENG cluster sees
# A solo expert madcat (ballistic bins) on grass.
# usage: bayfire183.sh [seconds-before-arming]
# =========================================================================
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
AT=${1:-20}
bt_expert_egg MP.EGG BF183.EGG
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; 0,/^vehicle=.*/s//vehicle=madcat/" BF183.EGG
LOG=bayfire183.log
rm -f "$LOG"
( export BT_BAYFIRE_TEST=$AT BT_DEATH_LOG=1 BT_BAYFIRE_LOG=1 BT_DEV_GAUGES=1
bt_launch "$LOG" BF183.EGG 0x03 )
sleep 55
bt_kill_ours
sleep 2
taskkill //F //IM btl4.exe > /dev/null 2>&1
echo "=== the chain ==="
grep -a "bayfire-test\|BAY FIRE\|EXTINGUISH\|gau-fire" "$LOG" | head -20