//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//

#include "cbase.h"
#include "team_train_watcher.h"
#include "trigger_area_capture.h"
#include "player.h"
#include "teamplay_gamerules.h"
#include "team.h"
#include "team_objectiveresource.h"
#include "team_control_point_master.h"
#include "teamplayroundbased_gamerules.h"

extern ConVar mp_capstyle;
extern ConVar mp_blockstyle;
extern ConVar mp_capdeteriorate_time;

IMPLEMENT_AUTO_LIST( ITriggerAreaCaptureAutoList );

BEGIN_DATADESC(CTriggerAreaCapture)

	// Touch functions
	DEFINE_FUNCTION( CTriggerAreaCaptureShim::Touch ),

	// Think functions
	DEFINE_THINKFUNC( CaptureThink ),

	// Keyfields
	DEFINE_KEYFIELD( m_iszCapPointName,	FIELD_STRING,	"area_cap_point" ),
	DEFINE_KEYFIELD( m_flCapTime,		FIELD_FLOAT,	"area_time_to_cap" ),

//	DEFINE_FIELD( m_iCapMode, FIELD_INTEGER ),
//	DEFINE_FIELD( m_bCapturing, FIELD_BOOLEAN ),
//	DEFINE_FIELD( m_nCapturingTeam, FIELD_INTEGER ),
//	DEFINE_FIELD( m_nOwningTeam, FIELD_INTEGER ),
//	DEFINE_FIELD( m_nTeamInZone, FIELD_INTEGER ),
//	DEFINE_FIELD( m_fTimeRemaining, FIELD_FLOAT ),
//	DEFINE_FIELD( m_flLastReductionTime, FIELD_FLOAT ),
//	DEFINE_FIELD( m_bBlocked, FIELD_BOOLEAN ),
//	DEFINE_FIELD( m_TeamData, CUtlVector < perteamdata_t > ),
//	DEFINE_FIELD( m_Blockers, CUtlVector < blockers_t > ),
//	DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
//	DEFINE_FIELD( m_iAreaIndex, FIELD_INTEGER ),
//	DEFINE_FIELD( m_hPoint, CHandle < CTeamControlPoint > ),
//	DEFINE_FIELD( m_bRequiresObject, FIELD_BOOLEAN ),
//	DEFINE_FIELD( m_iCapAttemptNumber, FIELD_INTEGER ),

	// Inputs
	DEFINE_INPUTFUNC( FIELD_VOID, "RoundSpawn", InputRoundSpawn ),
	DEFINE_INPUTFUNC( FIELD_STRING, "SetTeamCanCap", InputSetTeamCanCap ),
	DEFINE_INPUTFUNC( FIELD_STRING, "SetControlPoint", InputSetControlPoint ),
	DEFINE_INPUTFUNC( FIELD_VOID, "CaptureCurrentCP", InputCaptureCurrentCP ),

	// Outputs
	DEFINE_OUTPUT( m_OnStartTeam1,	"OnStartTeam1" ),
	DEFINE_OUTPUT( m_OnStartTeam2,	"OnStartTeam2" ),
	DEFINE_OUTPUT( m_OnBreakTeam1,	"OnBreakTeam1" ),
	DEFINE_OUTPUT( m_OnBreakTeam2,	"OnBreakTeam2" ),
	DEFINE_OUTPUT( m_OnCapTeam1,	"OnCapTeam1" ),
	DEFINE_OUTPUT( m_OnCapTeam2,	"OnCapTeam2" ),

	DEFINE_OUTPUT( m_StartOutput,	"OnStartCap" ),
	DEFINE_OUTPUT( m_BreakOutput,	"OnBreakCap" ),
	DEFINE_OUTPUT( m_CapOutput,		"OnEndCap" ),

	DEFINE_OUTPUT( m_OnNumCappersChanged, "OnNumCappersChanged" ),
	DEFINE_OUTPUT( m_OnNumCappersChanged2, "OnNumCappersChanged2" ),

END_DATADESC();

LINK_ENTITY_TO_CLASS( trigger_capture_area, CTriggerAreaCapture );

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CTriggerAreaCapture::CTriggerAreaCapture()
{
	m_TeamData.SetSize( GetNumberOfTeams() );
	m_bStartTouch = false;
	m_hTrainWatcher = NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::Spawn( void )
{
	BaseClass::Spawn();

	AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );

	InitTrigger();
	
	Precache();

	m_iAreaIndex = -1;

	SetTouch ( &CTriggerAreaCaptureShim::Touch );		
	SetThink( &CTriggerAreaCapture::CaptureThink );
	SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );

	for ( int i = 0; i < m_TeamData.Count(); i++ )
	{
		if ( m_TeamData[i].iNumRequiredToCap < 1 )
		{
			m_TeamData[i].iNumRequiredToCap = 1;
		}

		if ( m_TeamData[i].iNumRequiredToStartCap < 1 )
		{
			m_TeamData[i].iNumRequiredToStartCap = 1;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::KeyValue( const char *szKeyName, const char *szValue )
{
	if ( !Q_strncmp( szKeyName, "team_numcap_", 12 ) )
	{
		int iTeam = atoi(szKeyName+12);
		Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );

		m_TeamData[iTeam].iNumRequiredToCap = atoi(szValue);
	}
	else if ( !Q_strncmp( szKeyName, "team_cancap_", 12 ) )
	{
		int iTeam = atoi(szKeyName+12);
		Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );

		m_TeamData[iTeam].bCanCap = (atoi(szValue) != 0);
	}
	else if ( !Q_strncmp( szKeyName, "team_spawn_", 11 ) )
	{
		int iTeam = atoi(szKeyName+11);
		Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );

		m_TeamData[iTeam].iSpawnAdjust = atoi(szValue);
	}
	else if ( !Q_strncmp( szKeyName, "team_startcap_", 14 ) )
	{
		int iTeam = atoi(szKeyName+14);
		Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );

		m_TeamData[iTeam].iNumRequiredToStartCap = atoi(szValue);
	}
	else
	{
		return BaseClass::KeyValue( szKeyName, szValue );
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::Precache( void )
{
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetAreaIndex( int index )
{
	m_iAreaIndex = index;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::IsActive( void )
{
	return !m_bDisabled;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::StartTouch(CBaseEntity *pOther)
{
	BaseClass::StartTouch( pOther );

	if ( PassesTriggerFilters(pOther) && m_hPoint )
	{
		m_nOwningTeam = m_hPoint->GetOwner();

		IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_starttouch" );
		if ( event )
		{
			event->SetInt( "player", pOther->entindex() );
			event->SetInt( "area", m_hPoint->GetPointIndex() );
			gameeventmanager->FireEvent( event );
		}

		// Call capture think immediately to make it update our area's player counts.
		// If we don't do this, the player can receive the above event telling him he's
		// in a zone, but the objective resource still thinks he's not.
		m_bStartTouch = true;
		CaptureThink();
		m_bStartTouch = false;

		if ( m_bCapturing )
		{
			CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
			if ( pMaster )
			{
				float flRate = pMaster->GetPartialCapturePointRate();

				if ( flRate > 0.0f )
				{
					CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
					if ( pPlayer && pPlayer->GetTeamNumber() == m_nCapturingTeam )
					{
						pPlayer->StartScoringEscortPoints( flRate );
					}
				}		
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::EndTouch(CBaseEntity *pOther)
{
	if ( PassesTriggerFilters(pOther) && m_hPoint )
	{
		IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_endtouch" );
		if ( event )
		{
			event->SetInt( "player", pOther->entindex() );
			event->SetInt( "area", m_hPoint->GetPointIndex() );
			gameeventmanager->FireEvent( event );
		}

		// incase we leave but the area keeps capturing
		CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
		if ( pPlayer )
		{
			pPlayer->StopScoringEscortPoints();
		}
	}

	BaseClass::EndTouch( pOther );
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::CaptureModeScalesWithPlayers() const
{
	return mp_capstyle.GetBool();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::AreaTouch( CBaseEntity *pOther )
{
	if ( !IsActive() )
		return;
	if ( !PassesTriggerFilters(pOther) )
		return;

	// Don't cap areas unless the round is running
	if ( !TeamplayGameRules()->PointsMayBeCaptured() )
		return;

	Assert( m_iAreaIndex != -1 );

	// dont touch for non-alive or non-players
	if( !pOther->IsPlayer() || !pOther->IsAlive() )
		return;

	// make sure this point is in the round being played (if we're playing one)
	CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
	if ( pMaster && m_hPoint )
	{
		if ( !pMaster->IsInRound( m_hPoint ) )
		{
			return;
		}
	}

	if ( m_hPoint )
	{
		m_nOwningTeam = m_hPoint->GetOwner();
	}

	CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(pOther);
	Assert( pPlayer );

	if ( pPlayer->GetTeamNumber() != m_nOwningTeam )
	{
		if ( m_TeamData[ pPlayer->GetTeamNumber() ].bCanCap )
		{
			DisplayCapHintTo( pPlayer );
		}
	}
}

ConVar mp_simulatemultiplecappers( "mp_simulatemultiplecappers", "1", FCVAR_CHEAT );

#define MAX_CAPTURE_TEAMS 8

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::CaptureThink( void )
{
	SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );

	// make sure this point is in the round being played (if we're playing one)
	CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
	if ( pMaster && m_hPoint )
	{
		if ( !pMaster->IsInRound( m_hPoint ) )
		{
			return;
		}
	}

	if ( !TeamplayGameRules()->PointsMayBeCaptured() )
	{
		// Points aren't allowed to be captured. If we were 
		// being captured, we need to clean up and reset.
		if ( m_bCapturing )
		{
			BreakCapture( false );
			UpdateNumPlayers();
		}
		return;
	}

	// go through our list of players
	Assert( GetNumberOfTeams() <= MAX_CAPTURE_TEAMS );
	int iNumPlayers[MAX_CAPTURE_TEAMS];
	int iNumBlockablePlayers[MAX_CAPTURE_TEAMS]; // Players in the zone who can't cap, but can block / pause caps
	CBaseMultiplayerPlayer *pFirstPlayerTouching[MAX_CAPTURE_TEAMS];
	for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
	{
		iNumPlayers[i] = 0;
		iNumBlockablePlayers[i] = 0;
		pFirstPlayerTouching[i] = NULL;
	}

	if ( m_hPoint )
	{
		// Loop through the entities we're touching, and find players
		for ( int i = 0; i < m_hTouchingEntities.Count(); i++ )
		{
			CBaseEntity *ent = m_hTouchingEntities[i];
			if ( ent && ent->IsPlayer() )
			{
				CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer(ent);
				if ( pPlayer->IsAlive() )
				{	
					int iTeam = pPlayer->GetTeamNumber();

					// If a team's not allowed to cap a point, don't count players in it at all
					if ( !TeamplayGameRules()->TeamMayCapturePoint( iTeam, m_hPoint->GetPointIndex() ) )
						continue;

					if ( !TeamplayGameRules()->PlayerMayCapturePoint( pPlayer, m_hPoint->GetPointIndex() ) )
					{
						if ( TeamplayGameRules()->PlayerMayBlockPoint( pPlayer, m_hPoint->GetPointIndex() ) )
						{
							if ( iNumPlayers[iTeam] == 0 && iNumBlockablePlayers[iTeam] == 0 )
							{
								pFirstPlayerTouching[iTeam] = pPlayer;
							}

							iNumBlockablePlayers[iTeam] += TeamplayGameRules()->GetCaptureValueForPlayer( pPlayer );
						}
						continue;
					}

					if ( iTeam >= FIRST_GAME_TEAM )
					{
						if ( iNumPlayers[iTeam] == 0 && iNumBlockablePlayers[iTeam] == 0 )
						{
							pFirstPlayerTouching[iTeam] = pPlayer;
						}

						iNumPlayers[iTeam] += TeamplayGameRules()->GetCaptureValueForPlayer( pPlayer );
					}
				}
			}
		}
	}

	int iTeamsInZone = 0;
	bool bUpdatePlayers = false;
	m_nTeamInZone = TEAM_UNASSIGNED;
	for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
	{
		iNumPlayers[i] *= mp_simulatemultiplecappers.GetInt();

		if ( m_TeamData[i].iNumTouching != iNumPlayers[i] )
		{
			m_TeamData[i].iNumTouching = iNumPlayers[i];
			bUpdatePlayers = true;
		}
		m_TeamData[i].iBlockedTouching = m_TeamData[i].iNumTouching;

		if ( m_TeamData[i].iNumTouching )
		{
			iTeamsInZone++;

			m_nTeamInZone = i;
		}
	}

	if ( iTeamsInZone > 1 )
	{
		m_nTeamInZone = TEAM_UNASSIGNED;
	}
	else
	{
		// If we've got non-cappable, yet blockable players here for the team that's defending, they 
		// need to block the cap. This catches cases like the TF invulnerability, which needs to block
		// caps, but isn't allowed to contribute to a cap.
		for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
		{
			if ( !iNumBlockablePlayers[i] || m_nTeamInZone == i )
				continue;

			iTeamsInZone++;
		}
	}

	UpdateTeamInZone();

	bool bBlocked = false;

	// If the cap is being blocked, reset the number of players so the client
	// knows to stop the capture as well.
	if ( mp_blockstyle.GetInt() == 1 )
	{
		if ( m_bCapturing && iTeamsInZone > 1 )
		{
			bBlocked = true;

			for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
			{
				iNumPlayers[i] = 0;
				if ( m_TeamData[i].iNumTouching != iNumPlayers[i] )
				{
					m_TeamData[i].iNumTouching = iNumPlayers[i];
					bUpdatePlayers = true;
				}
			}
		}
	}

	if ( bUpdatePlayers )
	{
		UpdateNumPlayers( bBlocked );
	}

	// When a player blocks, tell them the cap index and attempt number
	// only give successive blocks to them if the attempt number is different
	if ( m_bCapturing )
	{
		if ( m_hPoint )
		{
			m_hPoint->SetLastContestedAt( gpGlobals->curtime );
		}

		// Calculate the amount of modification to the cap time
		float flTimeDelta = gpGlobals->curtime - m_flLastReductionTime;

		float flReduction = flTimeDelta;
		if ( CaptureModeScalesWithPlayers() )
		{
			// Diminishing returns for successive players.
			for ( int i = 1; i < m_TeamData[m_nTeamInZone].iNumTouching; i++ )
			{
				flReduction += (flTimeDelta / (float)(i+1));
			}
		}
		m_flLastReductionTime = gpGlobals->curtime;

		//if more than one team is in the zone
		if( iTeamsInZone > 1 )
		{
			if ( !m_bBlocked )
			{
				m_bBlocked = true;
				UpdateBlocked();
			}

			// See if anyone gets credit for the block
			float flPercentToGo = m_fTimeRemaining / m_flCapTime;
			if ( CaptureModeScalesWithPlayers() )
			{
				flPercentToGo = m_fTimeRemaining / ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
			}

			if ( ( flPercentToGo <= 0.5 || TeamplayGameRules()->PointsMayAlwaysBeBlocked() ) && m_hPoint )
			{
				// find the first player that is not on the capturing team
				// they have just broken a cap and should be rewarded		
				// tell the player the capture attempt number, for checking later
				CBaseMultiplayerPlayer *pBlockingPlayer = NULL;
				for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
				{
					if ( m_nCapturingTeam == i )
						continue;

					if ( pFirstPlayerTouching[i] )
					{
						pBlockingPlayer = pFirstPlayerTouching[i];
						break;
					}
				}
				Assert( pBlockingPlayer );

				if ( pBlockingPlayer )
				{
					bool bRepeatBlocker = false;
					for ( int i = m_Blockers.Count()-1; i >= 0; i-- )
					{
						if ( m_Blockers[i].hPlayer != pBlockingPlayer )
							continue;

						// If this guy's was a blocker, but not valid now, remove him from the list
						if ( m_Blockers[i].iCapAttemptNumber != m_iCapAttemptNumber || !IsTouching(m_Blockers[i].hPlayer) ||
							 ( TeamplayGameRules()->PointsMayAlwaysBeBlocked() && m_Blockers[i].flNextBlockTime < gpGlobals->curtime && m_bStartTouch ) )
						{
							m_Blockers.Remove(i);
							continue;
						}

						bRepeatBlocker = true;
						break;
					}

					if ( !bRepeatBlocker )
					{
                        m_hPoint->CaptureBlocked( pBlockingPlayer );

						// Add this guy to our blocker list
						int iNew = m_Blockers.AddToTail();
						m_Blockers[iNew].hPlayer = pBlockingPlayer;
						m_Blockers[iNew].iCapAttemptNumber = m_iCapAttemptNumber;
						m_Blockers[iNew].flNextBlockTime = gpGlobals->curtime + 10.0f;
					}
				}
			}

			if ( mp_blockstyle.GetInt() == 0 )
			{
				BreakCapture( false );
			}
			return;
		}

		if ( m_bBlocked )
		{
			m_bBlocked = false;
			UpdateBlocked();
		}

		float flTotalTimeToCap = m_flCapTime;
		if ( CaptureModeScalesWithPlayers() )
		{
			flTotalTimeToCap = ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
		}

		// Now remove the reduction amount after we've determined there's only 1 team in the area
		if ( m_nCapturingTeam == m_nTeamInZone )
		{
			SetCapTimeRemaining( m_fTimeRemaining - flReduction );
		}
		else if ( m_nOwningTeam == TEAM_UNASSIGNED && m_nTeamInZone != TEAM_UNASSIGNED )
		{
			SetCapTimeRemaining( m_fTimeRemaining + flReduction );
		}
		else
		{
			// Caps deteriorate over time
			if ( TeamplayRoundBasedRules() && m_hPoint && TeamplayRoundBasedRules()->TeamMayCapturePoint(m_nCapturingTeam,m_hPoint->GetPointIndex()) )
			{
				float flDecreaseScale = CaptureModeScalesWithPlayers() ? mp_capdeteriorate_time.GetFloat() : flTotalTimeToCap;
				float flDecrease = (flTotalTimeToCap / flDecreaseScale) * flTimeDelta;
				if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->InOvertime() )
				{
					flDecrease *= 6;
				}
				SetCapTimeRemaining( m_fTimeRemaining + flDecrease );
			}
			else
			{
				SetCapTimeRemaining( flTotalTimeToCap );
			}
		}

		/*
		//if no-one is in the area
		if( iTeamsInZone == 0 )
		{
			BreakCapture( true );
			return;
		}
		
		//if they've lost the number of players needed to cap
		int iTeamMembersHere = m_TeamData[m_nCapturingTeam].iNumTouching + iNumBlockablePlayers[m_nCapturingTeam];
		if ( (iTeamMembersHere == 0 ) || (mp_capstyle.GetInt() == 0 && iTeamMembersHere < m_TeamData[m_nCapturingTeam].iNumRequiredToCap) )
		{
			BreakCapture( true );
			return;
		}
		*/

		// if the cap is done
		if ( m_fTimeRemaining <= 0 )
		{
			EndCapture( m_nCapturingTeam );
			return;		//we're done
		}
		else
		{
			// We may get several simultaneous CaptureThink calls from StartTouch if there are several players on the trigger 
			// when it is enabled (like in Raid mode). We haven't started reducing m_fTimeRemaining yet but the second call to CaptureThink
			// from StartTouch has m_bCapturing set to true and we hit this condition and call BreakCapture right away.
			// We put this check here to prevent calling BreakCapture from the StartTouch call to CaptureThink. If the capture should
			// really be broken it will happen the next time the trigger thinks on its own.
			if ( !m_bStartTouch )
			{
				if ( m_fTimeRemaining >= flTotalTimeToCap )
				{
					BreakCapture( false );
					return;
				}
			}
		}
	}
	else	
	{
		// If there are any teams in the zone that aren't the owner, try to start capping
		if ( iTeamsInZone > 0 )
		{
			for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
			{
				if ( !m_TeamData[i].bCanCap || m_nOwningTeam == i )
					continue;

				if ( m_TeamData[i].iNumTouching == 0 )
					continue;

				if ( m_TeamData[i].iNumTouching < m_TeamData[i].iNumRequiredToStartCap )
					continue;

				if ( !CaptureModeScalesWithPlayers() && m_TeamData[i].iNumTouching < m_TeamData[i].iNumRequiredToCap )
					continue;

				StartCapture( i, CAPTURE_NORMAL );
				break;
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetCapTimeRemaining( float flTime )
{
	m_fTimeRemaining = flTime;

	float flCapPercentage = 0;
	if ( m_nCapturingTeam )
	{
		flCapPercentage = m_fTimeRemaining / m_flCapTime;
		if ( CaptureModeScalesWithPlayers() )
		{
			flCapPercentage = m_fTimeRemaining / ((m_flCapTime * 2) * m_TeamData[m_nCapturingTeam].iNumRequiredToCap);
		}
	}

	ObjectiveResource()->SetCPCapPercentage( m_hPoint->GetPointIndex(), flCapPercentage );

	if ( m_hPoint )
	{
		m_hPoint->UpdateCapPercentage();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetOwner( int team )
{
	//break any current capturing
	BreakCapture( false );

	HandleRespawnTimeAdjustments( m_nOwningTeam, team );

	//set the owner to the passed value
	m_nOwningTeam = team;

	UpdateOwningTeam();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::ForceOwner( int team )
{
	SetOwner( team );

	if ( m_hPoint )
	{
		m_hPoint->ForceOwner( team );
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::HandleRespawnTimeAdjustments( int oldTeam, int newTeam )
{
	if ( oldTeam > LAST_SHARED_TEAM )
	{
		// reverse the adjust made when the old team captured this point (if we made one)
		if ( m_TeamData[oldTeam].iSpawnAdjust != 0 )
		{
			TeamplayRoundBasedRules()->AddTeamRespawnWaveTime( oldTeam, -m_TeamData[oldTeam].iSpawnAdjust );
		}
	}

	if ( newTeam > LAST_SHARED_TEAM )
	{
		if ( m_TeamData[newTeam].iSpawnAdjust != 0 )
		{
			TeamplayRoundBasedRules()->AddTeamRespawnWaveTime( newTeam, m_TeamData[newTeam].iSpawnAdjust );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::StartCapture( int team, int capmode )
{
	// Remap team to get first game team = 1
	switch ( team - FIRST_GAME_TEAM+1 )
	{
	case 1: 
		m_OnStartTeam1.FireOutput( this, this );
		break;
	case 2: 
		m_OnStartTeam2.FireOutput( this, this );
		break;
	default:
		Assert(0);
		break;
	}

	m_StartOutput.FireOutput(this,this);
	
	m_nCapturingTeam = team;

	UpdateNumPlayers();

	if ( CaptureModeScalesWithPlayers() )
	{
		SetCapTimeRemaining( ((m_flCapTime * 2) * m_TeamData[team].iNumRequiredToCap) );
	}
	else
	{
		SetCapTimeRemaining( m_flCapTime );
	}
	m_bCapturing = true;
	m_bBlocked = false;
	m_iCapMode = capmode;

	m_flLastReductionTime = gpGlobals->curtime;

	UpdateCappingTeam( m_nCapturingTeam );
	UpdateBlocked();

	if( m_hPoint )
	{
		int numcappers = 0;
		int cappingplayers[MAX_AREA_CAPPERS];

		GetNumCappingPlayers( m_nCapturingTeam, numcappers, cappingplayers );
		m_hPoint->CaptureStart( m_nCapturingTeam, numcappers, cappingplayers );
	}

	// tell all touching players to start racking up capture points
	CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
	if ( pMaster )
	{
		float flRate = pMaster->GetPartialCapturePointRate();

		if ( flRate > 0.0f )
		{
			// for each player touch
			CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
			if ( pTeam )
			{
				for ( int i=0;i<pTeam->GetNumPlayers();i++ )
				{
					CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
					if ( pPlayer && IsTouching( pPlayer ) )
					{
						pPlayer->StartScoringEscortPoints( flRate );
					}
				}
			}
		}		
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::GetNumCappingPlayers( int team, int &numcappers, int *cappingplayers )
{
	numcappers = 0;

	for ( int i = 1; i <= gpGlobals->maxClients; i++ )
	{
		CBaseEntity *ent = UTIL_PlayerByIndex( i );
		if ( ent )
		{
			CBaseMultiplayerPlayer *player = ToBaseMultiplayerPlayer(ent);

			if ( IsTouching( player ) && ( player->GetTeamNumber() == team ) ) // need to make sure disguised spies aren't included in the list of capping players
			{
				if ( numcappers < MAX_AREA_CAPPERS-1 )
				{
					cappingplayers[numcappers] = i;
					numcappers++;
				}
			}
		}
	}

	if ( numcappers < MAX_AREA_CAPPERS )
	{
		cappingplayers[numcappers] = 0;	//null terminate :)
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::EndCapture( int team )
{
	IncrementCapAttemptNumber();

	// Remap team to get first game team = 1
	switch ( team - FIRST_GAME_TEAM+1 )
	{
	case 1: 
		m_OnCapTeam1.FireOutput( this, this );
		break;
	case 2: 
		m_OnCapTeam2.FireOutput( this, this );
		break;
	default:
		Assert(0);
		break;
	}

	m_CapOutput.FireOutput(this,this);

	int numcappers = 0;
	int cappingplayers[MAX_AREA_CAPPERS];

	GetNumCappingPlayers( team, numcappers, cappingplayers );

	// Handle this before we assign the new team as the owner of this area
	HandleRespawnTimeAdjustments( m_nOwningTeam, team );
		
	m_nOwningTeam = team;
	m_bCapturing = false;
	m_nCapturingTeam = TEAM_UNASSIGNED;
	SetCapTimeRemaining( 0 );

	//there may have been more than one capper, but only report this one.
	//he hasn't gotten points yet, and his name will go in the cap string if its needed
	//first capper gets name sent and points given by flag.
	//other cappers get points manually above, no name in message

	//send the player in the cap string
	if( m_hPoint )
	{
		OnEndCapture( m_nOwningTeam );

		UpdateOwningTeam();
		m_hPoint->SetOwner( m_nOwningTeam, true, numcappers, cappingplayers );
		m_hPoint->CaptureEnd();
	}

	SetNumCappers( 0 );

	// tell all touching players to stop racking up capture points
	CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
	if ( pTeam )
	{
		for ( int i=0;i<pTeam->GetNumPlayers();i++ )
		{
			CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
			if ( pPlayer && IsTouching( pPlayer ) )
			{	
				pPlayer->StopScoringEscortPoints();					
			}
		}
	}

	// play any special cap sounds
	if ( TeamplayRoundBasedRules() )
	{
		TeamplayRoundBasedRules()->PlaySpecialCapSounds( m_nOwningTeam );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::BreakCapture( bool bNotEnoughPlayers )
{
	if( m_bCapturing )
	{
		// Remap team to get first game team = 1
		switch ( m_nCapturingTeam - FIRST_GAME_TEAM+1 )
		{
		case 1: 
			m_OnBreakTeam1.FireOutput( this, this );
			break;
		case 2: 
			m_OnBreakTeam2.FireOutput( this, this );
			break;
		default:
			Assert(0);
			break;
		}

		m_BreakOutput.FireOutput(this,this);

		m_bCapturing = false;
		m_nCapturingTeam = TEAM_UNASSIGNED;

		UpdateCappingTeam( TEAM_UNASSIGNED );

		if ( bNotEnoughPlayers )
		{
			IncrementCapAttemptNumber();
		}

		SetCapTimeRemaining( 0 );

		if( m_hPoint )
		{
			m_hPoint->CaptureEnd();

			// The point reverted to it's previous owner.
			IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_capture_broken" );
			if ( event )
			{
				event->SetInt( "cp", m_hPoint->GetPointIndex() );
				event->SetString( "cpname", m_hPoint->GetName() );
				event->SetFloat( "time_remaining", m_fTimeRemaining );
				gameeventmanager->FireEvent( event );
			}
		}

		SetNumCappers( 0 );

		// tell all touching players to stop racking up capture points
		CTeam *pTeam = GetGlobalTeam( m_nCapturingTeam );
		if ( pTeam )
		{
			for ( int i=0;i<pTeam->GetNumPlayers();i++ )
			{
				CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pTeam->GetPlayer(i) );
				if ( pPlayer && IsTouching( pPlayer ) )
				{
					pPlayer->StopScoringEscortPoints();					
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::IncrementCapAttemptNumber( void )
{
	m_iCapAttemptNumber++;

	m_Blockers.Purge();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::InputRoundSpawn( inputdata_t &inputdata )
{
	// find the flag we're linked to
	if( !m_hPoint )
	{
		m_hPoint = dynamic_cast<CTeamControlPoint*>( gEntList.FindEntityByName(NULL, STRING(m_iszCapPointName) ) );

		if ( m_hPoint )
		{
			m_nOwningTeam = m_hPoint->GetOwner();

			for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
			{
				m_hPoint->SetCappersRequiredForTeam( i, m_TeamData[i].iNumRequiredToCap );

				ObjectiveResource()->SetCPRequiredCappers( m_hPoint->GetPointIndex(), i, m_TeamData[i].iNumRequiredToCap );
				ObjectiveResource()->SetTeamCanCap( m_hPoint->GetPointIndex(), i, m_TeamData[i].bCanCap );

				if ( CaptureModeScalesWithPlayers() )
				{
					ObjectiveResource()->SetCPCapTime( m_hPoint->GetPointIndex(), i, (m_flCapTime * 2) * m_TeamData[i].iNumRequiredToCap );
				}
				else
				{
					ObjectiveResource()->SetCPCapTime( m_hPoint->GetPointIndex(), i, m_flCapTime );
				}

				ObjectiveResource()->SetCPCapTimeScalesWithPlayers( m_hPoint->GetPointIndex(), CaptureModeScalesWithPlayers() );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::InputSetTeamCanCap( inputdata_t &inputdata )
{
	// Get the interaction name & target
	char parseString[255];
	Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));

	char *pszParam = strtok(parseString," ");
	if ( pszParam && pszParam[0] )
	{
		int iTeam = atoi( pszParam );
		pszParam = strtok(NULL," ");

		if ( pszParam && pszParam[0] )
		{
			bool bCanCap = (atoi(pszParam) != 0);

			if ( iTeam >= 0 && iTeam < GetNumberOfTeams() )
			{
				m_TeamData[iTeam].bCanCap = bCanCap;
				if ( m_hPoint )
				{
					ObjectiveResource()->SetTeamCanCap( m_hPoint->GetPointIndex(), iTeam, m_TeamData[iTeam].bCanCap );
				}
				return;
			}
		}
	}

	Warning("%s(%s) received SetTeamCanCap input with invalid format. Format should be: <team number> <can cap (0/1)>.\n", GetClassname(), GetDebugName() );
}

void CTriggerAreaCapture::InputCaptureCurrentCP( inputdata_t &inputdata )
{
	if ( m_bCapturing )
	{
		EndCapture( m_nCapturingTeam );
	}
}

void CTriggerAreaCapture::InputSetControlPoint( inputdata_t &inputdata )
{
	BreakCapture( false );	// clear the capping for the previous point, forces us to recalc on the new one

	char parseString[255];
	Q_strncpy(parseString, inputdata.value.String(), sizeof(parseString));

	m_iszCapPointName = MAKE_STRING( parseString );
	m_hPoint = NULL;	// force a reset of this
	InputRoundSpawn( inputdata );

	// force everyone touching to re-touch so the hud gets set up properly
	for ( int i = 0; i < m_hTouchingEntities.Count(); i++ )
	{
		CBaseEntity *ent = m_hTouchingEntities[i];
		if ( ent && ent->IsPlayer() )
		{
			EndTouch( ent );
			StartTouch( ent );
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Check if this player's death causes a block
// return FALSE if the player is not in this area
// return TRUE otherwise ( eg player is in area, but his death does not cause break )
//-----------------------------------------------------------------------------
bool CTriggerAreaCapture::CheckIfDeathCausesBlock( CBaseMultiplayerPlayer *pVictim, CBaseMultiplayerPlayer *pKiller )
{
	if ( !pVictim || !pKiller )
		return false;

	// make sure this player is in this area
	if ( !IsTouching( pVictim ) )
		return false;

	// Teamkills shouldn't give a block reward
	if ( pVictim->GetTeamNumber() == pKiller->GetTeamNumber() )
		return true;

	// return if the area is not being capped
	if ( !m_bCapturing )
		return true;

	int iTeam = pVictim->GetTeamNumber();

	// return if this player's team is not capping the area
	if ( iTeam != m_nCapturingTeam )
		return true;

	// break early incase we kill multiple people in the same frame
	bool bBreakCap = false;
	if ( CaptureModeScalesWithPlayers() )
	{
		bBreakCap = ( m_TeamData[m_nCapturingTeam].iBlockedTouching - 1 ) <= 0;
	}
	else
	{
		bBreakCap = ( m_TeamData[m_nCapturingTeam].iBlockedTouching - 1 < m_TeamData[m_nCapturingTeam].iNumRequiredToCap );
	}

	if ( bBreakCap )
	{
		m_hPoint->CaptureBlocked( pKiller );
		//BreakCapture( true );
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateNumPlayers( bool bBlocked /*= false */ )
{
	if( !m_hPoint )
		return;

	int index = m_hPoint->GetPointIndex();
	for ( int i = 0; i < m_TeamData.Count(); i++ )
	{
		if ( i >= FIRST_GAME_TEAM && i == m_nCapturingTeam )
		{
			SetNumCappers( m_TeamData[i].iNumTouching, bBlocked );
		}

		ObjectiveResource()->SetNumPlayers( index, i, m_TeamData[i].iNumTouching );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateOwningTeam( void )
{
	if ( m_hPoint )
	{
		ObjectiveResource()->SetOwningTeam( m_hPoint->GetPointIndex(), m_nOwningTeam );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateCappingTeam( int iTeam )
{
	if ( m_hPoint )
	{
		ObjectiveResource()->SetCappingTeam( m_hPoint->GetPointIndex(), iTeam );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateTeamInZone( void )
{
	if ( m_hPoint )
	{
		ObjectiveResource()->SetTeamInZone( m_hPoint->GetPointIndex(), m_nTeamInZone );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::UpdateBlocked( void )
{
	if ( m_hPoint )
	{
		ObjectiveResource()->SetCapBlocked( m_hPoint->GetPointIndex(), m_bBlocked );
		m_hPoint->CaptureInterrupted( m_bBlocked );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTriggerAreaCapture::SetNumCappers( int nNumCappers, bool bBlocked /* = false */ )
{
	m_OnNumCappersChanged.Set( nNumCappers, this, this );

	// m_OnNumCappersChanged2 sets -1 for a blocked cart (for movement decisions on hills)
	if ( bBlocked )
	{
		nNumCappers = -1;
	}

	m_OnNumCappersChanged2.Set( nNumCappers, this, this );

	if ( m_hTrainWatcher.Get() )
	{
		m_hTrainWatcher->SetNumTrainCappers( nNumCappers, this );
	}
}