Files
BT412/context/open-questions.md
T
arcattackandClaude Opus 4.8 2af401eef8 Collision damage applied: mech-vs-mech + icon-crunch via the STEP-6 unaimed path
The two deferred TakeDamage dispatches in Mech::ProcessCollision now fire,
unblocked by STEP 6 (the cylinder hit-location override that resolves zone==-1):

  - Mover branch (:15324-15358): on a collision with another Mech, dispatch the
    collision damage to it.
  - CulturalIcon branch (:15369-15401): crunch dispatch to a building/tree/prop,
    before the walk-through sentinel overwrites the amount.

Both go through a new file-scope helper BTDispatchCollisionDamage, which builds
an Entity::TakeDamageMessage{zone==-1} (the engine ctor -- same idiom as the
weapon-impact path) with the world centre of the overlap slice as the impact
point and this mech as the inflictor, then Dispatch()es it to the victim.  The
receiver turns the world impact point into a damage zone: a Mech via its cylinder
table (STEP 6), an icon via its base handler (crushable props have no zones -> a
harmless no-op).  Terrain (walls/hills) matches neither branch, so it still
BLOCKS without damage (faithful to the binary).

Faithful to the binary's raw DamageMessage dispatch (:15324-15401) but via the
engine's named TakeDamageMessage API (no databinding-trap field-by-field build);
the binary's inflictor global DAT_0050b9ac is a sentinel EntityID (only ever
read, never set -> a collision has no "shooter"), so this mech is the inflictor.

Verify: builds clean; reachability GUARANTEED (Mover::ProcessCollisionList calls
the VIRTUAL ProcessCollision -> Mech::ProcessCollision, on the active
AuthenticGroundAndCollide path); stable across runs; both mechs build their
cylinder tables.  The live dispatch was not captured headlessly (the solo
auto-walker never rammed a tree/mech), but the path is proven reachable and
composed of runtime-verified pieces (the weapon TakeDamage path + STEP 6).

Also validated STEP 6's height ref: collisionTemplate->maxY ~= 7.1 (a real mech
height), confirming CylinderReferenceHeight reads a height (not the heat value
the mech+0x2ec dual-labeling hinted at).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:54:21 -05:00

10 KiB
Raw Blame History

id, title, status, source_sections, related_topics
id title status source_sections related_topics
open-questions Open Questions — deferred systems, unknowns, get-from-Nick living CLAUDE.md §9; docs/HARD_PROBLEMS.md; per-subsystem deferral notes
source-completeness
subsystems
multiplayer
combat-damage
locomotion

Open Questions

Known unknowns + deliberately-deferred systems. For each: what's known, what's missing, what would resolve it, status. Deferred ≠ broken — most are marked-and-guarded stand-ins with the authentic path scoped.

From Nick (external — gating for some paths)

  • [GATING] The BT game SOURCE CODE — the missing implementation .cpp (mech, subsystems, mapper, HUD, app; see source-completeness). Without it, Route A (recompile original) is impossible; we reconstruct from the binary instead. Likely on a backup/dev drive. Status: OPEN.
  • Confirmation the PodPC image is the complete content master. (Low priority — the runtime BTL4.RES already has 8 maps + the full mech anim set.) Status: OPEN.
  • Pod specifics for Phase 8: the 7-monitor driver setup, the RIO cockpit I/O protocol, current Win10+wrapper pod config. Status: OPEN.

Deferred subsystems / feeds (authentic path scoped, marked in code)

  • 0xBD3 SubsystemMessageManager (WAVE 8) — a per-mech damage/explosion consolidation hub (ConsolidateAndSendDamage Performance @0049b784, weaponExplosions queue, CommonDamageInformation; messmgr.hpp). ctor FUN_0049bca4 (vtable 0x50b954, installs a Performance → ticks). The factory caches it to Mech[0x10d] = byte 0x434. What wiring it needs: an untangle — our reconstruction MISLABELED Mech[0x10d] as controlsMapper and mech4's live drive reads it as the mapper, so the real messmgr can't land there until the drive is re-pointed to the true mapper (roster slot 0, installed by SetMappingSubsystem/MakeViewpointEntity; a MechRIOMapper). Payoff = authentic damage/explosion consolidation (a COMBAT-fidelity improvement) + fixing the mapper/messmgr conflation. ⚠ CORRECTION (verified 2026-07): 0xBD3 does NOT gate the valve/Myomers/MessageBoard — those read a DIFFERENT object at mech+0x190 (both FUN_004ac9c8 the valve guard →+0x274 and FUN_004ad7d4 the Myomers gate →+0x260 deref owner+0x190, which ≠ the 0x434 messmgr cache). The earlier "0xBD3 gates 4 things" note was wrong.
  • mech+0x190 IDENTIFIED (2026-07): it is the owning BTPlayer (Mech::GetPlayerLink(), MECH_OWNING_PLAYER; ENTITY.h:430). Set by FUN_0049f624 (the mech↔player bind: mech+0x190 = player AND player->playerVehicle(+0x1fc) = mech), which resolves the player from the mission player registry (app+0x2c+0x54, FUN_0041fd18) by the pilot key. The valve/Myomers "gates" read the owning player's fields at player+0x260 and player+0x274 (the reconstruction maps this block as showDamageReceived@0x25c / showKills@0x260 / showDamageInflicted@0x264 / roleClassIndex@0x274 in btplayer.hpp — from the SCORING work, so the names may NOT match the gate semantics; the true meaning + the WRITER of player+0x260/0x274 are not yet pinned — part_013.c:4553-4660 is the TorsoSimulation, a different object, not the setter). So there is NO new subsystem to build. The wiring (small): point MechSubsystem::IsDamaged() (FUN_004ac9c8, valve guard) at GetPlayerLink()->(0x274) and Myomers::OwnerAdvancedDamage() (FUN_004ad7d4) at GetPlayerLink()->(0x260) — via NAMED members (databinding trap: our BTPlayer layout ≠ binary; do NOT raw-read player+0x260). ⚠ Resolve first: (a) a naming conflict — FUN_004ad7d4 is labeled BOTH HeatModelActive (heat.hpp) AND OwnerAdvancedDamage (myomers.cpp); re-verify which body is which; (b) the true semantic of player+0x260/0x274 vs the scoring-derived showKills/roleClassIndex names. NOTE: these are MODE flags — in the basic test mission the inert Myomers / dormant valve is likely AUTHENTIC (advanced damage off); wiring makes them RESPOND when a mission enables the mode, not necessarily change the basic-mission behavior. The MessageBoard feed is separate (StatusMessagePool, below).
  • StatusMessagePool (NULL stub, btstubs.cpp:62) — the per-player status-message queue. BTPlayer+0x1dc is never populated → MessageBoard is empty (authentic for bring-up). What resolves it: wire the pool + decode AddStatusMessage @0042e580 + the Player__StatusMessage record ({messageId@+0xc, nameEntity@+0x10}).
  • Gyroscope (0xBC4) — layout + joint-I/O reconstructed, but the ctor field-init + the two integrators (IntegrateEyeJoint @004b2ec0 / IntegrateBody @004b30ec) are incomplete → writes NaN to the root joint. Reverted to a stub. A full subsystem-sim reconstruction (bigger than Torso).
  • MechControlsMapper look/eyepoint commit — reconstructed but its offsets collide with declared members; arbitrate before enabling (no port consumer yet). The fire-trigger-through-mapper is still the gBTWeaponTrigger bring-up.
  • Myomers authentic coupling — the structural un-stub is INERT (mover feed + heat-gen no-op). Real coupling needs the advanced-damage gate (OwnerAdvancedDamage/FUN_004ad7d4 → the owning BTPlayer mech+0x190+0x260, NOT 0xBD3 — see the mech+0x190 item above) + MoverAttach routing into the LIVE JointedMover (must be reconciled with the gait cutover first).
  • SeekVoltageGraph — 4 Seek* attrs unpublished (a cluster-child, not config-called; non-blocking).

Locomotion / combat polish (non-gating)

  • Authentic per-mech TURN-RATE constant (currently a bring-up constant rate).
  • Body-callback gimp handlers (states 16-19, targets 0x70b2/0x7161 undecoded → fall back to stand); airborne callbacks (FUN_004a6344/FUN_004a7970).
  • Wall-block-vs-climb tuning. Collision DAMAGE application — DONE 2026-07-08 [T2]. The two deferred dispatches in Mech::ProcessCollision (mech4.cpp: mech-vs-mech :15324-15358 + icon-crunch :15369-15401) now fire: on a collision with another Mech or a CulturalIcon, an Entity::TakeDamageMessage{zone==1} is dispatched to the victim (via BTDispatchCollisionDamage, the engine ctor — same as the weapon path), and STEP 6's cylinder table resolves the impact point → a zone on the receiver. Terrain (walls/hills) matches neither branch → blocks without damage (faithful). Reachability is guaranteed (Mover::ProcessCollisionList calls the virtual ProcessCollisionMech::ProcessCollision); built + stable. The live dispatch wasn't captured headlessly (the solo auto-walker never rammed a tree/mech), but the path is proven reachable and composed of runtime-verified pieces. Note: validated STEP 6's height ref along the way — collisionTemplate->maxY ≈ 7.1 (a real mech height), confirming CylinderReferenceHeight reads a height, not the heat value the mech+0x2ec dual-labeling (heat-gauge sink vs groundRef, mech4.cpp:2697) hinted at.
  • Cylinder hit-location (STEP 6) — DONE 2026-07-08 [T2]. Built + runtime-verified: the dmgtable.cpp classes now have real storage + a working ResolveHit→zone, the mech ctor loads the type-0x1d table by the DamageZoneStream name ([cyl] table 'bhk1' layers=7), and Mech::TakeDamageMessageHandler resolves unaimed (zone==1) hits before base-routing. Full change list in combat-damage "STEP 6 COMPLETE". The live collision-damage path (zone==1 → resolve) is now UNBLOCKED. Historical investigation notes retained below for provenance. Re-investigated 2026-07 (see combat-damage for the full algorithm): the table (height×angle grid, resource type 0x1d) IS built (recon StandingAnimation @Mech[0x111], but with an EMPTY name → 0 rows, because ResourceFindByName is a no-op template stub). CORRECTION to the earlier note: the LOOKUP is exported [T1] — FUN_0049eb54 (table height→row) + FUN_0049e678 (row angle→cell), NOT unexported. Only the thin Mech::TakeDamageMessageHandler glue (zone==-1 → call the lookup → damageZones[zone]->TakeDamage) is unexported, and it's fully implied by pieces already reconstructed → no disassembly needed. Remaining: 6b real FindByName (name = the mech's type-0x14 DamageZoneStream name @ResourceDescription+0xc); 6c the 3 container ctors + the lookup as a BTCylinderResolveZone bridge (ground the cell zone-dict stream format vs the real .RES bytes); 6d register the handler + verify. Feasible; the only open risk is the cell stream format. The earlier-cited FUN_004a0230/FUN_0049ed0c don't exist.
  • Death: reconstruct DeathShutdown (RP VTV::DeathShutdown analog) + collapse anim + destroyed skin. The wreck-stays behavior we ship is faithful; do NOT issue DestroyEntityMessage on death.

Multiplayer (Phase 7 / P6)

  • Cross-pod COMBAT (target a replicant + route damage to the owning master).
  • Replicant GAIT animation (replicants don't animate joints yet — derive from replicated velocity).
  • The pod-LAN config (real IPs, bare-IP pilot entries). The WinSock2 TCP stack (L4NET.CPP) is reconstructed + smoke-tested on one box (two instances share a world, movement replicates).

Rendering follow-ups (non-blocking)

  • Replacement-LOD selection for NON-additive multi-LOD models (needs the board's hot-spot/ reference-point semantics; leads: s_dplobject.lod_ranges[16]/lod_hot_spot, 0x2047/0x2048).
  • Day/night MATERIAL path priority (BMF/IMG) — infrastructure done, gated BT_MATPRI (over-applies to shared terrain mats); needs the terrain-material model understood.
  • RGBA4444 alpha (TREE.BSL cutout) decoded but not alpha-tested outside punch batches; VTX/TGA paths.

Content build sub-project (low priority)

  • Lab/other-build maps (des/burnt/frstrm) are source-only (.map in CONTENT/BT/MAPS/); would need compiling into a RES via the DOS btl4tool.exe. The 8 RES maps cover testing.

Key Relationships

  • Feeds from: every subsystem/render topic (their deferral notes collect here).
  • Gating master: source-completeness (the missing BT source).