BT410: reconstructed source BOOTS into the live game loop (zero Fail)
Phase-5.1 crash solved + two BTPlayer bricks reconstructed. The tree now builds AND boots end-to-end with no staged Fail() reached, into the running per-frame game loop. - EMITTER.CPP: define Emitter::AttributePointers[]/AttributeIndex (ChargeLevel, chained to Subsystem::AttributeIndex) and point Emitter::DefaultData at it. EMITTER.HPP declared the index but the .cpp never defined it, so the surviving PPC.CPP (PPC::DefaultData binds inherited Emitter::AttributeIndex) and GAUSS.CPP (chains it) captured a NULL activeAttributeIndex -> GetAttributePointer faulted on the first PPC (roster slot 22). Fix clears all 33 roster slots and the control-mapping binding block. - BTPLAYER.CPP InitializePlayerLink: real body -- dispatch PlayerLinkMessage (our EntityID) to playerVehicle + replicants (mirrors engine CameraDirector::CreateCameraShip). No staging. - BTPLAYER.CPP VehicleDeadMessageHandler: non-death flavours (-2 drop-zone probe, >=0 respawn re-post) delegate to the authentic Player base handler; only the -1 death cycle stays staged (needs mech4 + scoring roles). Verified: 516-line smoke run, zero Fail/Exception/abort; boot reaches LBE4ControlsManager::Execute per-frame, waiting only on absent RIO hardware. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -358,10 +358,33 @@ void
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// InitializePlayerLink Tell our freshly-made vehicle (and its network
|
||||
// replicants) which player owns it, by dispatching a PlayerLinkMessage that
|
||||
// carries our own EntityID. Identical to the engine's own
|
||||
// CameraDirector::CreateCameraShip tail (DIRECTOR.CPP): BTPlayer, Player and
|
||||
// CameraDirector all derive from Entity, so PlayerLinkMessage /
|
||||
// PlayerLinkMessageID / GetEntityID resolve through the Entity base.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
BTPlayer::InitializePlayerLink()
|
||||
{
|
||||
Fail("BTPlayer::InitializePlayerLink -- btplayer.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
Check(playerVehicle);
|
||||
|
||||
PlayerLinkMessage
|
||||
player_link_message(
|
||||
PlayerLinkMessageID,
|
||||
sizeof(PlayerLinkMessage),
|
||||
GetEntityID()
|
||||
);
|
||||
|
||||
playerVehicle->Dispatch(&player_link_message);
|
||||
playerVehicle->DispatchToReplicants(&player_link_message);
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -369,10 +392,38 @@ void
|
||||
// Scoring / death handlers -- exercised in combat, not during bring-up boot.
|
||||
//#############################################################################
|
||||
//
|
||||
//
|
||||
//#############################################################################
|
||||
// VehicleDeadMessageHandler The BT death / respawn cycle. Three message
|
||||
// flavours arrive here, keyed on message->deathCount:
|
||||
// -1 the immediate death notification dispatched by the mech's death
|
||||
// transition (mech4.cpp) -- the full cycle (scoring-role life debit,
|
||||
// +5s respawn re-post, warp collapse) is downstream of the per-frame
|
||||
// mech simulation + scoring roles and is staged for now;
|
||||
// >= 0 our own 5s respawn re-post, and
|
||||
// -2 the engine's 2s "did the drop-zone reply arrive?" probe that
|
||||
// Player::HuntForDropZone posts during a normal spawn.
|
||||
// For everything but -1 the authentic engine base handler does exactly the
|
||||
// right thing: it re-hunts a drop zone only when this death is still current
|
||||
// AND one has not been acquired, so a freshly-spawned player (bring-up boot:
|
||||
// the -2 probe lands after DropZoneReply already made the mech) falls straight
|
||||
// through. So delegate the non-death flavours to the base.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
BTPlayer::VehicleDeadMessageHandler(VehicleDeadMessage *)
|
||||
BTPlayer::VehicleDeadMessageHandler(VehicleDeadMessage *message)
|
||||
{
|
||||
Fail("BTPlayer::VehicleDeadMessageHandler -- btplayer.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
if (message->deathCount != -1)
|
||||
{
|
||||
Player::VehicleDeadMessageHandler(message);
|
||||
Check_Fpu();
|
||||
return;
|
||||
}
|
||||
|
||||
Fail("BTPlayer::VehicleDeadMessageHandler(-1) -- death/respawn cycle deferred (mech4/scoring)");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -76,14 +76,36 @@ not fully located ([T4]). Rename is a follow-up wiring task; the VALUES are corr
|
||||
- Replicant-mission caching uses `application->GetCurrentMission()` (the real
|
||||
1995 accessor, PLAYER.CPP:689) instead of BT411's raw `application+0xc8`.
|
||||
|
||||
## Still staged (next bricks, in boot order)
|
||||
## Reconstructed 2026-07-20 (boot reaches the live game loop)
|
||||
|
||||
1. **CreatePlayerVehicle → Mech::Make** (CURRENT FRONTIER) — the spawn chain
|
||||
pulls in the whole Mech subsystem: Mech ctor + mech2/mech3/mech4, mechsub,
|
||||
the subsystems (sensor, gnrator, gyro, torso, hud, myomers), and the weapons
|
||||
(mechweap, ppc, gauss, projtile, missile...). Mech::Make is still Fail-staged
|
||||
in MECH.CPP. This is the large game-content frontier — a major milestone, not
|
||||
a single brick.
|
||||
2. InitializePlayerLink, the mech-placement tail of DropZoneReply (Mech::Reset).
|
||||
3. VehicleDead / Score / ScoreInflicted / ScoreUpdate handlers (scoring wave),
|
||||
- **InitializePlayerLink** (btplayer.cpp, was @004bfee0) — DONE. Dispatches a
|
||||
`PlayerLinkMessage` carrying our own `GetEntityID()` to `playerVehicle` and
|
||||
its network replicants, binding the freshly-made mech to its owning player.
|
||||
Byte-for-byte the same tail the engine's own `CameraDirector::CreateCameraShip`
|
||||
runs (DIRECTOR.CPP): `BTPlayer`/`Player`/`CameraDirector` all derive from
|
||||
`Entity`, so `PlayerLinkMessage` / `PlayerLinkMessageID` / `GetEntityID`
|
||||
resolve through the `Entity` base. No staging.
|
||||
- **VehicleDeadMessageHandler** (btplayer.cpp) — PARTIAL. The non-death
|
||||
flavours are real: the engine's `-2` "did the drop-zone reply arrive?" probe
|
||||
(posted by `Player::HuntForDropZone` during every spawn) and the `>=0`
|
||||
respawn re-post both delegate to the authentic base
|
||||
`Player::VehicleDeadMessageHandler`, which only re-hunts a drop zone when the
|
||||
death is still current and one has not been acquired — so a freshly-spawned
|
||||
player falls straight through (this is what unblocks bring-up boot). Only the
|
||||
`deathCount == -1` immediate death notification is still `Fail`-staged: its
|
||||
full cycle (scoring-role life debit, +5 s respawn re-post, warp collapse) is
|
||||
downstream of the per-frame mech sim (mech4) + scoring roles.
|
||||
|
||||
With these two, **the reconstructed tree boots into the running per-frame game
|
||||
loop** with zero `Fail()` reached: engine → app → mission → BTPlayer →
|
||||
CreatePlayerVehicle → Mech ctor (33 subsystems from the real TEST.EGG model) →
|
||||
InitializePlayerLink → DropZone acquire → the `-2` probe returns harmlessly →
|
||||
`LBE4ControlsManager::Execute` spins per-frame. The only thing it now waits on
|
||||
is RIO cockpit hardware (absent in the headless smoke test).
|
||||
|
||||
## Still staged (next bricks)
|
||||
|
||||
1. VehicleDeadMessageHandler(`-1`) death cycle — with mech4 + scoring roles.
|
||||
2. Score / ScoreInflicted / ScoreUpdate handlers (scoring wave),
|
||||
PlayerSimulation (console update), CalcInflictedScore.
|
||||
3. DropZoneReply respawn tail (`Mech::Reset` in-place heal/move).
|
||||
|
||||
@@ -25,11 +25,35 @@ Derivation
|
||||
"Emitter"
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Attribute Support. Emitter publishes the beam charge level (ChargeLevel):
|
||||
// the HUD weapon-charge gauge binds to it, and -- load-bearing -- GaussRifle's
|
||||
// AttributeIndex chains THIS index (GAUSS.CPP), and PPC::DefaultData binds it
|
||||
// via the inherited PPC::AttributeIndex. It MUST be a real defined index
|
||||
// chained to Subsystem::AttributeIndex, not the bare base (a declared-but-
|
||||
// undefined Emitter::AttributeIndex leaves activeAttributeIndex NULL and
|
||||
// faults the first GetAttributePointer on any PPC/Gauss).
|
||||
//#############################################################################
|
||||
//
|
||||
const Emitter::IndexEntry
|
||||
Emitter::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Emitter, ChargeLevel, chargeLevel)
|
||||
};
|
||||
|
||||
Emitter::AttributeIndexSet
|
||||
Emitter::AttributeIndex(
|
||||
ELEMENTS(Emitter::AttributePointers),
|
||||
Emitter::AttributePointers,
|
||||
Subsystem::AttributeIndex
|
||||
);
|
||||
|
||||
Emitter::SharedData
|
||||
Emitter::DefaultData(
|
||||
Emitter::ClassDerivations,
|
||||
Subsystem::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Emitter::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
|
||||
@@ -119,3 +119,34 @@ Phase-5 step 1 = per-subsystem AttributeIndex, and it's a precision task:
|
||||
mappings reference them by number).
|
||||
Then: watcher/plug/capability-chain wiring, then the mech2/3/4 per-frame sim
|
||||
(~9K lines, staged), then rendering. This is the behavioral half.
|
||||
|
||||
## PHASE-5.1 CRASH SOLVED + BOOT REACHES THE GAME LOOP (2026-07-20)
|
||||
|
||||
The `GetAttributePointer` fault was localized (per-slot probe over the roster)
|
||||
to roster **slot 22 = `PPC_1`**, an energy weapon: its `GetAttributePointer`
|
||||
read `entryCount` off a **NULL `activeAttributeIndex`**. Root cause: our
|
||||
reconstructed `EMITTER.HPP` DECLARES `static AttributeIndexSet AttributeIndex`
|
||||
(with a `ChargeLevelAttributeID`) but `EMITTER.CPP` never DEFINED it — and the
|
||||
surviving 4.10 `PPC.CPP` binds `PPC::DefaultData` to the inherited
|
||||
`PPC::AttributeIndex` (= `Emitter::AttributeIndex`), so the SharedData ctor's
|
||||
`activeAttributeIndex(&attribute_index)` captured a null/zero symbol. The
|
||||
surviving `GAUSS.CPP` also *chains* `Emitter::AttributeIndex` as its
|
||||
inheritance, so the same hole corrupts GaussRifle. (Sensor et al. never
|
||||
crashed: their `DefaultData` uses the defined `Subsystem::AttributeIndex`.)
|
||||
|
||||
**Fix (EMITTER.CPP):** define `Emitter::AttributePointers[]` (one entry,
|
||||
`ATTRIBUTE_ENTRY(Emitter, ChargeLevel, chargeLevel)`) and `Emitter::AttributeIndex`
|
||||
chained to `Subsystem::AttributeIndex`, and point `Emitter::DefaultData` at it.
|
||||
This is the correct 1995 reconstruction (Emitter publishes the beam charge
|
||||
level for the HUD gauge; PPC binds it; Gauss chains it). Static-init order is
|
||||
satisfied by ORDER_bt (`... emitter ppc ... gauss`) with `subsystm` in
|
||||
munga.lib (constructs first). After the fix the per-slot probe walks all 33
|
||||
slots (PPC_1/2, ERMLaser_1..3, AmmoBinSRM6_1/2, SRM6_1/2, MessageManager,
|
||||
MechTech, ...) cleanly, and the control-mapping binding block completes.
|
||||
|
||||
**Result:** with the Emitter fix + BTPlayer::InitializePlayerLink +
|
||||
VehicleDeadMessageHandler(non-death), the reconstructed tree now boots with
|
||||
**zero `Fail()`** all the way into the live per-frame game loop
|
||||
(`LBE4ControlsManager::Execute` spinning). See BTPLAYER.NOTES.md. The remaining
|
||||
gap to "playable" is behavioral (mech2/3/4 per-frame sim + renderer + real
|
||||
control input), not structural — the boot path is complete.
|
||||
|
||||
Reference in New Issue
Block a user