Commit Graph
3 Commits
Author SHA1 Message Date
Joe DiPrimaandClaude Opus 5 5a425320b7 #92: map the direct-fire hit->zone path; the FOOT is pickable over only 3% of a mech's height
Oracle, night 7: "Unable to damage the foot panels on Loki via direct fire from
front or side.  Was able to damage on Thor."

FIRST, THE MECHANISM -- it is not what the issue assumed (missing collision
geometry).  A direct-fire hit gets its zone one of two ways, decided at
Mech::TakeDamageMessageHandler by `invalidDamageZone`:

  AIMED   (invalidDamageZone=0): the zone rides in on the message from the
          per-part aim pick, MechSegmentPick (#73, btl4vid.cpp).
  UNAIMED (invalidDamageZone=1): DamageLookupTable::ResolveHit runs the authored
          cylinder lottery -- impact HEIGHT picks a layer, ANGLE picks a pie
          slice, and a weighted random roll picks the zone from that slice.

Measured live, both paths are in use: the Thor logged 16 unaimed hits
(invalidZone=1 zone=-1 -> ResolveHit) alongside aimed ones carrying real zones.

THE FINDING.  MechSegmentPick is a BOUNDING-SPHERE test whose primary key is
SMALLEST RADIUS WINS -- a larger sphere can never beat a smaller one the ray also
grazes.  The leg spheres, dumped in world space and IDENTICAL on both chassis:

    knee  centre y=1.868  r=1.505     spans y 0.364 .. 3.373
    toe   centre y=0.302  r=1.689     spans y -1.387 .. 1.991

The toe sphere is BIGGER than the knee's and they overlap heavily, so the toe can
only win where the ray misses the knee sphere outright -- i.e. below y=0.364.
Against a reference height of 11.16 that is a 0.36-unit window, **3.3% of the
mech's height**, and it sits right on the ground.  Everywhere else a shot at the
foot is credited to the LEG.  That is the reported symptom.

This is a PORT ARTIFACT, not authentic: btl4vid.cpp's own comment concedes the
sphere test approximates "the per-part semantic the 1995 mesh intersection
produced".  Real mesh intersection has no such interference -- aiming at the foot
mesh hits the foot.

NOT EXPLAINED, and stated plainly: the per-CHASSIS asymmetry.  Loki and Thor have
identical leg spheres, identical foot geometry (LOK_LFOT.BGF and THR_LFOT.BGF are
both 3082 bytes with the same token layout), and identical foot layers in their
damage tables (only the upper/cockpit layers differ).  So nothing found here says
the Loki should behave differently from the Thor.  The bench could not settle it
because BT_AIM moves the drawn RETICLE, not the pick ray -- there is currently no
harness to aim the pick at a chosen height.  That harness is the next step.

Diagnostics added:
  [pickgeom]   one-shot dump of every pick sphere in WORLD space (zone, r, centre)
  [pickcand]   which spheres a ray actually threaded, their perpendicular d, and
               which won -- the probe that makes "smallest wins" visible
  [dmgtable]   the whole authored DamageLookupTable: layers, slices, zone weights
  [dmgresolve] per hit: localY, heightRef, layer, theta, resolved zone
  [cylgate]    invalidDamageZone / table pointer / incoming zone at the gate
(the last three under BT_DMGTABLE_LOG, the first two under BT_PICK_LOG)
plus scratchpad/night8/footpick.sh.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 19:47:30 -05:00
arcattackandClaude Opus 4.8 d07ac7dd49 STEP 6 COMPLETE: cylinder hit-location LIVE — unaimed hits resolve to zones
The Mech per-impact hit-location resolver (the cylinder damage table) is now
functional, wired, and runtime-verified [T2].  Unaimed (zone==-1) hits — the
collision-damage path — now resolve an impact point to a damage zone via the
authentic height x angle grid + weighted dice roll, instead of dropping.

dmgtable.cpp/.hpp was a non-functional skeleton on no-op ReconTable/stream
shims; backed it with real std::vector storage and fixed 5 latent runtime bugs:
  - ReadEntries now consumes the leading cell name-string ([i32 len][len+1])
  - PieSlice ctor reads rotateWithTorso into the correct member
  - SelectSlice direct-indexes (was int lookup on a float-keyed table)
  - ResolveHit returns the zone (chains SelectSlice -> SelectZone)
  - real MemoryStream::ReadBytes (was a variadic no-op)

mech.cpp ctor: replaced the empty-name StandingAnimation stub with the real
load — FindResourceDescription(dzRes->resourceName, type 0x1d) -> stream ->
new DamageLookupTable, cached at mech[0x111]; ~Mech deletes it.

Mech::TakeDamageMessageHandler override registered (MESSAGE_ENTRY overlays
Entity's by ID): on invalidDamageZone, resolve via the table then base-route;
aimed reticle hits pass through unchanged.

Three named accessors (no databinding-trap raw reads): WorldToLocal
(localToWorld.MultiplyByInverse), CylinderReferenceHeight (standingTemplateMaxY
== collisionTemplate->maxY == binary mech+0x2ec[+0xc]), TorsoHeading via a
BTGetTorsoTwist bridge in torso.cpp (Torso::CurrentTwist == torso+0x1d8;
torso.hpp cannot be included into mech.cpp — subsystem-stub collision).

Stream format + geometry + roll + handler were all byte-verified against the
shipped BTL4.RES type-29 resources (18 tables, exact consumption) and the
disassembly (FUN_0049eb54/e678/de14, glue 0x49ed0c, handler @0x4a037a).

Runtime: boots clean, "[cyl] table 'bhk1' layers=7" (exact byte-verified layer
count, found by name), mech spawns + walks, no asserts/AV/0xCDCDCDCD.  Env gate
BT_CYL_LOG=1.  Unblocks collision-damage application.

KB updated (combat-damage.md STEP 6 COMPLETE, open-questions.md marked done).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:32:17 -05:00
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00