Diag: BT_AF_PERIOD=<sec> autofire cadence throttle + afc_dump.py (raw RES verify)
Closes the coolant/jam investigation. BT_AF_PERIOD pulses the trigger every <sec>s so a slower-than-max fire rate can be tested; afc_dump.py reads the AFC100 record raw from BTL4.RES. Both confirm the QA report is FAITHFULLY reproduced (see KB). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4e72f0c4f4
commit
fcaea1862a
@@ -3825,7 +3825,18 @@ void
|
||||
static int s_prevBtn[4] = {0, 0, 0, 0};
|
||||
static int s_afPhase = 0;
|
||||
s_afPhase ^= 1;
|
||||
const int autofire = (gBTDrive.fireForced && targetInArc) ? s_afPhase : 0;
|
||||
// DIAG (BT_AF_PERIOD=<sec>): throttle autofire cadence -- pulse the trigger
|
||||
// only briefly every <sec> seconds, to test a slower-than-max fire rate (the
|
||||
// coolant/jam mechanic is fire-rate dependent: at max spam the weapon overheats
|
||||
// regardless; at a realistic cadence coolant priority decides the jam).
|
||||
static float s_afClock = 0.0f;
|
||||
s_afClock += dt;
|
||||
static const float s_afPeriod =
|
||||
getenv("BT_AF_PERIOD") ? (float)atof(getenv("BT_AF_PERIOD")) : 0.0f;
|
||||
int afGate = 1;
|
||||
if (s_afPeriod > 0.0f)
|
||||
afGate = (fmodf(s_afClock, s_afPeriod) < 0.6f) ? 1 : 0;
|
||||
const int autofire = (gBTDrive.fireForced && targetInArc && afGate) ? s_afPhase : 0;
|
||||
const int buttons[4] = {
|
||||
LBE4ControlsManager::ButtonJoystickTrigger, // 0x40
|
||||
LBE4ControlsManager::ButtonJoystickPinky, // 0x45
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import struct
|
||||
data = open(r'C:/git/bt411/content/BTL4.RES','rb').read()
|
||||
n = len(data)
|
||||
def f32(o): return struct.unpack_from('<f', data, o)[0]
|
||||
def i32(o): return struct.unpack_from('<i', data, o)[0]
|
||||
def cstr(o, m=32):
|
||||
s = data[o:o+m].split(b'\0')[0]
|
||||
try: return s.decode('ascii')
|
||||
except: return None
|
||||
|
||||
# find AFC100 subsystem records (name @+0, classID @+0x20)
|
||||
hits=[]
|
||||
i=0
|
||||
while i < n-0x28:
|
||||
if data[i:i+7]==b'AFC100\0':
|
||||
cid=struct.unpack_from('<I',data,i+0x20)[0]
|
||||
sz=struct.unpack_from('<I',data,i+0x24)[0]
|
||||
if 0xBB0<=cid<=0xBE5 and 0xE0<=sz<=0x300:
|
||||
hits.append((i,cid,sz))
|
||||
i+=1
|
||||
print("AFC100 records:", [(hex(p),hex(c),hex(s)) for p,c,s in hits])
|
||||
if hits:
|
||||
p,cid,sz=hits[0]
|
||||
print(f"\n=== AFC100 @ {hex(p)} classID={hex(cid)} size={hex(sz)} ===")
|
||||
# dump the offsets the loader reads (per heat.cpp / mechweap.hpp comments)
|
||||
fields = {
|
||||
0xE4:"startingTemp", 0xE8:"degradationTemp", 0xEC:"failureTemp",
|
||||
0xF0:"thermalConductance", 0xF4:"thermalMass",
|
||||
0x1A4:"heatCostToFire", 0x1CC:"minJamChance",
|
||||
}
|
||||
for off,name in sorted(fields.items()):
|
||||
print(f" +{hex(off):>6} {name:20} f32={f32(p+off):<16g} i32={i32(p+off)}")
|
||||
print("\n --- full float scan (offset: value) for plausible fields 0xE0..0x1E0 ---")
|
||||
for off in range(0xE0, 0x1E0, 4):
|
||||
v=f32(p+off)
|
||||
if abs(v)>1e-3 and abs(v)<1e12:
|
||||
print(f" +{hex(off):>6}: {v:g}")
|
||||
Reference in New Issue
Block a user