Files
BT412/context/combat-damage.md
T
arcattackandClaude Opus 4.8 f914fc040a context-system: complete migration -> CLAUDE.md is now a 160-line router (zero context lost)
Full migration of the 2236-line monolithic CLAUDE.md into the progressive-context
knowledge graph (per spark-lesson / expert-seed.md), so the deep RE knowledge loads
on-demand instead of every session.

ZERO CONTEXT LOST:
- docs/PROGRESS_LOG.md = the complete old CLAUDE.md, VERBATIM (byte-identical) -- the
  lossless safety net + the "full detail" quick-lookup fallback.
- 18 context/*.md topic files (1343 lines) digest every section (§1-3 -> project-overview,
  §4 -> content-archives, §5 -> asset-formats/bgf-format, §5a -> source-completeness,
  §5b/§8 -> wintesla-port, §7/§10 -> locomotion, §10a -> build-and-run, §10b ->
  reconstruction-method, §10c -> combat-damage + reconstruction-gotchas, §10d -> subsystems,
  render notes -> rendering, gauges -> gauges-hud, MP -> multiplayer, §9 -> open-questions).
- reference/glossary.yaml (53 terms). decomp-reference.md = the offsets/ClassIDs/addresses hub.

CLAUDE.md (160 lines) = router: identity, answer/reason protocols, quick-lookup table,
evidence tiers (T0 engine-truth / T1 decompiled+verified / T2 reconstructed+runtime /
T3 guarded / T4 hypothesis), conventions + DO-NOT (the systemic bug classes), structure.
Retains the load-bearing work directives (build recipe pointer, "keep current" mandate).

Knowledge graph validates CLEAN (scratchpad/checkctx.py -- all [[links]] + quick-lookup +
docs refs resolve; [[name]] -> topic file or glossary term). docs/*.md ledgers stay as the
detailed logs; context/*.md are the curated digests that route into them.

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

75 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: combat-damage
title: "Combat & Damage — targeting, firing, damage zones, death"
status: established
source_sections: "PROGRESS_LOG.md §10c; docs/HARD_PROBLEMS.md (P5)"
related_topics: [subsystems, decomp-reference, reconstruction-gotchas, locomotion, rendering]
key_terms: [damage-zone, TakeDamage, viewpoint-entity, master, replicant]
open_questions:
- "Per-impact aim (cylinder lookup); collision damage application; DeathShutdown + collapse anim"
---
# Combat & Damage
The drive→target→fire→damage→destroy loop. Full detail: `docs/PROGRESS_LOG.md §10c`; the death/
teardown forensics: `docs/HARD_PROBLEMS.md` (P5).
## No AI — BT is PvP-only (verified, not a recovery gap)
The mission system is NETWORKED and BT never had NPCs/AI. Every mech is driven by a `BTPlayer` (or a
`BTCameraDirector` spectator). The BT registry knows only Mech/Projectile/Missile/BTPlayer/BTTeam; no
Bot/Brain/AI class exists; `BTL4OPT.EXE` has no AI strings. `Crusher/Blocker/Runner` are RED PLANET
classes, NOT BT. ⇒ a solo mission has no enemy by design; `BT_SPAWN_ENEMY` is a TEST DUMMY
(uncontrolled Mech). The authentic opponent path is [[multiplayer]]. [T1]
## Entity spawn + the per-mech gate
`Mech::Make(MakeMessage*)` (`FUN_004a2d48`) allocs `sizeof(Mech)` + runs the ctor; the base Entity
ctor self-registers it (renders + ticks). `Mech::PerformAndWatch` runs for EVERY mech → gate
player-only logic on `this == application->GetViewpointEntity()` (else a spawned enemy is driven by
player input). [T1]
## Targeting (mech offsets, [[decomp-reference]] §3)
`mech+0x37c` = target world Point3D; `mech+0x388` = target `Entity*` (the `HasActiveTarget()` gate);
`mech+0x38c` = targeted sub-zone (1=whole). Weapons cache hasTarget/targetPoint/muzzlePoint,
refreshed each frame. [T1]
## Firing arc + muzzles
There is NO firing-arc field in the binary (`Emitter::FireWeapon` fires whenever HasActiveTarget) —
the 1995 game got away with it because it was **cockpit-only** (a beam behind you is off-camera). The
port's external camera exposes it, so the arc is gated on the **torso twist range**
(`Torso::GetHorizontalReach`, 0 for the twist-disabled Blackhawk) + a base aim tolerance
(`BT_FIRE_ARC`, a labeled PORT presentation param). Beams fire from the mech's real gun-port SITE
segments (siterugunport etc.) resolved via `GetSegment(name)→segmentToEntity×localToWorld`. [T2]
## Weapon fire is a PORT reconstruction (the dpl_* beam layer is unported)
`Emitter::FireWeapon` builds a muzzle→target beam but the `dpl_*` beam renderable was never ported →
the beam is drawn by `BTPushBeam`/`BTDrawBeams` (L4VIDEO — an additive quad / the real `ermlaser.bgf`
tube with the scrolling `bexp` grit). Fire rate is decoded-exact (DischargeTime 0.2 + RechargeRate 2.2
per ER-M laser; [[decomp-reference]] §5). Flying projectiles (LRM/autocannon) are also a PORT
reconstruction (`BTPushProjectile` — the 2007 Entity is too small for the binary's raw integrator
offsets). [T2]
## Damage delivery + the real damage model
`Entity::TakeDamageMessage(id, size, inflictingEntityID, zone, Damage&)``target->Dispatch`.
**Base handler IGNORES zone==1** (the Mech cylinder-lookup override for unaimed hits is not
reconstructed — send a VALID zone). `Mech__DamageZone::TakeDamage` → engine armor model (`damageLevel
+= damageAmount*damageScale[type]`) → 1.0 = zone destroyed → death. The Mech ctor populates the
inherited `damageZones[]` from the DamageZoneStream (type 0x14) resource. `class Damage{ damageType
(Collision/Ballistic/Explosive/Laser/Energy), damageAmount, damageForce, surfaceNormal, impactPoint,
burstCount }`. Verified: `structure` climbs, vital zone destroyed → `*** TARGET DESTROYED ***`. [T2]
Two non-field fixes were needed: **message-handler chaining** (an unchained set drops TakeDamage
silently) + **entity validity** (`SetValidFlag()` on a manually-spawned entity) — see
[[reconstruction-gotchas]] §9. [T2]
## Death — the wreck STAYS (P5 CLOSED)
A killed mech does NOT disappear — BT death is a STATE transition
(`SetGraphicState(DestroyedGraphicState)` + death anim + effect/splash), the mech becomes a persistent
WRECK (RP analog: `VTV::DeathShutdown`). **NEVER issue `DestroyEntityMessage` on death** — the
teardown crash was an artifact of forcing a removal the original never does. The actual bug was ONE
thing: an EXPLICIT `JointedMover::~JointedMover()` in the reconstructed `~Mech` ran the whole base-dtor
chain TWICE (the dtor-epilogue rule, [[reconstruction-gotchas]] §7) → a double-free of `collisionLists`
+ the segment table. Removing it fixed BOTH the death-row crash AND app-exit crashes. [T2]
## Key Relationships
- Weapons/roster: [[subsystems]]. Aim source: [[locomotion]] (drive/facing). Effects: [[rendering]].
- P5 forensics: `docs/HARD_PROBLEMS.md`. Data: [[decomp-reference]] §4-5.