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
|
void
|
||||||
BTPlayer::InitializePlayerLink()
|
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.
|
// 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
|
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
|
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
|
- Replicant-mission caching uses `application->GetCurrentMission()` (the real
|
||||||
1995 accessor, PLAYER.CPP:689) instead of BT411's raw `application+0xc8`.
|
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
|
- **InitializePlayerLink** (btplayer.cpp, was @004bfee0) — DONE. Dispatches a
|
||||||
pulls in the whole Mech subsystem: Mech ctor + mech2/mech3/mech4, mechsub,
|
`PlayerLinkMessage` carrying our own `GetEntityID()` to `playerVehicle` and
|
||||||
the subsystems (sensor, gnrator, gyro, torso, hud, myomers), and the weapons
|
its network replicants, binding the freshly-made mech to its owning player.
|
||||||
(mechweap, ppc, gauss, projtile, missile...). Mech::Make is still Fail-staged
|
Byte-for-byte the same tail the engine's own `CameraDirector::CreateCameraShip`
|
||||||
in MECH.CPP. This is the large game-content frontier — a major milestone, not
|
runs (DIRECTOR.CPP): `BTPlayer`/`Player`/`CameraDirector` all derive from
|
||||||
a single brick.
|
`Entity`, so `PlayerLinkMessage` / `PlayerLinkMessageID` / `GetEntityID`
|
||||||
2. InitializePlayerLink, the mech-placement tail of DropZoneReply (Mech::Reset).
|
resolve through the `Entity` base. No staging.
|
||||||
3. VehicleDead / Score / ScoreInflicted / ScoreUpdate handlers (scoring wave),
|
- **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.
|
PlayerSimulation (console update), CalcInflictedScore.
|
||||||
|
3. DropZoneReply respawn tail (`Mech::Reset` in-place heal/move).
|
||||||
|
|||||||
@@ -25,11 +25,35 @@ Derivation
|
|||||||
"Emitter"
|
"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::SharedData
|
||||||
Emitter::DefaultData(
|
Emitter::DefaultData(
|
||||||
Emitter::ClassDerivations,
|
Emitter::ClassDerivations,
|
||||||
Subsystem::MessageHandlers,
|
Subsystem::MessageHandlers,
|
||||||
Subsystem::AttributeIndex,
|
Emitter::AttributeIndex,
|
||||||
Subsystem::StateCount
|
Subsystem::StateCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -119,3 +119,34 @@ Phase-5 step 1 = per-subsystem AttributeIndex, and it's a precision task:
|
|||||||
mappings reference them by number).
|
mappings reference them by number).
|
||||||
Then: watcher/plug/capability-chain wiring, then the mech2/3/4 per-frame sim
|
Then: watcher/plug/capability-chain wiring, then the mech2/3/4 per-frame sim
|
||||||
(~9K lines, staged), then rendering. This is the behavioral half.
|
(~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