diff --git a/context/locomotion.md b/context/locomotion.md index 39522f6..0d303ad 100644 --- a/context/locomotion.md +++ b/context/locomotion.md @@ -311,6 +311,23 @@ resolved with gait v5, no shadow code changed — though (a) may have contribute T4]; (c) genuine slope burial when the quad is flat/mis-tilted (the depth-bias margin is finite). Check `[shadowobj]` tag lines and `[sync]`/`BT_SYNC_LOG` before touching bias/tilt. +## CROUCH -- COMPLETE 2026-08-06 [T1 decode / T2 full-cycle + MP benched] +The manual's CROUCH button (streamed 0x13; glass key F4), end to end: DuckRequest (mech id 0x1a +@0049fa00) latches duckState@0x398 -> the master posture block (mech4, from the dark master-perf +@0x4a9cf0-0x4aa0af): posture selector @0x3f8 (mode 9/10/2/3/4 block, NOVICE simLive lockout, +myomer-alive gate) -> standing: SetLegAnimation(2) ('sqd', squat down; LegClipFinished case-2 +PARKS the pose + leg alarm 1) / ducked: SetLegAnimation(3) ('squ', rise; case-3 alarm 0); +stability alarm flips; ForceUpdate(8)+(1) ships the type-3 state record -- peers pose the squat +for FREE (screenshot receipt: the crouched crimson madcat from the observer's cockpit, +duckmpA_031). Clip data verified from BTL4.RES (root joint drops -2.22, knees +-1.14; squ/sqd +are exact mirrors; interior 'squi/sqdi' variants ship for all 8 chassis). Full solo cycle +benched (BT_BTNTEST + BT_BTNTEST2): squat -> hold (joints steady for minutes) -> rise -> +standing zeros. Myomer speed-cripple feeder (@004b8be3) still deferred [T3] -- see mech4 banner. +RESIDUAL (open-questions): the pilot's own cockpit EYE height does not ride the root drop +(the interior clip authors joint-0 translation for exactly that; the DPLEyeRenderable chain +composition needs a look). Bench traps for reruns: MP button delivery can miss if pressed +during round-start jitter (press >= poll 900 after GO); diags all BT_DUCK_LOG-gated. + ## Key Relationships - Detail: `docs/P3_LOCOMOTION.md`. Uses: [[asset-formats]] (SKL/ANI), [[decomp-reference]] (offsets). - Feeds: [[combat-damage]] (collision→damage), [[rendering]] (shadow/visual-conform). diff --git a/context/open-questions.md b/context/open-questions.md index 6a452fe..20b7b5d 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -1142,3 +1142,10 @@ Gitea #116 and `experience-levels.md`. - **des_night-family pages have subtle fog deltas** (both sets near-black); arena/polar night pages carry the dramatic 5↔40 near-plane swing. If testers report "searchlight does nothing" on some maps, it's page authoring, not the mechanism. + +## Crouch residual (2026-08-06 — the feature itself is LIVE, [[locomotion]] §CROUCH) +- **Pilot eye height does not ride the root drop.** The interior squat clip animates joint 0 + (ball+translation, −2.22) precisely so the cockpit eye lowers; the joint holds (probe-verified) + and the eye's segment chain passes through jointlocal, yet the cockpit view returns to standing + height after the squat parks. Suspect: DPLEyeRenderable's parent-chain composition not + including the BallTranslateJointRenderable's live matrix. Cosmetic (body + replication correct). diff --git a/engine/MUNGA_L4/L4CTRL.cpp b/engine/MUNGA_L4/L4CTRL.cpp index b5ea06d..28924c9 100644 --- a/engine/MUNGA_L4/L4CTRL.cpp +++ b/engine/MUNGA_L4/L4CTRL.cpp @@ -2624,6 +2624,13 @@ void Verify(rio_event.Data.Unit < ButtonCount); temp = rio_event.Data.Unit + 1; + // DIAG (BT_DUCK_LOG): the mode mask a press dispatches under -- the + // crouch-button (0x13) MP-delivery hunt (mode-gated streamed rows). + if (getenv("BT_DUCK_LOG")) + DEBUG_STREAM << "[duck] RIO press unit=0x" << std::hex + << (int)rio_event.Data.Unit << " modeMask=0x" + << (unsigned)mode_mask << std::dec << "\n" << std::flush; + Check(&buttonGroup[rio_event.Data.Unit]); buttonGroup[rio_event.Data.Unit].Update(&temp, mode_mask); //---------------------------------------- diff --git a/engine/MUNGA_L4/L4PADRIO.cpp b/engine/MUNGA_L4/L4PADRIO.cpp index 26ff495..3acf1a2 100644 --- a/engine/MUNGA_L4/L4PADRIO.cpp +++ b/engine/MUNGA_L4/L4PADRIO.cpp @@ -542,6 +542,33 @@ void DEBUG_STREAM << "[btntest] RELEASE 0x" << std::hex << s_btnAddr << std::dec << " at poll " << s_poll << "\n" << std::flush; } + // BT_BTNTEST2="addr,pressPoll,releasePoll": an optional SECOND + // scripted cycle (e.g. crouch then rise) on the same poll clock. + static int s_bt2Addr = -2, s_bt2On = 0, s_bt2Off = 0, s_bt2State = 0; + if (s_bt2Addr == -2) + { + s_bt2Addr = -1; + const char *e2 = getenv("BT_BTNTEST2"); + if (e2 != NULL) + sscanf(e2, "%i,%i,%i", &s_bt2Addr, &s_bt2On, &s_bt2Off); + } + if (s_bt2Addr >= 0) + { + if (s_bt2State == 0 && s_poll >= s_bt2On) + { + s_bt2State = 1; + EmitButton(s_bt2Addr, 1); + DEBUG_STREAM << "[btntest] PRESS2 0x" << std::hex << s_bt2Addr + << std::dec << " at poll " << s_poll << "\n" << std::flush; + } + else if (s_bt2State == 1 && s_poll >= s_bt2Off) + { + s_bt2State = 2; + EmitButton(s_bt2Addr, 0); + DEBUG_STREAM << "[btntest] RELEASE2 0x" << std::hex << s_bt2Addr + << std::dec << " at poll " << s_poll << "\n" << std::flush; + } + } } } diff --git a/game/reconstructed/btl4vid.cpp b/game/reconstructed/btl4vid.cpp index 0049583..acc581e 100644 --- a/game/reconstructed/btl4vid.cpp +++ b/game/reconstructed/btl4vid.cpp @@ -558,6 +558,11 @@ HierarchicalDrawComponent* // if (segment->IsSiteSegment() /* [0x10] */ != 0) { + if (getenv("BT_TREE_LOG")) + DEBUG_STREAM << "[tree] SITE seg=" << segment->GetIndex() + << " name=" << (segment->GetName() ? (const char *)segment->GetName() : "?") + << " parent=" << (segment->GetParent() ? segment->GetParentIndex() : -1) + << "\n" << std::flush; // The authentic COCKPIT EYEPOINT. Built for the true inside view // AND for the player's chase build (buildDebugChaseCamera), so the // V-key toggle can switch to it -- the pod's only view was this @@ -678,6 +683,14 @@ HierarchicalDrawComponent* // the JointSubsystem's joint table. // int segment_slot = segment->GetIndex() /* [0xcc] */; + // DIAG (BT_TREE_LOG): the built joint topology -- segment slot, name, + // joint index/type, parent -- the crouch root-drop chain hunt. + if (getenv("BT_TREE_LOG")) + DEBUG_STREAM << "[tree] seg=" << segment_slot + << " name=" << (segment->GetName() ? (const char *)segment->GetName() : "?") + << " joint=" << segment->GetJointIndex() + << " parent=" << (segment->GetParent() ? segment->GetParentIndex() : -1) + << "\n" << std::flush; Joint *this_joint = NULL; Joint::JointType joint_type; if (segment->GetJointIndex() /* [0xc0] */ == -1) diff --git a/game/reconstructed/mech2.cpp b/game/reconstructed/mech2.cpp index 6323e5b..93f5bf8 100644 --- a/game/reconstructed/mech2.cpp +++ b/game/reconstructed/mech2.cpp @@ -530,6 +530,8 @@ Scalar return 0.0f; // slot10 state 2 (0x4a6b37): SetLevel(1). case 2: + if (getenv("BT_DUCK_LOG")) + DEBUG_STREAM << "[duck] squat clip parked (case 2 -> leg alarm 1)\n" << std::flush; m->legStateAlarm.SetLevel(1); return 0.0f; // slot2 (0x4a6b21) + slot1 state 32 (0x4a6b4d) + slot9 state 4 (0x4a6d6e): SetLevel(0). case 3: case 4: case 8: case 9: case 20: case 21: diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 9fe16da..5357ded 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -4435,6 +4435,27 @@ void << " squat=" << squatCapable << ")\n" << std::flush; duckState = 0; // consumed (@0x4aa0a9) } + + // DIAG (BT_DUCK_LOG, 1 Hz): live joint sample -- does the parked + // crouch HOLD at the JOINT level? Discriminates render-side loss + // from a hidden joint writer (the crouch-hold hunt). + if (getenv("BT_DUCK_LOG")) + { + static Scalar s_duckProbe = 0.0f; + s_duckProbe += dt; + if (s_duckProbe >= 1.0f) + { + s_duckProbe = 0.0f; + JointSubsystem *duckJs = GetJointSubsystem(); + Joint *knee = (duckJs != 0) ? duckJs->GetJoint(11) : 0; + Joint *root = (duckJs != 0) ? duckJs->GetJoint(0) : 0; + DEBUG_STREAM << "[duck] probe legLvl=" << (int)legStateAlarm.GetLevel() + << " j11=" << (knee ? (float)knee->GetHinge().rotationAmount : -99.0f) + << " j0ty=" << ((root && root->GetJointType() == Joint::BallTranslationJointType) + ? (float)root->GetTranslation().y : -99.0f) + << "\n" << std::flush; + } + } } { diff --git a/scratchpad/night12/duckmp.sh b/scratchpad/night12/duckmp.sh new file mode 100644 index 0000000..d55bff5 --- /dev/null +++ b/scratchpad/night12/duckmp.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# SEARCHLIGHT bench 2 (external spot cone + replication): night 2-node. +# A walks to 40u of B and stands (facing B). B presses the searchlight button +# (BT_BTNTEST 0x14) at poll 300. B's lightState replicates (subsystem record +# 0x14) -> A's replicant Searchlight LightOn flips -> A's TickSearchlight shows +# the spot.bgf cone on B's mech. +# PASS: A log "[spot] searchlight pass: ... (external: spot cone)" for B's mech +# + "[spot] cone SHOWN (seg N)" after B's press; A BT_SHOT frames brighten near +# B post-toggle. B log: "[light] ... requested ON" + lightState 0 -> 1. +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +bt_assert_player_env +taskkill //F //IM btl4.exe > /dev/null 2>&1 +sleep 2 +rm -f duckmp_a.log duckmp_b.log duckmp_relay.log duckmpA_*.png +bt_expert_egg MP.EGG SG4.EGG +sed -i "s/^map=.*/map=grass/; s/^time=.*/time=night/; s/^vehicle=.*/vehicle=madcat/" SG4.EGG + +( + export BT_BTNTEST=0x13,900,960 BT_DUCK_LOG=1 + export BT_FIRE_LOG=1 BT_FOG_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1 + bt_launch duckmp_b.log SG4.EGG 0x0C -net 1601 +) +sleep 2 +( + export BT_GOTO=enemy BT_GOTO_STOP=40 BT_KEY_NOFOCUS=1 BT_DUCK_LOG=1 + export BT_SHOT_EVERY=60 BT_SHOT_PREFIX=duckmpA + export BT_FIRE_LOG=1 BT_FOG_LOG=1 BT_MP_LOG=1 + bt_launch duckmp_a.log SG4.EGG 0x03 -net 1501 +) +sleep 5 +python ../tools/btconsole.py SG4.EGG 127.0.0.1:1501 127.0.0.1:1601 > duckmp_relay.log 2>&1 & +RELAY=$! +sleep 90 +kill $RELAY 2>/dev/null +taskkill //F //IM btl4.exe > /dev/null 2>&1 +sleep 3 +echo "=== B (lamp owner):" +grep -aE "\[light\]|btntest|\[spot\]" duckmp_b.log | head -8 +echo "=== A (observer -- cone events):" +grep -aE "\[spot\]" duckmp_a.log | head -8