#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"

//#############################################################################
//########################    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);
	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();
		}
	}
	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());
}
