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>
|
# include <mission.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(APPMSG_HPP)
|
||||||
|
# include <appmsg.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(SCNROLE_HPP)
|
||||||
|
# include <scnrole.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
//#############################################################################
|
//#############################################################################
|
||||||
// Shared data support
|
// Shared data support
|
||||||
@@ -207,6 +215,38 @@ BTPlayer::BTPlayer(
|
|||||||
objectiveMech = NULL;
|
objectiveMech = NULL;
|
||||||
lastPerformance = lastConsoleUpdate = lastUpdate;
|
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();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,43 +616,193 @@ void
|
|||||||
++deathCount;
|
++deathCount;
|
||||||
message->deathCount = deathCount;
|
message->deathCount = deathCount;
|
||||||
|
|
||||||
if (GetScenarioRole() != NULL)
|
if (scenarioRole != NULL)
|
||||||
{
|
{
|
||||||
GetScenarioRole()->SetReturnFromDeath(
|
scenarioRole->SetReturnFromDeath(
|
||||||
GetScenarioRole()->GetReturnFromDeath() - 1);
|
scenarioRole->GetReturnFromDeath() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetScenarioRole() == NULL
|
if (scenarioRole == NULL
|
||||||
|| GetScenarioRole()->GetReturnFromDeath() > 0)
|
|| scenarioRole->GetReturnFromDeath() > 0)
|
||||||
{
|
{
|
||||||
Time when = Now();
|
Time when = Now();
|
||||||
when += 5.0f;
|
when += 5.0f;
|
||||||
application->Post(HighEventPriority, this, message, when);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (getenv("BT_MECH_LOG"))
|
|
||||||
{
|
{
|
||||||
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
|
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
|
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
|
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 //
|
// File: btplayer.hpp //
|
||||||
// Project: BattleTech //
|
// Project: BattleTech //
|
||||||
// Contents: Implementation details for the BT player //
|
// Contents: Implementation details for the BT player //
|
||||||
//---------------------------------------------------------------------------//
|
//---------------------------------------------------------------------------//
|
||||||
// Date Who Modification //
|
// 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 //
|
||||||
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
||||||
//===========================================================================//
|
//===========================================================================//
|
||||||
|
|
||||||
#if !defined(BTPLAYER_HPP)
|
#if !defined(BTPLAYER_HPP)
|
||||||
# define BTPLAYER_HPP
|
# define BTPLAYER_HPP
|
||||||
|
|
||||||
# if !defined(PLAYER_HPP)
|
# if !defined(PLAYER_HPP)
|
||||||
# include <player.hpp>
|
# include <player.hpp>
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
//##################### Forward Class Declarations #######################
|
//##################### Forward Class Declarations #######################
|
||||||
class Mech;
|
class Mech;
|
||||||
class BTTeam;
|
class BTTeam;
|
||||||
class Mission;
|
class Mission;
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
//####################### BTPlayer Make Message #########################
|
//####################### BTPlayer Make Message #########################
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|
||||||
class BTPlayer__MakeMessage:
|
class BTPlayer__MakeMessage:
|
||||||
public Player__MakeMessage
|
public Player__MakeMessage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CString roleName;
|
CString roleName;
|
||||||
CString teamName;
|
CString teamName;
|
||||||
|
|
||||||
BTPlayer__MakeMessage(
|
BTPlayer__MakeMessage(
|
||||||
Receiver::MessageID message_ID,
|
Receiver::MessageID message_ID,
|
||||||
size_t length,
|
size_t length,
|
||||||
Entity::ClassID class_ID,
|
Entity::ClassID class_ID,
|
||||||
const EntityID &owner_ID,
|
const EntityID &owner_ID,
|
||||||
ResourceDescription::ResourceID resource_ID,
|
ResourceDescription::ResourceID resource_ID,
|
||||||
LWord instance_flags,
|
LWord instance_flags,
|
||||||
const Origin &origin,
|
const Origin &origin,
|
||||||
int player_bitmap_index,
|
int player_bitmap_index,
|
||||||
CString role_name,
|
CString role_name,
|
||||||
CString team_name
|
CString team_name
|
||||||
):
|
):
|
||||||
Player__MakeMessage(
|
Player__MakeMessage(
|
||||||
message_ID,
|
message_ID,
|
||||||
length,
|
length,
|
||||||
class_ID,
|
class_ID,
|
||||||
owner_ID,
|
owner_ID,
|
||||||
resource_ID,
|
resource_ID,
|
||||||
instance_flags,
|
instance_flags,
|
||||||
origin,
|
origin,
|
||||||
player_bitmap_index
|
player_bitmap_index
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
roleName = role_name;
|
roleName = role_name;
|
||||||
teamName = team_name;
|
teamName = team_name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
//############################# BTPlayer ################################
|
//###########################################################################
|
||||||
//###########################################################################
|
//###################### BTPlayer Score Message #########################
|
||||||
|
//###########################################################################
|
||||||
class BTPlayer:
|
|
||||||
public Player
|
//
|
||||||
{
|
// The BT wire layout (extends the engine ScoreMessage's scoreAward
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// @+0x1c): scoreType @+0x20, damageAmount @+0x24 (the input every award
|
||||||
// Shared Data Support
|
// derives from), the point-source handles @+0x28..0x30 (console VTV
|
||||||
//
|
// feed -- headless zeros), senderMechID @+0x34 (the inflicting / victim
|
||||||
public:
|
// mech, resolved via the host manager).
|
||||||
static Derivation ClassDerivations;
|
//
|
||||||
static SharedData DefaultData;
|
class BTPlayer__ScoreMessage:
|
||||||
|
public Player__ScoreMessage
|
||||||
static const HandlerEntry
|
{
|
||||||
MessageHandlerEntries[];
|
public:
|
||||||
static MessageHandlerSet
|
int scoreType;
|
||||||
MessageHandlers;
|
Scalar damageAmount;
|
||||||
|
int pointSenderHi;
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
int pointSenderLo;
|
||||||
// Message Support
|
int auxID;
|
||||||
//
|
EntityID senderMechID;
|
||||||
public:
|
|
||||||
enum {
|
BTPlayer__ScoreMessage(
|
||||||
ScoreInflictedMessageID = Player::NextMessageID,
|
Receiver::MessageID message_ID,
|
||||||
ScoreUpdateMessageID,
|
size_t length,
|
||||||
NextMessageID
|
Scalar score,
|
||||||
};
|
int score_type,
|
||||||
|
Scalar damage_amount,
|
||||||
protected:
|
const EntityID &sender_mech_ID
|
||||||
void
|
):
|
||||||
DropZoneReplyMessageHandler(DropZone__ReplyMessage *message);
|
Player__ScoreMessage(message_ID, length, score),
|
||||||
void
|
scoreType(score_type),
|
||||||
VehicleDeadMessageHandler(VehicleDeadMessage *message);
|
damageAmount(damage_amount),
|
||||||
void
|
pointSenderHi(0),
|
||||||
ScoreMessageHandler(ScoreMessage *message);
|
pointSenderLo(0),
|
||||||
void
|
auxID(0),
|
||||||
ScoreInflictedMessageHandler(ScoreMessage *message);
|
senderMechID(sender_mech_ID)
|
||||||
void
|
{}
|
||||||
ScoreUpdateMessageHandler(ScoreMessage *message);
|
};
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//############################# BTPlayer ################################
|
||||||
// Vehicle creation
|
//###########################################################################
|
||||||
//
|
|
||||||
protected:
|
class BTPlayer:
|
||||||
virtual void
|
public Player
|
||||||
CreatePlayerVehicle(Origin mech_location);
|
{
|
||||||
void
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
InitializePlayerLink();
|
// Shared Data Support
|
||||||
|
//
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
public:
|
||||||
// Construction and Destruction
|
static Derivation ClassDerivations;
|
||||||
//
|
static SharedData DefaultData;
|
||||||
public:
|
|
||||||
typedef BTPlayer__MakeMessage MakeMessage;
|
static const HandlerEntry
|
||||||
|
MessageHandlerEntries[];
|
||||||
static BTPlayer*
|
static MessageHandlerSet
|
||||||
Make(MakeMessage *creation_message);
|
MessageHandlers;
|
||||||
|
|
||||||
BTPlayer(
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
MakeMessage *creation_message,
|
// Message Support
|
||||||
SharedData &shared_data = DefaultData
|
//
|
||||||
);
|
public:
|
||||||
~BTPlayer();
|
enum {
|
||||||
|
ScoreInflictedMessageID = Player::NextMessageID,
|
||||||
Logical
|
ScoreUpdateMessageID,
|
||||||
TestInstance() const;
|
NextMessageID
|
||||||
|
};
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
// Experience support
|
//
|
||||||
//
|
// The score types the BT ScoreMessage carries (branch selector
|
||||||
public:
|
// @004c02e4; type 0 has its own handler @004c0200).
|
||||||
Enumeration
|
//
|
||||||
GetExperienceLevel();
|
enum {
|
||||||
|
DamageInflictedScore = 0,
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
DamageReceivedScore = 1,
|
||||||
// Model Support
|
KillScore = 2
|
||||||
//
|
};
|
||||||
public:
|
|
||||||
typedef void
|
public:
|
||||||
(BTPlayer::*Performance)(Scalar time_slice);
|
typedef BTPlayer__ScoreMessage ScoreMessage;
|
||||||
|
|
||||||
void
|
//
|
||||||
SetPerformance(Performance performance)
|
// The shared damage -> points converter (binary @004c052c):
|
||||||
{
|
// (damage + bias) x role damageInflictedModifier x factor, where a
|
||||||
Check(this);
|
// friendly hit flips to -friendlyFirePenalty. Role-less players
|
||||||
activePerformance = (Simulation::Performance)performance;
|
// degrade to raw damage (the dev-mission shape).
|
||||||
}
|
//
|
||||||
|
Scalar
|
||||||
void
|
CalcInflictedScore(
|
||||||
PlayerSimulation(Scalar time_slice);
|
Scalar damage_amount,
|
||||||
|
Entity *other_mech,
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
Scalar bias
|
||||||
// Local Data
|
);
|
||||||
//
|
|
||||||
protected:
|
protected:
|
||||||
Mission
|
void
|
||||||
*btMission;
|
DropZoneReplyMessageHandler(DropZone__ReplyMessage *message);
|
||||||
char
|
void
|
||||||
teamName[64];
|
VehicleDeadMessageHandler(VehicleDeadMessage *message);
|
||||||
BTTeam
|
void
|
||||||
*teamEntity;
|
ScoreMessageHandler(ScoreMessage *message);
|
||||||
Logical
|
void
|
||||||
freeForAll;
|
ScoreInflictedMessageHandler(ScoreMessage *message);
|
||||||
Logical
|
void
|
||||||
consoleAttached;
|
ScoreUpdateMessageHandler(ScoreMessage *message);
|
||||||
Logical
|
|
||||||
suppressConsole;
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// Vehicle creation
|
||||||
//
|
//
|
||||||
// EXPERIENCE-LEVEL game-mode flags, derived in the constructor from the
|
protected:
|
||||||
// mission's experience level (novice / standard / veteran / expert) and
|
virtual void
|
||||||
// advanced-damage technician flag. TRUE SEMANTICS (the early "display
|
CreatePlayerVehicle(Origin mech_location);
|
||||||
// toggles" reading of this block was wrong -- see BTPLAYER.NOTES.md):
|
void
|
||||||
// binary rows for (simLive, heatModelOn, levelFlag26c, levelFlag270) =
|
InitializePlayerLink();
|
||||||
// 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);
|
// Construction and Destruction
|
||||||
// heatModelOn -- THE heat-model master switch (HeatModelActive):
|
//
|
||||||
// weapon-fire heat, the heat sim, coolant, movement
|
public:
|
||||||
// heat. Standard mode has NO heat consequences --
|
typedef BTPlayer__MakeMessage MakeMessage;
|
||||||
// authentic, not a gap;
|
|
||||||
// advancedDamageOn/2 -- the egg's technician flag (an int pair in the
|
static BTPlayer*
|
||||||
// binary; NOT part of the level switch);
|
Make(MakeMessage *creation_message);
|
||||||
// experienceLevel -- the raw 0..3 (the ==0 novice-lockout predicate).
|
|
||||||
//
|
BTPlayer(
|
||||||
Logical
|
MakeMessage *creation_message,
|
||||||
simLive;
|
SharedData &shared_data = DefaultData
|
||||||
Logical
|
);
|
||||||
heatModelOn;
|
~BTPlayer();
|
||||||
Logical
|
|
||||||
advancedDamageOn;
|
Logical
|
||||||
Logical
|
TestInstance() const;
|
||||||
advancedDamageOn2;
|
|
||||||
Logical
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
levelFlag26c;
|
// Experience support
|
||||||
Logical
|
//
|
||||||
levelFlag270;
|
public:
|
||||||
int
|
Enumeration
|
||||||
experienceLevel;
|
GetExperienceLevel();
|
||||||
|
|
||||||
public:
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Logical
|
// Model Support
|
||||||
IsSimLive() const { Check(this); return simLive; }
|
//
|
||||||
Logical
|
public:
|
||||||
IsHeatModelOn() const { Check(this); return heatModelOn; }
|
typedef void
|
||||||
Logical
|
(BTPlayer::*Performance)(Scalar time_slice);
|
||||||
IsAdvancedDamageOn() const { Check(this); return advancedDamageOn; }
|
|
||||||
|
void
|
||||||
protected:
|
SetPerformance(Performance performance)
|
||||||
|
{
|
||||||
int
|
Check(this);
|
||||||
killCount;
|
activePerformance = (Simulation::Performance)performance;
|
||||||
int
|
}
|
||||||
padScore;
|
|
||||||
Mech
|
void
|
||||||
*objectiveMech;
|
PlayerSimulation(Scalar time_slice);
|
||||||
Time
|
|
||||||
lastConsoleUpdate;
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
Logical
|
// Local Data
|
||||||
deathPending;
|
//
|
||||||
};
|
protected:
|
||||||
|
Mission
|
||||||
#endif
|
*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
|
scoring wave. The fresh-spawn branch does NOT yet run the shared
|
||||||
Reset/translocation-alarm tail (mech already placed by the MakeMessage;
|
Reset/translocation-alarm tail (mech already placed by the MakeMessage;
|
||||||
joins the cockpit wave).
|
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;
|
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;
|
fireImpulse = (GetWeaponState() == LoadedState) ? 1.0f : 0.0f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
#include <mechdmg.hpp> // Mech::DamageZone -- the hull zone fill (Pass 3)
|
#include <mechdmg.hpp> // Mech::DamageZone -- the hull zone fill (Pass 3)
|
||||||
#include <dmgtable.hpp> // DamageLookupTable -- the cylinder hit table
|
#include <dmgtable.hpp> // DamageLookupTable -- the cylinder hit table
|
||||||
#include <player.hpp> // Player::VehicleDeadMessage -- the death notification
|
#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;
|
lastInflictingID = EntityID::Null;
|
||||||
damageLookupTable = NULL;
|
damageLookupTable = NULL;
|
||||||
deathTransitionDone = 0;
|
deathTransitionDone = 0;
|
||||||
|
lastInflictingDamage = 0.0f;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Look-view angles: defaults until the GameModel read below overrides them
|
// Look-view angles: defaults until the GameModel read below overrides them
|
||||||
@@ -649,7 +652,8 @@ void
|
|||||||
<< endl << flush;
|
<< endl << flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastInflictingID = message->inflictingEntity;
|
lastInflictingID = message->inflictingEntity;
|
||||||
|
lastInflictingDamage = message->damageData.damageAmount;
|
||||||
|
|
||||||
//
|
//
|
||||||
// The cylinder resolve (binary @0x4a0264 tail): an UNAIMED hit arrives
|
// The cylinder resolve (binary @0x4a0264 tail): an UNAIMED hit arrives
|
||||||
@@ -1015,6 +1019,36 @@ void
|
|||||||
pilot->Dispatch(&dead);
|
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"))
|
if (getenv("BT_MECH_LOG"))
|
||||||
{
|
{
|
||||||
DEBUG_STREAM << "[death] mech " << GetEntityID()
|
DEBUG_STREAM << "[death] mech " << GetEntityID()
|
||||||
|
|||||||
@@ -455,6 +455,8 @@
|
|||||||
Entity *targetEntity;
|
Entity *targetEntity;
|
||||||
EntityID lastInflictingID; // binary mech+0x43c -- last
|
EntityID lastInflictingID; // binary mech+0x43c -- last
|
||||||
// attacker (zone LOD router)
|
// attacker (zone LOD router)
|
||||||
|
Scalar lastInflictingDamage; // the killing-blow
|
||||||
|
// magnitude (kill score)
|
||||||
int deathTransitionDone; // the once-per-death latch
|
int deathTransitionDone; // the once-per-death latch
|
||||||
// (binary movementMode 2||9)
|
// (binary movementMode 2||9)
|
||||||
DamageLookupTable *damageLookupTable; // binary mech+0x444 -- the
|
DamageLookupTable *damageLookupTable; // binary mech+0x444 -- the
|
||||||
@@ -473,7 +475,7 @@
|
|||||||
// mech2/mech3/mech4 simulation. Reserved until that path is
|
// mech2/mech3/mech4 simulation. Reserved until that path is
|
||||||
// reconstructed with named fields.
|
// reconstructed with named fields.
|
||||||
//
|
//
|
||||||
int reservedState[197];
|
int reservedState[196];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,6 +30,10 @@
|
|||||||
# include <random.hpp>
|
# include <random.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if !defined(BTPLAYER_HPP)
|
||||||
|
# include <btplayer.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
Derivation
|
Derivation
|
||||||
MechWeapon::ClassDerivations(
|
MechWeapon::ClassDerivations(
|
||||||
PoweredSubsystem::ClassDerivations,
|
PoweredSubsystem::ClassDerivations,
|
||||||
@@ -452,6 +456,26 @@ void
|
|||||||
);
|
);
|
||||||
target->Dispatch(&hit);
|
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"))
|
if (getenv("BT_MECH_LOG"))
|
||||||
{
|
{
|
||||||
DEBUG_STREAM << "[dmg] '" << GetName()
|
DEBUG_STREAM << "[dmg] '" << GetName()
|
||||||
|
|||||||
@@ -42,6 +42,14 @@
|
|||||||
# include <app.hpp>
|
# include <app.hpp>
|
||||||
#endif
|
#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).
|
// Guidance tuning (binary .rdata @004bf594..@004bf5b0, BT411-decoded).
|
||||||
// STAGED-DEFAULT flight envelope: lifetime / thrust come from the missile
|
// STAGED-DEFAULT flight envelope: lifetime / thrust come from the missile
|
||||||
@@ -347,6 +355,32 @@ void
|
|||||||
);
|
);
|
||||||
victim->Dispatch(&hit);
|
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"))
|
if (getenv("BT_MECH_LOG"))
|
||||||
{
|
{
|
||||||
DEBUG_STREAM << "[msl] DETONATE on zone " << zone
|
DEBUG_STREAM << "[msl] DETONATE on zone " << zone
|
||||||
|
|||||||
@@ -278,7 +278,18 @@ void
|
|||||||
{
|
{
|
||||||
forceFire = (getenv("BT_FORCE_FIRE") != NULL) ? 1 : 0;
|
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;
|
fireImpulse = (GetWeaponState() == LoadedState) ? 1.0f : 0.0f;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user