//
// AVP_ServerEntity_CommandPost.cpp
//
// Created by RDM - 21 November 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

/************/
/* Includes */
/************/

// main asura header
#include "Core/Asura.h"

// This file's header.
#include "Entity/PhysicalObject/CommandPost/AVP_ServerEntity_CommandPost.h"

// asura
#include "AI/Asura_AI_Navigation.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_HashedList.h"
#include "Core/Asura_Timers.h"
#include "Network/Asura_Message.h"
#include "Network/Asura_NetMsg.h"
#include "Network/Asura_Network.h"
#include "Physics/Asura_Physics.h"

#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
#include "Interface/Asura_CommandConsole.h"
#endif // #ifdef ASURA_SUPPORT_COMMAND_CONSOLE

#ifdef ASURA_TOOLS
#include "EntityTemplate/Tools_EntityTemplate_System.h"
#endif

// AVP
#include "Core/AVP_Settings.h"
#include "Entity/AVP_Entity_Classification.h"
#include "Entity/AVP_Entity_EventIDs.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/Vertebrates/AVP_ServerActor_Vertebrate.h"
#include "Entity/PhysicalObject/AVP_ServerEntity_PhysicalObject.h"
#include "Entity/PhysicalObject/CommandPost/AVP_ClientEntity_CommandPost.h"
//#include "Entity/PhysicalObject/Objective/AVP_ServerEntity_CommandPost_Node.h"
#include "Entity/PlayerInfo/AVP_ServerEntity_PlayerInfo.h"
#include "Multiplayer/AVP_Multiplayer_GameCore.h"
#include "Multiplayer/AVP_Multiplayer_GameRules.h"
#include "Multiplayer/AVP_Multiplayer_TeamScore.h"


/********/
/* Data */
/********/

// static members
Asura_Hashed_List<AVP_ServerEntity_CommandPost> AVP_ServerEntity_CommandPost::s_xList(3);
const float AVP_ServerEntity_CommandPost::s_fHoldTime = 15.0f;		//TODO: Blueprint

// debugvars
DEBUGVAR float dbg_fContestedCaptureMaxDuration = 30.0f;
DEBUGVAR float dbg_fMaxCaptureSpeed = 5.0f;
DEBUGVAR float dbg_fDefaultCaptureTime = 10.0f; 

// globals
// Bumped this to 6 to make a clean transition to having no waypoint link blocks.
static const int iAVP_COMMANDPOST_VERSION = 10;

/*************/
/* Functions */
/*************/

// constructor
AVP_ServerEntity_CommandPost::AVP_ServerEntity_CommandPost( Asura_Guid uGuid/*=ASURA_GUID_UNREGISTERED*/ )
: Asura_Entity_Server(uGuid)
, PARENT( 0 )
, m_eInitialTeam(AVP_Settings::AVP_TEAM_NONE)
, m_fHoldTimer(0.0f)
, m_fCaptureTime(dbg_fDefaultCaptureTime)
, m_uLocalisedNameHashID(ASURA_HASH_ID_UNSET)
, m_iCurrentFaction(AVP_Settings::AVP_TEAM_NONE)
, m_iCapturingFaction(AVP_Settings::AVP_TEAM_NONE)
, m_fContestedTimer(0.0f)
, m_fCaptureTimer(dbg_fDefaultCaptureTime)
, m_uCapturingGuid(ASURA_GUID_UNREGISTERED)
, m_fLastCapturedSpawnTime( 0.0f )
, m_fSpawnProbabilityValue(0.0f)
, m_uCommandPostFlags(0)
{
	SetDestructible( false );
	SetHealth( 100.0f );
	SetBlockNavDataGenerationFlag( true );
	SetPhysicalObjectFlags( GetPhysicalObjectFlags() | ASURA_PHYSICALOBJECT_FLAG_STATIC );  

	for ( u_int i = 0; i < AVP_COMMANDPOST_MAX_NUM_LINKED_GUARDZONES; ++i )
	{
		m_auLinkedGuardZoneGuids[i] = ASURA_GUID_UNREGISTERED;
	}

	Classification = AVP_ENTITYCLASS_COMMANDPOST;
	s_xList.AddToList(this);
}

// destructor
AVP_ServerEntity_CommandPost::~AVP_ServerEntity_CommandPost()
{
	s_xList.RemoveFromList(this);
}


/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::Update
 *
 * Author        : Richard May
 *
 * Description   : Main update function for command posts; detects
 *				   humanoids nearby for capturing purposes.
 *
 *************************************************************/
void AVP_ServerEntity_CommandPost::Update( void )
{	
	if (!AVP_Multiplayer_GameCore::IsMultiplayerMatchInProgress())
	{
		return;
	}

	PARENT::Update();

	if (IsCaptureDisabledForCurrrentGametype())
	{
		return;
	}

	// only allow capturing when defined as allowed in tools - PJC
	if( m_bCaptureDisabled ) return;

	UpdateHoldTimer();

	int iNewCapturingFaction = AVP_Settings::AVP_TEAM_NONE;
	float fCaptureSpeed = 1.0f;
	u_int uNumCapturing = 0;
	bool bIsContested = false;

	// Check for nearby humanoids.
	Asura_Hashed_List_It<AVP_ServerActor_Vertebrate> xIt( AVP_ServerActor_Vertebrate::GetList() );
	while( !xIt.Done() )
	{
		AVP_ServerActor_Vertebrate* pxVertebrate = xIt.GetCurrent();
		xIt.Next();

		if( CanCapture( pxVertebrate ) )
		{
			AVP_ServerEntity_PlayerInfo* pxPlayerInfo = AVP_ServerEntity_PlayerInfo::FindEntity( pxVertebrate->GetPlayerInfoGuid() );
			if (!pxPlayerInfo)
			{
				// No player info, can't capture 
				continue; 
			}
			if( (iNewCapturingFaction != AVP_Settings::AVP_TEAM_NONE)
			 && (iNewCapturingFaction != pxPlayerInfo->GetMultiplayerTeam()) )
			{
				// If there's already a capture attempt in progress by a faction that isn't this 
				// humanoid's faction, this is a contested capture, don't bother checking for further humanoids.
				bIsContested = true;
				SendMessagesForEvent( AVP_ENTITY_EVENT_CAPTURE_POINT_ONCONTESTED );
				break;
			}
			else
			{
				// Attempt a capture, even if we're the same faction as the command post
				iNewCapturingFaction = pxPlayerInfo->GetMultiplayerTeam();
				m_uCapturingGuid = pxVertebrate->GetGuid();
				//fCaptureSpeed += pxVertebrate->GetCaptureSpeed();
				++uNumCapturing;
			}
		}
	}

	// if contested, nothing changes, unless it's been contested for dbg_fContestedCaptureMaxDuration (30s)
	// in which case it reverts to neutral...
	if( bIsContested )
	{
		if( !IsContested() )
		{
			m_fContestedTimer = dbg_fContestedCaptureMaxDuration;
		}

		// This is a contested capture so descrease the contested time. When it reaches zero the
		// command post will revert to neutral status
		m_fContestedTimer -= Asura_Timers::GetGameTimeSlice();
		if( m_fContestedTimer <= 0.0f )
		{
			// revert to neutral status
			Capture( AVP_Settings::AVP_TEAM_NONE);
		}
	}
	else
	{	
		// no longer contested, so ensure timer is reset
		m_fContestedTimer = 0.0f;

		// clamp capture speed
		fCaptureSpeed = Asura_Maths::Min( fCaptureSpeed, dbg_fMaxCaptureSpeed );

		// someone capturing?
		if (iNewCapturingFaction != AVP_Settings::AVP_TEAM_NONE) 
		{
			if (iNewCapturingFaction == GetCurrentFaction())
			{
				// capturing faction is owner - increase the timer back to max.
				m_fCaptureTimer = Asura_Maths::Min( GetCaptureTime(), m_fCaptureTimer + Asura_Timers::GetGameTimeSlice() * fCaptureSpeed );
			}
			else if ( (GetCurrentFaction() == AVP_Settings::AVP_TEAM_NONE) && 
					  (m_iCapturingFaction != AVP_Settings::AVP_TEAM_NONE) &&
					  (iNewCapturingFaction != m_iCapturingFaction) )
			{
				// neutral, and other faction was capturing - need to undo their capture before we can start.
				m_fCaptureTimer += Asura_Timers::GetGameTimeSlice() * fCaptureSpeed;
				if (m_fCaptureTimer >= GetCaptureTime())
				{
					m_fCaptureTimer = GetCaptureTime();
					m_iCapturingFaction = iNewCapturingFaction;
				}
			}
			else
			{
				// capture in progress, decrement the capture timer
				m_fCaptureTimer -= Asura_Timers::GetGameTimeSlice() * fCaptureSpeed;

				if( m_fCaptureTimer <= 0.0f )
				{
					// successful capture: change owner faction
					// (MH - 13.12.2006) to neutral if we just took it from other side, or if it was already neutral to our faction.
					if (m_iCurrentFaction == AVP_Settings::AVP_TEAM_NONE)
					{
						Capture( iNewCapturingFaction );
					}
					else
					{
						Capture( AVP_Settings::AVP_TEAM_NONE );
					}
				}

				// set the current capturing faction
				m_iCapturingFaction = iNewCapturingFaction;
			}
		}

	}	
}
/* end of function Update */


void AVP_ServerEntity_CommandPost::UpdateHoldTimer()
{
	if ((m_iCurrentFaction != AVP_Settings::AVP_TEAM_NONE) && !IsContested() )
	{
		m_fHoldTimer += Asura_Timers::GetGameTimeSlice();
		if (m_fHoldTimer > s_fHoldTime)
		{
			// After holding the command post for s_fHoldTime, increase the team score
			int iBasicScoreHoldControlPoint = AVP_Multiplayer_GameRules::GetTeamScoreHoldControlPoint();
			AVP_Multiplayer_TeamScore* pxTeamScore = AVP_Multiplayer_TeamScore::FindTeamScore(static_cast<AVP_Settings::Team>(m_iCurrentFaction));
			if (pxTeamScore)
			{
				pxTeamScore->SetTeamScore( pxTeamScore->GetTeamScore() + iBasicScoreHoldControlPoint ); 
			}
			else
			{
				Asura_Assertf(pxTeamScore, ("Team score hasn't been created for team %d", m_iCurrentFaction));
			}

			// Start timing again
			m_fHoldTimer = 0.0f;
		}
	}
	else if (m_fHoldTimer > 0.0f)
	{
		m_fHoldTimer = 0.0f;
	}
}


/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::HandleMessage
 *
 * Author        : Paul Cardy
 *
 * Description   : Handles command post specific messages
 *
 *************************************************************/
bool AVP_ServerEntity_CommandPost::HandleMessage( const Asura_Message* pxMsg )
{
	switch( pxMsg->GetMsgID() )
	{
		case ASURA_MSG_ACTIVATE:
		case ASURA_MSG_DEACTIVATE:
		{
			bool bRet = PARENT::HandleMessage(pxMsg);
			//AVP_GameRules::CommandPostsChanged();
			return bRet;
		}
		case AVP_MSGID_CAPTUREPOINT_UNLOCK:
		{
			SetCaptureDisabled( false );
			return true;
		}
		case AVP_MSGID_CAPTUREPOINT_LOCK:
		{
			SetCaptureDisabled( true );
			return true;
		}
		case AVP_MSGID_CHANGE_OWNERSHIP_TO_TEAM1:
		{
			Capture(AVP_Settings::AVP_TEAM_ONE); 
			return true; 
		}
		case AVP_MSGID_CHANGE_OWNERSHIP_TO_TEAM2:
		{
			Capture(AVP_Settings::AVP_TEAM_TWO); 
			return true; 
		}
		case AVP_MSGID_CHANGE_OWNERSHIP_TO_TEAM3:
		{
			Capture(AVP_Settings::AVP_TEAM_THREE); 
			return true; 
		}
		case ASURA_MSG_CLIENT_REQUESTCREATEMSG:
		{
			MakeCreateMessage(pxMsg->GetFromGuid());
			return true;
		}
		case AVP_MSGID_SETMULTIPLAYERTEAM:
		{
			//const AVP_Msg_SetTeam* pxData = static_cast<const AVP_Msg_SetTeam*>( pxMsg->GetData() );
			//if (pxData)
			//{
			//	m_iCurrentFaction = static_cast<const AVP_Faction::AVP_TEAM>( pxData->m_iTeam );
			//	AVP_GameRules::CommandPostsChanged();
			//}
			return true;
		}
		default: break;
	}

	return PARENT::HandleMessage( pxMsg );
}
/* end of function Update */


void AVP_ServerEntity_CommandPost::MakeCreateMessage( Asura_Guid uRequestingClient, bool bSendGuaranteed /*= false */ )
{
	// for now just use the parent create message
	Asura_Message* pxMessage = Asura_Network::CreateMessage(ASURA_MSG_CLIENTCREATE, sizeof(AVP_Message_CommandPost_ClientCreate), GetClassification());
	if(pxMessage)
	{
		AVP_Message_CommandPost_ClientCreate* pxData = static_cast< AVP_Message_CommandPost_ClientCreate* >( pxMessage->GetData() );
		pxData->FillCreateCommandPost(this);

		pxMessage->SetToGuid( ASURA_GUID_MY_CLIENT_ENTITIES );
		pxMessage->SetFromGuid( GetGuid() );
		if (bSendGuaranteed)
		{
			pxMessage->UseGuaranteedDelivery();
		}
		Asura_Network::SendToSpecificMachine( pxMessage, uRequestingClient );
	}
}


/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::MakeClientUpdateMessage
 *
 * Author        : Richard May
 *
 * Description   : Sends an update message to our client entity.
 *
 *************************************************************/
void AVP_ServerEntity_CommandPost::MakeClientUpdateMessage( void )
{
	Asura_Message* pxMsg = Asura_Network::CreateMessage( ASURA_MSG_CLIENTUPDATE, sizeof(AVP_Message_CommandPost_ClientUpdate), GetClassification() );
	if( pxMsg )
	{
		AVP_Message_CommandPost_ClientUpdate* pxData = static_cast<AVP_Message_CommandPost_ClientUpdate*>( pxMsg->GetData() );
		if( pxData )
		{
			pxData->WriteToMessage( this );
		}

		Send( pxMsg, ASURA_GUID_MY_CLIENT_ENTITIES );
	}
}
/* end of function MakeClientUpdateMessage */



/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::Capture
 *
 * Author        : Richard May
 *
 * Description   : Changes the faction of this command point
 *
 *************************************************************/
void AVP_ServerEntity_CommandPost::Capture( int iCapturingFaction )
{
	// If we have been captured by a non-neutral faction, award scores to the capturing entities
	// and send any messages.
	if( iCapturingFaction != AVP_Settings::AVP_TEAM_NONE )
	{
		// Check for nearby humanoids. Currently only humanoids can attempt captures.
		Asura_Hashed_List_It<AVP_ServerActor_Vertebrate> xIt( AVP_ServerActor_Vertebrate::GetList() );
		while( !xIt.Done() )
		{
			AVP_ServerActor_Vertebrate* pxVertebrate = xIt.GetCurrent();
			xIt.Next();

			AVP_ServerEntity_PlayerInfo* pxPlayerInfo = AVP_ServerEntity_PlayerInfo::FindEntity( pxVertebrate->GetPlayerInfoGuid() );
			if (!pxPlayerInfo)
			{
				// No player info, can't capture 
				continue; 
			}

			if( CanCapture( pxVertebrate ) 
			 && ( pxPlayerInfo->GetMultiplayerTeam() == iCapturingFaction ))
			{
				// award points here
				AVP_Multiplayer_GameCore::OnActorCapturesCommandPost( pxVertebrate->GetGuid() ); 
			}
		}		

		// Send event messages
		if (iCapturingFaction == AVP_Settings::AVP_TEAM_ONE)
		{
			SendMessagesForEvent( AVP_ENTITY_EVENT_CAPTURE_POINT_ONCAPTUREDBYTEAM1 );
		}
		else if (iCapturingFaction == AVP_Settings::AVP_TEAM_TWO)
		{
			SendMessagesForEvent( AVP_ENTITY_EVENT_CAPTURE_POINT_ONCAPTUREDBYTEAM2 );
		}
		else if (iCapturingFaction == AVP_Settings::AVP_TEAM_THREE)
		{
			SendMessagesForEvent( AVP_ENTITY_EVENT_CAPTURE_POINT_ONCAPTUREDBYTEAM2 );
		}
	}
	else
	{
		Asura_Assert(m_iCurrentFaction != AVP_Settings::AVP_TEAM_NONE, "CP transiting from netural to neutral");
		//AVP_Points::RecordEvent( uAVP_PE_COMMAND_POST_LOST, AVP_Faction::GetTeamFromFaction(m_uFaction), this );

		SendMessagesForEvent( AVP_ENTITY_EVENT_CAPTURE_POINT_ONNEUTRALIZED );
	}

	// Update our state
	m_iCurrentFaction = iCapturingFaction;
	m_iCapturingFaction = AVP_Settings::AVP_TEAM_NONE;
	m_fCaptureTimer = GetCaptureTime();
	m_fLastCapturedSpawnTime = 0.0f;

	// update Game Rules
	//AVP_GameRules::CommandPostsChanged();
}
/* end of function Capture */



/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::CanCapture
 *
 * Author        : Richard May
 *
 * Description   : Does the given humanoid count for capturing purposes?
 *
 *************************************************************/
bool AVP_ServerEntity_CommandPost::CanCapture( const AVP_ServerActor_Vertebrate* pxVertebrate ) const
{
	const float fCaptureRadius = AVP_Multiplayer_GameRules::GetDDOMCaptureRadius(); 

	if( pxVertebrate
 	 && pxVertebrate->IsEntityActive()
	 && !pxVertebrate->IsDead() 
	 && pxVertebrate->GetPlayerInfoGuid() != ASURA_GUID_UNREGISTERED)
	{
		// Is this player team's role to capture command posts?
		AVP_ServerEntity_PlayerInfo* pxPlayerInfo = AVP_ServerEntity_PlayerInfo::FindEntity(  pxVertebrate->GetPlayerInfoGuid() ); 
		if (pxPlayerInfo)
		{
			// Only team 1 and 2 capture command posts
			if (pxPlayerInfo->GetMultiplayerTeam() != AVP_Settings::AVP_TEAM_ONE &&
				pxPlayerInfo->GetMultiplayerTeam() != AVP_Settings::AVP_TEAM_TWO )
			{
				return false;
			}
		}
		// Within range?
		Asura_Vector_3 xToVertebrate = pxVertebrate->GetPosition() - GetPosition();
		if( xToVertebrate.Magnitude() <= fCaptureRadius )
		{	
			return true;
		}
	}

	return false;
}
/* end of function Capture */

/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::WriteToChunkStream
 *
 * Author        : Richard May
 *
 * Description   : Save code.
 *
 *************************************************************/
void AVP_ServerEntity_CommandPost::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	// write version
	xStream << iAVP_COMMANDPOST_VERSION;

	// write parent data
	PARENT::WriteToChunkStream( xStream );

	xStream << static_cast<u_int>(m_eInitialTeam);
	xStream << m_fCaptureTime;
	xStream << m_uCommandPostFlags;

	m_xChildNodes.WriteToChunkStream( xStream );

	for ( u_int i = 0; i < AVP_COMMANDPOST_MAX_NUM_LINKED_GUARDZONES; ++i )
	{
		xStream << m_auLinkedGuardZoneGuids[i];
	}

	xStream << m_uLocalisedNameHashID;
}
/* end of function WriteToChunkStream */



/*************************************************************
 *
 * Name          : AVP_ServerEntity_CommandPost::ReadFromChunkStream
 *
 * Author        : Richard May
 *
 * Description   : Load code.
 *
 *************************************************************/
void AVP_ServerEntity_CommandPost::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	// read version
	int iVersion = 0;
    xStream >> iVersion;

	// read parent data
	if (iVersion <= 8)
	{
		AVP_ServerEntity_PhysicalObject::ReadFromChunkStream( xStream );
	}	
	else
	{
		PARENT::ReadFromChunkStream( xStream );
	}

	// version 0 command posts had health zero
	if( iVersion == 0 )
	{
		SetHealth( 100.0f );
	}

	if (iVersion >= 2)
	{
		u_int uTeam;
		xStream >> uTeam;
		xStream >> m_fCaptureTime;

		// call this after setting capture time as it also sets capture timer.
		SetInitialTeam( static_cast<AVP_Settings::Team>(uTeam) );
	}

	if (iVersion >= 3)
	{
		u_int uFlags = 0;
		xStream >> uFlags;

		if (iVersion >= 5)
		{
			m_uCommandPostFlags = uFlags;
		}
		else
		{
			m_uCommandPostFlags = 0;
			m_bCaptureDisabled  = (uFlags != 0);
		}
	}

	if (iVersion >= 4)
	{
		m_xChildNodes.ReadFromChunkStream( xStream );
	}

    if ( iVersion < 6 )
    {
        // Earlier versions had waypoint link block information in, which is wrong.
        SetBlockWaypointLinksFlag( false );
        UpdateWaypointLinkBlocks();
        DiscardWaypointLinkBlockInformation();
    }
	// (TI - 22.07.2008) - We now want to set CPs to block waypoint generation 
	// as they now have collision but designers cannot set this in tools.
	// Have to add this here as in the PARENT::Read this is set to false when the flags are read in.
	SetBlockNavDataGenerationFlag( true );

	if ( iVersion >= 7 )
	{
		for ( u_int i = 0; i < AVP_COMMANDPOST_MAX_NUM_LINKED_GUARDZONES; ++i )
		{
			xStream >> m_auLinkedGuardZoneGuids[i];
		}
	}

	if ( iVersion <= 9 )
	{
		// Skip position type, replaced
		int iPositionType;
		xStream >> iPositionType;
	}

	if ( iVersion >= 10 )
	{
		u_int uLocalisedNameHashID;
		xStream >> uLocalisedNameHashID;
		m_uLocalisedNameHashID = uLocalisedNameHashID;
	}
	else
	{
		m_uLocalisedNameHashID = ASURA_HASH_ID_UNSET; 
	}

}
/* end of function ReadFromChunkStream */

void AVP_ServerEntity_CommandPost::PostLoadInitialise()
{
	// Initialise active status
	SetEntityActive( IsEntityActive() );
}

bool AVP_ServerEntity_CommandPost::GetSpawnPositionAndOri( Asura_Vector_3& xPos, Asura_Matrix_3x3& xOri, bool bIncludeChildren /*= false*/) const
{
//	if (bIncludeChildren && !m_xChildNodes.IsEmpty())
//	{
////		Asura_Assertf(m_xChildNodes.HasValidProbabilities(), ("Invalid probabilities in child nodes for command post Guid %d. Their sum should be equal or less than 100 but it totals %d", GetGuid(), m_xChildNodes.GetSumOfProbabilities() ) );
//
//		u_int uSum = m_xChildNodes.GetSumOfProbabilities();
//
//		Asura_Assert(uSum, "GetSumOfProbabilites returned 0, I think thats very naughty");
//		if (!uSum) uSum = 1;
//
//		int iRandom = Asura_Random::GetIntU(1, uSum);
//
//		for (int i = 0; i < m_xChildNodes.s_uMAX_NODES; i++)
//		{
//			AVP_CommandPost_Node_ArrayElement xElement = m_xChildNodes.GetNode(i);
//			if (xElement.m_uGuid != ASURA_GUID_UNREGISTERED)
//			{
//				iRandom -= xElement.m_uProbability;
//				if ( iRandom <= 0 )
//				{
//					// Return the nodes position if we're within its probability
//					// Unless its inactive in which case give its probability to the command post itself
//					AVP_ServerEntity_CommandPost_Node* pxNode = AVP_ServerEntity_CommandPost_Node::FindEntity(xElement.m_uGuid);
//
//					#ifdef TEST_COMMAND_POST_SPAWNER
//					if (pxNode && pxNode->IsActive()) m_uCount[i]++;
//					#endif //__TEST_COMMAND_POST_SPAWNER__
//
//					if (pxNode)
//					{
//						xPos = (pxNode->IsEntityActive()) ? pxNode->GetPosition() : GetPosition();
//						xOri = (pxNode->IsEntityActive()) ? pxNode->GetSpawnOrientation() : GetSpawnOrientation();
//						return (pxNode->IsEntityActive()) ? pxNode->IsUsingOrientation() : IsUsingOrientation();
//					}
//					//Don't continue if we couldn't find the node, best just to return the default
//					break;
//				}
//			}
//		}
//	}
//
//	// Default action is to return the commands posts position (so that any leftover probability will represent that of the command post)
//	xPos = GetPosition();
//	xOri = GetSpawnOrientation();
//	return m_bUsesOrientation;
return false;
}

const Asura_Matrix_3x3 AVP_ServerEntity_CommandPost::GetSpawnOrientation() const
{
	if (!m_bUsesOrientation) return Asura_Maths::IdentityMatrix3x3;

	// Strip x and z rotations
	Asura_Matrix_3x3 xOri = GetOrientation();

	Asura_Euler_3 xEul;
	xEul.FromMatrix(xOri);
	xEul.x = 0;
	xEul.z = 0;
	xEul.ToMatrix(xOri);
	
	return xOri;
}

AVP_ServerEntity_CommandPost* AVP_ServerEntity_CommandPost::FindNearestEntity( const Asura_Vector_3& xPosition, int iFaction )
{
	float fLowestMagnitudeSquared = 99999999999.9f;
	AVP_ServerEntity_CommandPost* pxClosestCmdPst = NULL;
	for ( Asura_Hashed_List_It<AVP_ServerEntity_CommandPost> xIt( &s_xList ); !xIt.Done(); xIt.Next() )
	{
		AVP_ServerEntity_CommandPost* pxCmdPst = xIt.GetCurrent();
		Asura_Assert(pxCmdPst,"This should be impossible?");
		if (!pxCmdPst) continue;

		if ((pxCmdPst->GetCurrentFaction() == iFaction )&&(pxCmdPst->IsEntityActive()))
		{
			// Assume CP is centroid of child nodes
			Asura_Vector_3 xDist = pxCmdPst->GetPosition() - xPosition; 
            if (xDist.MagnitudeSquared() < fLowestMagnitudeSquared)
			{
				fLowestMagnitudeSquared = xDist.MagnitudeSquared();
				pxClosestCmdPst = pxCmdPst;
			}
		}
	}

	Asura_Assert(pxClosestCmdPst,"Couldnt find any command posts for supplied faction");
	return pxClosestCmdPst;
}

void AVP_ServerEntity_CommandPost::SetInitialTeam( AVP_Settings::Team eTeam )
{
	m_eInitialTeam = eTeam;
	m_iCurrentFaction = eTeam;
	m_iCapturingFaction = AVP_Settings::AVP_TEAM_NONE;
	m_fCaptureTimer = GetCaptureTime();
	m_fLastCapturedSpawnTime = 0.0f;
}


// message functions
void AVP_Message_CommandPost_ClientUpdate::WriteToMessage( const AVP_ServerEntity_CommandPost* pxCmdPost )
{
	PARENT::FillPhysicalObject( pxCmdPost );

	// write own data
	m_fCaptureTime		= pxCmdPost->GetCaptureTime();
	m_fCaptureTimer		= pxCmdPost->GetCaptureTimer();
	m_uCurrentFaction	= pxCmdPost->GetCurrentFaction();
	m_uCapturingFaction = pxCmdPost->GetCapturingFaction();
	m_uCapturingGuid    = pxCmdPost->GetCapturingGuid();

	// flags
	m_uCommandPostMsgFlags = 0;
	m_bContested		 = pxCmdPost->IsContested();
	m_bCaptureDisabled   = pxCmdPost->IsCaptureDisabled() || pxCmdPost->IsCaptureDisabledForCurrrentGametype();
	m_bDefaultSpawnPoint = pxCmdPost->IsDefaultSpawnPoint();
	m_bMobileCommandPost = pxCmdPost->IsMobileCommandPost();
}

void AVP_Message_CommandPost_ClientUpdate::ReadFromMessage( AVP_ClientEntity_CommandPost* pxCmdPost, float fSentTime ) const
{
	//PARENT::ReadFromMessage( pxCmdPost, fSentTime );

	// set data for client
	pxCmdPost->SetCaptureTime( m_fCaptureTime );
	pxCmdPost->SetCaptureTimer( m_fCaptureTimer );
	pxCmdPost->SetCurrentFaction( m_uCurrentFaction );
	pxCmdPost->SetCapturingFaction(m_uCapturingFaction);
	pxCmdPost->SetCapturingGuid( m_uCapturingGuid );

	pxCmdPost->SetContested( m_bContested );
	pxCmdPost->SetCaptureDisabled( m_bCaptureDisabled );
	pxCmdPost->SetDefaultSpawnPoint( m_bDefaultSpawnPoint );
	pxCmdPost->SetMobileCommandPost( m_bMobileCommandPost );
}

#ifdef ASURA_SUPPORT_NETMSG_SYSTEM
void AVP_Message_CommandPost_ClientUpdate::RegisterFields( u_short usMsgID, u_short usEntityClassification )
{
	Asura_Assert( usMsgID == ASURA_MSG_CLIENTUPDATE, "invalid msg ID" ); 
	Asura_Assert( !Asura_NetMsg::FindNetMsgObject( usMsgID, usEntityClassification ), "already exists" );

	u_char ucNumFields = GetNumFields();

	Asura_NetMsg* pxNetMsg = new Asura_NetMsg( usMsgID, usEntityClassification, ucNumFields, sizeof(AVP_Message_CommandPost_ClientUpdate) );
	RegisterFields( pxNetMsg );

	pxNetMsg->EndRegistration();
}

void AVP_Message_CommandPost_ClientUpdate::RegisterFields( class Asura_NetMsg* pxNetMsg )
{
	AVP_Message_CommandPost_ClientUpdate xDummy;
	PARENT::RegisterFields( pxNetMsg );

	Asura_NetMsg_RegisterFloatFieldBits( pxNetMsg, xDummy, Asura_FieldType_CompressedFloat, 16, 0.0f, fAVP_COMMAND_POST_MAX_CAPTURE_TIME, m_fCaptureTime, 1, Asura_Threshold_ConstantValue, 0.0f);
	Asura_NetMsg_RegisterFloatFieldBits( pxNetMsg, xDummy, Asura_FieldType_CompressedFloat, 16, 0.0f, fAVP_COMMAND_POST_MAX_CAPTURE_TIME, m_fCaptureTimer, 1, Asura_Threshold_AnyChange, 0.0f);
	Asura_NetMsg_RegisterField( pxNetMsg, xDummy, Asura_FieldType_Guid, m_uCapturingGuid, 1, Asura_Threshold_AnyChange, 0.0f );
	Asura_NetMsg_RegisterField( pxNetMsg, xDummy, Asura_FieldType_uint,  m_uCurrentFaction, 1, Asura_Threshold_AnyChange, 0.0f );
	Asura_NetMsg_RegisterField( pxNetMsg, xDummy, Asura_FieldType_uint,  m_uCapturingFaction, 1, Asura_Threshold_AnyChange, 0.0f );
	Asura_NetMsg_RegisterBitfieldFieldBits( pxNetMsg, xDummy, Asura_FieldType_uint_Bitfield, 4, 0, m_uCommandPostMsgFlags, Asura_Threshold_AnyChange, ASURA_NETMSG_SINGLEBITS );
}
#endif // #ifdef ASURA_SUPPORT_NETMSG_SYSTEM

//////////////////////////////////////////////////////////////////////////
// AVP_CommandPost_Node_Array											//
//																		//
// Created by PJC 07/03/07			`									//
//																		//
// For storing details of a command posts child nodes (used for			//
// providing multiple spawn locations									//
//////////////////////////////////////////////////////////////////////////

AVP_CommandPost_Node_Array::AVP_CommandPost_Node_Array()
{
	m_uFreeNodes = s_uMAX_NODES;
}

// Attempts to add the specified node, returns true if sucessful
bool AVP_CommandPost_Node_Array::AddNode( Asura_Guid uGuid, u_int m_uProbability )
{
	if ( (m_uFreeNodes != 0) && (!HasNode(uGuid)) )
	{
		int uNextElement = GetFreeElement();
		if (uNextElement != -1)
		{
			m_axChildNodes[uNextElement].m_uGuid = uGuid;
			m_axChildNodes[uNextElement].m_uProbability = m_uProbability;
			m_uFreeNodes--;
			return true;
		}
	}
	return false;
}

// Returns the next free element in the (unordered) array, or -1 if its full
int AVP_CommandPost_Node_Array::GetFreeElement()
{
	for (int i = 0; i < s_uMAX_NODES; i++)
	{
		if (m_axChildNodes[i].m_uGuid == ASURA_GUID_UNREGISTERED) return i;
	}
	return -1;
}


// Attempts to remove the corresponding node. returns true if successful
bool AVP_CommandPost_Node_Array::RemoveNode( Asura_Guid uGuid )
{
	for (int i = 0; i < s_uMAX_NODES; i++)
	{
		if (m_axChildNodes[i].m_uGuid == uGuid) 
		{
			m_axChildNodes[i].m_uGuid = ASURA_GUID_UNREGISTERED;
			m_axChildNodes[i].m_uProbability = 0;
			m_uFreeNodes++;
			return true;
		}
	}	
	return false;
}


// returns guid of first invalid node. Should be repeated until it returns ASURA_GUID_UNREGISTERED.
Asura_Guid AVP_CommandPost_Node_Array::ValidateNodes() const
{
	//for (int i = 0; i < s_uMAX_NODES; i++)
	//{
	//	if (m_axChildNodes[i].m_uGuid != ASURA_GUID_UNREGISTERED)
	//	{
	//		if ( AVP_ServerEntity_CommandPost_Node::FindEntity(m_axChildNodes[i].m_uGuid) == NULL)
	//		{
	//			return m_axChildNodes[i].m_uGuid;
	//		}
	//	}
	//}
	return ASURA_GUID_UNREGISTERED;
}

// returns whether any child nodes are present
bool AVP_CommandPost_Node_Array::IsEmpty() const
{
	return ( static_cast<int>(m_uFreeNodes) == s_uMAX_NODES );
}

// returns whether there are any spaces left in the array
bool AVP_CommandPost_Node_Array::IsFull() const
{
	return ( m_uFreeNodes == 0 );
}

// Finds the probability of a given node, returns -1 if the node doesn't exist
int AVP_CommandPost_Node_Array::FindNode( Asura_Guid uGuid ) const
{
	for (int i = 0; i < s_uMAX_NODES; i++)
	{
		if (m_axChildNodes[i].m_uGuid == uGuid) return m_axChildNodes[i].m_uProbability;
	}
	return -1;
}

// Returns whether a node is present in the list
bool AVP_CommandPost_Node_Array::HasNode( Asura_Guid uGuid ) const
{
	for (int i = 0; i < s_uMAX_NODES; i++)
	{
		if (m_axChildNodes[i].m_uGuid == uGuid) return true;
	}
	return false;
}

const AVP_CommandPost_Node_ArrayElement& AVP_CommandPost_Node_Array::GetNode( u_int iIndex ) const
{
	Asura_Assert(static_cast<int>(iIndex) < s_uMAX_NODES, "Invalid index");
	return (static_cast<int>(iIndex) < s_uMAX_NODES) ? m_axChildNodes[iIndex] : m_axChildNodes[0];
}

u_int& AVP_CommandPost_Node_Array::GetNodeProbability( u_int iIndex )
{
	Asura_Assert(static_cast<int>(iIndex) < s_uMAX_NODES, "Invalid index");
	return (static_cast<int>(iIndex) < s_uMAX_NODES) ? m_axChildNodes[iIndex].m_uProbability : m_axChildNodes[0].m_uProbability;
}

u_int AVP_CommandPost_Node_Array::GetSumOfProbabilities() const
{
	u_int uCount = 0;
	for (int i = 0; i < s_uMAX_NODES; i++)
	{
		if (m_axChildNodes[i].m_uGuid != ASURA_GUID_UNREGISTERED) uCount += m_axChildNodes[i].m_uProbability;
	}
	return uCount;
}

void AVP_CommandPost_Node_Array::WriteToChunkStream( Asura_Chunk_Stream& xStream ) const
{
	xStream << (s_uMAX_NODES - m_uFreeNodes);
	
	u_int uCount = 0;
	for (int i = 0; i < s_uMAX_NODES; i++)
	{
		if (m_axChildNodes[i].m_uGuid != ASURA_GUID_UNREGISTERED)
		{
			xStream << m_axChildNodes[i].m_uGuid;
			xStream << m_axChildNodes[i].m_uProbability;
			uCount++;
		}
	}
	Asura_Assert(uCount == (s_uMAX_NODES - m_uFreeNodes),"Problem streaming CommandPost_Node_Array");
}

void AVP_CommandPost_Node_Array::ReadFromChunkStream( Asura_Chunk_Stream& xStream )
{
	int uStoredNodes;
	xStream >> uStoredNodes;
	Asura_Assert( uStoredNodes <= s_uMAX_NODES, "Too many nodes to load!" );

	for (int i = 0; i < uStoredNodes; i++)
	{
		xStream >> m_axChildNodes[i].m_uGuid;
		xStream >> m_axChildNodes[i].m_uProbability;
		Asura_Assert(m_axChildNodes[i].m_uGuid != ASURA_GUID_UNREGISTERED,"Shouldn't be streaming in empty nodes");
	}

	m_uFreeNodes = s_uMAX_NODES - uStoredNodes;
}

bool AVP_ServerEntity_CommandPost::IsCaptureDisabledForCurrrentGametype()
{
	//return (!AVP_Multiplayer_GameRules::IsDominationBasedGame());
	return false;
}

void AVP_ServerEntity_CommandPost::DebugCommandPosts()
{
	AVP_ServerEntity_CommandPost* pxPost;
	Asura_Hashed_List_It<AVP_ServerEntity_CommandPost> xIterator( AVP_ServerEntity_CommandPost::GetList() );

	u_int num0=0,num1=0,num2=0,num3=0,num4=0;
	u_int active0=0,active1=0,active2=0,active3=0,active4=0;
	while( !xIterator.Done() )
	{
		pxPost = xIterator.GetCurrent();
		xIterator.Next();
		switch (pxPost->GetCurrentFaction())
		{
		case 0:
			num0++;
			if (pxPost->IsEntityActive()) active0++;
			break;
		case 1:
			num1++;
			if (pxPost->IsEntityActive()) active1++;
			break;
		case 2:
			num2++;
			if (pxPost->IsEntityActive()) active2++;
			break;
		case 3:
			num3++;
			if (pxPost->IsEntityActive()) active3++;
			break;
		case 4:
			num4++;
			if (pxPost->IsEntityActive()) active4++;
			break;
		}
	}

	#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
		Asura_CommandConsole::Printf("0: %d - active %d", num0,active0);
		Asura_CommandConsole::Printf("1: %d - active %d", num1,active1);
		Asura_CommandConsole::Printf("2: %d - active %d", num2,active2);
		Asura_CommandConsole::Printf("3: %d - active %d", num3,active3);
		Asura_CommandConsole::Printf("4: %d - active %d", num4,active4);
	#endif 
}

void AVP_ServerEntity_CommandPost::OnEntityActivate()
{
	PARENT::OnEntityActivate();
	//AVP_GameRules::CommandPostsChanged();
}

void AVP_ServerEntity_CommandPost::OnEntityDeactivate()
{
	PARENT::OnEntityDeactivate();
	//AVP_GameRules::CommandPostsChanged();
}

#ifdef ASURA_TOOLS
void AVP_ServerEntity_CommandPost::ApplyChangesForTemplateInstance(Tools_EntityTemplateInstance* pxInstance)
{
	PARENT::ApplyChangesForTemplateInstance(pxInstance);

	// Guids in node array need updating... need to delete and re-add each one...
	for (u_int i = 0; i < AVP_CommandPost_Node_Array::s_uMAX_NODES; i++)
	{
		const AVP_CommandPost_Node_ArrayElement& xNode = GetChildNodes().GetNode(i);
		if (xNode.m_uGuid != ASURA_GUID_UNREGISTERED)
		{
			const u_int uProb = xNode.m_uProbability;
			Asura_Guid xGuid = xNode.m_uGuid;
			m_xChildNodes.RemoveNode( xGuid );

			xGuid = pxInstance->ConvertGuid(xGuid);
			m_xChildNodes.AddNode(xGuid, uProb);
		}
	}
}
#endif

//////////////////////////////////////////////////////////////////////////

/***************/
/* End of File */
/***************/
