Files
RP412/RP/RPPLAYER.cpp
T
CydandClaude Fable 5 9389ec2003 Winners Circle: the award platform at the end of a race
The pod hall stood the finishers on a numbered platform when the race
ended. All of it shipped in this repo and none of it ever ran here: the
stand geometry, the eight ranked dropzones win1-win8 in every one of the
11 maps, and the sequence that places the racers on them. The sequence
lived on RPL4PlaybackApplication - the mission-review build - behind a
spool file, so the app the pods actually race has never called it.

RPL4Application now has its own StopMission handler that ranks the
finishers, drops each onto their spot, freezes them, re-sorts the name
plates into finishing order and frames a camera on the stand. It fires
once: StopMission arrives twice, from the console at the buzzer and again
from the player when the ending fade expires, and only the first is the
end of the race.

Ranking works in football as well as a race. CalcFootballRanking ranks
only the RunnerPlayers group, which would have placed the runners and
stopped - but nothing calls it. What runs is Player::CalcRanking, every
frame, over every scoring player by score.

Three pieces of the original had been stubbed out in the D3D9 port and
are restored:

  SetViewAngle was an empty function, so the 45 degrees the sequence asks
  for did nothing. It now rebuilds the projection the way DPLReadINIPage
  does and pushes it, and sets viewRatio, which nothing had written since
  the DPL body was commented out.

  winnersCircleFogStyle was an empty case. The stand sits far off the
  track in open ground where the track's own fog leaves it dark; this is
  the blue-violet the original used, with the fog pushed back to 100/1050
  and the clip plane pulled to 1100.

  The end-of-mission fade had to be told to stand down. It multiplies the
  fog colour and both fog distances toward zero every frame - correct when
  a race just ends, fatal to anything shown afterwards. That fade is what
  made the podium a black screen, and it took a while to find because
  every frame was being built and presented correctly the whole time.

The presentation camera overrides D3DTS_VIEW between the eye renderable
writing it and ExecuteImplementation reading it back for the draw calls,
so no CameraShip is needed. It builds with LookAt LH, not RH: the
projection is LH, and RH aims the camera the opposite way - ask to look
down at the stand and you get the sky behind you. The engine's own eye
renderable is right to use RH, because its forward and up come out of the
entity matrix already in that convention.

The mission is held open 11 seconds rather than 3. That fade timer is the
only thing keeping the simulation and the renderer alive once the race is
over, and it has no upper bound on the ending path.

Switches, all off-by-default behaviour aside: RP412PODIUM=0 skips it,
RP412PODIUMCAM=0 keeps the cockpit view, RP412PODIUMSTANDOFF/HEIGHT/AIM
frame the shot, RP412MISSIONSECONDS overrides the menu game length (the
shortest it offers is 3:00, a long wait when what you are testing is the
buzzer), and RP412RENDERDIAG=1 reports what a frame is made of.

Verified end to end on Wiseguy's Wake: the stand, its tiers, the blue 2
and 3, the red 4 through 8 and all eight name bays, held steady for the
full 11 seconds and then handing off to the results screen.

Known gaps: the name plates are blank, because the player1-8 textures are
runtime name bitmaps that do not resolve as files in this port, and your
own vehicle has no exterior model - you see the others, not yourself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 09:46:38 -05:00

1419 lines
35 KiB
C++

#include "rp.h"
#pragma hdrstop
#include "rpplayer.h"
#include "..\munga\dropzone.h"
#include "..\munga\mission.h"
#include "scorzone.h"
#include "rpcnsl.h"
#include "..\munga\app.h"
#include "vtv.h"
#include "..\munga\hostmgr.h"
#include "..\munga\nttmgr.h"
//
// How long the mission stays up after the buzzer, for the Winners Circle.
// This timer is the only thing holding the simulation and the renderer
// open once the race is over - when it runs out the player dispatches the
// StopMission that retires the application. The stock 3 seconds is the
// fade; the rest is the podium. Kept under the +30s LightsOut post so that
// never fires while the stand is up.
//
const Scalar winnersCircleHoldTime = 11.0f;
//#############################################################################
//######################## RPPlayer__StatusMessage ######################
//#############################################################################
MemoryBlock *RPPlayer__StatusMessage::GetAllocatedMemory()
{
static MemoryBlock allocatedMemory(sizeof(RPPlayer__StatusMessage), RPPlayer__StatusMessage::numberOfAllocatedBlocks, RPPlayer__StatusMessage::numberOfAllocatedBlocks);
return &allocatedMemory;
}
//#############################################################################
//############################### RPPlayer ##############################
//#############################################################################
//#############################################################################
// Message Support
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// General PlayerType Message Handlers
//
const Receiver::HandlerEntry
RPPlayer::MessageHandlerEntries[]=
{
MESSAGE_ENTRY(RPPlayer, TakeDamage),
MESSAGE_ENTRY(RPPlayer, VehicleDead),
MESSAGE_ENTRY(RPPlayer, Score),
MESSAGE_ENTRY(RPPlayer, DropZoneReply),
MESSAGE_ENTRY(RPPlayer, ScoreZoneReply),
MESSAGE_ENTRY(RPPlayer, MissionStarting),
MESSAGE_ENTRY(RPPlayer, MissionEnding)
};
Receiver::MessageHandlerSet& RPPlayer::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(RPPlayer::MessageHandlerEntries), RPPlayer::MessageHandlerEntries, Player::GetMessageHandlers());
return messageHandlers;
}
//#############################################################################
// Shared Data Support
//
Derivation* RPPlayer::GetClassDerivations()
{ static Derivation classDerivations(Player::GetClassDerivations(), "RPPlayer");
return &classDerivations;
}
RPPlayer::SharedData
RPPlayer::DefaultData(
RPPlayer::GetClassDerivations(),
RPPlayer::GetMessageHandlers(),
RPPlayer::GetAttributeIndex(),
RPPlayer::StateCount,
(RPPlayer::MakeHandler)RPPlayer::Make
);
//#############################################################################
// Attribute Support
//
const RPPlayer::IndexEntry
RPPlayer::AttributePointers[]=
{
ATTRIBUTE_ENTRY(RPPlayer, GoalEntity, goalEntity)
};
RPPlayer::AttributeIndexSet& RPPlayer::GetAttributeIndex()
{
static RPPlayer::AttributeIndexSet attributeIndex(ELEMENTS(RPPlayer::AttributePointers),
RPPlayer::AttributePointers,
Player::GetAttributeIndex()
);
return attributeIndex;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
static Origin first_drop;
Entity *first_goal;
void
RPPlayer::MissionStartingMessageHandler(Message *message)
{
Check(this);
Check(message);
Player::MissionStartingMessageHandler(message);
Check(application);
if (application->GetApplicationState() == Application::ResumingMission)
{
Check(playerVehicle);
if (playerVehicle->IsDerivedFrom(*VTV::GetClassDerivations()))
{
currentScore = 1000.0f;
}
}
else
{
first_goal = goalEntity;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::MissionEndingMessageHandler(Message *message)
{
Player::MissionEndingMessageHandler(message);
Check(application);
// feed the in-process console the same final score a real console
// gets (no-op when none is registered)
if (gConsoleScoreSink != NULL &&
application->GetApplicationState() == Application::EndingMission)
{
(*gConsoleScoreSink)((int) ownerID, (int) currentScore);
}
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
Host *console_host = host_manager->GetConsoleHost();
if (console_host)
{
Check(console_host);
if (application->GetApplicationState() == Application::EndingMission)
{
ConsoleApplicationEndMissionMessage
score_message(ownerID, (int)currentScore);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&score_message
);
}
else
{
ConsoleApplicationAbortMissionMessage
score_message(ownerID);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&score_message
);
}
}
lastConsoleUpdate -= 15.0f;
Check(application);
if (application->GetApplicationState() == Application::SuspendingMission)
{
Check(playerVehicle);
if (playerVehicle->IsDerivedFrom(*VTV::GetClassDerivations()))
{
if (first_goal != goalEntity)
{
ScoreZone::ProcessScoreZoneRequestMessage
message(
ScoreZone::ProcessScoreZoneRequestMessageID,
sizeof(ScoreZone::ProcessScoreZoneRequestMessage),
GetEntityID(),
ScoreZoneReplyMessageID,
goalEntity->localOrigin.linearPosition
);
goalEntity->Dispatch(&message);
}
VTV *vtv = (VTV*) playerVehicle;
Check(vtv);
vtv->Reset(first_drop, True);
ForceUpdate();
}
}
//
//---------------------------------------------------------------------
// Hold the mission open for the Winners Circle.
//
// The base handler sets a 3 second fade, and when it runs out the
// player dispatches the StopMission that retires the application and
// takes the renderer with it. That is the only thing keeping the sim
// and the renderer alive after the race, so the podium gets exactly as
// long as this timer says. Sim and render both keep running throughout
// EndingMission - neither has a case for it that bails out.
//
// Only on a clean finish: an abort should still leave promptly.
//
if (application->GetApplicationState() == Application::EndingMission)
{
fadeTimeRemaining = winnersCircleHoldTime;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RPPlayer::CreatePlayerVehicle(Origin vtv_location)
{
Check(this);
Check(&vtv_location);
Check(application);
ResourceFile *resources = application->GetResourceFile();
Check(resources);
Check_Pointer(playerMission->GetGameModel());
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
//
// Create MUNGA level vehicles
//
Player::CreatePlayerVehicle(vtv_location);
//
//-----------------------------------------------------------------------
// If the player has made no vehicle yet, make him a new one and link the
// player to it
//-----------------------------------------------------------------------
//
if (!playerVehicle)
{
ResourceDescription *vtv_res =
resources->FindResourceDescription(
playerMission->GetGameModel(),
ResourceDescription::ModelListResourceType
);
Check(vtv_res);
vtv_res->Lock();
VTV::MakeMessage
create_player(
VTV::MakeMessageID,
sizeof(VTV::MakeMessage),
EntityID(host_manager->GetLocalHostID()),
VTV::VTVClassID,
EntityID::Null,
vtv_res->resourceID,
VTV::DefaultFlags|VTV::InitialStasisFlag,
vtv_location,
Motion::Identity,
Motion::Identity,
playerMission->GetBadgeName(),
playerMission->GetColorName()
);
vtv_res->Unlock();
playerVehicle = application->MakeAndLinkViewpointEntity(&create_player);
Register_Object(playerVehicle);
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RPPlayer::InitializePlayerLink()
{
Check(this);
PlayerLinkMessage player_link_message (
PlayerLinkMessageID,
sizeof(PlayerLinkMessage),
this->GetEntityID()
);
Check(playerVehicle);
playerVehicle->Dispatch(&player_link_message);
playerVehicle->DispatchToReplicants(&player_link_message);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RPPlayer::SetGoalToFirstScoreZone()
{
//
//----------------------------------------------------------
// Change the goalEntity to the first sequential scorezone
//----------------------------------------------------------
//
EntityGroup *score_zone_group =
application->GetEntityManager()->FindGroup("ActiveScoreZones");
if(score_zone_group)
{
ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
ScoreZone *score_zone;
while ((score_zone = (ScoreZone*)iterator.ReadAndNext()) != NULL)
{
if (
(
(score_zone->scoreZoneType == LocalSequential) ||
(score_zone->scoreZoneType == GlobalSequential)
) &&
score_zone->sequenceNumber == 1
)
{
goalEntity = score_zone;
break;
}
}
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RPPlayer::PointVTVTowardGoal()
{
Check(this);
if (!goalEntity)
{
return;
}
Vector3D to_goal;
to_goal.Subtract(
goalEntity->localOrigin.linearPosition,
playerVehicle->localOrigin.linearPosition
);
UnitVector current_heading;
playerVehicle->localToWorld.GetFromAxis(Z_Axis, &current_heading);
Scalar length_to_goal;
length_to_goal = to_goal.LengthSquared();
if (length_to_goal > SMALL)
{
Scalar dot_prod;
dot_prod = (to_goal * current_heading) / Sqrt(length_to_goal);
if (dot_prod >= 0.0f)
{
Quaternion turn_around;
Quaternion y_roll(0.0f,1.0f,0.0f,0.0);
Check(playerVehicle);
turn_around.Multiply(
playerVehicle->localOrigin.angularPosition,
y_roll
);
playerVehicle->localOrigin.angularPosition = turn_around;
playerVehicle->localToWorld = playerVehicle->localOrigin;
playerVehicle->ForceUpdate();
}
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void RPPlayer::ResetAfterDeath(DropZone::ReplyMessage *message)
{
Check(this);
Check(message);
Check(application);
message->deathCount = ++deathCount;
ForceUpdate();
SetSimulationState(DropZoneAcquiredState);
dropZoneLocation = message->dropZoneLocation;
Time when = Now();
when += 1.0f;
application->Post(HighEventPriority, this, message, when);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::DropZoneReplyMessageHandler(
DropZone::ReplyMessage *message
)
{
Check(this);
Check(message);
//
//-----------------------------------------------------------------------
// If the player has made no vehicle yet, make him a new one and link the
// player to it
//-----------------------------------------------------------------------
//
if (!playerVehicle)
{
first_drop = message->dropZoneLocation;
CreatePlayerVehicle(message->dropZoneLocation);
InitializePlayerLink();
SetGoalToFirstScoreZone();
switch(playerVehicle->GetClassID())
{
case VTVClassID:
SetPerformance(&RPPlayer::PlayerSimulation);
break;
case CameraShipClassID:
SetPerformance(&RPPlayer::CameraShipSimulation);
playerRanking = -1;
break;
}
AlwaysExecute();
deathCount = 0;
}
//
//-------------------------------------------------------------------------
// Otherwise, just set the new position of the VTV and turn off the burning
// stuff.
//-------------------------------------------------------------------------
//
else if (deathCount == message->deathCount)
{
if (GetSimulationState() == MissionEndingState)
{
Check_Fpu();
return;
}
if (GetSimulationState() != DropZoneAcquiredState)
{
ResetAfterDeath(message);
return;
}
}
//
//---------------------------------
// Make sure we ignore old messages
//---------------------------------
//
else
{
Check_Fpu();
return;
}
//
//-----------------------------------
// Delete All offensivePlayers
//-----------------------------------
//
DeleteAllOffensivePlayers();
ForceUpdate();
SetSimulationState(VehicleTranslocatedState);
if (playerVehicle->GetClassID() == VTVClassID)
{
VTV *vtv = (VTV*)playerVehicle;
Check(vtv);
vtv->Reset(message->dropZoneLocation, VTV::RegularReset);
}
//
//---------------------------------------------------
// Always Point vtv in the direction of the scorezone
//---------------------------------------------------
//
PointVTVTowardGoal();
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// GetPlayer
//
RPPlayer*
RPPlayer::GetPlayer(EntityID playerID)
{
Check(this);
Check(&playerID);
//
//---------------------------------------------------
// Get the pointer to the RPPlayer from the playerID
//---------------------------------------------------
//
Check(application);
HostManager *host = application->GetHostManager();
Check(host);
RPPlayer *rp_player = Cast_Object(
RPPlayer*,
host->GetEntityPointer(playerID)
);
Check(rp_player);
Check_Fpu();
return rp_player;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::DeleteAllOffensivePlayers()
{
VChainIteratorOf<OffensivePlayer*, Enumeration>
iterator(offensivePlayerList);
OffensivePlayer
*current_player;
while ((current_player = iterator.GetCurrent()) != NULL)
{
Unregister_Object(current_player);
delete current_player;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::CleanUpOffensivePlayers()
{
VChainIteratorOf<OffensivePlayer*, Enumeration>
iterator(offensivePlayerList);
OffensivePlayer
*current_player;
while ((current_player = iterator.GetCurrent()) != NULL)
{
Check(current_player);
if(Now() - current_player->timeStamp >= 2.0f)
{
OffensivePlayer *stale_player = iterator.GetCurrent();
Unregister_Object(stale_player);
delete stale_player;
}
else
{
iterator.Next();
}
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::TakeDamageMessageHandler (TakeDamageMessage *message)
{
Check(this);
Check(message);
//
//-------------------------------------------------------
// Scale the damage amount to point loss on this vehicle
//-------------------------------------------------------
//
VTV*
our_vtv = (VTV*)playerVehicle;
Check(our_vtv);
if (GetSimulationState() != MissionEndingState)
{
Scalar score_loss = message->damageData.damageAmount;
if (message->damageData.damageType == Damage::CollisionDamageType)
{
score_loss *= our_vtv->deathConstant;
}
ScoreMessage score_message(
RPPlayer::ScoreMessageID,
sizeof(RPPlayer::ScoreMessageID),
DamageScoreLossPointType,
-score_loss,
GetEntityID()
);
Dispatch(&score_message);
//
//-----------------------------------------------------------
// Clean up the list, and look for a player to give points to
//-----------------------------------------------------------
//
CleanUpOffensivePlayers();
VChainIteratorOf<OffensivePlayer*, Enumeration>
iterator(offensivePlayerList);
if(iterator.GetCurrent())
{
//
//--------------------------------------------------------
// Send a score message to the player at the top
// of the offensivePlayer list the points for this damage
//--------------------------------------------------------
//
OffensivePlayer *top_player = iterator.GetCurrent();
RPPlayer *player_points = top_player->offensivePlayer;
if (
top_player->deathBonus
&& top_player->timeStamp < ((RPPlayer*)our_vtv)->lastPerformance
&& top_player->offensivePlayer != this
)
{
if (top_player->deathBonus < 0.0f)
{
score_loss = -score_loss;
}
ScoreMessage score_message(
RPPlayer::ScoreMessageID,
sizeof(RPPlayer::ScoreMessage),
DamageBonusPointType,
score_loss,
GetEntityID()
);
if (score_loss < 0.0f)
{
score_message.scoreType = DropZoneHitType;
}
player_points->Dispatch(&score_message);
Check(application);
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
Host *console_host = host_manager->GetConsoleHost();
if (console_host)
{
Check(console_host);
ConsolePlayerVTVDamagedMessage
scored_message(
ownerID,
player_points->ownerID,
score_loss,
score_loss
);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&scored_message
);
}
}
}
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::AddOffensivePlayer(OffensivePlayer &offensive_player)
{
Check(this);
Check(&offensive_player);
//
//----------------------------------------------------------------
// Calculate the deathBonus based on playerType and damageType
//----------------------------------------------------------------
//
if(offensive_player.damageType == Damage::CollisionDamageType)
{
offensive_player.deathBonus = 500.0f;
}
else
{
offensive_player.deathBonus = 0.0f;
}
offensivePlayerList.AddValue(
&offensive_player,
offensive_player.damageType
);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::ScoreMessageHandler(ScoreMessage *message)
{
Check(this);
Check(message);
Check(playerVehicle);
if (GetSimulationState() == MissionEndingState)
{
return;
}
//
//-------------------------------------------------------------------------
// If this is a score zone message, tell the console (if there is one) that
// we scored
//-------------------------------------------------------------------------
//
Check(application);
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
switch (message->scoreType)
{
case ScoreZonePointType:
{
Host
*console_host = host_manager->GetConsoleHost();
if (console_host)
{
Check(console_host);
ConsolePlayerVTVScoredMessage
scored_message(ownerID);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&scored_message
);
}
//-------------------------------------
// Inform pilot of score zone entry
//-------------------------------------
RPPlayer
*player =
(RPPlayer*)host_manager->GetEntityPointer(message->pointSender);
Check(player);
if (player->playerType != RunnerPlayerType)
{
RPStatusMessage *message = new RPStatusMessage(NULL, RPStatusMessage::ScoreZoneMessage, 6.0f);
Register_Object(message);
AddStatusMessage((StatusMessage *) message);
}
}
break;
case DeathBonusPointType:
case DropZoneKillType:
{
//-------------------------------------
// Inform pilot of killer's identity
//-------------------------------------
RPPlayer
*killer =
(RPPlayer*)host_manager->GetEntityPointer(message->pointSender);
Check(killer);
RPStatusMessage *message = new RPStatusMessage(killer, RPStatusMessage::DestroyedMessage, 6.0f);
Register_Object(message);
AddStatusMessage((StatusMessage *) message);
}
break;
}
Player::ScoreMessageHandler(message);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::VehicleDeadMessageHandler(VehicleDeadMessage *message)
{
Check(this);
Check(message);
if (GetSimulationState() == MissionEndingState)
{
Check_Fpu();
return;
}
//
//-------------------------------------------------------------------------
// If this is not the initial death notice, or we are still looking for our
// initial dropzone, let the player handler do its thing
//-------------------------------------------------------------------------
//
if (message->deathCount != -1)
{
Player::VehicleDeadMessageHandler(message);
Check_Fpu();
return;
}
//
//-------------------------
// Reset the VTV subsystems
//-------------------------
//
Check(playerVehicle);
VTV
*vtv = (VTV*) playerVehicle;
Check(vtv);
if ((playerType == CrusherPlayerType) ||
(playerType == BlockerPlayerType)
)
{
vtv->DeathShutdown(VTV::FootballReset);
}
else
{
vtv->DeathShutdown(VTV::RegularReset);
}
//
//----------------------------------------------------
// See if any other players get points for this death
//----------------------------------------------------
//
CleanUpOffensivePlayers();
HostID
killer_id = ownerID;
VChainIteratorOf<OffensivePlayer*, Enumeration>
iterator(offensivePlayerList);
if (iterator.GetCurrent())
{
//
//----------------------------------------------------
// Yes, send score messages...
//----------------------------------------------------
//
OffensivePlayer
*offensive_player = iterator.GetCurrent();
Check(offensive_player);
ScoreMessage
score_message(
RPPlayer::ScoreMessageID,
sizeof(RPPlayer::ScoreMessage),
DeathBonusPointType,
offensive_player->deathBonus,
GetEntityID()
);
if (offensive_player->deathBonus < 0.0f)
{
score_message.scoreType = DropZoneKillType;
}
RPPlayer
*other_player = offensive_player->offensivePlayer;
Check(other_player);
other_player->Dispatch(&score_message);
Check(application);
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
Host *console_host = host_manager->GetConsoleHost();
if (console_host)
{
Check(console_host);
ConsolePlayerVTVDamagedMessage
scored_message(
ownerID,
other_player->ownerID,
offensive_player->deathBonus,
offensive_player->deathBonus
);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&scored_message
);
}
//
//----------------------------------------------------
// ...keep track of the killer...
//----------------------------------------------------
//
killer_id = other_player->ownerID;
//
//----------------------------------------------------
// ...and send a notice to the pilot.
//----------------------------------------------------
//
RPStatusMessage *message = new RPStatusMessage(other_player, RPStatusMessage::DestroyedByMessage, 6.0f);
Register_Object(message);
AddStatusMessage((StatusMessage *)message);
}
else
{
//
//----------------------------------------------------
// No, tell the pilot that it was self-induced.
//----------------------------------------------------
//
RPStatusMessage *message = new RPStatusMessage(NULL, RPStatusMessage::PilotErrorMessage, 6.0f);
Register_Object(message);
AddStatusMessage((StatusMessage *) message);
}
//
//------------------------------------------------
// Tell the console (if there is one) that we died
//------------------------------------------------
//
Check(application);
HostManager
*host_manager = application->GetHostManager();
Check(host_manager);
Host
*console_host = host_manager->GetConsoleHost();
if (console_host)
{
Check(console_host);
ConsolePlayerVTVKilledMessage killed_message(ownerID, killer_id);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&killed_message
);
}
//
//------------------------------------------------------------------------
// Send a score message of deathpointloss, and notify ourselves again in 5
// seconds so we can begin looking for the drop zone
//------------------------------------------------------------------------
//
ScoreMessage
score_message(
RPPlayer::ScoreMessageID,
sizeof(RPPlayer::ScoreMessage),
DeathScoreLossPointType,
-vtv->deathScoreLoss,
GetEntityID()
);
Dispatch(&score_message);
Time when = Now();
when += 5.0f;
message->deathCount = deathCount;
application->Post(HighEventPriority, this, message, when);
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::ScoreZoneReplyMessageHandler(
ScoreZone::ReplyMessage *message
)
{
Check(this);
Check(message);
if (GetSimulationState() == MissionEndingState)
{
Check_Fpu();
return;
}
//
//-------------------------------------------------
// Send Score message if score zone was covered
//-------------------------------------------------
//
ScoreMessage
score_message(
RPPlayer::ScoreMessageID,
sizeof(RPPlayer::ScoreMessageID),
ScoreZonePointType,
message->pointValue,
GetEntityID()
);
Dispatch(&score_message);
//
//--------------------------------------------------------------------
// Change the goalEntity to the new sequential scorezone if necessary
//--------------------------------------------------------------------
//
EntityGroup
*score_zone_group =
application->GetEntityManager()->FindGroup("ActiveScoreZones");
if(score_zone_group)
{
ChainIteratorOf<Node*>
iterator(score_zone_group->groupMembers);
ScoreZone
*score_zone;
while ((score_zone = (ScoreZone*) iterator.ReadAndNext()) != NULL)
{
Check(score_zone);
if (
(score_zone->scoreZoneType == LocalSequential) ||
(score_zone->scoreZoneType == GlobalSequential)
)
{
goalEntity = score_zone;
}
}
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::PlayerSimulation(Scalar time_slice)
{
Check(this);
Check(application);
Check(application->GetEntityManager());
Check(playerVehicle);
//
//------------------------------------------------------------------------
// Check for our synchronization signal
//------------------------------------------------------------------------
//
if (
(GetSimulationState() == MissionStartingState) &&
((fadeTimeRemaining - time_slice) <= 0.0f)
)
{
lastConsoleUpdate = Now();
}
if (!goalEntity)
{
SetGoalToFirstScoreZone();
PointVTVTowardGoal();
}
//
//------------------------------------------------------------------------
// Call PlayerSimulation
//------------------------------------------------------------------------
//
Player::PlayerSimulation(time_slice);
if (
application->GetApplicationState() != Application::RunningMission
&& application->GetApplicationState() != Application::EndingMission
)
{
return;
}
//
//------------------------------------------------------------------------
// Update everybody once per second
//------------------------------------------------------------------------
//
if (lastPerformance - lastUpdate >= 1.0f)
{
ForceUpdate();
}
//
//------------------------------------------------------------------------
// Update the console every 15 seconds
//------------------------------------------------------------------------
//
if (lastPerformance - lastConsoleUpdate >= 15.0f)
{
lastConsoleUpdate = lastPerformance;
//
//---------------------------------------------
// Tell the console (if there is one) our score
//---------------------------------------------
//
Check(application);
HostManager *host_manager = application->GetHostManager();
Check(host_manager);
Host *console_host = host_manager->GetConsoleHost();
if (console_host)
{
Check(console_host);
ConsolePlayerVTVScoreUpdateMessage
score_message(ownerID, (int)currentScore);
application->SendMessage(
console_host->GetHostID(),
NetworkClient::ConsoleClientID,
&score_message
);
}
}
//
//------------------------------------------------------------------------
// See if there are any zones to check
//------------------------------------------------------------------------
//
if (
GetSimulationState() != MissionEndingState
&& application->GetApplicationState() == Application::RunningMission
)
{
EntityGroup *score_zone_group =
application->GetEntityManager()->FindGroup("ActiveScoreZones");
if(!score_zone_group)
{
Check_Fpu();
return;
}
//
//----------------------------------
// If VTV is in a BurningState ie. dead
// Exit from loop and don't check
// score zones
//----------------------------------
//
VTV *vtv = (VTV*)playerVehicle;
Check(vtv);
if (vtv->GetSimulationState() == VTV::BurningState)
{
Check_Fpu();
return;
}
//
//----------------------------------
// Make an iterator and send the
// "do I get points" message to
// all the active score zones
//----------------------------------
//
Check(score_zone_group);
ChainIteratorOf<Node*>
iterator(score_zone_group->groupMembers);
ScoreZone::ProcessScoreZoneRequestMessage
message(
ScoreZone::ProcessScoreZoneRequestMessageID,
sizeof(ScoreZone::ProcessScoreZoneRequestMessage),
GetEntityID(),
ScoreZoneReplyMessageID,
playerVehicle->localOrigin.linearPosition
);
ScoreZone
*score_zone;
while ((score_zone = (ScoreZone*)iterator.ReadAndNext()) != NULL)
{
score_zone->Dispatch(&message);
}
//
//---------------------------------
// Get Points for speed if
// going in the right direction
//---------------------------------
//
AccumulateSpeedPoints(time_slice);
Check_Fpu();
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
RPPlayer::CalcRanking()
{
Check(this);
Player::CalcRanking();
Check_Fpu();
return 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::CalcFootballRanking()
{
//
//---------------------------------------
// Get all players from the Runner group
//---------------------------------------
//
EntityGroup
*runner_group =
application->GetEntityManager()->FindGroup("RunnerPlayers");
Check(runner_group);
Player *active_runner;
//
//-----------------------------------------------------
// Iterate through the players add to the sorted chain
//-----------------------------------------------------
//
ChainIteratorOf<Node*>
iterator(runner_group->groupMembers);
VChainOf<Player*, Scalar>
player_rank(NULL, False);
while ((active_runner = (Player*) iterator.ReadAndNext()) != NULL)
{
player_rank.AddValue(active_runner, active_runner->currentScore);
}
int num_runners = iterator.GetSize() - 1;
//
//------------------------------------------------
// Iterate through the chain assigning the rank
//------------------------------------------------
//
VChainIteratorOf<Player*, Scalar>
rank_iterator(player_rank);
while ((active_runner = rank_iterator.ReadAndNext()) != NULL)
{
active_runner->playerRanking = num_runners;
--num_runners;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
RPPlayer::AccumulateSpeedPoints(Scalar time_slice)
{
Check(this);
Scalar
compression=1.0f;
if(scoreCompression)
{
Scalar
top_score=-1.0f;
EntityGroup
*player_group =
application->GetEntityManager()->FindGroup("Players");
Check(player_group);
Player
*active_player;
//
//-----------------------------------------------------
// Iterate through the players find top ranked player
//-----------------------------------------------------
//
ChainIteratorOf<Node*>
iterator(player_group->groupMembers);
while ((active_player = (Player*) iterator.ReadAndNext()) != NULL)
{
if (active_player->playerRanking == 0)
{
top_score = active_player->currentScore;
break;
}
}
if (currentScore < (top_score - 400.0f))
{
compression *= ( (top_score - currentScore) + 400.0f)/800;
}
}
//
//------------------------------------------------------
// Get Points for speed if going in the right direction
//------------------------------------------------------
//
Check(playerVehicle);
Check(goalEntity);
Vector3D
to_goal,
current_heading;
VTV
*vtv = Cast_Object(VTV*, playerVehicle);
to_goal.Subtract(
vtv->localOrigin.linearPosition,
goalEntity->localOrigin.linearPosition);
current_heading = vtv->worldLinearVelocity;
Scalar
length_to_goal,
length_current_heading;
length_to_goal = to_goal.LengthSquared();
length_current_heading = current_heading.LengthSquared();
if (length_to_goal > SMALL && length_current_heading > SMALL)
{
Scalar
dot_prod = (to_goal * current_heading) /
(Sqrt(length_to_goal) * Sqrt(length_current_heading));
if (dot_prod <= 0.985f)
{
Scalar
speed_bonus = length_current_heading;
speed_bonus /= POINTS_SPEED_RATIO;
speed_bonus *= time_slice * compression;
//
//-------------------------------------------
// Send a score message with the speed bonus
//-------------------------------------------
//
ScoreMessage
score_message(
RPPlayer::ScoreMessageID,
sizeof(RPPlayer::ScoreMessageID),
SpeedBonusPointType,
speed_bonus,
GetEntityID()
);
if (!vtv->insideWorld)
{
score_message.scoreAward = -speed_bonus;
score_message.scoreType = OutOfBoundsType;
}
Dispatch(&score_message);
}
}
Check_Fpu();
}
//#############################################################################
// Construction and Destruction
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPPlayer::RPPlayer(
RPPlayer::MakeMessage *creation_message,
SharedData &shared_data
):
Player(creation_message, shared_data),
offensivePlayerList(NULL, 0)
{
currentScore = 1000.0f;
lastConsoleUpdate = Now();
goalEntity = NULL;
//
//--------------------------------
// Get the info from the mission
//--------------------------------
//
teamID = creation_message->teamID;
scoreCompression = creation_message->scoreCompression;
playerType = (PlayerType) creation_message->playerType;
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPPlayer*
RPPlayer::Make(RPPlayer::MakeMessage *creation_message)
{
return new RPPlayer(creation_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
RPPlayer::~RPPlayer()
{
Check(this);
DeleteAllOffensivePlayers();
Check_Fpu();
}
//#############################################################################
// Offensive Player
//
RPPlayer__OffensivePlayer::RPPlayer__OffensivePlayer(RPPlayer *offensive_player, int damage_type, Time time_stamp)
{
//
//-----------------------------------------
// Initialize the offensivePlayer fields
//-----------------------------------------
//
offensivePlayer = offensive_player;
damageType = damage_type;
timeStamp = time_stamp;
deathBonus = 0.0f;
Check_Fpu();
}
//#############################################################################
// RP Status message
//
RPPlayer__StatusMessage::RPPlayer__StatusMessage(
RPPlayer *player_involved,
int message_type,
Scalar display_time,
Scalar damage_amount
) :
Player__StatusMessage(
(Player *) player_involved,
message_type,
display_time
)
{
damageAmount = damage_amount;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
RPPlayer::TestInstance() const
{
return IsDerivedFrom(*GetClassDerivations());
}