Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.
Layout:
engine/ MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
models) + image codec; the minimal rp/ headers the audio HAL needs
game/ reconstructed BT logic + surviving-original BT source + fwd shims
+ WinMain launcher
content/ full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
docs/ format specs + reconstruction ledgers
reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
tools/ MP console emulator + map/resource scanners
One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,876 @@
|
||||
#include "munga.h"
|
||||
#pragma hdrstop
|
||||
|
||||
#include "player.h"
|
||||
#include "icom.h"
|
||||
#include "camship.h"
|
||||
#include "dropzone.h"
|
||||
#include "mission.h"
|
||||
#include "audio.h"
|
||||
#include "director.h"
|
||||
#include "app.h"
|
||||
#include "hostmgr.h"
|
||||
#include "nttmgr.h"
|
||||
#include "console.h"
|
||||
#include "appmsg.h"
|
||||
|
||||
//#############################################################################
|
||||
//####################### Player::StatusMessage #########################
|
||||
//#############################################################################
|
||||
|
||||
Player__StatusMessage::Player__StatusMessage(Player *player_involved, int message_type, Scalar display_time)
|
||||
{
|
||||
playerInvolved = player_involved;
|
||||
messageType = message_type;
|
||||
displayTime = display_time;
|
||||
}
|
||||
|
||||
//#############################################################################
|
||||
//############################### Player ################################
|
||||
//#############################################################################
|
||||
|
||||
//#############################################################################
|
||||
// Shared Data Support
|
||||
//
|
||||
Derivation* Player::GetClassDerivations()
|
||||
{
|
||||
static Derivation classDerivations(Entity::GetClassDerivations(), "Player");
|
||||
return &classDerivations;
|
||||
}
|
||||
|
||||
Player::SharedData
|
||||
Player::DefaultData(
|
||||
Player::GetClassDerivations(),
|
||||
Player::GetMessageHandlers(),
|
||||
Player::GetAttributeIndex(),
|
||||
Player::StateCount,
|
||||
(Entity::MakeHandler)Player::Make
|
||||
);
|
||||
|
||||
//#############################################################################
|
||||
// Attribute Support
|
||||
//
|
||||
|
||||
const Player::IndexEntry
|
||||
Player::AttributePointers[]=
|
||||
{
|
||||
ATTRIBUTE_ENTRY(Player, CurrentScore, currentScore),
|
||||
ATTRIBUTE_ENTRY(Player, PlayerRanking, playerRanking),
|
||||
ATTRIBUTE_ENTRY(Player, DropZoneLocation, dropZoneLocation),
|
||||
ATTRIBUTE_ENTRY(Player, StatusMessagePointer,statusMessagePointer),
|
||||
ATTRIBUTE_ENTRY(Player, PlayerBitmapIndex, playerBitmapIndex),
|
||||
ATTRIBUTE_ENTRY(Player, PlayerHighlighted, playerHighlighted)
|
||||
};
|
||||
|
||||
Player::AttributeIndexSet& Player::GetAttributeIndex()
|
||||
{
|
||||
static Player::AttributeIndexSet attributeIndex(ELEMENTS(Player::AttributePointers),
|
||||
Player::AttributePointers,
|
||||
Entity::GetAttributeIndex()
|
||||
);
|
||||
return attributeIndex;
|
||||
}
|
||||
|
||||
//#############################################################################
|
||||
// Message Support
|
||||
//
|
||||
const Receiver::HandlerEntry
|
||||
Player::MessageHandlerEntries[]=
|
||||
{
|
||||
MESSAGE_ENTRY(Player, DropZoneReply),
|
||||
MESSAGE_ENTRY(Player, Score),
|
||||
MESSAGE_ENTRY(Player, VehicleDead),
|
||||
MESSAGE_ENTRY(Player, MissionStarting),
|
||||
MESSAGE_ENTRY(Player, MissionEnding)
|
||||
};
|
||||
|
||||
Receiver::MessageHandlerSet& Player::GetMessageHandlers()
|
||||
{
|
||||
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(Player::MessageHandlerEntries), Player::MessageHandlerEntries, Entity::GetMessageHandlers());
|
||||
return messageHandlers;
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::MissionStartingMessageHandler(Message *)
|
||||
{
|
||||
Check(this);
|
||||
Tell("Player::MissionStartingMessageHandler - starting fade in\n");
|
||||
fadeTimeRemaining = 3.0f;
|
||||
SetSimulationState(MissionStartingState);
|
||||
Check_Fpu();
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::MissionEndingMessageHandler(Message *)
|
||||
{
|
||||
Check(this);
|
||||
fadeTimeRemaining = 3.0f;
|
||||
ForceUpdate();
|
||||
SetSimulationState(MissionEndingState);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
Enumeration
|
||||
Player::GetAudioRepresentation(
|
||||
#if DEBUG_LEVEL>0
|
||||
Entity *linked_entity
|
||||
#else
|
||||
Entity *
|
||||
#endif
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Check(linked_entity);
|
||||
Check_Fpu();
|
||||
|
||||
return
|
||||
(GetInstance() == MasterInstance) ?
|
||||
InternalAudioRepresentation : ExternalAudioRepresentation;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::ScoreMessageHandler(ScoreMessage *message)
|
||||
{
|
||||
Check(this);
|
||||
Check(message);
|
||||
|
||||
currentScore += message->scoreAward;
|
||||
|
||||
//
|
||||
// HACK - ECH 7/6/95 - Allow the player vehicle to respond to score
|
||||
// messages, allows attribute system to be used for scoring
|
||||
//
|
||||
Check(playerVehicle);
|
||||
playerVehicle->RespondToScoreMessage(message);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::DropZoneReplyMessageHandler(
|
||||
DropZone::ReplyMessage * //message
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Fail("Drop zone reply should not be handled by base player class!\n");
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::CreatePlayerVehicle(Origin camera_origin)
|
||||
{
|
||||
Check(this);
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Create any MUNGA level vehicles
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
if (IsCameraShipPlayer())
|
||||
{
|
||||
Check(application);
|
||||
ResourceFile *resources = application->GetResourceFile();
|
||||
Check(resources);
|
||||
Check_Pointer(playerMission->GetGameModel());
|
||||
|
||||
HostManager *host_manager = application->GetHostManager();
|
||||
Check(host_manager);
|
||||
ResourceDescription *camera_res =
|
||||
resources->FindResourceDescription(
|
||||
playerMission->GetGameModel(),
|
||||
ResourceDescription::ModelListResourceType
|
||||
);
|
||||
Check(camera_res);
|
||||
CameraShip::MakeMessage
|
||||
create_camera(
|
||||
CameraShip::MakeMessageID,
|
||||
sizeof(CameraShip::MakeMessage),
|
||||
EntityID(host_manager->GetLocalHostID()),
|
||||
CameraShipClassID,
|
||||
EntityID::Null,
|
||||
camera_res->resourceID,
|
||||
CameraShip::DefaultFlags,
|
||||
camera_origin,
|
||||
Motion::Identity,
|
||||
Motion::Identity
|
||||
);
|
||||
CameraShip *camera =
|
||||
(CameraShip*)application->MakeAndLinkViewpointEntity(&create_camera);
|
||||
Register_Object(camera);
|
||||
playerVehicle = camera;
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::VehicleDeadMessageHandler(VehicleDeadMessage *message)
|
||||
{
|
||||
Check(this);
|
||||
Check(message);
|
||||
Check(application);
|
||||
|
||||
|
||||
//DEBUG_STREAM << "VEHICLEDEAD : " << endl << std::flush;
|
||||
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
// If we have let the player fry, send a message to the dropzone to give us
|
||||
// a new location to return to
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
if (
|
||||
message->deathCount == deathCount
|
||||
&& GetSimulationState() != DropZoneAcquiredState)
|
||||
{
|
||||
DropZone *dropzone;
|
||||
|
||||
|
||||
|
||||
//
|
||||
//---------------------------------------------------------------------
|
||||
// If no dropzone has been assigned to this death, find the closest one
|
||||
//---------------------------------------------------------------------
|
||||
//
|
||||
if (message->dropZoneID == EntityID::Null)
|
||||
{
|
||||
|
||||
//DEBUG_STREAM << " Find Closest" << endl << std::flush;
|
||||
|
||||
EntityManager *entity_manager = application->GetEntityManager();
|
||||
Check(entity_manager);
|
||||
EntityGroup *dropzones = entity_manager->FindGroup("DropZones");
|
||||
Check(dropzones);
|
||||
ChainIteratorOf<Node*> iterator(dropzones->groupMembers);
|
||||
|
||||
DropZone *closest_dropzone;
|
||||
Vector3D range;
|
||||
Scalar closest_range;
|
||||
|
||||
while ((dropzone = (DropZone*)iterator.ReadAndNext()) != NULL)
|
||||
{
|
||||
Check(dropzone);
|
||||
|
||||
if (!strnicmp(dropzone->GetDropZoneName(), "win", 3))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
closest_dropzone = dropzone;
|
||||
range.Subtract(
|
||||
localOrigin.linearPosition,
|
||||
dropzone->localOrigin.linearPosition
|
||||
);
|
||||
closest_range = range.LengthSquared();
|
||||
break;
|
||||
}
|
||||
|
||||
while ((dropzone = (DropZone*)iterator.ReadAndNext()) != NULL)
|
||||
{
|
||||
Check(dropzone);
|
||||
if (!strnicmp(dropzone->GetDropZoneName(), "win", 3))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
range.Subtract(
|
||||
localOrigin.linearPosition,
|
||||
dropzone->localOrigin.linearPosition
|
||||
);
|
||||
Scalar len = range.LengthSquared();
|
||||
if (len < closest_range)
|
||||
{
|
||||
closest_dropzone = dropzone;
|
||||
closest_range = len;
|
||||
}
|
||||
}
|
||||
message->dropZoneID = closest_dropzone->GetEntityID();
|
||||
dropzone = closest_dropzone;
|
||||
}
|
||||
|
||||
//
|
||||
//----------------------------------------------
|
||||
// Otherwise, get this one from the host manager
|
||||
//----------------------------------------------
|
||||
//
|
||||
else
|
||||
{
|
||||
//DEBUG_STREAM << " Use Assigned" << endl << std::flush;
|
||||
|
||||
HostManager *host = application->GetHostManager();
|
||||
Check(host);
|
||||
dropzone =
|
||||
Cast_Object(
|
||||
DropZone*,
|
||||
host->GetEntityPointer(message->dropZoneID)
|
||||
);
|
||||
}
|
||||
|
||||
Check(dropzone);
|
||||
DropZone::AssignDropZoneMessage
|
||||
request(
|
||||
DropZone::AssignDropZoneMessageID,
|
||||
sizeof(DropZone::AssignDropZoneMessage),
|
||||
GetEntityID(),
|
||||
DropZoneReplyMessageID,
|
||||
deathCount
|
||||
);
|
||||
dropzone->Dispatch(&request);
|
||||
Time when = Now();
|
||||
when += 2.0f;
|
||||
application->Post(HighEventPriority, this, message, when);
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::HuntForDropZone(Scalar)
|
||||
{
|
||||
Check(this);
|
||||
Check(playerMission);
|
||||
|
||||
//
|
||||
//----------------------------------------------------------------------
|
||||
// See if our designated drop zone is around yet. If the dropzone group
|
||||
// does not exist, then no dropzones at all are available
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
Check(application);
|
||||
EntityManager *entity_manager = application->GetEntityManager();
|
||||
Check(entity_manager);
|
||||
EntityGroup *dropzones = entity_manager->FindGroup("DropZones");
|
||||
if (!dropzones)
|
||||
{
|
||||
Check_Fpu();
|
||||
return;
|
||||
}
|
||||
|
||||
Check(dropzones);
|
||||
ChainIteratorOf<Node*> iterator(dropzones->groupMembers);
|
||||
DropZone *dropzone;
|
||||
const char* zone_name = playerMission->GetDropZoneName();
|
||||
while ((dropzone = (DropZone*)iterator.ReadAndNext()) != NULL)
|
||||
{
|
||||
if (!strcmp(dropzone->GetDropZoneName(), zone_name))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//------------------------------------------------------------------------
|
||||
// If it is, reset our performance, and send a message to the drop zone to
|
||||
// give us a location
|
||||
//------------------------------------------------------------------------
|
||||
//
|
||||
if (dropzone)
|
||||
{
|
||||
|
||||
//DEBUG_STREAM << "FOUND DROPZONE" << endl << std::flush;
|
||||
|
||||
Check(dropzone);
|
||||
SetPerformance(&Player::DoNothingOnce);
|
||||
VehicleDeadMessage
|
||||
dead(
|
||||
VehicleDeadMessageID,
|
||||
sizeof(VehicleDeadMessage),
|
||||
dropzone->GetEntityID()
|
||||
);
|
||||
dead.deathCount = -2;
|
||||
|
||||
DropZone::AssignDropZoneMessage
|
||||
request(
|
||||
DropZone::AssignDropZoneMessageID,
|
||||
sizeof(DropZone::AssignDropZoneMessage),
|
||||
GetEntityID(),
|
||||
DropZoneReplyMessageID,
|
||||
deathCount
|
||||
);
|
||||
dropzone->Dispatch(&request);
|
||||
Time when = Now();
|
||||
when += 2.0f;
|
||||
application->Post(HighEventPriority, this, &dead, when);
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::ManageApplicationStatus(Scalar time_slice)
|
||||
{
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
// If we are in the game start/stop phase, count the timer down and send the
|
||||
// appropriate message to the application when done
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
Check(this);
|
||||
Check(application);
|
||||
switch (GetSimulationState())
|
||||
{
|
||||
case MissionStartingState:
|
||||
Verify(fadeTimeRemaining <= 3.0f);
|
||||
fadeTimeRemaining -= time_slice;
|
||||
if (fadeTimeRemaining <= 0.0f)
|
||||
{
|
||||
SetSimulationState(VehicleTranslocatedState);
|
||||
Check(application);
|
||||
switch (application->GetApplicationState())
|
||||
{
|
||||
case Application::LaunchingMission:
|
||||
{
|
||||
Application::RunMissionMessage message;
|
||||
application->Dispatch(&message);
|
||||
}
|
||||
break;
|
||||
case Application::ResumingMission:
|
||||
{
|
||||
Application::ResumeMissionMessage message;
|
||||
application->Dispatch(&message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MissionEndingState:
|
||||
fadeTimeRemaining -= time_slice;
|
||||
if (fadeTimeRemaining <= 0.0f)
|
||||
{
|
||||
Check(application);
|
||||
switch (application->GetApplicationState())
|
||||
{
|
||||
case Application::EndingMission:
|
||||
{
|
||||
// Fill in appropriate exit code here. GAH
|
||||
Application::StopMissionMessage message(NullExitCodeID);
|
||||
application->Dispatch(&message);
|
||||
}
|
||||
break;
|
||||
case Application::AbortingMission:
|
||||
{
|
||||
// Fill in appropriate exit code here. GAH
|
||||
Application::AbortMissionMessage message(NullExitCodeID);
|
||||
application->Dispatch(&message);
|
||||
}
|
||||
break;
|
||||
case Application::SuspendingMission:
|
||||
{
|
||||
Application::SuspendMissionMessage message;
|
||||
application->Dispatch(&message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::CameraShipSimulation(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
CalcRanking();
|
||||
|
||||
ManageApplicationStatus(time_slice);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::PlayerSimulation(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
//-------------------------------
|
||||
// Calculate the player's ranking
|
||||
//-------------------------------
|
||||
CalcRanking();
|
||||
|
||||
//----------------------------------
|
||||
// ...manage the application status?
|
||||
//----------------------------------
|
||||
ManageApplicationStatus(time_slice);
|
||||
|
||||
//-------------------------------
|
||||
// Copy vehicle position
|
||||
//-------------------------------
|
||||
if (playerVehicle)
|
||||
{
|
||||
Check(playerVehicle);
|
||||
localOrigin = playerVehicle->localOrigin;
|
||||
localToWorld = playerVehicle->localToWorld;
|
||||
}
|
||||
|
||||
//---------------------------------
|
||||
// Service the status message queue
|
||||
//---------------------------------
|
||||
StatusMessageUpdate(time_slice);
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::GoToVehicle(Scalar)
|
||||
{
|
||||
Check(this);
|
||||
if (playerVehicle)
|
||||
{
|
||||
Check(playerVehicle);
|
||||
localOrigin = playerVehicle->localOrigin;
|
||||
localToWorld = playerVehicle->localToWorld;
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
int
|
||||
Player::CalcRanking()
|
||||
{
|
||||
|
||||
//
|
||||
//---------------------------------------
|
||||
// Get all players from the entity group
|
||||
//---------------------------------------
|
||||
//
|
||||
EntityGroup *player_group =
|
||||
application->GetEntityManager()->FindGroup("Players");
|
||||
if(player_group)
|
||||
{
|
||||
int num_players = 0;
|
||||
Player *active_player;
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
// Iterate through the players add to the sorted chain
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
ChainIteratorOf<Node*> iterator(player_group->groupMembers);
|
||||
VChainOf<Player*, Scalar> player_rank(NULL, False);
|
||||
while ((active_player = (Player*) iterator.ReadAndNext()) != NULL)
|
||||
{
|
||||
if(active_player->IsScoringPlayer())
|
||||
{
|
||||
player_rank.AddValue(active_player, active_player->currentScore);
|
||||
++num_players;
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Reset Player Highlighted
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
active_player->playerHighlighted = False;
|
||||
}
|
||||
else
|
||||
{
|
||||
active_player->playerRanking = -1;
|
||||
}
|
||||
|
||||
}
|
||||
--num_players;
|
||||
//
|
||||
//------------------------------------------------
|
||||
// Iterate through the chain assigning the rank
|
||||
//------------------------------------------------
|
||||
//
|
||||
VChainIteratorOf<Player*, Scalar> rank_iterator(player_rank);
|
||||
|
||||
|
||||
while ((active_player = rank_iterator.ReadAndNext()) != NULL)
|
||||
{
|
||||
active_player->playerRanking = num_players;
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// If player in first place turn on player highlighted
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
if (!active_player->playerRanking)
|
||||
{
|
||||
active_player->playerHighlighted = True;
|
||||
}
|
||||
--num_players;
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
//#############################################################################
|
||||
// Update Support
|
||||
//
|
||||
void
|
||||
Player::ReadUpdateRecord(Simulation::UpdateRecord *message)
|
||||
{
|
||||
Check(this);
|
||||
Check_Pointer(message);
|
||||
Entity::ReadUpdateRecord(message);
|
||||
UpdateRecord* record = (UpdateRecord*) message;
|
||||
|
||||
currentScore = record->currentScore;
|
||||
dropZoneLocation = record->dropZoneLocation;
|
||||
Check_Fpu();
|
||||
}
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::WriteUpdateRecord(
|
||||
Simulation::UpdateRecord *record,
|
||||
int update_model
|
||||
)
|
||||
{
|
||||
Check(this);
|
||||
Check_Pointer(record);
|
||||
|
||||
Entity::WriteUpdateRecord(record, update_model);
|
||||
|
||||
UpdateRecord* update = (UpdateRecord*) record;
|
||||
|
||||
update->recordLength = sizeof(*update);
|
||||
update->currentScore = currentScore;
|
||||
update->dropZoneLocation = dropZoneLocation;
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
|
||||
//#############################################################################
|
||||
// Construction and Destruction
|
||||
//
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
Player::Player(
|
||||
Player::MakeMessage *creation_message,
|
||||
Player::SharedData &virtual_data
|
||||
):
|
||||
Entity(creation_message, virtual_data),
|
||||
statusMessageQueue(NULL)
|
||||
{
|
||||
Check_Pointer(this);
|
||||
Check_Pointer(creation_message);
|
||||
|
||||
scenarioRole = NULL;
|
||||
//---------------------------------
|
||||
// Initialize data
|
||||
//---------------------------------
|
||||
statusMessagePointer = NULL;
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Initialize the playerMission
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
playerMission = application->GetCurrentMission();
|
||||
Check(playerMission);
|
||||
//
|
||||
//---------------------------------
|
||||
// Set the resource ID for a player
|
||||
//---------------------------------
|
||||
//
|
||||
Check(application);
|
||||
Check(application->GetResourceFile());
|
||||
ResourceDescription *resource_description =
|
||||
application->GetResourceFile()->FindResourceDescription(
|
||||
"player",
|
||||
ResourceDescription::ModelListResourceType
|
||||
);
|
||||
if (resource_description != NULL)
|
||||
{
|
||||
Verify(resourceID == ResourceDescription::NullResourceID);
|
||||
resourceID = resource_description->resourceID;
|
||||
}
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Get the host type associated with the entity ID
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
HostType host_type;
|
||||
|
||||
HostID host_ID = entityID.GetHostID();
|
||||
Check(application);
|
||||
HostManager *host_mgr = application->GetHostManager();
|
||||
Check(host_mgr);
|
||||
Host *local_host = host_mgr->GetLocalHost();
|
||||
Check(local_host);
|
||||
if (host_ID == local_host->GetHostID())
|
||||
{
|
||||
host_type = local_host->GetHostType();
|
||||
}
|
||||
else
|
||||
{
|
||||
HostManager::RemoteHostIterator iterator(host_mgr);
|
||||
local_host = iterator.Find(host_ID);
|
||||
Check(local_host);
|
||||
host_type = local_host->GetHostType();
|
||||
}
|
||||
|
||||
if (IsCameraShipPlayer())
|
||||
{
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Make A CameraPlayers Entity Group
|
||||
// since cameras are not involved in
|
||||
// standard game play they need a
|
||||
// separate Entity Group!
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
playerBitmapIndex = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Make A Regular Players Entity Group
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
Check(application);
|
||||
EntityManager *entity_manager = application->GetEntityManager();
|
||||
Check(entity_manager);
|
||||
EntityGroup *players = entity_manager->UseGroup("Players");
|
||||
Check(players);
|
||||
players->Add(this);
|
||||
playerBitmapIndex = creation_message->playerBitmapIndex;
|
||||
}
|
||||
|
||||
SetValidFlag();
|
||||
playerVehicle = NULL;
|
||||
if (GetInstance() != ReplicantInstance)
|
||||
{
|
||||
Check(application->GetCurrentMission());
|
||||
playerMission = application->GetCurrentMission();
|
||||
SetPerformance(&Player::HuntForDropZone);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPerformance(&Player::GoToVehicle);
|
||||
playerMission = NULL;
|
||||
}
|
||||
|
||||
playerRanking = 0;
|
||||
deathCount = -2;
|
||||
|
||||
//-------------------------------------
|
||||
// Clear score
|
||||
//-------------------------------------
|
||||
currentScore = 0.0f;
|
||||
//-------------------------------------
|
||||
// Create intercom object
|
||||
//-------------------------------------
|
||||
Check(application);
|
||||
IcomManager
|
||||
*intercom_manager = application->GetIntercomManager();
|
||||
Check(intercom_manager);
|
||||
|
||||
intercomPointer = intercom_manager->MakeIcom(GetOwnerID());
|
||||
Check(intercomPointer);
|
||||
// registered in MakeIcom()
|
||||
|
||||
playerHighlighted = False;
|
||||
fadeTimeRemaining = 0.0f;
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
Player* Player::Make(Player::MakeMessage *creation_message)
|
||||
{
|
||||
Check_Fpu();
|
||||
return new Player(creation_message, DefaultData);
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
Player::~Player()
|
||||
{
|
||||
Check(this);
|
||||
|
||||
//--------------------------------------------
|
||||
// Delete intercom object
|
||||
//--------------------------------------------
|
||||
Check(intercomPointer);
|
||||
Unregister_Object(intercomPointer);
|
||||
delete intercomPointer;
|
||||
intercomPointer = NULL;
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::AddStatusMessage(StatusMessage *status_message)
|
||||
{
|
||||
Check(this);
|
||||
Check_Pointer(status_message);
|
||||
statusMessageQueue.Add(status_message);
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
void
|
||||
Player::StatusMessageUpdate(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
//-------------------------------------
|
||||
// We handle this queue as a STACK.
|
||||
// The most recent message is presented
|
||||
// first, causing older messages to
|
||||
// wait "below" them on the stack.
|
||||
// This allows a default message to
|
||||
// sit at the "bottom" of the stack
|
||||
// and allow others to override it
|
||||
// temporarily.
|
||||
//-------------------------------------
|
||||
//
|
||||
ChainIteratorOf<StatusMessage*>
|
||||
iterator(statusMessageQueue);
|
||||
//----------------------------------------
|
||||
// ...we really want iterator.GetLast(),
|
||||
// but since it doesn't exist we'll
|
||||
// do it directly.
|
||||
//----------------------------------------
|
||||
statusMessagePointer = NULL;
|
||||
{
|
||||
StatusMessage
|
||||
*the_message;
|
||||
|
||||
while ((the_message = iterator.ReadAndNext()) != NULL)
|
||||
{
|
||||
statusMessagePointer = the_message;
|
||||
}
|
||||
}
|
||||
//----------------------------------------
|
||||
// If a message exists, decrement the
|
||||
// timer: discard it if timed out.
|
||||
//----------------------------------------
|
||||
if (statusMessagePointer != NULL)
|
||||
{
|
||||
Check_Pointer(statusMessagePointer);
|
||||
|
||||
statusMessagePointer->displayTime -= time_slice;
|
||||
if (statusMessagePointer->displayTime <= (Scalar) 0.0f)
|
||||
{
|
||||
Unregister_Object(statusMessagePointer);
|
||||
delete statusMessagePointer;
|
||||
statusMessagePointer = NULL;
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Logical
|
||||
Player::TestInstance() const
|
||||
{
|
||||
return IsDerivedFrom(*GetClassDerivations());
|
||||
}
|
||||
Reference in New Issue
Block a user