Files
TeslaRel410/restoration/source410/BT/MECHWEAP.NOTES.md
T
CydandClaude Fable 5 76cfc64353 BT410 Phase 5.3.16: targeting + damage -- two-mech fights are LIVE
The simulation is now a FIGHT. Mech target slot (+0x388), authentic
Loaded->Firing gate (viewFireEnable && HasActiveTarget), UpdateTargeting
range refresh, and SendDamage via the engine TakeDamageMessage.

MECHDMG.CPP is BORN against the AUTHENTIC surviving CODE/BT/BT/MECHDMG.HPP:
the Mech ctor's Pass-3 hull zone fill constructs Mech__DamageZone per zone
(the Entity base only allocates the raw pointer array -- unfilled slots were
crash #1; a base-class fill parses each record short and skews the stream --
crash #2). Streamed ctor parses the full BT tail (flags / Scalar-weighted
criticals with the Master+Dynamic plug gate / LOD redirect table) and
normalizes the armor economy: scale[type]=1/(raw x armorPoints), legs
halved -- BT411-verified @0049ce50.

Live two-mech verification (BT_SPAWN_ENEMY harness in DropZoneReply):
20 named hull zones stream on both mechs; PPC lands 11.77/hit (12 x
chargeRatio^2, type 4), ER-M laser 3.43 (type 3), SRM6 salvos 5.83 x 6
rounds on independent zones (type 2, one message per missile -- burstCount
is NOT in the zone formula); repeat hits climb linearly, leg zones absorb
at half scale, zones reach 1.0 Burning/Destroyed. No-target and novice
regressions hold (weapons load but never fire without a target).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 10:21:43 -05:00

153 lines
9.5 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.
# MECHWEAP.CPP / .HPP — reconstruction notes
`MechWeapon` (: PoweredSubsystem) is the weapon base: PPC/GaussRifle bind it via
Emitter, the SRMs via ProjectileWeapon/MissileLauncher. The resource streams
rechargeRate / weaponRange / damage / heatCostToFire (+ pip fields).
## Reconstructed
- Statics (ClassDerivations, DefaultData on Subsystem::MessageHandlers/
AttributeIndex), ctor streaming the tuning fields, damageData prime.
- **View-fire gating (2026-07-21, the look-commit wave):** real `rearFiring` /
`viewFireEnable` members (1995 binary @0x334/@0x3E0) + `IsRearFiring()` /
`GetViewFireEnable()` / `SetViewFireEnable()`. The ctor resolves REAR-FIRING
authentically (binary ctor tail @004b99a8): the mount SEGMENT site name
(`owner->GetSegment(GetSegmentIndex())`) is tested for the 'b' (back) marker —
only the back gun ports (`sitelbgunport`/`siterbgunport`) carry it. Spawn
state = armed for the forward view. `Mech::CommitLookState` re-arms on every
view change (see MECH.NOTES.md increment 7). Verified: the TEST.EGG mech's
two rear lasers (ERMLaser_2/3) arm only in LOOK-BACK.
## Fire path (2026-07-21, Phase 5.3.8)
- **The authentic attribute table IS published** (binary @0x511890, IDs pinned):
PercentDone 0x12 → rechargeLevel, **TriggerState 0x13 → fireImpulse** (the
streamed per-mech fire-button mappings bind this ID — the real trigger
wiring), DistanceToTarget 0x14, TargetWithinRange 0x15, WeaponRange 0x16 →
effectiveRange, EstimatedReadyTime 0x1A, RearFiring 0x1B, WeaponState 0x1C →
weaponAlarm. Pads bridge our chain gap 2..0x11 (parents publish nothing yet;
they shrink to the authentic 0x0F..0x11 when the mechsub/heat/powersub
attribute waves land — SENSOR.HPP pins PoweredSubsystem::NextAttributeID at
0x0F). **Pads are LOAD-BEARING**: AttributeIndexSet::Build leaves uncovered
gap slots as UNINITIALIZED GARBAGE (`new IndexEntry[entryCount]`, only
inherited + listed entries written), so every ID up to the max must be
covered or Find() on a gap ID returns a wild pointer. Pip* (0x17-0x19) are
pads → rechargeLevel until the pip members are reconstructed.
- **Fire state machine members**: fireImpulse/previousFireImpulse (the trigger
sample pair), rechargeLevel, rangeToTarget, effectiveRange,
estimatedReadyTime, recoil, weaponAlarm (the state carrier: 0 Firing /
2 Loaded / 3 Loading / 4 trigger-during-load). Spawn = charged + Loaded.
- **CheckFireEdge** (@004b9608): rising-edge on fireImpulse comparing BIT
PATTERNS as signed ints — TriggerState carries raw ControlsButton ints
(+(button+1) press / (button+1) release); the release bit-pattern is a
negative NaN as float, and an IEEE float compare would latch the detector
shut after the first release (BT411 task #8). Int compare reproduces the
binary's x87 semantics exactly.
- **ComputeOutputVoltage** (@004b9c9c): `rechargeLevel = (rechargeRate
recoil) / rechargeRate` — the recharge dial.
- **Emitter::EmitterSimulation** (PARTIAL, @004baa88) installed on every
non-replicant energy weapon: Firing → discharge countdown →
ResetFiringState(Loading); Loaded + trigger edge → viewFireEnable gate →
FireWeapon; Loading → recoil decays over the authored RechargeRate seconds →
Loaded. Deferred within it: the leading PoweredSubsystemSimulation electrical
step, the destroyed/heat-failure/dead-mech hard gates, the TrackSeekVoltage
generator charge integration (the partial uses the authored seconds
directly), and the HasActiveTarget gate (targeting wave).
- **Emitter::FireWeapon** (PARTIAL, @004bace8): discharge bookkeeping (beam
countdown re-arm, charge spent, recoil loaded). Deferred: the 0.5·V²·EC
energy/damage/heat closed forms (need the electrical model), the heat dump
(heat wave), the beam build + Damage submission (targeting + renderer).
- Emitter's index re-chained to MechWeapon's (ChargeLevel at the authentic
0x1D past the table end); Emitter::DefaultData now binds
MechWeapon::MessageHandlers.
- DEV hook `BT_FORCE_FIRE=1`: pulses the trigger whenever a weapon is Loaded →
every armed weapon auto-fires at its authored cadence.
**VERIFIED headlessly**: forward view — PPC_1/2 + ERMLaser_1 cycle
FIRED→LOADED→FIRED at the authored 0.6 s discharge / 1 s recharge, rear lasers
silent; `BT_FORCE_LOOK=3` (look-behind) — ONLY ERMLaser_2/3 fire. The full
view-fire × fire-FSM interlock holds in both directions. Zero Fail. (SRM6s are
MissileLaunchers — the ballistic ProjectileWeaponSimulation is a later
increment.)
## Ballistic fire path (2026-07-21, Phase 5.3.9)
- **ProjectileWeaponSimulation** (PARTIAL, binary @004bbd04 — FULLY RECOVERED
in the BT411 RE) installed on every non-replicant ballistic weapon. The
authentic FSM shape: one trigger-edge sample before the gates; gate 2 (the
linked AmmoBin dry → pin NoAmmo(7) every frame); Loaded(2): edge →
viewFireEnable gate → **the ammo pull happens in the CALLER** (a failed pull
stays Loaded silently; a denied shot does the 4→2 DENIAL BLIP with no ammo
pulled — early-returning gates from FireWeapon while the caller cycles the
alarm is exactly the 1995 "denied shot fakes a full firing cycle" defect);
granted → Firing(0) → FireWeapon → Loading(3) (or NoAmmo if the pull emptied
the bin), recoil = rechargeRate; Loading(3): recoil bleeds → 0 → Loaded when
a round is chambered, ComputeOutputVoltage animates the dial; Jammed(5) held
at full recoil; **NoAmmo(7) is the roach-motel** (re-asserts unconditionally;
only a mission reset leaves it). Deferred inside: the leading electrical
step, gate 1 (destroyed/FailureHeat/mech-disabled), magazine eject, the
electrical-Ready recoil gate, the heat-scaled jam roll, HasActiveTarget.
- **AmmoBin feed interface**: `GetAmmoState()` (1 round-ready / 2 EMPTY),
`FeedAmmo()` (pull one round; 0 when dry). Feed pacing + cook-off join the
heat wave.
- **ammoBinLink wiring**: the launcher ctor resolves the bin from the
resource's `ammoBinIndex` (the bin's roster slot) via the owner's subsystem
table — WIRE-VERIFIED: the raw stream carries 27/29, exactly the
AmmoBinSRM6_1/2 slots.
- **Resource alignment (wire-verified)**: a raw-stream dump showed the
ProjectileWeapon field block sits +3 ints past our staged struct — the
MechWeapon resource ancestry runs three ints short (the pip-family fields,
not yet broken out). `resourceAlignPad[3]` re-aligns it; the phantom
muzzleVelocity/missileCount tail fields were dropped (BT411's verified
overlay ends at minJamChance; missileCount belongs to MissileLauncher's own
extension and now reads its true 6). The full resource-overlay verification
(every ancestor struct byte-checked) is its own wave.
- MissileLauncher::FireWeapon PARTIAL (@004bcc60: authentically heat + the
missileCount-salvo Missile spawn ONLY — spawn needs the entity/targeting
waves).
**VERIFIED headlessly** (BT_FORCE_FIRE): bins resolve with the authored 24
rounds; SRM6s fire 6-missile salvos and reload at the authored 1 s cadence,
rounds counting 24→0; after the 24th salvo the launcher goes DRY and stays
silent across ~3,000 subsequent fire events while the energy weapons keep
cycling — the NoAmmo roach-motel holds. Zero Fail.
## Still staged
- The Missile entity spawn/seeker, `SendDamage` (damage/networking),
`CreateStreamedSubsystem` (tool-side model build), the jam roll
(CheckForJam), magazine eject.
- ~~`MechWeapon::MessageHandlers` declared but not defined~~ — **FIXED
2026-07-21**: it is now a real defined set (`(0, NULL,
Subsystem::MessageHandlers)` — no own handlers yet, inherits the chain), and
`MechWeapon::DefaultData` binds it. This mattered because the surviving CODE
PPC.CPP binds the inherited name (`PPC::MessageHandlers`) into
PPC::DefaultData, and a declared-but-undefined static links as a zero-filled
common block — the same silent-NULL trap as the Emitter::AttributeIndex
crash. Real weapon handlers (fire/damage messages) come with the combat wave
(grow the table in place).
## 5.3.16 — targeting + damage delivery
- `HasActiveTarget()` = owner's target slot non-NULL (binary owner+0x388);
`UpdateTargeting()` (@004b9bdc) refreshes rangeToTarget/targetWithinRange
from `target->localOrigin` every FSM tick (both weapon families call it
right after the trigger sample). The Loaded→Firing gate is now the authentic
`viewFireEnable && HasActiveTarget()` — a denied pull is the one-frame blip.
- `SendDamage(target, damage)`: builds the engine 5-arg
`Entity::TakeDamageMessage` (inflictor = our mech's EntityID) and Dispatches
AT the target; the engine handler routes `damageZones[zone]->TakeDamage`.
INTERIM zone pick = `Random(target->damageZoneCount)` — the authentic
resolvers are the reticle aimed hit and the cylinder hit-location table
(unaimed, zone 1 + the Mech TakeDamage override), both later waves.
- Salvo semantics (BT411-verified): **burstCount is NOT in the zone armor
formula** — one message = one amount×scale application. MissileLauncher
therefore dispatches ONE message PER MISSILE (each carrying the ctor's
salvo-split amount, each on its own random zone); a single split message
would under-deliver the salvo by the round count. Interim all-rounds-hit
until the Missile entity flight/seeker wave.
- Live-verified (two-mech fight): PPC 11.77/hit (12×chargeRatio², type 4
Energy), ER-M 3.43 (type 3 Laser), SRM6 5.83×6 (type 2 Explosive); repeat
hits climb linearly; zones reach 1.0 Burning/Destroyed; no-target runs hold
fire (0 FIRED across a 65s force-fire run without a target).