The energy weapons now run a live fire cycle, and the REAL trigger wiring is in place: the 1995 MechWeapon attribute table (binary @0x511890) is published with PINNED IDs, so the streamed per-mech fire-button mappings bind TriggerState (0x13) -> fireImpulse and the controls push writes the trigger into the weapon. - MECHWEAP: attribute enum + table (PercentDone 0x12, TriggerState 0x13, DistanceToTarget 0x14, TargetWithinRange 0x15, WeaponRange 0x16, EstimatedReadyTime 0x1A, RearFiring 0x1B, WeaponState 0x1C). Pads bridge the chain gap 2..0x11 -- LOAD-BEARING: AttributeIndexSet::Build leaves uncovered gap slots as uninitialized garbage, so every ID up to the max must be covered (they shrink to the authentic 0x0F..0x11 when the parent attribute waves land; SENSOR.HPP pins PoweredSubsystem::NextAttributeID at 0x0F). - MECHWEAP: fire-machine members (fireImpulse/previousFireImpulse, rechargeLevel, rangeToTarget, effectiveRange, estimatedReadyTime, recoil, weaponAlarm 0/2/3/4); CheckFireEdge (@004b9608, BIT-PATTERN int compare -- TriggerState carries raw ControlsButton ints whose release value is a float NaN; IEEE compare would latch the detector shut); ComputeOutputVoltage (@004b9c9c recharge dial). - EMITTER: EmitterSimulation (PARTIAL @004baa88) -- Firing/Loaded/Loading FSM on the weapon alarm, viewFireEnable gate at Loaded->Firing, recoil-decay recharge over the authored RechargeRate seconds; FireWeapon (PARTIAL @004bace8) discharge bookkeeping; ChargeLevel re-pinned to the authentic 0x1D; index re-chained to MechWeapon's. Deferred: electrical charge model (TrackSeekVoltage), heat dump, HasActiveTarget gate, beam/damage submission. - BT_FORCE_FIRE dev hook: trigger pulse whenever Loaded (auto-fire cadence). VERIFIED headlessly: forward view -- PPC_1/2 + ERMLaser_1 cycle FIRED->LOADED at the authored 0.6s/1s cadence, rear lasers silent; BT_FORCE_LOOK=3 -- ONLY the rear lasers (ERMLaser_2/3) fire. The view-fire x fire-FSM interlock holds both directions. Zero Fail. (SRM6 ballistic FSM = a later increment.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
5.3 KiB
Markdown
87 lines
5.3 KiB
Markdown
# 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.)
|
||
|
||
## Still staged
|
||
|
||
- The ballistic fire path (ProjectileWeaponSimulation @004bbd04, missile
|
||
launch/seeker), `SendDamage` (damage/networking), `CreateStreamedSubsystem`
|
||
(tool-side model build).
|
||
- ~~`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).
|