1102 lines
33 KiB
C++
1102 lines
33 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "team_control_point.h"
|
|
#include "player.h"
|
|
#include "teamplay_gamerules.h"
|
|
#include "teamplayroundbased_gamerules.h"
|
|
#include "team.h"
|
|
#include "team_control_point_master.h"
|
|
#include "mp_shareddefs.h"
|
|
#include "engine/IEngineSound.h"
|
|
#include "soundenvelope.h"
|
|
|
|
#ifdef TF_DLL
|
|
#include "tf_shareddefs.h"
|
|
#include "tf_gamerules.h"
|
|
#endif
|
|
|
|
#define CONTROL_POINT_UNLOCK_THINK "UnlockThink"
|
|
|
|
BEGIN_DATADESC(CTeamControlPoint)
|
|
DEFINE_KEYFIELD( m_iszPrintName, FIELD_STRING, "point_printname" ),
|
|
DEFINE_KEYFIELD( m_iCPGroup, FIELD_INTEGER, "point_group" ),
|
|
DEFINE_KEYFIELD( m_iDefaultOwner, FIELD_INTEGER, "point_default_owner" ),
|
|
DEFINE_KEYFIELD( m_iPointIndex, FIELD_INTEGER, "point_index" ),
|
|
DEFINE_KEYFIELD( m_iWarnOnCap, FIELD_INTEGER, "point_warn_on_cap" ),
|
|
DEFINE_KEYFIELD( m_iszWarnSound, FIELD_STRING, "point_warn_sound" ),
|
|
|
|
DEFINE_KEYFIELD( m_iszCaptureStartSound, FIELD_STRING, "point_capture_start_sound" ),
|
|
DEFINE_KEYFIELD( m_iszCaptureEndSound, FIELD_STRING, "point_capture_end_sound" ),
|
|
DEFINE_KEYFIELD( m_iszCaptureInProgress, FIELD_STRING, "point_capture_progress_sound" ),
|
|
DEFINE_KEYFIELD( m_iszCaptureInterrupted, FIELD_STRING, "point_capture_interrupted_sound" ),
|
|
DEFINE_KEYFIELD( m_bRandomOwnerOnRestart, FIELD_BOOLEAN, "random_owner_on_restart" ),
|
|
DEFINE_KEYFIELD( m_bLocked, FIELD_BOOLEAN, "point_start_locked" ),
|
|
|
|
DEFINE_FUNCTION( UnlockThink ),
|
|
|
|
// DEFINE_FIELD( m_iTeam, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_iIndex, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_TeamData, CUtlVector < perteamdata_t > ),
|
|
// DEFINE_FIELD( m_bPointVisible, FIELD_INTEGER ),
|
|
// DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
|
|
// DEFINE_FIELD( m_iszName, FIELD_STRING ),
|
|
// DEFINE_FIELD( m_bStartDisabled, FIELD_BOOLEAN ),
|
|
// DEFINE_FIELD( m_flLastContestedAt, FIELD_FLOAT ),
|
|
// DEFINE_FIELD( m_pCaptureInProgressSound, CSoundPatch ),
|
|
|
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetOwner", InputSetOwner ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "ShowModel", InputShowModel ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "HideModel", InputHideModel ),
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
|
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetLocked", InputSetLocked ),
|
|
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetUnlockTime", InputSetUnlockTime ),
|
|
|
|
DEFINE_OUTPUT( m_OnCapTeam1, "OnCapTeam1" ), // these are fired whenever the point changes modes
|
|
DEFINE_OUTPUT( m_OnCapTeam2, "OnCapTeam2" ),
|
|
DEFINE_OUTPUT( m_OnCapReset, "OnCapReset" ),
|
|
|
|
DEFINE_OUTPUT( m_OnOwnerChangedToTeam1, "OnOwnerChangedToTeam1" ), // these are fired when a team does the work to change the owner
|
|
DEFINE_OUTPUT( m_OnOwnerChangedToTeam2, "OnOwnerChangedToTeam2" ),
|
|
|
|
DEFINE_OUTPUT( m_OnRoundStartOwnedByTeam1, "OnRoundStartOwnedByTeam1" ), // these are fired when a round is starting
|
|
DEFINE_OUTPUT( m_OnRoundStartOwnedByTeam2, "OnRoundStartOwnedByTeam2" ),
|
|
|
|
DEFINE_OUTPUT( m_OnUnlocked, "OnUnlocked" ),
|
|
|
|
DEFINE_THINKFUNC( AnimThink ),
|
|
END_DATADESC();
|
|
|
|
LINK_ENTITY_TO_CLASS( team_control_point, CTeamControlPoint );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CTeamControlPoint::CTeamControlPoint()
|
|
{
|
|
m_TeamData.SetSize( GetNumberOfTeams() );
|
|
m_pCaptureInProgressSound = NULL;
|
|
|
|
m_bLocked = false;
|
|
m_flUnlockTime = -1;
|
|
m_bBotsIgnore = false;
|
|
|
|
#ifdef TF_DLL
|
|
UseClientSideAnimation();
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::Spawn( void )
|
|
{
|
|
// Validate our default team
|
|
if ( m_iDefaultOwner < 0 || m_iDefaultOwner >= GetNumberOfTeams() )
|
|
{
|
|
Warning( "team_control_point '%s' has bad point_default_owner.\n", GetDebugName() );
|
|
m_iDefaultOwner = TEAM_UNASSIGNED;
|
|
}
|
|
|
|
#ifdef TF_DLL
|
|
if ( m_iszCaptureStartSound == NULL_STRING )
|
|
{
|
|
m_iszCaptureStartSound = AllocPooledString( "Hologram.Start" );
|
|
}
|
|
if ( m_iszCaptureEndSound == NULL_STRING )
|
|
{
|
|
m_iszCaptureEndSound = AllocPooledString( "Hologram.Stop" );
|
|
}
|
|
if ( m_iszCaptureInProgress == NULL_STRING )
|
|
{
|
|
m_iszCaptureInProgress = AllocPooledString( "Hologram.Move" );
|
|
}
|
|
if ( m_iszCaptureInterrupted == NULL_STRING )
|
|
{
|
|
m_iszCaptureInterrupted = AllocPooledString( "Hologram.Interrupted" );
|
|
}
|
|
#endif
|
|
|
|
Precache();
|
|
|
|
InternalSetOwner( m_iDefaultOwner, false ); //init the owner of this point
|
|
TeamplayRoundBasedRules()->RecalculateControlPointState();
|
|
|
|
SetActive( !m_bStartDisabled );
|
|
|
|
BaseClass::Spawn();
|
|
|
|
SetPlaybackRate( 1.0 );
|
|
SetThink( &CTeamControlPoint::AnimThink );
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
|
|
if ( FBitSet( m_spawnflags, SF_CAP_POINT_HIDE_MODEL ) )
|
|
{
|
|
AddEffects( EF_NODRAW );
|
|
}
|
|
|
|
if ( FBitSet( m_spawnflags, SF_CAP_POINT_HIDE_SHADOW ) )
|
|
{
|
|
AddEffects( EF_NOSHADOW );
|
|
}
|
|
|
|
m_bBotsIgnore = FBitSet( m_spawnflags, SF_CAP_POINT_BOTS_IGNORE ) > 0;
|
|
|
|
m_flLastContestedAt = -1;
|
|
|
|
m_pCaptureInProgressSound = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CTeamControlPoint::KeyValue( const char *szKeyName, const char *szValue )
|
|
{
|
|
if ( !Q_strncmp( szKeyName, "team_capsound_", 14 ) )
|
|
{
|
|
int iTeam = atoi(szKeyName+14);
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
|
|
m_TeamData[iTeam].iszCapSound = AllocPooledString(szValue);
|
|
}
|
|
else if ( !Q_strncmp( szKeyName, "team_model_", 11 ) )
|
|
{
|
|
int iTeam = atoi(szKeyName+11);
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
|
|
m_TeamData[iTeam].iszModel = AllocPooledString(szValue);
|
|
}
|
|
else if ( !Q_strncmp( szKeyName, "team_timedpoints_", 17 ) )
|
|
{
|
|
int iTeam = atoi(szKeyName+17);
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
|
|
m_TeamData[iTeam].iTimedPoints = atoi(szValue);
|
|
}
|
|
else if ( !Q_strncmp( szKeyName, "team_bodygroup_", 15 ) )
|
|
{
|
|
int iTeam = atoi(szKeyName+15);
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
|
|
m_TeamData[iTeam].iModelBodygroup = atoi(szValue);
|
|
}
|
|
else if ( !Q_strncmp( szKeyName, "team_icon_", 10 ) )
|
|
{
|
|
int iTeam = atoi(szKeyName+10);
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
|
|
m_TeamData[iTeam].iszIcon = AllocPooledString(szValue);
|
|
}
|
|
else if ( !Q_strncmp( szKeyName, "team_overlay_", 13 ) )
|
|
{
|
|
int iTeam = atoi(szKeyName+13);
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
|
|
m_TeamData[iTeam].iszOverlay = AllocPooledString(szValue);
|
|
}
|
|
else if ( !Q_strncmp( szKeyName, "team_previouspoint_", 19 ) )
|
|
{
|
|
int iTeam;
|
|
int iPoint = 0;
|
|
sscanf( szKeyName+19, "%d_%d", &iTeam, &iPoint );
|
|
Assert( iTeam >= 0 && iTeam < m_TeamData.Count() );
|
|
Assert( iPoint >= 0 && iPoint < MAX_PREVIOUS_POINTS );
|
|
m_TeamData[iTeam].iszPreviousPoint[iPoint] = AllocPooledString(szValue);
|
|
}
|
|
else
|
|
{
|
|
return BaseClass::KeyValue( szKeyName, szValue );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::Precache( void )
|
|
{
|
|
for ( int i = 0; i < m_TeamData.Count(); i++ )
|
|
{
|
|
// Skip over spectator
|
|
if ( i == TEAM_SPECTATOR )
|
|
continue;
|
|
|
|
if ( m_TeamData[i].iszCapSound != NULL_STRING )
|
|
{
|
|
PrecacheScriptSound( STRING(m_TeamData[i].iszCapSound) );
|
|
}
|
|
|
|
if ( m_TeamData[i].iszModel != NULL_STRING )
|
|
{
|
|
PrecacheModel( STRING(m_TeamData[i].iszModel) );
|
|
}
|
|
|
|
if ( m_TeamData[i].iszIcon != NULL_STRING )
|
|
{
|
|
PrecacheMaterial( STRING( m_TeamData[i].iszIcon ) );
|
|
m_TeamData[i].iIcon = GetMaterialIndex( STRING( m_TeamData[i].iszIcon ) );
|
|
Assert( m_TeamData[i].iIcon != 0 );
|
|
}
|
|
|
|
if ( !m_TeamData[i].iIcon )
|
|
{
|
|
Warning( "Invalid hud icon material for team %d in control point '%s' ( point index %d )\n", i, GetDebugName(), GetPointIndex() );
|
|
}
|
|
|
|
if ( m_TeamData[i].iszOverlay != NULL_STRING )
|
|
{
|
|
PrecacheMaterial( STRING( m_TeamData[i].iszOverlay ) );
|
|
m_TeamData[i].iOverlay = GetMaterialIndex( STRING( m_TeamData[i].iszOverlay ) );
|
|
Assert( m_TeamData[i].iOverlay != 0 );
|
|
|
|
if ( !m_TeamData[i].iOverlay )
|
|
{
|
|
Warning( "Invalid hud overlay material for team %d in control point '%s' ( point index %d )\n", i, GetDebugName(), GetPointIndex() );
|
|
}
|
|
}
|
|
}
|
|
|
|
PrecacheScriptSound( STRING( m_iszCaptureStartSound ) );
|
|
PrecacheScriptSound( STRING( m_iszCaptureEndSound ) );
|
|
PrecacheScriptSound( STRING( m_iszCaptureInProgress ) );
|
|
PrecacheScriptSound( STRING( m_iszCaptureInterrupted ) );
|
|
|
|
if ( m_iszWarnSound != NULL_STRING )
|
|
{
|
|
PrecacheScriptSound( STRING( m_iszWarnSound ) );
|
|
}
|
|
|
|
#ifdef TF_DLL
|
|
PrecacheScriptSound( "Announcer.ControlPointContested" );
|
|
PrecacheScriptSound( "Announcer.ControlPointContested_Neutral" );
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Purpose:
|
|
//------------------------------------------------------------------------------
|
|
void CTeamControlPoint::AnimThink( void )
|
|
{
|
|
StudioFrameAdvance();
|
|
DispatchAnimEvents(this);
|
|
SetNextThink( gpGlobals->curtime + 0.1f );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Used by ControlMaster to this point to its default owner
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputReset( inputdata_t &input )
|
|
{
|
|
m_flLastContestedAt = -1;
|
|
InternalSetOwner( m_iDefaultOwner, false );
|
|
ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
|
|
TeamplayRoundBasedRules()->RecalculateControlPointState();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::HandleScoring( int iTeam )
|
|
{
|
|
if ( TeamplayRoundBasedRules() && !TeamplayRoundBasedRules()->ShouldScorePerRound() )
|
|
{
|
|
GetGlobalTeam( iTeam )->AddScore( 1 );
|
|
TeamplayRoundBasedRules()->HandleTeamScoreModify( iTeam, 1 );
|
|
|
|
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
|
|
if ( pMaster && !pMaster->WouldNewCPOwnerWinGame( this, iTeam ) )
|
|
{
|
|
#ifdef TF_DLL
|
|
if ( TeamplayRoundBasedRules()->GetGameType() == TF_GAMETYPE_ESCORT )
|
|
{
|
|
CBroadcastRecipientFilter filter;
|
|
EmitSound( filter, entindex(), "Hud.EndRoundScored" );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
CTeamRecipientFilter filter( iTeam );
|
|
EmitSound( filter, entindex(), "Hud.EndRoundScored" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Used by Area caps to set the owner
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputSetOwner( inputdata_t &input )
|
|
{
|
|
int iCapTeam = input.value.Int();
|
|
|
|
Assert( iCapTeam >= 0 && iCapTeam < GetNumberOfTeams() );
|
|
|
|
Assert( input.pCaller );
|
|
|
|
if ( !input.pCaller )
|
|
return;
|
|
|
|
if ( GetOwner() == iCapTeam )
|
|
return;
|
|
|
|
if ( TeamplayGameRules()->PointsMayBeCaptured() )
|
|
{
|
|
// must be done before setting the owner
|
|
HandleScoring( iCapTeam );
|
|
|
|
if ( input.pCaller->IsPlayer() )
|
|
{
|
|
int iCappingPlayer = input.pCaller->entindex();
|
|
InternalSetOwner( iCapTeam, true, 1, &iCappingPlayer );
|
|
}
|
|
else
|
|
{
|
|
InternalSetOwner( iCapTeam, false );
|
|
}
|
|
|
|
ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
|
|
TeamplayRoundBasedRules()->RecalculateControlPointState();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputShowModel( inputdata_t &input )
|
|
{
|
|
RemoveEffects( EF_NODRAW );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputHideModel( inputdata_t &input )
|
|
{
|
|
AddEffects( EF_NODRAW );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetCurrentHudIconIndex( void )
|
|
{
|
|
return m_TeamData[GetOwner()].iIcon;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetHudIconIndexForTeam( int iGameTeam )
|
|
{
|
|
return m_TeamData[iGameTeam].iIcon;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetHudOverlayIndexForTeam( int iGameTeam )
|
|
{
|
|
return m_TeamData[iGameTeam].iOverlay;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetPreviousPointForTeam( int iGameTeam, int iPrevPoint )
|
|
{
|
|
Assert( iPrevPoint >= 0 && iPrevPoint < MAX_PREVIOUS_POINTS );
|
|
|
|
int iRetVal = -1;
|
|
CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, STRING(m_TeamData[iGameTeam].iszPreviousPoint[iPrevPoint]) );
|
|
|
|
if ( pEntity )
|
|
{
|
|
CTeamControlPoint *pPoint = dynamic_cast<CTeamControlPoint*>( pEntity );
|
|
|
|
if ( pPoint )
|
|
{
|
|
iRetVal = pPoint->GetPointIndex();
|
|
}
|
|
}
|
|
|
|
return iRetVal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::ForceOwner( int iTeam )
|
|
{
|
|
InternalSetOwner( iTeam, false, 0, 0 );
|
|
ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
|
|
TeamplayRoundBasedRules()->RecalculateControlPointState();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::SetOwner( int iCapTeam, bool bMakeSound, int iNumCappers, int *pCappingPlayers )
|
|
{
|
|
if ( TeamplayGameRules()->PointsMayBeCaptured() )
|
|
{
|
|
// must be done before setting the owner
|
|
HandleScoring( iCapTeam );
|
|
|
|
InternalSetOwner( iCapTeam, bMakeSound, iNumCappers, pCappingPlayers );
|
|
ObjectiveResource()->SetOwningTeam( GetPointIndex(), m_iTeam );
|
|
TeamplayRoundBasedRules()->RecalculateControlPointState();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::CaptureStart( int iCapTeam, int iNumCappingPlayers, int *pCappingPlayers )
|
|
{
|
|
int iNumCappers = iNumCappingPlayers;
|
|
|
|
float flLastOwnershipChangeTime = -1.f;
|
|
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointMasterName() );
|
|
while( pEnt )
|
|
{
|
|
CTeamControlPointMaster *pMaster = dynamic_cast<CTeamControlPointMaster *>( pEnt );
|
|
if ( pMaster && pMaster->IsActive() )
|
|
{
|
|
flLastOwnershipChangeTime = pMaster->GetLastOwnershipChangeTime();
|
|
}
|
|
pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointMasterName() );
|
|
}
|
|
|
|
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_startcapture" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "cp", m_iPointIndex );
|
|
event->SetString( "cpname", STRING( m_iszPrintName ) );
|
|
event->SetInt( "team", m_iTeam );
|
|
event->SetInt( "capteam", iCapTeam );
|
|
event->SetFloat( "captime", gpGlobals->curtime - flLastOwnershipChangeTime );
|
|
|
|
// safety check
|
|
if ( iNumCappers > 8 )
|
|
{
|
|
iNumCappers = 8;
|
|
}
|
|
|
|
char cappers[9]; // pCappingPlayers should be max length 8
|
|
int i;
|
|
for( i = 0 ; i < iNumCappers ; i++ )
|
|
{
|
|
cappers[i] = (char)pCappingPlayers[i];
|
|
}
|
|
|
|
cappers[i] = '\0';
|
|
|
|
// pCappingPlayers is a null terminated list of player indices
|
|
event->SetString( "cappers", cappers );
|
|
event->SetInt( "priority", 7 );
|
|
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::CaptureEnd( void )
|
|
{
|
|
StopLoopingSounds();
|
|
|
|
if ( !FBitSet( m_spawnflags, SF_CAP_POINT_NO_CAP_SOUNDS ) )
|
|
{
|
|
EmitSound( STRING( m_iszCaptureEndSound ) );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::CaptureInterrupted( bool bBlocked )
|
|
{
|
|
StopLoopingSounds();
|
|
|
|
if ( FBitSet( m_spawnflags, SF_CAP_POINT_NO_CAP_SOUNDS ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
const char *pSoundName = NULL;
|
|
|
|
if ( bBlocked == true )
|
|
{
|
|
pSoundName = STRING( m_iszCaptureInterrupted );
|
|
}
|
|
else
|
|
{
|
|
pSoundName = STRING( m_iszCaptureInProgress );
|
|
EmitSound( STRING( m_iszCaptureStartSound ) );
|
|
}
|
|
|
|
if ( m_pCaptureInProgressSound == NULL && pSoundName != NULL )
|
|
{
|
|
CPASFilter filter( GetAbsOrigin() );
|
|
|
|
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
|
m_pCaptureInProgressSound = controller.SoundCreate( filter, entindex(), pSoundName );
|
|
|
|
controller.Play( m_pCaptureInProgressSound, 1.0, 100 );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::StopLoopingSounds( void )
|
|
{
|
|
CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
|
|
|
|
if ( m_pCaptureInProgressSound )
|
|
{
|
|
controller.SoundDestroy( m_pCaptureInProgressSound );
|
|
m_pCaptureInProgressSound = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets the new owner of the point, plays the appropriate sound and shows the right model
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InternalSetOwner( int iCapTeam, bool bMakeSound, int iNumCappers, int *pCappingPlayers )
|
|
{
|
|
Assert( iCapTeam >= 0 && iCapTeam < GetNumberOfTeams() );
|
|
|
|
int iOldTeam = m_iTeam;
|
|
|
|
m_iTeam = iCapTeam;
|
|
ChangeTeam( iCapTeam );
|
|
|
|
if ( bMakeSound )
|
|
{
|
|
CBroadcastRecipientFilter filter;
|
|
EmitSound( filter, entindex(), STRING( m_TeamData[m_iTeam].iszCapSound ) );
|
|
}
|
|
|
|
// Update visuals
|
|
SetModel( STRING(m_TeamData[m_iTeam].iszModel) );
|
|
SetBodygroup( 0, m_iTeam );
|
|
m_nSkin = ( m_iTeam == TEAM_UNASSIGNED ) ? 2 : (m_iTeam - 2);
|
|
ResetSequence( LookupSequence("idle") );
|
|
|
|
// We add 1 to the index because we consider the default "no points capped" as 0.
|
|
TeamplayGameRules()->SetLastCapPointChanged( m_iPointIndex+1 );
|
|
|
|
// Determine the pose parameters for each team
|
|
for ( int i = 0; i < m_TeamData.Count(); i++ )
|
|
{
|
|
// Skip spectator
|
|
if ( i == TEAM_SPECTATOR )
|
|
continue;
|
|
|
|
if ( GetModelPtr() && GetModelPtr()->SequencesAvailable() )
|
|
{
|
|
m_TeamData[i].iTeamPoseParam = LookupPoseParameter( UTIL_VarArgs( "cappoint_%d_percentage", i ) );
|
|
}
|
|
else
|
|
{
|
|
m_TeamData[i].iTeamPoseParam = -1;
|
|
}
|
|
}
|
|
UpdateCapPercentage();
|
|
|
|
if ( m_iTeam == TEAM_UNASSIGNED )
|
|
{
|
|
m_OnCapReset.FireOutput( this, this );
|
|
}
|
|
else
|
|
{
|
|
// Remap team to get first game team = 1
|
|
switch ( m_iTeam - FIRST_GAME_TEAM+1 )
|
|
{
|
|
case 1:
|
|
m_OnCapTeam1.FireOutput( this, this );
|
|
break;
|
|
case 2:
|
|
m_OnCapTeam2.FireOutput( this, this );
|
|
break;
|
|
default:
|
|
Assert(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we're playing a sound, this is a true cap by players.
|
|
if ( bMakeSound )
|
|
{
|
|
if ( iOldTeam > LAST_SHARED_TEAM && iOldTeam != m_iTeam )
|
|
{
|
|
// Make the members of our old team say something
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
|
|
{
|
|
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
|
|
if ( !pPlayer )
|
|
continue;
|
|
if ( pPlayer->GetTeamNumber() == iOldTeam )
|
|
{
|
|
pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_LOST_CONTROL_POINT );
|
|
}
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < iNumCappers; i++ )
|
|
{
|
|
int playerIndex = pCappingPlayers[i];
|
|
|
|
Assert( playerIndex > 0 && playerIndex <= gpGlobals->maxClients );
|
|
|
|
CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( playerIndex ) );
|
|
PlayerCapped( pPlayer );
|
|
|
|
#ifdef TF_DLL
|
|
if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_EOTL ) )
|
|
{
|
|
TFGameRules()->DropBonusDuck( pPlayer->GetAbsOrigin(), ToTFPlayer( pPlayer ), NULL, NULL, false, true );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Remap team to get first game team = 1
|
|
switch ( m_iTeam - FIRST_GAME_TEAM+1 )
|
|
{
|
|
case 1:
|
|
m_OnOwnerChangedToTeam1.FireOutput( this, this );
|
|
break;
|
|
case 2:
|
|
m_OnOwnerChangedToTeam2.FireOutput( this, this );
|
|
break;
|
|
}
|
|
|
|
if ( m_iTeam != TEAM_UNASSIGNED && iNumCappers )
|
|
{
|
|
SendCapString( m_iTeam, iNumCappers, pCappingPlayers );
|
|
}
|
|
|
|
#ifdef TF_DLL
|
|
if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_Halloween ) )
|
|
{
|
|
TFGameRules()->DropHalloweenSoulPackToTeam( 5, GetAbsOrigin(), m_iTeam, TEAM_SPECTATOR );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Have control point master check the win conditions now!
|
|
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointMasterName() );
|
|
|
|
while( pEnt )
|
|
{
|
|
CTeamControlPointMaster *pMaster = dynamic_cast<CTeamControlPointMaster *>( pEnt );
|
|
|
|
if ( pMaster->IsActive() )
|
|
{
|
|
pMaster->CheckWinConditions();
|
|
pMaster->SetLastOwnershipChangeTime( gpGlobals->curtime );
|
|
}
|
|
|
|
pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointMasterName() );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::SendCapString( int iCapTeam, int iNumCappingPlayers, int *pCappingPlayers )
|
|
{
|
|
if ( strlen( STRING(m_iszPrintName) ) <= 0 )
|
|
return;
|
|
|
|
int iNumCappers = iNumCappingPlayers;
|
|
|
|
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_captured" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "cp", m_iPointIndex );
|
|
event->SetString( "cpname", STRING( m_iszPrintName ) );
|
|
event->SetInt( "team", iCapTeam );
|
|
|
|
// safety check
|
|
if ( iNumCappers > 8 )
|
|
{
|
|
iNumCappers = 8;
|
|
}
|
|
|
|
char cappers[9]; // pCappingPlayers should be max length 8
|
|
int i;
|
|
for( i = 0 ; i < iNumCappers ; i++ )
|
|
{
|
|
cappers[i] = (char)pCappingPlayers[i];
|
|
}
|
|
|
|
cappers[i] = '\0';
|
|
|
|
// pCappingPlayers is a null terminated list of player indices
|
|
event->SetString( "cappers", cappers );
|
|
event->SetInt( "priority", 9 );
|
|
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::CaptureBlocked( CBaseMultiplayerPlayer *pPlayer, CBaseMultiplayerPlayer *pVictim )
|
|
{
|
|
if( strlen( STRING(m_iszPrintName) ) <= 0 )
|
|
return;
|
|
|
|
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_capture_blocked" );
|
|
|
|
if ( event )
|
|
{
|
|
event->SetInt( "cp", m_iPointIndex );
|
|
event->SetString( "cpname", STRING(m_iszPrintName) );
|
|
event->SetInt( "blocker", pPlayer->entindex() );
|
|
event->SetInt( "priority", 9 );
|
|
if ( pVictim )
|
|
{
|
|
event->SetInt( "victim", pVictim->entindex() );
|
|
}
|
|
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
|
|
PlayerBlocked( pPlayer );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetOwner( void ) const
|
|
{
|
|
return m_iTeam;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetDefaultOwner( void ) const
|
|
{
|
|
return m_iDefaultOwner;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::GetCPGroup( void )
|
|
{
|
|
return m_iCPGroup;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the time-based point value of this control point
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::PointValue( void )
|
|
{
|
|
if ( GetOwner() != m_iDefaultOwner )
|
|
return m_TeamData[ GetOwner() ].iTimedPoints;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::SetActive( bool active )
|
|
{
|
|
m_bActive = active;
|
|
|
|
if( active )
|
|
{
|
|
RemoveEffects( EF_NODRAW );
|
|
}
|
|
else
|
|
{
|
|
AddEffects( EF_NODRAW );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::SetCappersRequiredForTeam( int iGameTeam, int iCappers )
|
|
{
|
|
m_TeamData[iGameTeam].iPlayersRequired = iCappers;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return true if this point has ever been contested, false if the enemy has never contested this point yet
|
|
//-----------------------------------------------------------------------------
|
|
bool CTeamControlPoint::HasBeenContested( void ) const
|
|
{
|
|
return m_flLastContestedAt > 0.0f;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
float CTeamControlPoint::LastContestedAt( void )
|
|
{
|
|
return m_flLastContestedAt;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::SetLastContestedAt( float flTime )
|
|
{
|
|
m_flLastContestedAt = flTime;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::UpdateCapPercentage( void )
|
|
{
|
|
for ( int i = LAST_SHARED_TEAM+1; i < m_TeamData.Count(); i++ )
|
|
{
|
|
// Skip spectator
|
|
if ( i == TEAM_SPECTATOR )
|
|
continue;
|
|
|
|
float flPerc = GetTeamCapPercentage(i);
|
|
|
|
if ( m_TeamData[i].iTeamPoseParam != -1 )
|
|
{
|
|
SetPoseParameter( m_TeamData[i].iTeamPoseParam, flPerc );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
float CTeamControlPoint::GetTeamCapPercentage( int iTeam )
|
|
{
|
|
int iCappingTeam = ObjectiveResource()->GetCappingTeam( GetPointIndex() );
|
|
if ( iCappingTeam == TEAM_UNASSIGNED )
|
|
{
|
|
// No-one's capping this point.
|
|
if ( iTeam == m_iTeam )
|
|
return 1.0;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
float flCapPerc = ObjectiveResource()->GetCPCapPercentage( GetPointIndex() );
|
|
if ( iTeam == iCappingTeam )
|
|
return (1.0 - flCapPerc);
|
|
if ( iTeam == m_iTeam )
|
|
return flCapPerc;
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int CTeamControlPoint::DrawDebugTextOverlays( void )
|
|
{
|
|
int text_offset = BaseClass::DrawDebugTextOverlays();
|
|
|
|
if (m_debugOverlays & OVERLAY_TEXT_BIT)
|
|
{
|
|
char tempstr[1024];
|
|
Q_snprintf(tempstr, sizeof(tempstr), "INDEX: (%d)", GetPointIndex() );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
|
|
Q_snprintf( tempstr, sizeof(tempstr), "Red Previous Points: ");
|
|
for ( int i = 0; i < MAX_PREVIOUS_POINTS; i++ )
|
|
{
|
|
if ( m_TeamData[2].iszPreviousPoint[i] != NULL_STRING )
|
|
{
|
|
Q_strncat( tempstr, STRING(m_TeamData[2].iszPreviousPoint[i]), 1024, COPY_ALL_CHARACTERS );
|
|
Q_strncat( tempstr, ", ", 1024, COPY_ALL_CHARACTERS );
|
|
}
|
|
}
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
|
|
Q_snprintf( tempstr, sizeof(tempstr), "Blue Previous Points: " );
|
|
for ( int i = 0; i < MAX_PREVIOUS_POINTS; i++ )
|
|
{
|
|
if ( m_TeamData[3].iszPreviousPoint[i] != NULL_STRING )
|
|
{
|
|
Q_strncat( tempstr, STRING(m_TeamData[3].iszPreviousPoint[i]), 1024, COPY_ALL_CHARACTERS );
|
|
Q_strncat( tempstr, ", ", 1024, COPY_ALL_CHARACTERS );
|
|
}
|
|
}
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
|
|
for ( int i = 0; i < MAX_CONTROL_POINT_TEAMS; i++ )
|
|
{
|
|
if ( ObjectiveResource()->GetBaseControlPointForTeam(i) == GetPointIndex() )
|
|
{
|
|
Q_snprintf(tempstr, sizeof(tempstr), "Base Control Point for Team %d", i );
|
|
EntityText(text_offset,tempstr,0);
|
|
text_offset++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return text_offset;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: The specified player took part in capping this point.
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::PlayerCapped( CBaseMultiplayerPlayer *pPlayer )
|
|
{
|
|
if ( pPlayer )
|
|
{
|
|
pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_CAPTURED_POINT );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: The specified player blocked the enemy team from capping this point.
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::PlayerBlocked( CBaseMultiplayerPlayer *pPlayer )
|
|
{
|
|
if ( pPlayer )
|
|
{
|
|
pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_CAPTURE_BLOCKED );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputRoundActivate( inputdata_t &inputdata )
|
|
{
|
|
switch ( m_iTeam - FIRST_GAME_TEAM+1 )
|
|
{
|
|
case 1:
|
|
m_OnRoundStartOwnedByTeam1.FireOutput( this, this );
|
|
break;
|
|
case 2:
|
|
m_OnRoundStartOwnedByTeam2.FireOutput( this, this );
|
|
break;
|
|
}
|
|
|
|
InternalSetLocked( m_bLocked );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputSetLocked( inputdata_t &inputdata )
|
|
{
|
|
// never lock/unlock the point if we're in waiting for players
|
|
if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
|
|
return;
|
|
|
|
bool bLocked = inputdata.value.Int() > 0;
|
|
InternalSetLocked( bLocked );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InternalSetLocked( bool bLocked )
|
|
{
|
|
if ( !bLocked && m_bLocked )
|
|
{
|
|
// unlocked this point
|
|
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_unlocked" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "cp", m_iPointIndex );
|
|
event->SetString( "cpname", STRING( m_iszPrintName ) );
|
|
event->SetInt( "team", m_iTeam );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
else if ( bLocked && !m_bLocked )
|
|
{
|
|
// locked this point
|
|
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_point_locked" );
|
|
if ( event )
|
|
{
|
|
event->SetInt( "cp", m_iPointIndex );
|
|
event->SetString( "cpname", STRING( m_iszPrintName ) );
|
|
event->SetInt( "team", m_iTeam );
|
|
gameeventmanager->FireEvent( event );
|
|
}
|
|
}
|
|
|
|
m_bLocked = bLocked;
|
|
|
|
if ( ObjectiveResource() && GetPointIndex() < ObjectiveResource()->GetNumControlPoints() )
|
|
{
|
|
ObjectiveResource()->SetCPLocked( GetPointIndex(), m_bLocked );
|
|
ObjectiveResource()->SetCPUnlockTime( GetPointIndex(), 0.0f );
|
|
}
|
|
|
|
if ( !m_bLocked )
|
|
{
|
|
m_flUnlockTime = -1;
|
|
m_OnUnlocked.FireOutput( this, this );
|
|
SetContextThink( NULL, 0, CONTROL_POINT_UNLOCK_THINK );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::InputSetUnlockTime( inputdata_t &inputdata )
|
|
{
|
|
// never lock/unlock the point if we're in waiting for players
|
|
if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
|
|
return;
|
|
|
|
int nTime = inputdata.value.Int();
|
|
|
|
if ( nTime <= 0 )
|
|
{
|
|
InternalSetLocked( false );
|
|
return;
|
|
}
|
|
|
|
m_flUnlockTime = gpGlobals->curtime + nTime;
|
|
|
|
if ( ObjectiveResource() )
|
|
{
|
|
ObjectiveResource()->SetCPUnlockTime( GetPointIndex(), m_flUnlockTime );
|
|
}
|
|
|
|
SetContextThink( &CTeamControlPoint::UnlockThink, gpGlobals->curtime + 0.1, CONTROL_POINT_UNLOCK_THINK );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTeamControlPoint::UnlockThink( void )
|
|
{
|
|
if ( m_flUnlockTime > 0 &&
|
|
m_flUnlockTime < gpGlobals->curtime &&
|
|
( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->State_Get() == GR_STATE_RND_RUNNING ) )
|
|
{
|
|
InternalSetLocked( false );
|
|
return;
|
|
}
|
|
|
|
SetContextThink( &CTeamControlPoint::UnlockThink, gpGlobals->curtime + 0.1, CONTROL_POINT_UNLOCK_THINK );
|
|
}
|