BT410: entity simulation goes LIVE -- mission reaches RunningMission, mech + roster tick per-frame
The world now executes. Entity::Execute only PerformAndWatch's entities in application state RunningMission, reached by two RunMissionMessages (WaitingForLaunch->LaunchingMission->RunningMission). The 2nd is dispatched by Player::ManageApplicationStatus when the launch fade expires -- but that only runs inside PlayerSimulation, not the launch-phase HuntForDropZone. So the player must switch Performance onto PlayerSimulation after it spawns. - BTPLAYER.CPP DropZoneReplyMessageHandler: after CreatePlayerVehicle + InitializePlayerLink, choose the per-vehicle Performance by class -- Mech -> SetPerformance(PlayerSimulation)+SetScoringPlayerFlag; else CameraShipSimulation. - BTPLAYER.CPP PlayerSimulation: chains the authentic base Player::PlayerSimulation (CalcRanking + ManageApplicationStatus + vehicle-pos copy + status service); console SCORE-delta flush deferred to the scoring wave. - SENSOR.CPP SensorSimulation: minimal-safe partial (radarPercent = 1 - GetSubsystemDamageLevel, sensor healthy) so the roster tick path can't Fail once RunningMission engages. The heat/electrical gating awaits the power/heat sim wave (PoweredSubsystem/Generator/HeatSink/HeatWatcher *Simulation staged). Verified: BT_MECH_LOG run reaches "[tick] roster live (first Sensor frame)", two RunMissionHandler transitions, zero Fail/Exception. The mech is executed each frame; its BODY Performance is still DoNothingOnce (real Mech::Simulate = Phase 5.3, now the frontier). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -268,10 +268,29 @@ void
|
||||
InitializePlayerLink();
|
||||
|
||||
//
|
||||
// A piloted mech runs the combat PlayerSimulation and is a scoring
|
||||
// player; a camera-ship observer runs CameraShipSimulation and never
|
||||
// ranks. (Vehicle-class dispatch + mech placement deferred with the
|
||||
// Mech subsystem.)
|
||||
// Choose the per-vehicle Performance from the vehicle's class. A piloted
|
||||
// mech runs the combat PlayerSimulation and is a scoring player; a
|
||||
// camera-ship observer runs CameraShipSimulation and never ranks. This
|
||||
// is load-bearing for mission flow: switching off HuntForDropZone onto
|
||||
// PlayerSimulation is what starts the launch-fade countdown that advances
|
||||
// the application to RunningMission (so the world begins executing).
|
||||
//
|
||||
if (playerVehicle->GetClassID() == (Entity::ClassID)MechClassID)
|
||||
{
|
||||
SetPerformance(&BTPlayer::PlayerSimulation);
|
||||
SetScoringPlayerFlag();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPerformance((Performance)&Player::CameraShipSimulation);
|
||||
playerRanking = -1;
|
||||
}
|
||||
|
||||
//
|
||||
// (Mech placement -- Mech::Reset heal+move to the drop-zone origin, the
|
||||
// translocation cockpit alarm, and the warp-sphere reveal -- is deferred
|
||||
// with the mech per-frame simulation; the fresh mech sits at its ctor
|
||||
// origin for now.)
|
||||
//
|
||||
AlwaysExecute();
|
||||
deathCount = 0;
|
||||
@@ -446,13 +465,26 @@ void
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// PlayerSimulation -- console-update performance (not yet reconstructed).
|
||||
// The constructor installs this as the replicant Performance; its body is
|
||||
// exercised only on a networked console-driven run.
|
||||
// PlayerSimulation -- the master player's per-frame Performance (installed by
|
||||
// DropZoneReply once the mech exists, and by the replicant ctor). Chains the
|
||||
// authentic engine base Player::PlayerSimulation, which is what actually drives
|
||||
// mission flow: CalcRanking + ManageApplicationStatus (counts down the launch
|
||||
// fade and, when it expires, dispatches the second RunMissionMessage that
|
||||
// advances LaunchingMission -> RunningMission so the world starts executing) +
|
||||
// vehicle-position copy + status-message service.
|
||||
//
|
||||
// The BT-specific tail is the periodic console SCORE-delta flush
|
||||
// (ConsolePlayerVTVScoreUpdateMessage over the network to the operator
|
||||
// console). It is a no-op without a live console host and needs the networked
|
||||
// console-message type; it is deferred to the scoring wave.
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
BTPlayer::PlayerSimulation(Scalar)
|
||||
BTPlayer::PlayerSimulation(Scalar time_slice)
|
||||
{
|
||||
Fail("BTPlayer::PlayerSimulation -- btplayer.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
|
||||
Player::PlayerSimulation(time_slice);
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
@@ -103,9 +103,47 @@ 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).
|
||||
|
||||
## Reconstructed 2026-07-20 (entity simulation goes LIVE)
|
||||
|
||||
- **DropZoneReplyMessageHandler** now, after the fresh spawn (CreatePlayerVehicle
|
||||
+ InitializePlayerLink), CHOOSES the per-vehicle Performance from the vehicle
|
||||
class: a Mech gets `SetPerformance(&BTPlayer::PlayerSimulation)` +
|
||||
`SetScoringPlayerFlag()`; anything else (camera ship) gets
|
||||
`Player::CameraShipSimulation` + rank −1. This is load-bearing for mission
|
||||
flow (see below). The mech PLACEMENT tail (`Mech::Reset` heal/move to the
|
||||
drop-zone origin, translocation alarm, warp-sphere reveal) stays deferred with
|
||||
the mech per-frame sim; the fresh mech sits at its ctor origin.
|
||||
- **PlayerSimulation** (btplayer.cpp) — chains the authentic engine base
|
||||
`Player::PlayerSimulation` (CalcRanking + ManageApplicationStatus + vehicle-
|
||||
position copy + StatusMessageUpdate). The BT console SCORE-delta flush
|
||||
(ConsolePlayerVTVScoreUpdateMessage, only meaningful with a live console host)
|
||||
is deferred to the scoring wave.
|
||||
|
||||
### Why this is the piece that starts the world executing
|
||||
|
||||
`Entity::Execute` only calls `PerformAndWatch` (i.e. simulates entities) when
|
||||
`Application::GetApplicationState() == RunningMission`. The app is a state
|
||||
machine advanced one step per `RunMissionMessage`:
|
||||
`WaitingForLaunch → LaunchingMission → RunningMission`. With no console,
|
||||
`CheckLoadMessageHandler` posts the FIRST RunMission (→ LaunchingMission, which
|
||||
dispatches MissionStarting → the player starts a 3 s fade). The SECOND
|
||||
RunMission (→ RunningMission) is dispatched by `Player::ManageApplicationStatus`
|
||||
when that fade expires — but ManageApplicationStatus only runs inside
|
||||
`PlayerSimulation`/`CameraShipSimulation`, NOT inside the launch-phase
|
||||
`HuntForDropZone`. So the player must switch its Performance onto PlayerSimulation
|
||||
after it spawns, or the fade never counts down and the world never runs.
|
||||
That switch (above) + a non-Fail PlayerSimulation is what takes the boot from
|
||||
"controls loop only" to a **live per-frame entity simulation**: verified — the
|
||||
mech is executed each frame and its subsystem roster ticks (Sensor first-frame
|
||||
marker), zero Fail. The mech BODY is still `DoNothingOnce` (its real
|
||||
`Mech::Simulate` is Phase 5.3).
|
||||
|
||||
## 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).
|
||||
1. `Mech::Simulate` — the mech's real per-frame Performance (Phase 5.3): now
|
||||
the frontier, since the entity tick path is live and calls the mech's
|
||||
Performance each frame.
|
||||
2. VehicleDeadMessageHandler(`-1`) death cycle — with mech4 + scoring roles.
|
||||
3. Score / ScoreInflicted / ScoreUpdate handlers (scoring wave); the console
|
||||
SCORE-delta flush inside PlayerSimulation.
|
||||
4. DropZoneReply respawn tail (`Mech::Reset` in-place heal/move).
|
||||
|
||||
@@ -119,7 +119,45 @@ void
|
||||
void
|
||||
Sensor::SensorSimulation(Scalar)
|
||||
{
|
||||
Fail("Sensor::SensorSimulation -- sensor.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
|
||||
//
|
||||
// Minimal safe per-frame sensor update (PARTIAL reconstruction). The
|
||||
// authentic SensorSimulation (BT411 @004b1c4c) first runs
|
||||
// PoweredSubsystem::PoweredSubsystemSimulation, then gates radarPercent /
|
||||
// selfTest / badVoltage on the heat state (Normal / Degradation / Failure)
|
||||
// and the electrical Ready state -- both of which live in the power/heat
|
||||
// per-frame sim chain that is not yet reconstructed (the powersub/heat
|
||||
// *Simulation methods are staged). Until that wave, derive radarPercent
|
||||
// from the one reconstructed input -- this sensor's own structural damage
|
||||
// level ([0,1], 0 intact .. 1 destroyed) -- and report the sensor healthy.
|
||||
// This keeps the engine's roster tick path (Entity::PerformAndWatch) safe
|
||||
// while producing a real radar-capability value. See SENSOR.NOTES.md.
|
||||
//
|
||||
radarPercent = 1.0f - GetSubsystemDamageLevel(); // RadarBaseline - zoneDamage
|
||||
if (radarPercent < 0.0f)
|
||||
{
|
||||
radarPercent = 0.0f;
|
||||
}
|
||||
selfTest = True;
|
||||
badVoltage = False;
|
||||
|
||||
{
|
||||
//
|
||||
// One-shot confirmation that the engine's per-frame entity/roster tick
|
||||
// (Entity::PerformAndWatch) has engaged -- i.e. the mission reached
|
||||
// RunningMission and the mech is being simulated each frame.
|
||||
//
|
||||
static int firstTick = 0;
|
||||
if (!firstTick && getenv("BT_MECH_LOG"))
|
||||
{
|
||||
firstTick = 1;
|
||||
DEBUG_STREAM << "[tick] roster live (first Sensor frame), radarPercent="
|
||||
<< radarPercent << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# SENSOR.CPP — reconstruction notes
|
||||
|
||||
Built against the surviving 4.10 CODE header `CODE/BT/BT/SENSOR.HPP` (Sensor :
|
||||
PoweredSubsystem). Attributes RadarPercent / SelfTest / BadVoltage chain
|
||||
`PoweredSubsystem::NextAttributeID`.
|
||||
|
||||
## Reconstructed
|
||||
|
||||
- Ctor: primes radarPercent=1, selfTest=False, badVoltage=False and installs
|
||||
`Sensor::SensorSimulation` as the per-frame Performance (unless the owner is a
|
||||
replicant copy, which is console-driven).
|
||||
- `TestClass` / `TestInstance` / `ResetToInitialState` / the shared-data statics.
|
||||
|
||||
## PARTIAL: Sensor::SensorSimulation (per-frame)
|
||||
|
||||
The authentic `SensorSimulation` (BT411 @004b1c4c) does, each frame:
|
||||
1. `PoweredSubsystem::PoweredSubsystemSimulation(time_slice)` (power/voltage tick),
|
||||
2. `radarPercent = RadarBaseline - GetSubsystemDamageLevel()`,
|
||||
3. gate on the electrical Ready state (`badVoltage`, zeroes radar if not Ready),
|
||||
4. gate on the heat state — Normal / Degradation (`radar *= 0.5`) / Failure
|
||||
(radar 0, selfTest off).
|
||||
|
||||
Steps 1/3/4 depend on the **power/heat per-frame simulation chain that is not
|
||||
yet reconstructed** — `PoweredSubsystem::PoweredSubsystemSimulation`,
|
||||
`Generator::GeneratorSimulation`, `HeatSink::HeatSinkSimulation`,
|
||||
`HeatWatcher::WatchSimulation`, `PowerWatcher::Simulation` are all staged
|
||||
`Fail`. None of those are installed as Performances yet (only Sensor installs
|
||||
one), so the roster otherwise ticks `DoNothingOnce`.
|
||||
|
||||
Until the power/heat wave, `SensorSimulation` runs the one reconstructed input
|
||||
(step 2, using `MechSubsystem::GetSubsystemDamageLevel`, clamped ≥ 0) and
|
||||
reports the sensor healthy (selfTest=True, badVoltage=False). This is a
|
||||
**deliberately safe partial**: it keeps the engine's `Entity::PerformAndWatch`
|
||||
roster-tick path alive (it must not `Fail`, or the whole per-frame simulation
|
||||
aborts the frame the mission reaches RunningMission) while producing a real
|
||||
radar-capability value. Deepen it with the power/heat simulation wave.
|
||||
|
||||
A gated one-shot (`BT_MECH_LOG`) logs the first Sensor frame — the marker that
|
||||
the entity/roster simulation has engaged (RunningMission).
|
||||
Reference in New Issue
Block a user