4.10 reconstruction: real BTPlayer ctor (binary-accurate experience-level flags)

Boot ladder advances past player creation. BTPlayer ctor/dtor/Make/
TestInstance/GetExperienceLevel reconstructed from BT411's Ghidra-recovered
body (@004c0bc8) backdated to 1995 idiom; the reserved[38] placeholder in
BTPLAYER.HPP replaced with the real BT-own members.

Key fidelity call: the game-mode flags are derived from
btMission->ExperienceLevel() (the egg's per-pilot experience key) with the
binary-accurate switch rows from the [T1] disassembly -- NOT from the scenario
role's returnFromDeath, which BT411's port uses as a [T3] stand-in. Mapping and
residual naming/[T4] uncertainty documented in BTPLAYER.NOTES.md.

Live boot now runs: banner -> resources -> app ctor -> network single-user ->
egg -> mission -> SetPlayerData -> BTRegistry::MakePlayer -> BTPlayer ctor
(resolves team, seeds experience flags) -> halts at the base
Player::DropZoneReplyMessageHandler stub (BTPlayer's own message-handler table
is the next brick -- DefaultData still reuses Player::MessageHandlers).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-19 20:18:37 -05:00
co-authored by Claude Fable 5
parent 0906d1efa6
commit c43e6d873a
3 changed files with 308 additions and 5 deletions
+176 -4
View File
@@ -1,5 +1,11 @@
//===========================================================================// //===========================================================================//
// File: btplayer.cpp //
// Project: BattleTech // // Project: BattleTech //
// Contents: Implementation details for the BT player //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// //
//---------------------------------------------------------------------------// //---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. // // Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide // // All Rights reserved worldwide //
@@ -13,6 +19,27 @@
# include <btplayer.hpp> # include <btplayer.hpp>
#endif #endif
#if !defined(BTTEAM_HPP)
# include <btteam.hpp>
#endif
#if !defined(BTMSSN_HPP)
# include <btmssn.hpp>
#endif
#if !defined(NTTMGR_HPP)
# include <nttmgr.hpp>
#endif
#if !defined(APP_HPP)
# include <app.hpp>
#endif
//
//#############################################################################
// Shared data support
//#############################################################################
//
Derivation Derivation
BTPlayer::ClassDerivations( BTPlayer::ClassDerivations(
Player::ClassDerivations, Player::ClassDerivations,
@@ -28,22 +55,167 @@ BTPlayer::SharedData
(Entity::MakeHandler)BTPlayer::Make (Entity::MakeHandler)BTPlayer::Make
); );
//
//#############################################################################
// Constructor
//
// Copies the team name out of the creation message, resolves the owning
// BTTeam, caches the mission, and derives the game-mode display / heat-model
// flags from the mission's experience level and advanced-damage flag.
//#############################################################################
//
BTPlayer::BTPlayer( BTPlayer::BTPlayer(
MakeMessage *creation_message, MakeMessage *creation_message,
SharedData &shared_data SharedData &shared_data
): ):
Player(creation_message, shared_data) Player(creation_message, shared_data)
{ {
Fail("BTPlayer -- btplayer.cpp not yet reconstructed"); consoleAttached = False;
suppressConsole = False;
currentScore = 0;
deathPending = False;
//
// Remember our team name from the creation message.
//
strcpy(teamName, creation_message->teamName);
//
// Resolve the BTTeam entity that owns this team name.
//
teamEntity = NULL;
EntityGroup *teams = application->GetEntityManager()->FindGroup("Teams");
if (teams)
{
ChainIteratorOf<Node*> iterator(teams->groupMembers);
BTTeam *team;
while ((team = (BTTeam *)iterator.ReadAndNext()) != NULL)
{
if (strcmp(team->teamName, teamName) == 0)
{
teamEntity = team;
break;
}
}
}
freeForAll = True;
//
// Cache the mission (experience level / advanced-damage source).
//
BTMission *bt_mission =
Cast_Object(BTMission *, application->GetCurrentMission());
btMission = bt_mission;
if (GetInstance() == ReplicantInstance)
{
//
// Replicant: driven by console-update messages.
//
SetPerformance(&BTPlayer::PlayerSimulation);
}
else
{
//
// Master: derive the game-mode flags from the mission experience
// level (novice / standard / veteran / expert) and advanced-damage
// technician flag. See BTPLAYER.NOTES.md for the flag mapping.
//
Check(bt_mission);
switch (bt_mission->ExperienceLevel())
{
case BTMission::NoviceMode:
showDamageReceived = 0;
showKills = 0;
roleReturnDelay2 = 0;
showScore = 0;
break;
case BTMission::StandardMode:
showDamageReceived = 1;
showKills = 0;
roleReturnDelay2 = 1;
showScore = 1;
break;
case BTMission::VeteranMode:
showDamageReceived = 1;
showKills = 1;
roleReturnDelay2 = 1;
showScore = 1;
break;
case BTMission::ExpertMode:
showDamageReceived = 1;
showKills = 1;
roleReturnDelay2 = 0;
showScore = 1;
break;
}
showDamageInflicted = bt_mission->AdvancedDamageOn();
roleReturnDelay = bt_mission->AdvancedDamageOn();
roleClassIndex = bt_mission->ExperienceLevel();
}
killCount = 0;
padScore = 0;
objectiveMech = NULL;
lastPerformance = lastConsoleUpdate = lastUpdate;
Check_Fpu();
} }
//
//#############################################################################
//#############################################################################
//
BTPlayer::~BTPlayer() BTPlayer::~BTPlayer()
{ {
Check(this);
Check_Fpu();
} }
//
//#############################################################################
// Make
//#############################################################################
//
BTPlayer* BTPlayer*
BTPlayer::Make(MakeMessage *) BTPlayer::Make(MakeMessage *creation_message)
{ {
Fail("BTPlayer::Make -- btplayer.cpp not yet reconstructed"); return new BTPlayer(creation_message);
return NULL; }
//
//#############################################################################
//#############################################################################
//
Logical
BTPlayer::TestInstance() const
{
return IsDerivedFrom(ClassDerivations);
}
//
//#############################################################################
// GetExperienceLevel
//#############################################################################
//
Enumeration
BTPlayer::GetExperienceLevel()
{
Check(this);
Check(btMission);
return Cast_Object(BTMission *, btMission)->ExperienceLevel();
}
//
//#############################################################################
// 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.
//#############################################################################
//
void
BTPlayer::PlayerSimulation(Scalar)
{
Fail("BTPlayer::PlayerSimulation -- btplayer.cpp not yet reconstructed");
} }
+61 -1
View File
@@ -22,6 +22,7 @@
//##################### Forward Class Declarations ####################### //##################### Forward Class Declarations #######################
class Mech; class Mech;
class BTTeam; class BTTeam;
class Mission;
//########################################################################### //###########################################################################
//####################### BTPlayer Make Message ######################### //####################### BTPlayer Make Message #########################
@@ -106,11 +107,70 @@
Enumeration Enumeration
GetExperienceLevel(); GetExperienceLevel();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Model Support
//
public:
typedef void
(BTPlayer::*Performance)(Scalar time_slice);
void
SetPerformance(Performance performance)
{
Check(this);
activePerformance = (Simulation::Performance)performance;
}
void
PlayerSimulation(Scalar time_slice);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local Data // Local Data
// //
protected: protected:
int reserved[38]; Mission
*btMission;
char
teamName[64];
BTTeam
*teamEntity;
Logical
freeForAll;
Logical
consoleAttached;
Logical
suppressConsole;
//
// Game-mode flags derived in the constructor from the mission's
// experience level (novice / standard / veteran / expert) and
// advanced-damage technician flag. See BTPLAYER.NOTES.md.
//
Logical
showDamageReceived;
Logical
showKills;
Logical
showDamageInflicted;
Logical
showScore;
Scalar
roleReturnDelay;
Scalar
roleReturnDelay2;
int
roleClassIndex;
int
killCount;
int
padScore;
Mech
*objectiveMech;
Time
lastConsoleUpdate;
Logical
deathPending;
}; };
#endif #endif
@@ -0,0 +1,71 @@
# BTPLAYER.CPP / .HPP — reconstruction notes
**Status: constructor / dtor / Make / TestInstance / GetExperienceLevel
RECONSTRUCTED; message-handler table + handlers + PlayerSimulation +
CreatePlayerVehicle still staged.**
## Sources
- BT411 `game/reconstructed/btplayer.cpp` (Ghidra-recovered ctor @004c0bc8,
Make @004c0ecc, dtor @004c0efc, TestInstance @004c0f28) — byte-offset
annotated body.
- Surviving 1995 headers pin every base API: PLAYER.HPP (base members
currentScore/scenarioRole/deathCount/playerMission, GetMission), SIMULATE.HPP
(lastPerformance/lastUpdate/simulationFlags/activePerformance — `lastPerformance`
is a BASE member, not BT-own), ENTITY.HPP (GetInstance/ReplicantInstance),
NTTMGR.HPP (EntityManager::FindGroup, EntityGroup::groupMembers = ChainOf<Node*>),
TEAM.HPP (Team::teamName[64], public — BTTeam inherits it), BTMSSN.HPP
(BTMission::ExperienceLevel()/AdvancedDamageOn(), enum Novice/Standard/
Veteran/Expert = 0..3), SCNROLE.HPP.
## Member layout note
The staged header's `int reserved[38]` placeholder was replaced with the real
BT-own members (btMission, teamName[64], teamEntity, freeForAll, consoleAttached,
suppressConsole, the experience-flag block, killCount/padScore, objectiveMech,
lastConsoleUpdate, deathPending). Byte offsets do NOT need to match the shipped
0x294-byte object — this is a fresh self-consistent build; only wire-format
structs (MakeMessage) must match the binary, and those are unchanged.
## Experience-level flag mapping — the [T1] correction
The ctor derives the game-mode flags from **btMission->ExperienceLevel()**
(mission+0xe4, the egg's per-pilot "experience" key), NOT from the scenario
role's returnFromDeath — see [[bt-experience-levels]]. BT411's ported ctor uses
`scenarioRole->GetReturnFromDeath()` as a stand-in and marks it [T3]; this 1995
reconstruction encodes the binary-accurate reading instead.
Binary switch rows (member ← value per level), from the [T1] disassembly:
| level | showDamageReceived (0x25c) | showKills (0x260) | roleReturnDelay2 (0x26c) | showScore (0x270) |
|----------|:--:|:--:|:--:|:--:|
| novice | 0 | 0 | 0 | 0 |
| standard | 1 | 0 | 1 | 1 |
| veteran | 1 | 1 | 1 | 1 |
| expert | 1 | 1 | 0 | 1 |
Plus `showDamageInflicted` (0x264) = `roleReturnDelay` (0x268) = advancedDamageOn.
**Member NAMES are wrong-but-kept** (they date from the earlier scoring work):
`showKills` is really the HEAT-MODEL master switch (FUN_004ad7d4), `showDamageReceived`
is the "sim live / novice lockout" gate, `showScore`/`roleReturnDelay2` consumers
not fully located ([T4]). Rename is a follow-up wiring task; the VALUES are correct.
## Deliberate differences from BT411's port
- Make: the 1995 `return new BTPlayer(creation_message);` — BT411's replicant
`SetValidFlag()` is a P6 network-bring-up addition, dropped.
- 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)
1. **Message-handler table** — DefaultData currently reuses `Player::MessageHandlers`,
so BTPlayer's own handlers are unwired and the base
`Player::DropZoneReplyMessageHandler` Fails ("should not be handled by base
player class!", PLAYER.CPP:179). Next brick = BTPlayer MessageHandlerEntries[]
+ MessageHandlers + real DropZoneReplyMessageHandler (spawn handshake).
2. **DropZoneReply → CreatePlayerVehicle → Mech::Make** — the spawn chain pulls
in the mech subsystem (Mech::Make still Fail-staged).
3. VehicleDead / Score / ScoreInflicted / ScoreUpdate handlers (scoring wave),
PlayerSimulation (console update), InitializePlayerLink, CalcInflictedScore.