#include "rp.h"
#pragma hdrstop

#include "crusher.h"
#include "..\munga\dropzone.h"
#include "vtv.h"
#include "..\munga\hostmgr.h"
#include "..\munga\app.h"
#include "scorzone.h"
#include "..\munga\nttmgr.h"

//#############################################################################
//###############################    Crusher     ##############################
//#############################################################################

//#############################################################################
// Shared Data Support
//
Derivation* Crusher::GetClassDerivations()
{	static Derivation classDerivations(RPPlayer::GetClassDerivations(), "Crusher");
	return &classDerivations;
}


Crusher::SharedData
	Crusher::DefaultData(
		Crusher::GetClassDerivations(),
		Crusher::GetMessageHandlers(),
		Crusher::GetAttributeIndex(),
		Crusher::StateCount,
		(Crusher::MakeHandler)Crusher::Make
	);

const Receiver::HandlerEntry
	Crusher::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(Crusher, Score),
	MESSAGE_ENTRY(Crusher, DropZoneReply),
	MESSAGE_ENTRY(Crusher, ScoreZoneReply)
};

Receiver::MessageHandlerSet& Crusher::GetMessageHandlers()
{
	static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(Crusher::MessageHandlerEntries), Crusher::MessageHandlerEntries, RPPlayer::GetMessageHandlers());
	return messageHandlers;
}

//#############################################################################
// Message Support
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void Crusher::DropZoneReplyMessageHandler(
		DropZone::ReplyMessage *message
	)
{
	Check(this);
	Check(message);

	if (!playerVehicle)
	{
		CreatePlayerVehicle(message->dropZoneLocation);
		InitializePlayerLink();
		SetPerformance(&Crusher::PlayerSimulation);
		AlwaysExecute();
		deathCount = 0;
	}
	else if (deathCount == message->deathCount)
	{
		if (GetSimulationState() == MissionEndingState)
		{
			Check_Fpu();
			return;
		}
		if (GetSimulationState() != DropZoneAcquiredState)
		{
			ResetAfterDeath(message);
			Check_Fpu();
			return;
		}
	}
	else
	{
		Check_Fpu();
		return;
	}
	DeleteAllOffensivePlayers();
	ForceUpdate();
	SetSimulationState(VehicleTranslocatedState);
	VTV *vtv = (VTV*)playerVehicle;
	Check(vtv);
	vtv->Reset(message->dropZoneLocation, True);
	if (goalEntity)
	{
		Check(goalEntity);
		PointVTVTowardGoal();
		vtv->ForceUpdate();
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Crusher::AddOffensivePlayer(OffensivePlayer &offensive_player)
{
	Check(this);
	Check(offensive_player.offensivePlayer);
	RPPlayer *rp_player = offensive_player.offensivePlayer;
	//
	//---------------------------------------------
	// Damage & Death Points awarded if ....
	//  This Crusher gets hit by a runner or a Blocker
	// from the other team with collision Damage!
	//---------------------------------------------
	//
	if (
		(
			rp_player->playerType == BlockerPlayerType
			 || rp_player->playerType == RunnerPlayerType
		)
		 && teamID != rp_player->teamID
		 && 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
	Crusher::ScoreMessageHandler(ScoreMessage *message)
{
	Check(this);
	Check(message);
	Check(application);

	RPPlayer::ScoreMessageHandler(message);
	HostManager *host = application->GetHostManager();
	Check(host);
	RPPlayer *rp_player = (RPPlayer*)
			host->GetEntityPointer(message->pointSender);
	if (!rp_player)
	{
		Check_Fpu();
		return;
	}

	Check(rp_player);
	if (
		rp_player->playerType == RunnerPlayerType
		 && rp_player->teamID != teamID
	)
	{
		Scalar score_award = message->scoreAward * 2.0f;
		//
		//------------------------------------------------------
		// Create the score award and pass it on to the runner
		//------------------------------------------------------
		//
		ScoreMessage score_message(
				RPPlayer::ScoreMessageID,
				sizeof(RPPlayer::ScoreMessage),
				CrusherScorePointType,
				score_award,
				GetEntityID()
			);
		if (teamRunner)
		{
			Check(teamRunner);
			teamRunner->Dispatch(&score_message);
		}
	}

	//
	// HACK - ECH 7/6/95 - Allow the player vehicle to respond to score
	// messages, allows attribute system to be used for scoring
	//
	Check(playerVehicle);
	playerVehicle->RespondToScoreMessage(message);
	Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Crusher::ScoreZoneReplyMessageHandler(
		ScoreZone::ReplyMessage *//message
	)
{
	//empty
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Crusher::PlayerSimulation(Scalar time_slice)
{
	Check(this);
	//
	//------------------------------------------------------------------------
	// Service the status message queue
	//------------------------------------------------------------------------
	//
	StatusMessageUpdate(time_slice);

	//
	//-----------------------------------
	// Find the runners if we haven't yet
	//-----------------------------------
	//
	Player::PlayerSimulation(time_slice);
	InitTeamRunner();

	//
	//--------------------------------------------------------------------------
	// If we know who the enemy is, but not what his vehicle is, try to find the
	// vehicle
	//--------------------------------------------------------------------------
	//
	if (enemyRunner)
	{
		Entity *our_vehicle = GetPlayerVehicle();
		Check(our_vehicle);
		if (!goalEntity)
		{
			goalEntity = enemyRunner->GetPlayerVehicle();
			if (goalEntity)
			{
				PointVTVTowardGoal();
				our_vehicle->ForceUpdate();
			}
		}

		//
		//----------------------------------------------------------------------
		// Otherwise, if we aren't running yet, make sure that we are pointed in
		// the correct direction - HACK
		//----------------------------------------------------------------------
		//
		else if (
			application->GetApplicationState() < Application::RunningMission
		)
		{
			Scalar alignment = our_vehicle->localToWorld(2,2);
			PointVTVTowardGoal();
			if (alignment * goalEntity->localToWorld(2,2) < 0.0f)
			{
				our_vehicle->ForceUpdate();
			}
		}

		//
		//----------------------------------------------
		// If the game is running, set the goal properly
		//----------------------------------------------
		//
		else
		{
			goalEntity = enemyRunner->GetPlayerVehicle();
		}
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	Crusher::PointVTVTowardGoal()
{
	Check(this);
	Vector3D	to_goal(Vector3D::Identity);

	if (goalEntity)
	{
		//
		//------------------------------
		// Find the distance to the goal
		//------------------------------
		//

		to_goal.Subtract(
			goalEntity->localOrigin.linearPosition,
			playerVehicle->localOrigin.linearPosition
		);

		Scalar length_to_goal;
		length_to_goal = to_goal.LengthSquared();

		//
		//----------------------------
		// Less than 100 meters away?
		//----------------------------
		//
		if (length_to_goal < 10000.0f)
		{
			//
			//-----------------------------------------------
			// The goalEntity is in front of us so point in
			// the same direction as the goalEntity
			//-----------------------------------------------
			//
			playerVehicle->localOrigin.angularPosition.x = 0.0f;
			playerVehicle->localOrigin.angularPosition.z = 0.0f;
			if (goalEntity->localToWorld(2,2) >= 0.0f)
			{
				playerVehicle->localOrigin.angularPosition.y = 0.0f;
				playerVehicle->localOrigin.angularPosition.w = 1.0f;
			}
			else
			{
				playerVehicle->localOrigin.angularPosition.y = 1.0f;
				playerVehicle->localOrigin.angularPosition.w = 0.0f;
			}
		}
		else
		{
			//
			//---------------------------------
			// Point toward the goal Entity
			// 		disregard and Y direction
			//---------------------------------
			//
			playerVehicle->localOrigin.angularPosition.x = 0.0f;
			playerVehicle->localOrigin.angularPosition.z = 0.0f;
			//
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			// is the goal in front or behind me
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			//
			if (to_goal.z <= 0.0f)
			{
				playerVehicle->localOrigin.angularPosition.y = 0.0f;
				playerVehicle->localOrigin.angularPosition.w = 1.0f;
			}
			else
			{
				playerVehicle->localOrigin.angularPosition.y = 1.0f;
				playerVehicle->localOrigin.angularPosition.w = 0.0f;
			}
		}
		playerVehicle->localToWorld = playerVehicle->localOrigin;
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Crusher::Crusher(
	MakeMessage *creation_message,
	SharedData  &shared_data
)
: RPPlayer(creation_message, shared_data)
{
	StatusMessage
		*message = new StatusMessage(NULL,RPStatusMessage::CrusherMessage,1e10);
	Register_Object(message);
	AddStatusMessage(message);

	teamRunner = NULL;
	enemyRunner = NULL;
	Check_Fpu();
}

Crusher::~Crusher()
{
	Check(this);
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Crusher*
	Crusher::Make(MakeMessage *creation_message)
{
	Check_Fpu();
	return new Crusher(creation_message);
}

//#############################################################################
//
void
	Crusher::InitTeamRunner()
{
	Check(this);

	Check(application);
	EntityManager *entity_mgr = application->GetEntityManager();
	Check(entity_mgr);
	EntityGroup *runner_group = entity_mgr->FindGroup("RunnerPlayers");
	if(runner_group)
	{
		Check(runner_group);
		ChainIteratorOf<Node*> iterator(runner_group->groupMembers);
		RPPlayer *runner;
		int winning_rank = 10000;
		while ((runner = (RPPlayer*) iterator.ReadAndNext()) != NULL)
		{
			Check(runner);
			if (runner->teamID == teamID)
			{
				teamRunner = runner;
			}
			else
			{
				if (runner->playerRanking < winning_rank)
				{
					enemyRunner = runner;
					winning_rank = runner->playerRanking;
				}
			}
		}
	}
	Check_Fpu();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
	Crusher::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}
