diff --git a/game/reconstructed/dmgtable.cpp b/game/reconstructed/dmgtable.cpp index a61ec11..a685644 100644 --- a/game/reconstructed/dmgtable.cpp +++ b/game/reconstructed/dmgtable.cpp @@ -175,6 +175,7 @@ DamageZonePercentTable * { if (sliceCount <= 0) return 0; + Scalar thetaIn = theta; if (rotateWithTorso) // binary this[3] != 0 { // #124 frame adapter, angular half: theta arrives in the 1995 frame @@ -189,6 +190,19 @@ DamageZonePercentTable * int index = (int)floorf(theta * (Scalar)sliceCount * (1.0f / TwoPi)); if (index < 0) index = 0; if (index > sliceCount - 1) index = sliceCount - 1; + + // #124 twist-sign bench: expose the twist term itself -- the leaf is a + // weighted roll, so the SLICE choice (not the rolled zone) is the signal. + if (getenv("BT_DMGTABLE_LOG")) + { + static int s_sl = 0; + if (s_sl++ < 400) + DEBUG_STREAM << "[slice] rot=" << (int)rotateWithTorso + << " twist=" << (float)owner->TorsoHeading() + << " thetaIn=" << (float)thetaIn + << " thetaAdj=" << (float)theta + << " -> slice " << index << "/" << sliceCount << std::endl; + } return slices[index]; } @@ -327,6 +341,12 @@ int { static int s_rh = 0; if (s_rh++ < 400) + { + extern int BTMechZoneSegAndName(void *mech_v, int zone_idx, + int *seg_out, const char **name_out); + int seg = -1; const char *zname = 0; + if (resolved >= 0) + BTMechZoneSegAndName((void *)owner, resolved, &seg, &zname); DEBUG_STREAM << "[dmgresolve] impact=(" << impact.x << "," << impact.y << "," << impact.z << ")" << " local=(" << local.x << "," << local.y << "," << local.z << ")" @@ -334,7 +354,9 @@ int << " frac=" << (depth / heightRef) << " layer=" << layerIndex << "/" << layerCount << " theta=" << theta - << " -> zone " << resolved << std::endl; + << " -> zone " << resolved + << " '" << (zname ? zname : "?") << "'" << std::endl; + } } return resolved; } diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 1447c0e..1023d4a 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -3384,6 +3384,34 @@ void } sPrevXT = xtNow; gBTTwistAxis = sTwist; + // DIAG (BT_FORCE_TWIST=<-1..1>): pin the twist axis for + // the #124 twist-sign bench (the axis is a RATE demand, + // so a pinned axis slews the torso to its per-mech + // LIMIT and holds -- a known, stable twist state). + // Same pattern as BT_FORCE_ELEV below. + { + static float s_ft = -2.0f; + if (s_ft < -1.5f) + { + const char *ft = getenv("BT_FORCE_TWIST"); + s_ft = ft ? (float)atof(ft) : -3.0f; + } + if (s_ft >= -1.0f) + { + gBTTwistAxis = s_ft; + // BASIC mode ignores the twist axis (the stick + // steers the legs; the torso auto-centers), so + // the pin also cycles the control mode ONCE: + // Basic -> Standard puts the mapper's twist + // branch in charge. + static int s_ftMode = 0; + if (!s_ftMode) + { + s_ftMode = 1; + gBTModeCycle = 1; + } + } + } // TORSO ELEVATION (pitch aim -- the pod stick's Y // axis; every 1995 control mode routes it to diff --git a/game/reconstructed/mechmppr.cpp b/game/reconstructed/mechmppr.cpp index e8e5773..985ef5d 100644 --- a/game/reconstructed/mechmppr.cpp +++ b/game/reconstructed/mechmppr.cpp @@ -840,6 +840,15 @@ void extern float gBTElevAxis; stickPosition.y = gBTElevAxis; } + // #124 twist-sign bench probe: is the axis routed, and in what mode? + if (getenv("BT_TORSO_LOG")) + { + static int s_mp = 0; + if ((s_mp++ % 60) == 0 && s_mp < 1200) + DEBUG_STREAM << "[mppr] mode=" << (int)controlMode + << " twistAxis=" << gBTTwistAxis + << " keyTurn=" << key_turn << "\n" << std::flush; + } if (controlMode == BasicMode) { stickPosition.x = key_turn; diff --git a/game/reconstructed/torso.cpp b/game/reconstructed/torso.cpp index f95de2b4..fc77c2a 100644 --- a/game/reconstructed/torso.cpp +++ b/game/reconstructed/torso.cpp @@ -587,6 +587,23 @@ void static int s_sweep = 0; analogTwistAxis = ((++s_sweep / 90) & 1) ? -1.0f : 1.0f; // +/- every ~90 frames } + // #124 twist-sign bench (BT_FORCE_TWIST=<-1..1>): HOLD the analog axis at a + // constant (the sweep above alternates) -- the sim integrates currentTwist + // to the per-mech limit and stays, giving the cylinder-table bench a known + // twist state at the SIM level, independent of the input plumbing. + { + static float s_forceTwist = -2.0f; + if (s_forceTwist < -1.5f) + { + const char *ft = getenv("BT_FORCE_TWIST"); + s_forceTwist = ft ? (float)atof(ft) : -3.0f; + } + if (s_forceTwist >= -1.0f) + { + effectiveTwistRate = baseTwistRate; + analogTwistAxis = s_forceTwist; + } + } // TORSO GATE PROBE (BT_TORSO_LOG): why is the twist rate zero? if (getenv("BT_TORSO_LOG")) diff --git a/scratchpad/night11/twistsign.sh b/scratchpad/night11/twistsign.sh new file mode 100644 index 0000000..9893310 --- /dev/null +++ b/scratchpad/night11/twistsign.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# #124 TWIST-SIGN VERIFY -- the follow-up the frame-adapter work left open: +# SelectSlice's torso-twist term sign was INFERRED from the z-negation +# reflection, never exercised (every probe ran at twist 0). Rig: stationary +# madcat TARGET pinned at full twist (BT_FORCE_TWIST, rate-demand axis -> +# slews to the per-mech limit and holds), shooter approaches to a fixed +# bearing and pulses LRM salvos (BT_AF_MISSILE -- missiles are the authentic +# cylinder-path weapon; beams with zone -1 were DROPPED by the binary). +# Two configs: twist +1 vs -1. READ: target's [dmgresolve] zone spread -- +# hull-fixed wheels (legs/hip) must NOT move between runs (control); torso +# wheels must swap L/R families to match the flank visibly facing the +# shooter (A's BT_SHOT captures are the ground truth). +set -x +. /c/git/bt411/scratchpad/night6/bench_common.sh +cd /c/git/bt411/content || exit 1 +bt_assert_player_env + +for TW in 0 +1 -1; do + TAG=Z; [ "$TW" = "+1" ] && TAG=P; [ "$TW" = "-1" ] && TAG=N + taskkill //F //IM btl4.exe > /dev/null 2>&1 + sleep 2 + rm -f ts_${TAG}_a.log ts_${TAG}_b.log ts${TAG}_*.png + bt_expert_egg MP.EGG TS.EGG + sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" TS.EGG + + # TARGET: stationary, torso pinned; log every cylinder resolution. + BT_FORCE_TWIST=$TW BT_DMGTABLE_LOG=1 BT_CYL_LOG=1 BT_DMG_LOG=1 BT_MP_LOG=1 \ + bt_launch ts_${TAG}_b.log TS.EGG 0x0C -net 1601 + sleep 2 + # SHOOTER: approach to fixed range, missile salvos only, periodic captures. + BT_GOTO=enemy BT_GOTO_STOP=90 BT_KEY_NOFOCUS=1 \ + BT_AF_MISSILE=1 BT_AF_PERIOD=6 \ + BT_DMG_LOG=1 BT_MP_LOG=1 BT_SHOT_EVERY=300 BT_SHOT_PREFIX=ts$TAG \ + bt_launch ts_${TAG}_a.log TS.EGG 0x03 -net 1501 + sleep 5 + python ../tools/btconsole.py TS.EGG 127.0.0.1:1501 127.0.0.1:1601 \ + > ts_${TAG}_relay.log 2>&1 & + RELAY=$! + sleep 170 + kill $RELAY 2>/dev/null + taskkill //F //IM btl4.exe > /dev/null 2>&1 + sleep 3 +done +echo "=== resolves per run:" +grep -c dmgresolve ts_Z_b.log ts_P_b.log ts_N_b.log