From 82f1ea118349de5e7b72b70d03b580511bb2a4ec Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Thu, 30 Jul 2026 12:32:26 -0500 Subject: [PATCH] bench: symbolic leg-zone targeting, gimp-follow/goto-release, legal egg colors - BT_SELF_DAMAGE_ZONE=leg resolves the mech's actual leg zone via the streamed LegDamageZone flags (new Mech__DamageZone::IsLegZone) -- numeric zone ids are per-mech-type and a wrong one aims at a vital (killed the limp bench's mech instead of gimping it: 2 ticks, death cycle, respawn healed, harness spent -- 3 minutes of healthy circling). - BT_GOTO=gimp: beeline mode that prefers the nearest LIMPING peer (gimp half-states 3/4 ride the one-cell in every update record); stands and waits while nobody limps. BT_GOTO_RELEASE=1 hands the mech back to the keyboard once the beeline arrives. - MP.EGG/MP4.EGG/MP4L.EGG: color=Red and color=Blue are PATCH values, not colors (RES vehicletable colors: Black/Brown/Crimson/Green/Grey/Tan/ White) -- the two pilots carrying them rendered as untextured pastel mechs. Boreas Red->Crimson, Caicias Blue->Grey. MP4L.EGG = the 4-pilot bench egg on the open grass map. 4-node limp verification (user-eyeballed, all instances): the gimped mech step-drags with the authentic limp on every observer -- no skating (#82 holds in the 4-node overlap case). Co-Authored-By: Claude Fable 5 --- content/MP.EGG | 2 +- content/MP4.EGG | 4 +- content/MP4L.EGG | 274 +++++++++++++++++++++++++++++++++ game/reconstructed/mech4.cpp | 58 ++++++- game/reconstructed/mechdmg.hpp | 7 + 5 files changed, 335 insertions(+), 10 deletions(-) create mode 100644 content/MP4L.EGG diff --git a/content/MP.EGG b/content/MP.EGG index f062d5e..a45967d 100644 --- a/content/MP.EGG +++ b/content/MP.EGG @@ -185,7 +185,7 @@ role=Role::Default dropzone=one vehicle=ava1 vehicleValue=1000 -color=Red +color=Crimson [largebitmap] bitmap=BitMap::Large::Aeolus [BitMap::Large::Aeolus] diff --git a/content/MP4.EGG b/content/MP4.EGG index dc7e6cb..6ab2508 100644 --- a/content/MP4.EGG +++ b/content/MP4.EGG @@ -187,7 +187,7 @@ role=Role::Default dropzone=one vehicle=ava1 vehicleValue=1000 -color=Red +color=Crimson [127.0.0.1:1702] hostType=0 advancedDamage=1 @@ -201,7 +201,7 @@ role=Role::Default dropzone=one vehicle=bhk1 vehicleValue=1000 -color=Blue +color=Grey [127.0.0.1:1802] hostType=0 advancedDamage=1 diff --git a/content/MP4L.EGG b/content/MP4L.EGG new file mode 100644 index 0000000..cbadc06 --- /dev/null +++ b/content/MP4L.EGG @@ -0,0 +1,274 @@ +[mission] +adventure=BattleTech +map=grass +scenario=freeforall +time=night +weather=clear +temperature=27 +length=600 +[ordinals] +bitmap=Ordinal::BitMap::1 +bitmap=Ordinal::BitMap::2 +bitmap=Ordinal::BitMap::3 +bitmap=Ordinal::BitMap::4 +[Ordinal::BitMap::1] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=000038000003C000001FF00000000F00 +bitmap=0000F8000003C000003FF80000000F00 +bitmap=0001F8000003C00000707C0000000F00 +bitmap=0001F8000003C00000603C0000000F00 +bitmap=00007801FC0FF00000003C3DF807FF00 +bitmap=00007803FE0FF00000003C3FFC0FFF00 +bitmap=00007803C703C00000003C3E3C1F0F00 +bitmap=000078078303C0000000783C1E1E0F00 +bitmap=000078078003C0000000783C1E1E0F00 +bitmap=00007807C003C0000000F03C1E1E0F00 +bitmap=00007807F003C0000001E03C1E1E0F00 +bitmap=00007803FC03C0000003C03C1E1E0F00 +bitmap=00007801FE03C0000007803C1E1E0F00 +bitmap=000078007F03C000000F003C1E1E0F00 +bitmap=000078001F03C000001E003C1E1E0F00 +bitmap=000078000F03C000003C003C1E1E0F00 +bitmap=000078060F03C0000078003C1E1E0F00 +bitmap=000078071E03E0000078003C1E1F1F00 +bitmap=00007803FE01F000007FFC3C1E0FFF00 +bitmap=00007801FC00F000007FFC3C1E07EF00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::2] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF0000003C0000003C03C0780000 +bitmap=001FFF0000003C0000007C03C0780000 +bitmap=00000E0000003C000000FC03C0780000 +bitmap=00003C0000003C000001BC03C0780000 +bitmap=0000700F3E1FFC000003BC0FF07BF000 +bitmap=0001E00F7E3FFC0000073C0FF07FF800 +bitmap=0003800FFE7C3C0000063C03C07C7800 +bitmap=0007F80FFE783C00000C3C03C0783C00 +bitmap=0007FE0F80783C0000183C03C0783C00 +bitmap=00001E0F00783C0000383C03C0783C00 +bitmap=00000F0F00783C0000703C03C0783C00 +bitmap=00000F0F00783C00007FFF03C0783C00 +bitmap=00000F0F00783C00007FFF03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00000F0F00783C0000003C03C0783C00 +bitmap=00180F0F00783C0000003C03C0783C00 +bitmap=001C1F0F007C7C0000003C03E0783C00 +bitmap=000FFE0F003FFC0000003C01F0783C00 +bitmap=0007FC0F001FBC0000003C00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::3] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF03C07800000000FC03C0780000 +bitmap=001FFF03C07800000003FC03C0780000 +bitmap=001E0003C07800000007C003C0780000 +bitmap=001E0003C0780000000F0003C0780000 +bitmap=001E000FF07BF000000F000FF07BF000 +bitmap=001E000FF07FF800001E000FF07FF800 +bitmap=001E0003C07C7800001E0003C07C7800 +bitmap=001FFC03C0783C00001EFC03C0783C00 +bitmap=001FFE03C0783C00001FFE03C0783C00 +bitmap=00001F03C0783C00001F1F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00000F03C0783C00001E0F03C0783C00 +bitmap=00180F03C0783C00001E0F03C0783C00 +bitmap=001C1F03E0783C00001F1F03E0783C00 +bitmap=000FFE01F0783C00000FFE01F0783C00 +bitmap=0007FC00F0783C000007FC00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[Ordinal::BitMap::4] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=001FFF03C07800000007FC03C0780000 +bitmap=001FFF03C0780000000FFE03C0780000 +bitmap=00000F03C0780000001F1F03C0780000 +bitmap=00000F03C0780000001E0F03C0780000 +bitmap=00000F0FF07BF000001E0F0FF07BF000 +bitmap=00001F0FF07FF800001E0F0FF07FF800 +bitmap=00001E03C07C7800001E0F03C07C7800 +bitmap=00003E03C0783C00001E0F03C0783C00 +bitmap=00003C03C0783C00000F1E03C0783C00 +bitmap=00003C03C0783C000007FC03C0783C00 +bitmap=00007803C0783C000007FC03C0783C00 +bitmap=00007803C0783C00000F1E03C0783C00 +bitmap=00007803C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003C0783C00001E0F03C0783C00 +bitmap=0000F003E0783C00000F1E03E0783C00 +bitmap=0000F001F0783C00000FFE01F0783C00 +bitmap=0000F000F0783C000007FC00F0783C00 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[pilots] +pilot=127.0.0.1:1502 +pilot=127.0.0.1:1602 +pilot=127.0.0.1:1702 +pilot=127.0.0.1:1802 +[127.0.0.1:1502] +hostType=0 +advancedDamage=1 +loadzones=1 +name=Aeolus +bitmapindex=1 +experience=novice +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=bhk1 +vehicleValue=1000 +color=White +[127.0.0.1:1602] +hostType=0 +advancedDamage=1 +loadzones=1 +name=Boreas +bitmapindex=2 +experience=novice +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=ava1 +vehicleValue=1000 +color=Crimson +[127.0.0.1:1702] +hostType=0 +advancedDamage=1 +loadzones=1 +name=Caicias +bitmapindex=3 +experience=novice +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=bhk1 +vehicleValue=1000 +color=Grey +[127.0.0.1:1802] +hostType=0 +advancedDamage=1 +loadzones=1 +name=Deimos +bitmapindex=4 +experience=novice +badge=VGL +patch=Yellow +role=Role::Default +dropzone=one +vehicle=ava1 +vehicleValue=1000 +color=Green +[largebitmap] +bitmap=BitMap::Large::Aeolus +[BitMap::Large::Aeolus] +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=000003FE0000000001E0000000000000 +bitmap=000007FF0000000001E0000000000000 +bitmap=00000F8F8000000001E0000000000000 +bitmap=00000F078000000001E0000000000000 +bitmap=00000F0781FF00FF81E0F0781FC00000 +bitmap=00000F0783FF81FFC1E0F0783FE00000 +bitmap=00000F0787C7C3E3E1E0F0783C700000 +bitmap=00000F078783C3C1E1E0F07878300000 +bitmap=00000F078783C3C1E1E0F07878000000 +bitmap=00000FFF8783C3C1E1E0F0787C000000 +bitmap=00000FFF8783C3C1E1E0F0787F000000 +bitmap=00000F0787FFC3C1E1E0F0783FC00000 +bitmap=00000F0787FFC3C1E1E0F0781FE00000 +bitmap=00000F07878003C1E1E0F07807F00000 +bitmap=00000F07878003C1E1E0F07801F00000 +bitmap=00000F07878003C1E1E0F07800F00000 +bitmap=00000F078780C3C1E1E0F07860F00000 +bitmap=00000F0787C1C3E3E1E078F871E00000 +bitmap=00000F0783FF81FFC1E07FF83FE00000 +bitmap=00000F0781FF00FF81E03F781FC00000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +bitmap=00000000000000000000000000000000 +x=128 +y=32 +width=8 +[smallbitmap] +bitmap=BitMap::Small::Aeolus +[BitMap::Small::Aeolus] +bitmap=00000000000000000000000000000000 +bitmap=0000000000000000003E00000C000000 +bitmap=006300000C00000000631F0F8CC63C00 +bitmap=00633198CCC66200007F3198CCC66000 +bitmap=00633F98CCC6780000633018CCC61E00 +bitmap=00633018CCC6060000633098CCC64600 +bitmap=00631F0F8C7E3C000000000000000000 +bitmap=00000000000000000000000000000000 +x=64 +y=16 +width=4 +[Role::Default] +model=dfltrole +[Role::NoReturn] +model=noretun diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 7e9dff3..0918b81 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -3504,10 +3504,11 @@ void { const char *gv = getenv("BT_GOTO"); if (gv && !stricmp(gv, "enemy")) s_goto = 2; // chase nearest replicant + else if (gv && !stricmp(gv, "gimp")) s_goto = 3; // follow the LIMPING peer (else nearest) else if (gv && sscanf(gv, "%f %f", &s_gx, &s_gz) == 2) s_goto = 1; else s_goto = 0; } - if (s_goto == 2) + if (s_goto == 2 || s_goto == 3) { // DYNAMIC target (test harness): beeline toward the CLOSEST live // peer replicant wherever it currently is -- MP spawn positions @@ -3515,7 +3516,14 @@ void extern int BTGetTargetCandidates(Entity *shooter, Entity **out, int maxOut); Entity *ec[32]; const int enc = BTGetTargetCandidates((Entity *)this, ec, 32); - float best = 1e30f; int found = 0; + // mode 3 ("gimp"): prefer the nearest LIMPING peer -- the gimp + // half-states 3/4 ride every update record in the one-cell + // (movementMode), so a replicant knows. Falls back to nearest + // while nobody limps (pre-damage phase), so the pack forms up + // and then locks onto the wounded mech. + float best = 1e30f, bestG = 1e30f; + int found = 0, foundG = 0; + float gxN = 0.0f, gzN = 0.0f, gxG = 0.0f, gzG = 0.0f; for (int ei = 0; ei < enc; ++ei) { Mech *em = (Mech *)ec[ei]; @@ -3524,11 +3532,17 @@ void float edx = (float)ep.x - (float)localOrigin.linearPosition.x; float edz = (float)ep.z - (float)localOrigin.linearPosition.z; float ed2 = edx*edx + edz*edz; - if (ed2 < best) { best = ed2; s_gx = (float)ep.x; s_gz = (float)ep.z; found = 1; } + if (ed2 < best) { best = ed2; gxN = (float)ep.x; gzN = (float)ep.z; found = 1; } + int emm = em->MovementMode(); // one-cell (simulationState) -- replicated + if (s_goto == 3 && (emm == 3 || emm == 4) + && ed2 < bestG) + { bestG = ed2; gxG = (float)ep.x; gzG = (float)ep.z; foundG = 1; } } - if (found) s_goto = 2; else { gBTGotoActive = 0; } + if (foundG) { s_gx = gxG; s_gz = gzG; } + else if (found) { s_gx = gxN; s_gz = gzN; } + else { gBTGotoActive = 0; } } - if (s_goto == 1 || (s_goto == 2 && (s_gx != 0.0f || s_gz != 0.0f))) + if (s_goto == 1 || (s_goto >= 2 && (s_gx != 0.0f || s_gz != 0.0f))) { float ddx = s_gx - (float)localOrigin.linearPosition.x; float ddz = s_gz - (float)localOrigin.linearPosition.z; @@ -3554,7 +3568,7 @@ void // instead of ramming; a fixed point drives right up to the spot. // BT_GOTO_STOP overrides (issue #16 parallax test: approach the // dummy from range all the way in to 3/4-screen). - float stopDist = (s_goto == 2) ? 300.0f : 5.0f; + float stopDist = (s_goto >= 2) ? 300.0f : 5.0f; { static float s_stopOv = -1.0f; if (s_stopOv < 0.0f) @@ -3606,6 +3620,22 @@ void if (s_thrOv > 0.0f) gthr = s_thrOv; } gBTDrive.forcedThrottle = arrived ? 0.0f : gthr; + // BT_GOTO_RELEASE=1: hand the mech BACK to the keyboard once + // the beeline arrives -- the observer rig auto-converges on + // the target, then the USER drives freely (goto disengages + // for good; gBTDrive.forced held keys dead otherwise). + { + static int s_rel = -1; + if (s_rel < 0) s_rel = getenv("BT_GOTO_RELEASE") ? 1 : 0; + if (arrived && s_rel) + { + s_goto = 0; + gBTGotoActive = 0; + gBTDrive.forced = 0; + gBTDrive.forcedThrottle = 0.0f; + DEBUG_STREAM << "[goto] ARRIVED -- released to manual control\n" << std::flush; + } + } turn = gBTGotoTurn; // non-real-controls fallback path } } @@ -5727,7 +5757,21 @@ void { const char *ze = getenv("BT_SELF_DAMAGE_ZONE"); if (ze != 0 && *ze != '\0') - sdZone = atoi(ze); + { + if (!stricmp(ze, "leg")) + { + // symbolic: first streamed LEG zone of THIS mech -- + // numeric zone ids are per-mech-type and a wrong one + // aims at a vital (killed the limp bench's mech). + for (int zi = 0; zi < damageZoneCount; ++zi) + { + Mech__DamageZone *zz = Zone(zi); + if (zz != 0 && zz->IsLegZone()) { sdZone = zi; break; } + } + } + else + sdZone = atoi(ze); + } if (sdZone < -1 || sdZone >= damageZoneCount) sdZone = -1; } diff --git a/game/reconstructed/mechdmg.hpp b/game/reconstructed/mechdmg.hpp index 1b36416..985b338 100644 --- a/game/reconstructed/mechdmg.hpp +++ b/game/reconstructed/mechdmg.hpp @@ -254,6 +254,13 @@ MechCriticalSubsystem **criticalSubsystems; // @0x1b4 array[criticalSubsystemCount] + public: + // Bench query (BT_SELF_DAMAGE_ZONE=leg): leg zones stream the + // LegDamageZone flags, so the harness resolves "the leg" symbolically -- + // zone NUMBERING is per-mech-type (MadCat 16, bhk1 18, ...) and a magic + // number aimed at the wrong type has killed the mech instead of gimping. + int IsLegZone() const { return (leftLeg || rightLeg) ? 1 : 0; } + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Damage-state descriptor table (binary this+0xd4). Streamed from the // type-0x1e resource by LoadCriticalSubsystems (FUN_0041e4a8 -> FUN_0042a748);