RP412PODIUM=0 promises "straight to the results" and delivered eleven seconds of black screen first: the winners' circle hold was applied unconditionally at the buzzer, and the timer never asked whether there was a stand to hold the mission open FOR. Found by the -egg harness, which could reach the end of a race unattended and noticed the promise not being kept. With the podium off, the hold now stands aside and the base 3-second race fade runs the show. With it on, RP412PODIUMHOLD tunes the length (1-60 seconds, default the same 11 as always) - eleven seconds of one parked pod is a long look in single player, and that is now a choice rather than a constant. The decision point logs which path it took and the value it applied, verified all three ways: podium off - the race fade stands (3s) and the results come straight up holding the mission open 5s for the stand holding the mission open 11s for the stand Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1575 lines
40 KiB
C++
1575 lines
40 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.
|
|
//
|
|
// RP412PODIUMHOLD tunes it, and RP412PODIUM=0 declines it entirely: that
|
|
// option promises "straight to the results", and it used to get the full
|
|
// eleven seconds against a black screen anyway, because this timer never
|
|
// asked whether there was a podium to hold the mission open FOR. Zero
|
|
// here means "do not override the base fade" - the stock 3 seconds.
|
|
//
|
|
static Scalar
|
|
WinnersCircleHoldTime()
|
|
{
|
|
static Scalar
|
|
hold = (Scalar) -1;
|
|
|
|
if (hold < (Scalar) 0)
|
|
{
|
|
const char *podium = getenv("RP412PODIUM");
|
|
if (podium != NULL && atoi(podium) == 0)
|
|
{
|
|
hold = (Scalar) 0;
|
|
}
|
|
else
|
|
{
|
|
const char *setting = getenv("RP412PODIUMHOLD");
|
|
hold = (setting != NULL) ? (Scalar) atof(setting) : (Scalar) 11;
|
|
//
|
|
// Under a second cuts into the race's own fade-out, and a
|
|
// minute is a stuck-looking screen; both read as bugs, not
|
|
// choices.
|
|
//
|
|
if (hold < (Scalar) 1) hold = (Scalar) 1;
|
|
if (hold > (Scalar) 60) hold = (Scalar) 60;
|
|
}
|
|
}
|
|
return hold;
|
|
}
|
|
|
|
//#############################################################################
|
|
//######################## 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)
|
|
{
|
|
Scalar hold = WinnersCircleHoldTime();
|
|
if (hold > (Scalar) 0)
|
|
{
|
|
fadeTimeRemaining = hold;
|
|
DEBUG_STREAM << "WinnersCircle: holding the mission open "
|
|
<< hold << "s for the stand\n" << std::flush;
|
|
}
|
|
else
|
|
{
|
|
DEBUG_STREAM << "WinnersCircle: podium off - the race fade "
|
|
<< "stands (" << fadeTimeRemaining << "s) and the results "
|
|
<< "come straight up\n" << std::flush;
|
|
}
|
|
}
|
|
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, ¤t_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;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Fixed-step: the RECOVERY goes on the vehicle's own step grid.
|
|
//
|
|
// The event queue runs on wall clock, so a Reset fired from it
|
|
// lands between different sim steps on every run - the crash was
|
|
// measured bit-identical between runs and the first divergence was
|
|
// the step after the pod stood back up. The vehicle applies the
|
|
// teleport itself at the first step one SIM second after now; the
|
|
// message still makes its round trip below, but only for the
|
|
// player-state bookkeeping - the handler leaves the physics to the
|
|
// schedule it can see is pending.
|
|
//------------------------------------------------------------------
|
|
//
|
|
if (Simulation::FixedStep() > (Scalar) 0 &&
|
|
playerVehicle != NULL && playerVehicle->GetClassID() == VTVClassID)
|
|
{
|
|
VTV *vtv = (VTV *) playerVehicle;
|
|
|
|
//
|
|
// Anchored to the DEATH and quantized to a half-second grid.
|
|
//
|
|
// This handler runs when the drop-zone reply finally comes off
|
|
// the event queue, and the whole death-to-here chain is wall
|
|
// clock - the fry retries repost at Now()+2.0, and the measured
|
|
// arrival is about five sim-seconds after death, give or take a
|
|
// few STEPS of queue jitter. An anchor of death+1.0 is long past
|
|
// by then, so "fire at the next step" inherited the jitter
|
|
// whole.
|
|
//
|
|
// The death stamp is the last step-exact event in the chain, so:
|
|
// take the measured gap, add the second the old code waited, and
|
|
// round UP to the next half-second AFTER THE DEATH. The jitter
|
|
// is hundredths; the nearest grid boundary is tenths away; every
|
|
// run lands in the same cell and fires on the same step. The
|
|
// felt delay is the same six-ish seconds it has always been.
|
|
//
|
|
Time due;
|
|
Time death_mark;
|
|
if (vtv->ConsumeDeathClock(&death_mark))
|
|
{
|
|
Scalar gap = vtv->GetLastPerformance() - death_mark;
|
|
const Scalar quantum = (Scalar) 0.5;
|
|
int cells = (int)((gap + (Scalar) 1.0) / quantum) + 1;
|
|
|
|
due = death_mark;
|
|
due += quantum * (Scalar) cells;
|
|
}
|
|
else
|
|
{
|
|
due = vtv->GetLastPerformance();
|
|
due += 1.0f;
|
|
}
|
|
vtv->ScheduleRespawn(
|
|
message->dropZoneLocation, due,
|
|
(goalEntity != NULL)
|
|
? goalEntity->localOrigin.linearPosition
|
|
: Point3D(0.0f, 0.0f, 0.0f),
|
|
(goalEntity != NULL) ? True : False);
|
|
respawnScheduled = True;
|
|
|
|
//
|
|
// Under the trace, say what was scheduled in run-comparable
|
|
// terms: the pad (identity by position), and how far ahead of
|
|
// the vehicle's clock the due time sits. Two runs that disagree
|
|
// here diverge before the physics gets a vote.
|
|
//
|
|
{
|
|
static int diag = -1;
|
|
if (diag < 0)
|
|
{
|
|
const char *setting = getenv("RP412PHYSTRACE");
|
|
diag = (setting != NULL && atoi(setting) != 0) ? 1 : 0;
|
|
}
|
|
if (diag)
|
|
{
|
|
char buffer[160];
|
|
sprintf(buffer,
|
|
"PhysTrace: respawn #%d scheduled, pad %.2f,%.2f "
|
|
"due in %.4f sim-s\n",
|
|
deathCount,
|
|
(double) message->dropZoneLocation.linearPosition.x,
|
|
(double) message->dropZoneLocation.linearPosition.z,
|
|
(double)(Scalar)(due - vtv->GetLastPerformance()));
|
|
DEBUG_STREAM << buffer << std::flush;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
respawnScheduled = False;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// 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);
|
|
|
|
//
|
|
// A SCHEDULED respawn means the vehicle already holds - or has
|
|
// already applied - its teleport and goal-flip, on its own step
|
|
// grid. Resetting it AGAIN here, at whatever wall instant this
|
|
// message came off the queue, would re-teleport it mid-step and
|
|
// put the nondeterminism straight back.
|
|
//
|
|
// The flag, not "is fixed stepping on": this same leg also runs
|
|
// for the FIRST spawn of the mission, where the Reset below is
|
|
// what wakes a Mover out of its initial stasis. Gating on the
|
|
// mode alone skipped that wake-up and parked the pod, frozen at
|
|
// exactly its spawn point, for an entire race.
|
|
//
|
|
if (respawnScheduled)
|
|
{
|
|
respawnScheduled = False;
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
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());
|
|
}
|