Mech::Reset: restore the POSTURE clears the port had dropped (#142)
Oracle: "crouch wasn't resetting on respawn ... mechs always spawn standing".
Correct -- Mech::Reset (@0049fb74) stands the mech up and the port cleared
none of it:
*(this+0x398) = 0 duckState
Set_Alarm_Level(this+0x39c,0) legStateAlarm -> standing
Set_Alarm_Level(this+0x714,0) bodyStateAlarm -> standing
*(this+0x650/0x654/0x658) = 0 death + leg/body reset latches
*(this+0x5ac) = 1.0f idleStrideScale
A pilot who died CROUCHED came back crouched -- leg parked in 'sqd' -- and now
that the cockpit strip works, showing the up-arrow "press to rise" frame on a
standing mech.
Benched (crouchrespawn.sh): A squats, dies while down, respawns -> legLvl 0
(standing) after Mech::Reset. Weak but the failure mode (stuck legLvl=1) is
absent. NB the first attempt was void: force-damage kept A dying before it
could crouch (legLvl 22/24 = death clips), so the run tested a STANDING death.
Switched to self-damage so the mech is stopped long enough to crouch.
Also carries the #142 gauge work: the crouch strip is a BUTTON-STATE indicator
(grey unavailable / orange down-arrow ready / orange up-arrow crouched),
decoded by rendering BDUCK.PCC rather than inferring it; and the gauge
factory's missing-image path no longer uses the no-op DebugStream.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCJQkvq6G2JNrpVbA75tVZ
This commit is contained in:
co-authored by
Claude Opus 5
parent
03c4d55672
commit
1f0923747b
@@ -2172,6 +2172,26 @@ void
|
||||
poseSyncLatch = 0; // @0x77c
|
||||
graphicAlarm.SetLevel(0); // clear >=9 (the vital-kill trigger)
|
||||
|
||||
// --- STAND THE MECH UP. Restored 2026-08-07 (#142, Oracle: "crouch wasn't
|
||||
// resetting on respawn"). These are in the binary's own Reset sweep and the
|
||||
// port had dropped all of them, so a pilot who died CROUCHED respawned
|
||||
// crouched -- leg parked in 'sqd', the cockpit strip still showing the
|
||||
// up-arrow "press to rise" frame. Verbatim from FUN_0049fb74:
|
||||
// *(this+0x398) = 0 duckState -- not crouching
|
||||
// FUN_0041bbd8(this+0x39c, 0) legStateAlarm -> 0 (standing)
|
||||
// FUN_0041bbd8(this+0x714, 0) bodyStateAlarm -> 0 (standing)
|
||||
// *(this+0x650/0x654/0x658) = 0 death + leg/body reset latches
|
||||
// *(this+0x5ac) = 1.0f idleStrideScale
|
||||
duckState = 0; // @0x398
|
||||
duckRequest = 0; // port-only pending flag (#142)
|
||||
legStateAlarm.SetLevel(0); // @0x39c -- stand
|
||||
bodyStateAlarm.SetLevel(0); // @0x714 -- stand
|
||||
stabilityAlarm.SetLevel(1); // risen (the rise path's value)
|
||||
deathAnimationLatched = 0; // @0x650
|
||||
legResetLatch = 0; // @0x654
|
||||
bodyResetLatch = 0; // @0x658
|
||||
idleStrideScale = 1.0f; // @0x5ac = 0x3f800000
|
||||
|
||||
// --- HEAL every damage zone: full structure, intact skin, no burning ---
|
||||
for (int z = 0; z < damageZoneCount; ++z)
|
||||
{
|
||||
@@ -4514,10 +4534,53 @@ void
|
||||
// back to 0/1 by the next visit, so "am I still moving?" has to be
|
||||
// asked fresh, and the strip settled whenever the answer is no.
|
||||
{
|
||||
// SETTLE to the binary's VALUE RANGE: duckState is 0 or 1, never
|
||||
// 2. Corrected 2026-08-07 after checking FUN_004a9b5c -- the
|
||||
// mech4 master perf is fully indexed and exported (5645 bytes,
|
||||
// no dark region inside) and never references +0x398. So the
|
||||
// binary has NO code reader of duckState at all: the gauge
|
||||
// attribute is the only consumer, and the only writers are the
|
||||
// handler (=1) and Mech::Reset (=0). Frame 2 of bduck.pcc was
|
||||
// unreachable in the original too.
|
||||
//
|
||||
// The strip is therefore a TWO-STATE indicator -- standing and
|
||||
// crouched -- and the "crouch animation" is the MECH's 'sqd'
|
||||
// clip, not the symbol stepping. An earlier revision here made
|
||||
// duckState a 3-state posture (0/1/2); that was an invention on
|
||||
// top of a stand-in and it put the crouched pose on the wrong
|
||||
// frame.
|
||||
// #142: report EVERY change of the value the cockpit strip is fed,
|
||||
// so 'which frames actually got shown, and for how long' stops
|
||||
// being guesswork. Ungated: one line per posture change.
|
||||
static int s_lastDuck = -1;
|
||||
// THE STRIP IS A BUTTON-STATE INDICATOR, not a pose animation.
|
||||
// Decoded 2026-08-07 by RENDERING THE ASSET itself
|
||||
// (content/GAUGE/BDUCK.PCC -- PCX, 108x102, three 36x102 frames;
|
||||
// see scratchpad/night13/bduck_frames.png):
|
||||
//
|
||||
// frame 0 GREY mech standing, GREY down-arrow -- UNAVAILABLE
|
||||
// frame 1 ORANGE standing, YELLOW down -- ready to crouch
|
||||
// frame 2 ORANGE CROUCHED, YELLOW up -- press to rise
|
||||
//
|
||||
// There is NO mid-transition pose. The arrow tells the pilot
|
||||
// what the next press will do, and the grey frame says the
|
||||
// button is inert -- which is the visual half of the
|
||||
// must-be-stopped rule (Lynx: "when a mech STOPS").
|
||||
//
|
||||
// Two earlier readings were wrong: a 2-state flag (which
|
||||
// ignores frame 2's up-arrow entirely) and a stand/moving/
|
||||
// crouched pose animation (there is no mid pose). Both were
|
||||
// inferred from code and logs; only the ART settled it, and it
|
||||
// also explains the field report -- a stopped mech that COULD
|
||||
// crouch was drawing the grey "unavailable" frame.
|
||||
const int lvlNow = (int)legStateAlarm.GetLevel();
|
||||
if (lvlNow != 2 && lvlNow != 3) // not playing 'sqd'/'squ'
|
||||
duckState = (lvlNow == 1) ? 2 : 0; // crouched : standing
|
||||
// else: hold 1, the middle frame, while the clip runs.
|
||||
if (lvlNow != 2 && lvlNow != 3) // hold through the clip
|
||||
{
|
||||
if (lvlNow == 1) duckState = 2; // crouched
|
||||
else if (squatCapable != 0 && mapPosture == 1)
|
||||
duckState = 1; // ready
|
||||
else duckState = 0; // inert
|
||||
}
|
||||
}
|
||||
|
||||
// (3b) AIRBORNE AUTO-RISE -- recovered 2026-08-06 by the #60
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# =========================================================================
|
||||
# #142 -- does CROUCH reset on respawn? (Oracle: "crouch wasn't resetting
|
||||
# on respawn ... i guess mechs always spawn standing")
|
||||
#
|
||||
# Mech::Reset (@0049fb74) stands the mech up in the binary:
|
||||
# *(this+0x398) = 0 duckState
|
||||
# legStateAlarm -> 0 (this+0x39c)
|
||||
# bodyStateAlarm -> 0 (this+0x714)
|
||||
# death + leg/body reset latches, idleStrideScale = 1.0
|
||||
# The port had dropped every one of those, so a pilot who died CROUCHED came
|
||||
# back crouched.
|
||||
#
|
||||
# THE TEST: A crouches, then is killed while crouched, then respawns.
|
||||
# PASS: after "[respawn] Mech::Reset", the leg alarm is 0 (standing) and
|
||||
# duckState is 0 -- and no squat clip is parked.
|
||||
# FAIL: legLvl stays 1 (parked in 'sqd') across the respawn.
|
||||
#
|
||||
# B force-damages A so the death lands while A is parked in the squat.
|
||||
# =========================================================================
|
||||
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
|
||||
rm -f dr_a.log dr_b.log dr_relay.log
|
||||
bt_expert_egg MP.EGG DR.EGG
|
||||
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" DR.EGG
|
||||
|
||||
( export BT_DEATH_LOG=1 BT_MP_LOG=1
|
||||
bt_launch dr_b.log DR.EGG 0x0C -net 1601 )
|
||||
sleep 2
|
||||
# A: crouch early and STAY down, so the kill lands on a crouched mech
|
||||
( export BT_BTNTEST=0x13,900,960 BT_SELF_DAMAGE=6
|
||||
export BT_DUCK_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1 BT_KEY_NOFOCUS=1
|
||||
bt_launch dr_a.log DR.EGG 0x03 -net 1501 )
|
||||
sleep 5
|
||||
python ../tools/btconsole.py DR.EGG 127.0.0.1:1501 127.0.0.1:1601 > dr_relay.log 2>&1 &
|
||||
RELAY=$!
|
||||
sleep 260
|
||||
kill $RELAY 2>/dev/null
|
||||
sleep 3
|
||||
bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe > /dev/null 2>&1; sleep 3
|
||||
|
||||
echo "=================== #142 CROUCH ACROSS RESPAWN ==================="
|
||||
echo "--- did A crouch, then die, then respawn? ---"
|
||||
grep -aE "\[duck\] (SQUAT|RISE)|death cycle START|Mech::Reset" dr_a.log | head -8
|
||||
echo
|
||||
echo "--- leg alarm around the respawn (1 = parked in 'sqd', 0 = standing) ---"
|
||||
grep -aE "probe legLvl|Mech::Reset" dr_a.log | tail -12
|
||||
echo
|
||||
echo "--- the strip's frame after respawn (must NOT be the up-arrow) ---"
|
||||
grep -a "probe legLvl" dr_a.log | tail -3
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Audit Mech::Reset (@0049fb74) -- every field the binary clears vs ours.
|
||||
|
||||
Motivated by finding EIGHT dropped posture clears (duckState, both state alarms,
|
||||
the death/reset latches, idleStrideScale) in the port's Reset, and by #137
|
||||
"respawn came back with MYOMERS heat MAXED" + Sauron's overheating generator D:
|
||||
if the reconstruction dropped the posture clears it may have dropped others.
|
||||
"""
|
||||
import io, re
|
||||
|
||||
DECOMP = r"C:\git\bt411\reference\decomp\all\part_012.c"
|
||||
HPP = r"C:\git\bt411\game\reconstructed\mech.hpp"
|
||||
CPP = r"C:\git\bt411\game\reconstructed\mech4.cpp"
|
||||
|
||||
# --- 1. every offset the BINARY's Reset touches ---------------------------
|
||||
lines = io.open(DECOMP, encoding="latin-1", errors="replace").readlines()
|
||||
body = "".join(lines[14191:14400]) # FUN_0049fb74
|
||||
bin_off = sorted({int(m, 16) for m in re.findall(r"param_1 \+ (0x[0-9a-f]+)", body)})
|
||||
|
||||
# --- 2. offset -> our field name, from mech.hpp's offset comments ---------
|
||||
name_of = {}
|
||||
for ln in io.open(HPP, encoding="latin-1", errors="replace"):
|
||||
m = re.search(r"^\s*(?:[A-Za-z_][\w:<>* ]*?)\s+(\w+)\s*(?:\[\d+\])?\s*;.*?(?:@|//\s*)0x([0-9a-fA-F]+)", ln)
|
||||
if m:
|
||||
name_of.setdefault(int(m.group(2), 16), m.group(1))
|
||||
|
||||
# --- 3. what OUR Reset assigns -------------------------------------------
|
||||
cpp = io.open(CPP, encoding="latin-1", errors="replace").read()
|
||||
i = cpp.find("Mech::Reset(const Origin &origin, int mode)")
|
||||
ours_body = cpp[i:i + 9000] if i != -1 else ""
|
||||
ours = set(re.findall(r"^\s*(\w+)\s*(?:\.\w+\([^)]*\)|=)", ours_body, re.M))
|
||||
|
||||
print("Mech::Reset @0049fb74 -- %d distinct fields touched by the binary\n" % len(bin_off))
|
||||
missing, covered, unknown = [], [], []
|
||||
for off in bin_off:
|
||||
nm = name_of.get(off)
|
||||
if nm is None:
|
||||
unknown.append(off)
|
||||
elif nm in ours:
|
||||
covered.append((off, nm))
|
||||
else:
|
||||
missing.append((off, nm))
|
||||
|
||||
print("COVERED by our Reset (%d):" % len(covered))
|
||||
print(" " + ", ".join("%s@0x%x" % (n, o) for o, n in covered) + "\n")
|
||||
print("*** NOT CLEARED by our Reset (%d) -- named fields the binary resets ***" % len(missing))
|
||||
for o, n in missing:
|
||||
print(" 0x%-5x %s" % (o, n))
|
||||
print("\nUNMAPPED offsets (%d) -- no named field at that offset in mech.hpp:" % len(unknown))
|
||||
print(" " + " ".join("0x%x" % o for o in unknown))
|
||||
Reference in New Issue
Block a user