BT410 Phase 5.3.25: scoring + the COMPLETE mission lifecycle -- egg to GAME-RC
Scoring roles bound (binary ctor tail @004c0bc8): the mission role dict -> Player::scenarioRole; TEST.EGG's Role::Default = the baked dfltrole (lives=1000, killBonus=500) -- authored data flowing. The three score handlers are live per the decomp derivations (@004c02e4 / @004c0200 / @004c02a8 + CalcInflictedScore @004c052c; tonnage/bias staged); the producers post damage scores per delivered hit and the kill credit from the death transition with the latched killing-blow magnitude. First live kill: award=3.43, kills=1, score=123.6. Out of lives -> the mission END: the binary's +10s review post sits in the one decomp gap, so the staged stand-in enters the engine's own cascade (StopMissionMessage +10s) -- EndingMission -> the 3s fade -> the second Stop -> Application::Stop -> RunMissions exits. The BTL4/L4 stop layers run authentically (plasma score display off, egress lamps). VERIFIED END-TO-END: a 2-lives egg (role-page return=2 override) -- death #1 debits and respawns healed; death #2 is OUT OF LIVES; +10s later the mission ends and BTL4OPT.EXE exits CLEANLY to DOS (GAME-RC prints). The full 1995 pod mission loop -- egg, boot, spawn, fight, score, die, respawn, die, mission end, exit -- now runs from reconstructed source. Zero faults; fight/smoke/novice regressions green. Dev knob: BT_PLAYER_PASSIVE=1 (piloted mechs hold force-fire). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -55,6 +55,14 @@
|
||||
# include <mission.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(APPMSG_HPP)
|
||||
# include <appmsg.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(SCNROLE_HPP)
|
||||
# include <scnrole.hpp>
|
||||
#endif
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Shared data support
|
||||
@@ -207,6 +215,38 @@ BTPlayer::BTPlayer(
|
||||
objectiveMech = NULL;
|
||||
lastPerformance = lastConsoleUpdate = lastUpdate;
|
||||
|
||||
//
|
||||
// Bind the SCORING ROLE (the binary ctor tail @004c0bc8: look the role
|
||||
// up in the mission's role dictionary by the MakeMessage roleName and
|
||||
// stash it at this+0x208). BTL4Mission::SetPlayerData populated the
|
||||
// dictionary before any player exists; the role carries the score
|
||||
// modifiers AND the lives counter (returnFromDeath). A missing page
|
||||
// leaves the base ctor's NULL (dev shape: infinite lives, raw-damage
|
||||
// scores).
|
||||
//
|
||||
if (btMission != NULL)
|
||||
{
|
||||
scenarioRole =
|
||||
Cast_Object(BTMission *, btMission)->GetScenarioRole(
|
||||
creation_message->roleName);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[role] '" << creation_message->roleName << "'";
|
||||
if (scenarioRole != NULL)
|
||||
{
|
||||
DEBUG_STREAM
|
||||
<< " lives=" << scenarioRole->GetReturnFromDeath()
|
||||
<< " killBonus=" << scenarioRole->GetKillBonus()
|
||||
<< " dmgMod=" << scenarioRole->GetDamageInflictedModifier();
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_STREAM << " NOT FOUND (role-less: infinite lives)";
|
||||
}
|
||||
DEBUG_STREAM << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
@@ -576,43 +616,193 @@ void
|
||||
++deathCount;
|
||||
message->deathCount = deathCount;
|
||||
|
||||
if (GetScenarioRole() != NULL)
|
||||
if (scenarioRole != NULL)
|
||||
{
|
||||
GetScenarioRole()->SetReturnFromDeath(
|
||||
GetScenarioRole()->GetReturnFromDeath() - 1);
|
||||
scenarioRole->SetReturnFromDeath(
|
||||
scenarioRole->GetReturnFromDeath() - 1);
|
||||
}
|
||||
|
||||
if (GetScenarioRole() == NULL
|
||||
|| GetScenarioRole()->GetReturnFromDeath() > 0)
|
||||
if (scenarioRole == NULL
|
||||
|| scenarioRole->GetReturnFromDeath() > 0)
|
||||
{
|
||||
Time when = Now();
|
||||
when += 5.0f;
|
||||
application->Post(HighEventPriority, this, message, when);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[vdead] death #" << deathCount
|
||||
<< " lives="
|
||||
<< ((scenarioRole != NULL)
|
||||
? scenarioRole->GetReturnFromDeath() : -1)
|
||||
<< " -- respawn posted +5s" << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
else
|
||||
{
|
||||
DEBUG_STREAM << "[vdead] death #" << deathCount
|
||||
<< " -- respawn posted +5s" << endl << flush;
|
||||
//
|
||||
// OUT OF LIVES. The binary posts an id-0x18 message at +10s (the
|
||||
// mission-review kick; its exact receiver sits in the one decomp
|
||||
// gap 4c05c4..4c083c). STAGED stand-in with the engine's own
|
||||
// end-of-mission entry: StopMissionMessage at +10s -- the full
|
||||
// authentic cascade runs from there (EndingMission -> the player's
|
||||
// 3s fade -> the second Stop -> Application::Stop -> RunMissions
|
||||
// exits -> GAME-RC). The review/playback launch joins the
|
||||
// mission-review wave.
|
||||
//
|
||||
Application::StopMissionMessage stop_mission(NullExitCodeID);
|
||||
Time when = Now();
|
||||
when += 10.0f;
|
||||
application->Post(HighEventPriority, application, &stop_mission, when);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[vdead] death #" << deathCount
|
||||
<< " OUT OF LIVES -- mission end posted +10s"
|
||||
<< endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BTPlayer::ScoreMessageHandler(ScoreMessage *)
|
||||
BTPlayer::ScoreMessageHandler(ScoreMessage *message)
|
||||
{
|
||||
Fail("BTPlayer::ScoreMessageHandler -- btplayer.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
//
|
||||
// Binary @004c02e4. Master-only; everything is ignored once the
|
||||
// mission is ending. Type 0 has its own handler; type 1 = points LOST
|
||||
// for damage taken; type 2 = the kill credit. The tonnage ratio
|
||||
// (mech+0x4bc) and mech damage bias (+0x354) are STAGED at 1.0/0.0
|
||||
// (members not reconstructed): awards degrade to the role-scaled
|
||||
// damage figures, raw damage with no role (the dev shape).
|
||||
//
|
||||
if (GetSimulationState() == MissionEndingState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HostManager *host_manager = application->GetHostManager();
|
||||
Entity *sender_mech =
|
||||
(Entity *)host_manager->GetEntityPointer(message->senderMechID);
|
||||
|
||||
switch (message->scoreType)
|
||||
{
|
||||
case DamageReceivedScore:
|
||||
if (sender_mech != NULL && sender_mech != playerVehicle
|
||||
&& scenarioRole != NULL)
|
||||
{
|
||||
message->scoreAward =
|
||||
scenarioRole->CalcDamageReceivedScore(message->damageAmount);
|
||||
// (the console VTV-damaged feed joins the console wave)
|
||||
Player::ScoreMessageHandler(message);
|
||||
}
|
||||
break;
|
||||
|
||||
case KillScore:
|
||||
{
|
||||
Scalar award = CalcInflictedScore(
|
||||
message->damageAmount, sender_mech, message->scoreAward);
|
||||
if (sender_mech != NULL && sender_mech == playerVehicle)
|
||||
{
|
||||
//
|
||||
// Suicide: no credit, no kill count.
|
||||
//
|
||||
award = -award;
|
||||
}
|
||||
else
|
||||
{
|
||||
++killCount;
|
||||
// (the victim-side status ticker joins the console wave)
|
||||
}
|
||||
message->scoreAward = award;
|
||||
Player::ScoreMessageHandler(message);
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[score] KILL award=" << award
|
||||
<< " kills=" << killCount
|
||||
<< " score=" << currentScore << endl << flush;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
//
|
||||
// Type 0 belongs to the ScoreInflicted handler (the binary asserts
|
||||
// here); drop it quietly in the opt shape.
|
||||
//
|
||||
break;
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
BTPlayer::ScoreInflictedMessageHandler(ScoreMessage *)
|
||||
BTPlayer::ScoreInflictedMessageHandler(ScoreMessage *message)
|
||||
{
|
||||
Fail("BTPlayer::ScoreInflictedMessageHandler -- btplayer.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
//
|
||||
// Binary @004c0200 (type 0 only): points EARNED for damage delivered.
|
||||
// Accumulates DIRECTLY into currentScore (not via the base handler);
|
||||
// negated for self-hits. Tonnage ratio staged 1.0.
|
||||
//
|
||||
if (GetSimulationState() == MissionEndingState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HostManager *host_manager = application->GetHostManager();
|
||||
Entity *target_mech =
|
||||
(Entity *)host_manager->GetEntityPointer(message->senderMechID);
|
||||
|
||||
Scalar award = CalcInflictedScore(message->damageAmount, target_mech, 0.0f);
|
||||
if (target_mech != NULL && target_mech == playerVehicle)
|
||||
{
|
||||
award = -award;
|
||||
}
|
||||
currentScore += award;
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
void
|
||||
BTPlayer::ScoreUpdateMessageHandler(ScoreMessage *)
|
||||
BTPlayer::ScoreUpdateMessageHandler(ScoreMessage *message)
|
||||
{
|
||||
Fail("BTPlayer::ScoreUpdateMessageHandler -- btplayer.cpp not yet reconstructed");
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
//
|
||||
// Binary @004c02a8: the network score resync -- fold the carried award
|
||||
// straight through the base handler.
|
||||
//
|
||||
Player::ScoreMessageHandler(message);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// CalcInflictedScore (binary @004c052c) -- the shared damage -> points
|
||||
// converter. factor = -friendlyFirePenalty on a team hit, else
|
||||
// (mechDamageBias x roleDamageBias + 1); STAGED: mech damage bias (+0x354)
|
||||
// unreconstructed = 0, so factor degrades to 1 (or the friendly penalty).
|
||||
//#############################################################################
|
||||
//
|
||||
Scalar
|
||||
BTPlayer::CalcInflictedScore(
|
||||
Scalar damage_amount,
|
||||
Entity * /*other_mech -- the team / bias source, staged*/,
|
||||
Scalar bias
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (scenarioRole == NULL)
|
||||
{
|
||||
return damage_amount + bias;
|
||||
}
|
||||
return (damage_amount + bias)
|
||||
* scenarioRole->GetDamageInflictedModifier();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -1,229 +1,295 @@
|
||||
//===========================================================================//
|
||||
// File: btplayer.hpp //
|
||||
// Project: BattleTech //
|
||||
// Contents: Implementation details for the BT player //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#if !defined(BTPLAYER_HPP)
|
||||
# define BTPLAYER_HPP
|
||||
|
||||
# if !defined(PLAYER_HPP)
|
||||
# include <player.hpp>
|
||||
# endif
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
class BTTeam;
|
||||
class Mission;
|
||||
|
||||
//###########################################################################
|
||||
//####################### BTPlayer Make Message #########################
|
||||
//###########################################################################
|
||||
|
||||
class BTPlayer__MakeMessage:
|
||||
public Player__MakeMessage
|
||||
{
|
||||
public:
|
||||
CString roleName;
|
||||
CString teamName;
|
||||
|
||||
BTPlayer__MakeMessage(
|
||||
Receiver::MessageID message_ID,
|
||||
size_t length,
|
||||
Entity::ClassID class_ID,
|
||||
const EntityID &owner_ID,
|
||||
ResourceDescription::ResourceID resource_ID,
|
||||
LWord instance_flags,
|
||||
const Origin &origin,
|
||||
int player_bitmap_index,
|
||||
CString role_name,
|
||||
CString team_name
|
||||
):
|
||||
Player__MakeMessage(
|
||||
message_ID,
|
||||
length,
|
||||
class_ID,
|
||||
owner_ID,
|
||||
resource_ID,
|
||||
instance_flags,
|
||||
origin,
|
||||
player_bitmap_index
|
||||
)
|
||||
{
|
||||
roleName = role_name;
|
||||
teamName = team_name;
|
||||
}
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//############################# BTPlayer ################################
|
||||
//###########################################################################
|
||||
|
||||
class BTPlayer:
|
||||
public Player
|
||||
{
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static const HandlerEntry
|
||||
MessageHandlerEntries[];
|
||||
static MessageHandlerSet
|
||||
MessageHandlers;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Message Support
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
ScoreInflictedMessageID = Player::NextMessageID,
|
||||
ScoreUpdateMessageID,
|
||||
NextMessageID
|
||||
};
|
||||
|
||||
protected:
|
||||
void
|
||||
DropZoneReplyMessageHandler(DropZone__ReplyMessage *message);
|
||||
void
|
||||
VehicleDeadMessageHandler(VehicleDeadMessage *message);
|
||||
void
|
||||
ScoreMessageHandler(ScoreMessage *message);
|
||||
void
|
||||
ScoreInflictedMessageHandler(ScoreMessage *message);
|
||||
void
|
||||
ScoreUpdateMessageHandler(ScoreMessage *message);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Vehicle creation
|
||||
//
|
||||
protected:
|
||||
virtual void
|
||||
CreatePlayerVehicle(Origin mech_location);
|
||||
void
|
||||
InitializePlayerLink();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Construction and Destruction
|
||||
//
|
||||
public:
|
||||
typedef BTPlayer__MakeMessage MakeMessage;
|
||||
|
||||
static BTPlayer*
|
||||
Make(MakeMessage *creation_message);
|
||||
|
||||
BTPlayer(
|
||||
MakeMessage *creation_message,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
~BTPlayer();
|
||||
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Experience support
|
||||
//
|
||||
public:
|
||||
Enumeration
|
||||
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
|
||||
//
|
||||
protected:
|
||||
Mission
|
||||
*btMission;
|
||||
char
|
||||
teamName[64];
|
||||
BTTeam
|
||||
*teamEntity;
|
||||
Logical
|
||||
freeForAll;
|
||||
Logical
|
||||
consoleAttached;
|
||||
Logical
|
||||
suppressConsole;
|
||||
|
||||
//
|
||||
// EXPERIENCE-LEVEL game-mode flags, derived in the constructor from the
|
||||
// mission's experience level (novice / standard / veteran / expert) and
|
||||
// advanced-damage technician flag. TRUE SEMANTICS (the early "display
|
||||
// toggles" reading of this block was wrong -- see BTPLAYER.NOTES.md):
|
||||
// binary rows for (simLive, heatModelOn, levelFlag26c, levelFlag270) =
|
||||
// nov 0,0,0,0 / std 1,0,1,1 / vet 1,1,1,1 / exp 1,1,0,1.
|
||||
// simLive -- the novice lockout: jams, searchlight, powersub
|
||||
// short events (CheckForJam's LiveFireEnabled gate);
|
||||
// heatModelOn -- THE heat-model master switch (HeatModelActive):
|
||||
// weapon-fire heat, the heat sim, coolant, movement
|
||||
// heat. Standard mode has NO heat consequences --
|
||||
// authentic, not a gap;
|
||||
// advancedDamageOn/2 -- the egg's technician flag (an int pair in the
|
||||
// binary; NOT part of the level switch);
|
||||
// experienceLevel -- the raw 0..3 (the ==0 novice-lockout predicate).
|
||||
//
|
||||
Logical
|
||||
simLive;
|
||||
Logical
|
||||
heatModelOn;
|
||||
Logical
|
||||
advancedDamageOn;
|
||||
Logical
|
||||
advancedDamageOn2;
|
||||
Logical
|
||||
levelFlag26c;
|
||||
Logical
|
||||
levelFlag270;
|
||||
int
|
||||
experienceLevel;
|
||||
|
||||
public:
|
||||
Logical
|
||||
IsSimLive() const { Check(this); return simLive; }
|
||||
Logical
|
||||
IsHeatModelOn() const { Check(this); return heatModelOn; }
|
||||
Logical
|
||||
IsAdvancedDamageOn() const { Check(this); return advancedDamageOn; }
|
||||
|
||||
protected:
|
||||
|
||||
int
|
||||
killCount;
|
||||
int
|
||||
padScore;
|
||||
Mech
|
||||
*objectiveMech;
|
||||
Time
|
||||
lastConsoleUpdate;
|
||||
Logical
|
||||
deathPending;
|
||||
};
|
||||
|
||||
#endif
|
||||
//===========================================================================//
|
||||
// File: btplayer.hpp //
|
||||
// Project: BattleTech //
|
||||
// Contents: Implementation details for the BT player //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Date Who Modification //
|
||||
// -------- --- ---------------------------------------------------------- //
|
||||
// //
|
||||
//---------------------------------------------------------------------------//
|
||||
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
|
||||
// All Rights reserved worldwide //
|
||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||
//===========================================================================//
|
||||
|
||||
#if !defined(BTPLAYER_HPP)
|
||||
# define BTPLAYER_HPP
|
||||
|
||||
# if !defined(PLAYER_HPP)
|
||||
# include <player.hpp>
|
||||
# endif
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
class BTTeam;
|
||||
class Mission;
|
||||
|
||||
//###########################################################################
|
||||
//####################### BTPlayer Make Message #########################
|
||||
//###########################################################################
|
||||
|
||||
class BTPlayer__MakeMessage:
|
||||
public Player__MakeMessage
|
||||
{
|
||||
public:
|
||||
CString roleName;
|
||||
CString teamName;
|
||||
|
||||
BTPlayer__MakeMessage(
|
||||
Receiver::MessageID message_ID,
|
||||
size_t length,
|
||||
Entity::ClassID class_ID,
|
||||
const EntityID &owner_ID,
|
||||
ResourceDescription::ResourceID resource_ID,
|
||||
LWord instance_flags,
|
||||
const Origin &origin,
|
||||
int player_bitmap_index,
|
||||
CString role_name,
|
||||
CString team_name
|
||||
):
|
||||
Player__MakeMessage(
|
||||
message_ID,
|
||||
length,
|
||||
class_ID,
|
||||
owner_ID,
|
||||
resource_ID,
|
||||
instance_flags,
|
||||
origin,
|
||||
player_bitmap_index
|
||||
)
|
||||
{
|
||||
roleName = role_name;
|
||||
teamName = team_name;
|
||||
}
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//###########################################################################
|
||||
//###################### BTPlayer Score Message #########################
|
||||
//###########################################################################
|
||||
|
||||
//
|
||||
// The BT wire layout (extends the engine ScoreMessage's scoreAward
|
||||
// @+0x1c): scoreType @+0x20, damageAmount @+0x24 (the input every award
|
||||
// derives from), the point-source handles @+0x28..0x30 (console VTV
|
||||
// feed -- headless zeros), senderMechID @+0x34 (the inflicting / victim
|
||||
// mech, resolved via the host manager).
|
||||
//
|
||||
class BTPlayer__ScoreMessage:
|
||||
public Player__ScoreMessage
|
||||
{
|
||||
public:
|
||||
int scoreType;
|
||||
Scalar damageAmount;
|
||||
int pointSenderHi;
|
||||
int pointSenderLo;
|
||||
int auxID;
|
||||
EntityID senderMechID;
|
||||
|
||||
BTPlayer__ScoreMessage(
|
||||
Receiver::MessageID message_ID,
|
||||
size_t length,
|
||||
Scalar score,
|
||||
int score_type,
|
||||
Scalar damage_amount,
|
||||
const EntityID &sender_mech_ID
|
||||
):
|
||||
Player__ScoreMessage(message_ID, length, score),
|
||||
scoreType(score_type),
|
||||
damageAmount(damage_amount),
|
||||
pointSenderHi(0),
|
||||
pointSenderLo(0),
|
||||
auxID(0),
|
||||
senderMechID(sender_mech_ID)
|
||||
{}
|
||||
};
|
||||
|
||||
//############################# BTPlayer ################################
|
||||
//###########################################################################
|
||||
|
||||
class BTPlayer:
|
||||
public Player
|
||||
{
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Shared Data Support
|
||||
//
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static const HandlerEntry
|
||||
MessageHandlerEntries[];
|
||||
static MessageHandlerSet
|
||||
MessageHandlers;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Message Support
|
||||
//
|
||||
public:
|
||||
enum {
|
||||
ScoreInflictedMessageID = Player::NextMessageID,
|
||||
ScoreUpdateMessageID,
|
||||
NextMessageID
|
||||
};
|
||||
|
||||
//
|
||||
// The score types the BT ScoreMessage carries (branch selector
|
||||
// @004c02e4; type 0 has its own handler @004c0200).
|
||||
//
|
||||
enum {
|
||||
DamageInflictedScore = 0,
|
||||
DamageReceivedScore = 1,
|
||||
KillScore = 2
|
||||
};
|
||||
|
||||
public:
|
||||
typedef BTPlayer__ScoreMessage ScoreMessage;
|
||||
|
||||
//
|
||||
// The shared damage -> points converter (binary @004c052c):
|
||||
// (damage + bias) x role damageInflictedModifier x factor, where a
|
||||
// friendly hit flips to -friendlyFirePenalty. Role-less players
|
||||
// degrade to raw damage (the dev-mission shape).
|
||||
//
|
||||
Scalar
|
||||
CalcInflictedScore(
|
||||
Scalar damage_amount,
|
||||
Entity *other_mech,
|
||||
Scalar bias
|
||||
);
|
||||
|
||||
protected:
|
||||
void
|
||||
DropZoneReplyMessageHandler(DropZone__ReplyMessage *message);
|
||||
void
|
||||
VehicleDeadMessageHandler(VehicleDeadMessage *message);
|
||||
void
|
||||
ScoreMessageHandler(ScoreMessage *message);
|
||||
void
|
||||
ScoreInflictedMessageHandler(ScoreMessage *message);
|
||||
void
|
||||
ScoreUpdateMessageHandler(ScoreMessage *message);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Vehicle creation
|
||||
//
|
||||
protected:
|
||||
virtual void
|
||||
CreatePlayerVehicle(Origin mech_location);
|
||||
void
|
||||
InitializePlayerLink();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Construction and Destruction
|
||||
//
|
||||
public:
|
||||
typedef BTPlayer__MakeMessage MakeMessage;
|
||||
|
||||
static BTPlayer*
|
||||
Make(MakeMessage *creation_message);
|
||||
|
||||
BTPlayer(
|
||||
MakeMessage *creation_message,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
~BTPlayer();
|
||||
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Experience support
|
||||
//
|
||||
public:
|
||||
Enumeration
|
||||
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
|
||||
//
|
||||
protected:
|
||||
Mission
|
||||
*btMission;
|
||||
char
|
||||
teamName[64];
|
||||
BTTeam
|
||||
*teamEntity;
|
||||
Logical
|
||||
freeForAll;
|
||||
Logical
|
||||
consoleAttached;
|
||||
Logical
|
||||
suppressConsole;
|
||||
|
||||
//
|
||||
// EXPERIENCE-LEVEL game-mode flags, derived in the constructor from the
|
||||
// mission's experience level (novice / standard / veteran / expert) and
|
||||
// advanced-damage technician flag. TRUE SEMANTICS (the early "display
|
||||
// toggles" reading of this block was wrong -- see BTPLAYER.NOTES.md):
|
||||
// binary rows for (simLive, heatModelOn, levelFlag26c, levelFlag270) =
|
||||
// nov 0,0,0,0 / std 1,0,1,1 / vet 1,1,1,1 / exp 1,1,0,1.
|
||||
// simLive -- the novice lockout: jams, searchlight, powersub
|
||||
// short events (CheckForJam's LiveFireEnabled gate);
|
||||
// heatModelOn -- THE heat-model master switch (HeatModelActive):
|
||||
// weapon-fire heat, the heat sim, coolant, movement
|
||||
// heat. Standard mode has NO heat consequences --
|
||||
// authentic, not a gap;
|
||||
// advancedDamageOn/2 -- the egg's technician flag (an int pair in the
|
||||
// binary; NOT part of the level switch);
|
||||
// experienceLevel -- the raw 0..3 (the ==0 novice-lockout predicate).
|
||||
//
|
||||
Logical
|
||||
simLive;
|
||||
Logical
|
||||
heatModelOn;
|
||||
Logical
|
||||
advancedDamageOn;
|
||||
Logical
|
||||
advancedDamageOn2;
|
||||
Logical
|
||||
levelFlag26c;
|
||||
Logical
|
||||
levelFlag270;
|
||||
int
|
||||
experienceLevel;
|
||||
|
||||
public:
|
||||
Logical
|
||||
IsSimLive() const { Check(this); return simLive; }
|
||||
Logical
|
||||
IsHeatModelOn() const { Check(this); return heatModelOn; }
|
||||
Logical
|
||||
IsAdvancedDamageOn() const { Check(this); return advancedDamageOn; }
|
||||
|
||||
protected:
|
||||
|
||||
int
|
||||
killCount;
|
||||
int
|
||||
padScore;
|
||||
Mech
|
||||
*objectiveMech;
|
||||
Time
|
||||
lastConsoleUpdate;
|
||||
Logical
|
||||
deathPending;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -193,3 +193,36 @@ return -- ends the 2s probe loop). Out-of-lives -> mission review post =
|
||||
scoring wave. The fresh-spawn branch does NOT yet run the shared
|
||||
Reset/translocation-alarm tail (mech already placed by the MakeMessage;
|
||||
joins the cockpit wave).
|
||||
|
||||
## 5.3.25 — scoring roles + the COMPLETE mission lifecycle
|
||||
|
||||
- The ctor binds Player::scenarioRole from the mission role dictionary by
|
||||
the MakeMessage roleName (binary @004c0bc8 tail; the dict was populated
|
||||
by BTL4Mission::SetPlayerData before any player exists). TEST.EGG's
|
||||
Role::Default -> baked 'dfltrole': lives=1000, killBonus=500, dmgMod=1
|
||||
(authored data flowing). Egg override: `return=N` goes on the ROLE page
|
||||
([Role::Default]), NOT the player page (BTL4MSSN.CPP:307 reads role_node).
|
||||
- Score handlers live: Score @004c02e4 (type 1 DamageReceived via
|
||||
role->CalcDamageReceivedScore; type 2 Kill via CalcInflictedScore + the
|
||||
suicide negate + killCount, then the base applies scoreAward),
|
||||
ScoreInflicted @004c0200 (type 0, accumulates DIRECTLY into currentScore,
|
||||
self-hit negated), ScoreUpdate @004c02a8 (base fold-through).
|
||||
CalcInflictedScore @004c052c staged: tonnage ratios (mech+0x4bc) and
|
||||
mechDamageBias (+0x354) unreconstructed -> 1.0/0.0; friendly-fire factor
|
||||
joins the team wave; console VTV feeds join the console wave.
|
||||
- Producers (the authentic emitters sit in the unexported master-perf
|
||||
writer 0x4a9770-0x4ab188; the port shape is used): SendDamage + the
|
||||
missile fuse post type-0 damage scores to the shooter's pilot; the mech
|
||||
death transition posts the type-2 kill to the killer's pilot with the
|
||||
latched killing-blow magnitude (Mech::lastInflictingDamage).
|
||||
- OUT OF LIVES: the binary's +10s id-0x18 review post lives in the one
|
||||
decomp gap (4c05c4..4c083c); STAGED stand-in = StopMissionMessage at
|
||||
+10s -- the ENGINE cascade is fully authentic from there: EndingMission
|
||||
-> MissionEnding fade (3s) -> the second Stop -> Application::Stop ->
|
||||
RunMissions exits -> GAME-RC. The BTL4/L4 layers run authentically
|
||||
(plasma score display off; egress lamps).
|
||||
- VERIFIED END-TO-END (lifecycle2.conf, KILLTEST.EGG return=2,
|
||||
BT_PLAYER_PASSIVE=1): lives=2 -> death#1 debit+respawn -> death#2 OUT OF
|
||||
LIVES -> plasma off -> clean GAME-RC exit. THE FULL 1995 POD MISSION
|
||||
LOOP, egg to DOS exit, from reconstructed source. Dev knobs:
|
||||
BT_PLAYER_PASSIVE (piloted mechs hold force-fire).
|
||||
|
||||
@@ -482,7 +482,18 @@ void
|
||||
{
|
||||
forceFire = (getenv("BT_FORCE_FIRE") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (forceFire)
|
||||
//
|
||||
// BT_PLAYER_PASSIVE=1: the piloted mech holds fire (the lifecycle
|
||||
// test -- let the enemy win).
|
||||
//
|
||||
static int playerPassive = -1;
|
||||
if (playerPassive < 0)
|
||||
{
|
||||
playerPassive = (getenv("BT_PLAYER_PASSIVE") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (forceFire
|
||||
&& !(playerPassive && owner != NULL
|
||||
&& owner->GetPlayerLink() != NULL))
|
||||
{
|
||||
fireImpulse = (GetWeaponState() == LoadedState) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
#include <mechdmg.hpp> // Mech::DamageZone -- the hull zone fill (Pass 3)
|
||||
#include <dmgtable.hpp> // DamageLookupTable -- the cylinder hit table
|
||||
#include <player.hpp> // Player::VehicleDeadMessage -- the death notification
|
||||
#include <btplayer.hpp> // BTPlayer::ScoreMessage -- the kill credit
|
||||
#include <hostmgr.hpp> // HostManager::GetEntityPointer -- killer resolve
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
@@ -191,6 +193,7 @@ Mech::Mech(
|
||||
lastInflictingID = EntityID::Null;
|
||||
damageLookupTable = NULL;
|
||||
deathTransitionDone = 0;
|
||||
lastInflictingDamage = 0.0f;
|
||||
|
||||
//
|
||||
// Look-view angles: defaults until the GameModel read below overrides them
|
||||
@@ -649,7 +652,8 @@ void
|
||||
<< endl << flush;
|
||||
}
|
||||
|
||||
lastInflictingID = message->inflictingEntity;
|
||||
lastInflictingID = message->inflictingEntity;
|
||||
lastInflictingDamage = message->damageData.damageAmount;
|
||||
|
||||
//
|
||||
// The cylinder resolve (binary @0x4a0264 tail): an UNAIMED hit arrives
|
||||
@@ -1015,6 +1019,36 @@ void
|
||||
pilot->Dispatch(&dead);
|
||||
}
|
||||
|
||||
//
|
||||
// The KILL CREDIT: resolve the last attacker and post the
|
||||
// type-2 ScoreMessage to the KILLER's pilot, carrying the
|
||||
// killing-blow magnitude (the whole award derives from it --
|
||||
// binary emitter in the unexported master-perf writer; the
|
||||
// kill-bonus bias rides scoreAward, staged 0). MASTER-only,
|
||||
// and an unpiloted killer (the dev enemy) earns nothing.
|
||||
//
|
||||
if (GetInstance() != Entity::ReplicantInstance
|
||||
&& !(lastInflictingID == EntityID::Null))
|
||||
{
|
||||
Entity *killer = (Entity *)application->GetHostManager()->
|
||||
GetEntityPointer(lastInflictingID);
|
||||
if (killer != NULL && killer != (Entity *)this
|
||||
&& killer->IsDerivedFrom(Mech::ClassDerivations)
|
||||
&& killer->GetPlayerLink() != NULL)
|
||||
{
|
||||
BTPlayer::ScoreMessage
|
||||
kill_score(
|
||||
Player::ScoreMessageID,
|
||||
sizeof(BTPlayer::ScoreMessage),
|
||||
0.0f,
|
||||
BTPlayer::KillScore,
|
||||
lastInflictingDamage,
|
||||
GetEntityID()
|
||||
);
|
||||
killer->GetPlayerLink()->Dispatch(&kill_score);
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[death] mech " << GetEntityID()
|
||||
|
||||
@@ -455,6 +455,8 @@
|
||||
Entity *targetEntity;
|
||||
EntityID lastInflictingID; // binary mech+0x43c -- last
|
||||
// attacker (zone LOD router)
|
||||
Scalar lastInflictingDamage; // the killing-blow
|
||||
// magnitude (kill score)
|
||||
int deathTransitionDone; // the once-per-death latch
|
||||
// (binary movementMode 2||9)
|
||||
DamageLookupTable *damageLookupTable; // binary mech+0x444 -- the
|
||||
@@ -473,7 +475,7 @@
|
||||
// mech2/mech3/mech4 simulation. Reserved until that path is
|
||||
// reconstructed with named fields.
|
||||
//
|
||||
int reservedState[197];
|
||||
int reservedState[196];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
# include <random.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(BTPLAYER_HPP)
|
||||
# include <btplayer.hpp>
|
||||
#endif
|
||||
|
||||
Derivation
|
||||
MechWeapon::ClassDerivations(
|
||||
PoweredSubsystem::ClassDerivations,
|
||||
@@ -452,6 +456,26 @@ void
|
||||
);
|
||||
target->Dispatch(&hit);
|
||||
|
||||
//
|
||||
// The DAMAGE SCORE: post the type-0 ScoreInflicted to OUR pilot (the
|
||||
// authentic producer sits in the unexported master-perf writer; the
|
||||
// port shape posts per delivered hit). senderMechID = the VICTIM.
|
||||
//
|
||||
if (owner != NULL && owner->GetInstance() != Entity::ReplicantInstance
|
||||
&& owner->GetPlayerLink() != NULL)
|
||||
{
|
||||
BTPlayer::ScoreMessage
|
||||
damage_score(
|
||||
BTPlayer::ScoreInflictedMessageID,
|
||||
sizeof(BTPlayer::ScoreMessage),
|
||||
0.0f,
|
||||
BTPlayer::DamageInflictedScore,
|
||||
damage.damageAmount,
|
||||
target->GetEntityID()
|
||||
);
|
||||
owner->GetPlayerLink()->Dispatch(&damage_score);
|
||||
}
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[dmg] '" << GetName()
|
||||
|
||||
@@ -42,6 +42,14 @@
|
||||
# include <app.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(HOSTMGR_HPP)
|
||||
# include <hostmgr.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(BTPLAYER_HPP)
|
||||
# include <btplayer.hpp>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Guidance tuning (binary .rdata @004bf594..@004bf5b0, BT411-decoded).
|
||||
// STAGED-DEFAULT flight envelope: lifetime / thrust come from the missile
|
||||
@@ -347,6 +355,32 @@ void
|
||||
);
|
||||
victim->Dispatch(&hit);
|
||||
|
||||
//
|
||||
// The DAMAGE SCORE to the launching pilot (type 0, victim as
|
||||
// sender), resolved through the latched launcher id.
|
||||
//
|
||||
{
|
||||
Entity *launcher_entity =
|
||||
(Entity *)application->GetHostManager()->
|
||||
GetEntityPointer(launcherEntityID);
|
||||
if (launcher_entity != NULL
|
||||
&& launcher_entity->GetInstance()
|
||||
!= Entity::ReplicantInstance
|
||||
&& launcher_entity->GetPlayerLink() != NULL)
|
||||
{
|
||||
BTPlayer::ScoreMessage
|
||||
damage_score(
|
||||
BTPlayer::ScoreInflictedMessageID,
|
||||
sizeof(BTPlayer::ScoreMessage),
|
||||
0.0f,
|
||||
BTPlayer::DamageInflictedScore,
|
||||
damageData.damageAmount,
|
||||
victim->GetEntityID()
|
||||
);
|
||||
launcher_entity->GetPlayerLink()->Dispatch(&damage_score);
|
||||
}
|
||||
}
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[msl] DETONATE on zone " << zone
|
||||
|
||||
@@ -278,7 +278,18 @@ void
|
||||
{
|
||||
forceFire = (getenv("BT_FORCE_FIRE") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (forceFire)
|
||||
//
|
||||
// BT_PLAYER_PASSIVE=1: the piloted mech holds fire (the lifecycle
|
||||
// test -- let the enemy win).
|
||||
//
|
||||
static int playerPassive = -1;
|
||||
if (playerPassive < 0)
|
||||
{
|
||||
playerPassive = (getenv("BT_PLAYER_PASSIVE") != NULL) ? 1 : 0;
|
||||
}
|
||||
if (forceFire
|
||||
&& !(playerPassive && owner != NULL
|
||||
&& owner->GetPlayerLink() != NULL))
|
||||
{
|
||||
fireImpulse = (GetWeaponState() == LoadedState) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user