source-engine/game/server/team_control_point_master.cpp
2022-03-01 23:00:42 +03:00

1347 lines
41 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "team_objectiveresource.h"
#include "team_control_point_master.h"
#include "teamplayroundbased_gamerules.h"
#if defined ( TF_DLL )
#include "tf_gamerules.h"
#endif
BEGIN_DATADESC( CTeamControlPointMaster )
DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_KEYFIELD( m_iszCapLayoutInHUD, FIELD_STRING, "caplayout" ),
DEFINE_KEYFIELD( m_iInvalidCapWinner, FIELD_INTEGER, "cpm_restrict_team_cap_win" ),
DEFINE_KEYFIELD( m_bSwitchTeamsOnWin, FIELD_BOOLEAN, "switch_teams" ),
DEFINE_KEYFIELD( m_bScorePerCapture, FIELD_BOOLEAN, "score_style" ),
DEFINE_KEYFIELD( m_bPlayAllRounds, FIELD_BOOLEAN, "play_all_rounds" ),
DEFINE_KEYFIELD( m_flPartialCapturePointsRate, FIELD_FLOAT, "partial_cap_points_rate" ),
DEFINE_KEYFIELD( m_flCustomPositionX, FIELD_FLOAT, "custom_position_x" ),
DEFINE_KEYFIELD( m_flCustomPositionY, FIELD_FLOAT, "custom_position_y" ),
// DEFINE_FIELD( m_ControlPoints, CUtlMap < int , CTeamControlPoint * > ),
// DEFINE_FIELD( m_bFoundPoints, FIELD_BOOLEAN ),
// DEFINE_FIELD( m_ControlPointRounds, CUtlVector < CTeamControlPointRound * > ),
// DEFINE_FIELD( m_iCurrentRoundIndex, FIELD_INTEGER ),
// DEFINE_ARRAY( m_iszTeamBaseIcons, FIELD_STRING, MAX_TEAMS ),
// DEFINE_ARRAY( m_iTeamBaseIcons, FIELD_INTEGER, MAX_TEAMS ),
// DEFINE_FIELD( m_bFirstRoundAfterRestart, FIELD_BOOLEAN ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetWinner", InputSetWinner ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetWinnerAndForceCaps", InputSetWinnerAndForceCaps ),
DEFINE_INPUTFUNC( FIELD_VOID, "RoundSpawn", InputRoundSpawn ),
DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetCapLayout", InputSetCapLayout ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetCapLayoutCustomPositionX", InputSetCapLayoutCustomPositionX ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetCapLayoutCustomPositionY", InputSetCapLayoutCustomPositionY ),
DEFINE_FUNCTION( CPMThink ),
DEFINE_OUTPUT( m_OnWonByTeam1, "OnWonByTeam1" ),
DEFINE_OUTPUT( m_OnWonByTeam2, "OnWonByTeam2" ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( team_control_point_master, CTeamControlPointMaster );
ConVar mp_time_between_capscoring( "mp_time_between_capscoring", "30", FCVAR_GAMEDLL, "Delay between scoring of owned capture points.", true, 1, false, 0 );
// sort function for the list of control_point_rounds (we're sorting them by priority...highest first)
int ControlPointRoundSort( CTeamControlPointRound* const *p1, CTeamControlPointRound* const *p2 )
{
// check the priority
if ( (*p2)->GetPriorityValue() > (*p1)->GetPriorityValue() )
{
return 1;
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: init
//-----------------------------------------------------------------------------
CTeamControlPointMaster::CTeamControlPointMaster()
{
m_flPartialCapturePointsRate = 0.0f;
m_flCustomPositionX = -1.f;
m_flCustomPositionY = -1.f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::Spawn( void )
{
Precache();
SetTouch( NULL );
m_bFoundPoints = false;
SetDefLessFunc( m_ControlPoints );
m_iCurrentRoundIndex = -1;
m_bFirstRoundAfterRestart = true;
m_flLastOwnershipChangeTime = -1;
BaseClass::Spawn();
if ( g_hControlPointMasters.Find(this) == g_hControlPointMasters.InvalidIndex() )
{
g_hControlPointMasters.AddToTail( this );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::UpdateOnRemove( void )
{
BaseClass::UpdateOnRemove();
g_hControlPointMasters.FindAndRemove( this );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::KeyValue( const char *szKeyName, const char *szValue )
{
if ( !Q_strncmp( szKeyName, "team_base_icon_", 15 ) )
{
int iTeam = atoi(szKeyName+15);
Assert( iTeam >= 0 && iTeam < MAX_TEAMS );
m_iszTeamBaseIcons[iTeam] = AllocPooledString(szValue);
}
else
{
return BaseClass::KeyValue( szKeyName, szValue );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::Precache( void )
{
for ( int i = 0; i < MAX_TEAMS; i++ )
{
if ( m_iszTeamBaseIcons[i] != NULL_STRING )
{
PrecacheMaterial( STRING( m_iszTeamBaseIcons[i] ) );
m_iTeamBaseIcons[i] = GetMaterialIndex( STRING( m_iszTeamBaseIcons[i] ) );
Assert( m_iTeamBaseIcons[i] != 0 );
}
}
BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::Activate( void )
{
BaseClass::Activate();
// Find control points right away. This allows client hud elements to know the
// number & starting state of control points before the game actually starts.
FindControlPoints();
FindControlPointRounds();
SetBaseControlPoints();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::RoundRespawn( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::Reset( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::FindControlPoints( void )
{
//go through all the points
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointName() );
int numFound = 0;
while( pEnt )
{
CTeamControlPoint *pPoint = assert_cast<CTeamControlPoint *>(pEnt);
if( pPoint->IsActive() && !pPoint->IsMarkedForDeletion() )
{
int index = pPoint->GetPointIndex();
Assert( index >= 0 );
if( m_ControlPoints.Find( index ) == m_ControlPoints.InvalidIndex())
{
DevMsg( 2, "**** Adding control point %s with index %d to control point master\n", pPoint->GetName(), index );
m_ControlPoints.Insert( index, pPoint );
numFound++;
}
else
{
Warning( "!!!!\nMultiple control points with the same index, duplicates ignored\n!!!!\n" );
UTIL_Remove( pPoint );
}
}
pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointName() );
}
if( numFound > MAX_CONTROL_POINTS )
{
Warning( "Too many control points! Max is %d\n", MAX_CONTROL_POINTS );
}
//Remap the indeces of the control points so they are 0-based
//======================
unsigned int j;
bool bHandled[MAX_CONTROL_POINTS];
memset( bHandled, 0, sizeof(bHandled) );
unsigned int numPoints = m_ControlPoints.Count();
unsigned int newIndex = 0;
while( newIndex < numPoints )
{
//Find the lowest numbered, unhandled point
int lowestIndex = -1;
int lowestValue = 999;
//find the lowest unhandled index
for( j=0; j<numPoints; j++ )
{
if( !bHandled[j] && m_ControlPoints[j]->GetPointIndex() < lowestValue )
{
lowestIndex = j;
lowestValue = m_ControlPoints[j]->GetPointIndex();
}
}
//Don't examine this point again
bHandled[lowestIndex] = true;
//Give it its new index
m_ControlPoints[lowestIndex]->SetPointIndex( newIndex );
newIndex++;
}
if( m_ControlPoints.Count() == 0 )
{
Warning( "Error! No control points found in map!\n");
return false;
}
// Now setup the objective resource
ObjectiveResource()->SetNumControlPoints( m_ControlPoints.Count() );
for ( unsigned int i = 0; i < m_ControlPoints.Count(); i++ )
{
CTeamControlPoint *pPoint = m_ControlPoints[i];
int iPointIndex = m_ControlPoints[i]->GetPointIndex();
ObjectiveResource()->SetOwningTeam( iPointIndex, pPoint->GetOwner() );
ObjectiveResource()->SetCPVisible( iPointIndex, pPoint->PointIsVisible() );
ObjectiveResource()->SetCPPosition( iPointIndex, pPoint->GetAbsOrigin() );
ObjectiveResource()->SetWarnOnCap( iPointIndex, pPoint->GetWarnOnCap() );
ObjectiveResource()->SetWarnSound( iPointIndex, pPoint->GetWarnSound() );
ObjectiveResource()->SetCPGroup( iPointIndex, pPoint->GetCPGroup() );
for ( int team = 0; team < GetNumberOfTeams(); team++ )
{
ObjectiveResource()->SetCPIcons( iPointIndex, team, pPoint->GetHudIconIndexForTeam(team) );
ObjectiveResource()->SetCPOverlays( iPointIndex, team, pPoint->GetHudOverlayIndexForTeam(team) );
for ( int prevpoint = 0; prevpoint < MAX_PREVIOUS_POINTS; prevpoint++ )
{
ObjectiveResource()->SetPreviousPoint( iPointIndex, team, prevpoint, pPoint->GetPreviousPointForTeam(team, prevpoint) );
}
}
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::SetBaseControlPoints( void )
{
for ( int team = 0; team < GetNumberOfTeams(); team++ )
{
ObjectiveResource()->SetTeamBaseIcons( team, m_iTeamBaseIcons[team] );
ObjectiveResource()->SetBaseCP( GetBaseControlPoint(team), team );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::FindControlPointRounds( void )
{
bool bFoundRounds = false;
m_ControlPointRounds.RemoveAll();
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, GetControlPointRoundName() );
while( pEnt )
{
CTeamControlPointRound *pRound = assert_cast<CTeamControlPointRound *>( pEnt );
if( pRound && ( m_ControlPointRounds.Find( pRound ) == m_ControlPointRounds.InvalidIndex() ) )
{
DevMsg( 2, "**** Adding control point round %s to control point master\n", pRound->GetEntityName().ToCStr() );
m_ControlPointRounds.AddToHead( pRound );
}
pEnt = gEntList.FindEntityByClassname( pEnt, GetControlPointRoundName() );
}
if ( m_ControlPointRounds.Count() > 0 )
{
// sort them in our list by priority (highest priority first)
m_ControlPointRounds.Sort( ControlPointRoundSort );
bFoundRounds = true;
}
if ( g_pObjectiveResource )
{
g_pObjectiveResource->SetPlayingMiniRounds( bFoundRounds );
g_pObjectiveResource->SetCapLayoutInHUD( STRING(m_iszCapLayoutInHUD) );
g_pObjectiveResource->SetCapLayoutCustomPosition( m_flCustomPositionX, m_flCustomPositionY );
}
return bFoundRounds;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::IsInRound( CTeamControlPoint *pPoint )
{
// are we playing a round and is this point in the round?
if ( m_ControlPointRounds.Count() > 0 && m_iCurrentRoundIndex != -1 )
{
return m_ControlPointRounds[m_iCurrentRoundIndex]->IsControlPointInRound( pPoint );
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTeamControlPointMaster::NumPlayableControlPointRounds( void )
{
int nRetVal = 0;
for ( int i = 0 ; i < m_ControlPointRounds.Count() ; ++i )
{
CTeamControlPointRound *pRound = m_ControlPointRounds[i];
if ( pRound )
{
if ( pRound->IsPlayable() )
{
// we found one that's playable
nRetVal++;
}
}
}
return nRetVal;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::SelectSpecificRound( void )
{
CTeamControlPointRound *pRound = NULL;
CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
if ( pRules )
{
if ( pRules->GetRoundToPlayNext() != NULL_STRING )
{
// do we have the name of a round?
pRound = dynamic_cast<CTeamControlPointRound*>( gEntList.FindEntityByName( NULL, STRING( pRules->GetRoundToPlayNext() ) ) );
if ( pRound )
{
if ( ( m_ControlPointRounds.Find( pRound )== m_ControlPointRounds.InvalidIndex() ) ||
( !pRound->IsPlayable() && !pRound->MakePlayable() ) )
{
pRound = NULL;
}
}
pRules->SetRoundToPlayNext( NULL_STRING );
}
}
// do we have a round to play?
if ( pRound )
{
m_iCurrentRoundIndex = m_ControlPointRounds.Find( pRound );
m_ControlPointRounds[m_iCurrentRoundIndex]->SelectedToPlay();
if ( pRules )
{
pRules->SetRoundOverlayDetails();
}
FireRoundStartOutput();
DevMsg( 2, "**** Selected round %s to play\n", m_ControlPointRounds[m_iCurrentRoundIndex]->GetEntityName().ToCStr() );
if ( !pRules->IsInWaitingForPlayers() )
{
UTIL_LogPrintf( "World triggered \"Mini_Round_Selected\" (round \"%s\")\n", m_ControlPointRounds[m_iCurrentRoundIndex]->GetEntityName().ToCStr() );
UTIL_LogPrintf( "World triggered \"Mini_Round_Start\"\n" );
}
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::RegisterRoundBeingPlayed( void )
{
// let the game rules know what round we're playing
CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
if ( pRules )
{
string_t iszEntityName = m_ControlPointRounds[m_iCurrentRoundIndex]->GetEntityName();
pRules->AddPlayedRound( iszEntityName );
if ( m_bFirstRoundAfterRestart )
{
pRules->SetFirstRoundPlayed( iszEntityName );
m_bFirstRoundAfterRestart = false;
}
}
IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_selected" );
if ( event )
{
event->SetString( "round", m_ControlPointRounds[m_iCurrentRoundIndex]->GetEntityName().ToCStr() );
gameeventmanager->FireEvent( event );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::GetControlPointRoundToPlay( void )
{
int i = 0;
// are we trying to pick a specific round?
if ( SelectSpecificRound() )
{
SetBaseControlPoints();
RegisterRoundBeingPlayed();
return true;
}
// rounds are sorted with the higher priority rounds first
for ( i = 0 ; i < m_ControlPointRounds.Count() ; ++i )
{
CTeamControlPointRound *pRound = m_ControlPointRounds[i];
if ( pRound )
{
if ( pRound->IsPlayable() )
{
// we found one that's playable
break;
}
}
}
if ( i >= m_ControlPointRounds.Count() || m_ControlPointRounds[i] == NULL )
{
// we didn't find one to play
m_iCurrentRoundIndex = -1;
return false;
}
// we have a priority value, now we need to randomly pick a round with this priority that's playable
int nPriority = m_ControlPointRounds[i]->GetPriorityValue();
CUtlVector<int> nRounds;
CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
string_t iszLastRoundPlayed = pRules ? pRules->GetLastPlayedRound() : NULL_STRING;
int iLastRoundPlayed = -1;
string_t iszFirstRoundPlayed = pRules ? pRules->GetFirstRoundPlayed() : NULL_STRING;
int iFirstRoundPlayed = -1; // after a full restart
// loop through and find the rounds with this priority value
for ( i = 0 ; i < m_ControlPointRounds.Count() ; ++i )
{
CTeamControlPointRound *pRound = m_ControlPointRounds[i];
if ( pRound )
{
string_t iszRoundName = pRound->GetEntityName();
if ( pRound->IsPlayable() && pRound->GetPriorityValue() == nPriority )
{
if ( iszLastRoundPlayed == iszRoundName ) // is this the last round we played?
{
iLastRoundPlayed = i;
}
if ( m_bFirstRoundAfterRestart )
{
// is this the first round we played after the last full restart?
if ( ( iszFirstRoundPlayed != NULL_STRING ) && ( iszFirstRoundPlayed == iszRoundName ) )
{
iFirstRoundPlayed = i;
}
}
nRounds.AddToHead(i);
}
}
}
if ( nRounds.Count() <= 0 )
{
// we didn't find one to play
m_iCurrentRoundIndex = -1;
return false;
}
// if we have more than one and the last played round is in our list, remove it
if ( nRounds.Count() > 1 )
{
if ( iLastRoundPlayed != -1 )
{
int elementIndex = nRounds.Find( iLastRoundPlayed );
nRounds.Remove( elementIndex );
}
}
// if this is the first round after a full restart, we still have more than one round in our list,
// and the first played round (after the last full restart) is in our list, remove it
if ( m_bFirstRoundAfterRestart )
{
if ( nRounds.Count() > 1 )
{
if ( iFirstRoundPlayed != -1 )
{
int elementIndex = nRounds.Find( iFirstRoundPlayed );
nRounds.Remove( elementIndex );
}
}
}
// pick one to play but try to avoid picking one that we have recently played if there are other rounds to play
int index = random->RandomInt( 0, nRounds.Count() - 1 );
// only need to check this if we have more than one round with this priority value
if ( pRules && nRounds.Count() > 1 )
{
// keep picking a round until we find one that's not a previously played round
// or until we don't have any more rounds to choose from
while ( pRules->IsPreviouslyPlayedRound( m_ControlPointRounds[ nRounds[ index ] ]->GetEntityName() ) &&
nRounds.Count() > 1 )
{
nRounds.Remove( index ); // we have played this round recently so get it out of the list
index = random->RandomInt( 0, nRounds.Count() - 1 );
}
}
// pick one to play and fire its OnSelected output
m_iCurrentRoundIndex = nRounds[ index ];
m_ControlPointRounds[m_iCurrentRoundIndex]->SelectedToPlay();
if ( pRules )
{
pRules->SetRoundOverlayDetails();
}
FireRoundStartOutput();
DevMsg( 2, "**** Selected round %s to play\n", m_ControlPointRounds[m_iCurrentRoundIndex]->GetEntityName().ToCStr() );
if ( !pRules->IsInWaitingForPlayers() )
{
UTIL_LogPrintf( "World triggered \"Mini_Round_Selected\" (round \"%s\")\n", m_ControlPointRounds[m_iCurrentRoundIndex]->GetEntityName().ToCStr() );
UTIL_LogPrintf( "World triggered \"Mini_Round_Start\"\n" );
}
SetBaseControlPoints();
RegisterRoundBeingPlayed();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Called every 0.1 seconds and checks the status of all the control points
// if one team owns them all, it gives points and resets
// Think also gives the time based points at the specified time intervals
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::CPMThink( void )
{
if ( m_bDisabled || !TeamplayGameRules()->PointsMayBeCaptured() )
{
SetContextThink( &CTeamControlPointMaster::CPMThink, gpGlobals->curtime + 0.2, CPM_THINK );
return;
}
// If we call this from team_control_point, this function should never
// trigger a win. but we'll leave it here just in case.
CheckWinConditions();
// the next time we 'think'
SetContextThink( &CTeamControlPointMaster::CPMThink, gpGlobals->curtime + 0.2, CPM_THINK );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::CheckWinConditions( void )
{
if ( m_bDisabled )
return;
if ( m_ControlPointRounds.Count() > 0 )
{
if ( m_iCurrentRoundIndex != -1 )
{
// Check the current round to see if one team is a winner yet
int iWinners = m_ControlPointRounds[m_iCurrentRoundIndex]->CheckWinConditions();
if ( iWinners != -1 && iWinners >= FIRST_GAME_TEAM )
{
bool bForceMapReset = ( NumPlayableControlPointRounds() == 0 ); // are there any more rounds to play?
if ( !bForceMapReset )
{
// we have more rounds to play
TeamplayGameRules()->SetWinningTeam( iWinners, WINREASON_ALL_POINTS_CAPTURED, bForceMapReset );
}
else
{
// we have played all of the available rounds
TeamplayGameRules()->SetWinningTeam( iWinners, WINREASON_ALL_POINTS_CAPTURED, bForceMapReset, m_bSwitchTeamsOnWin );
}
FireTeamWinOutput( iWinners );
}
}
}
else
{
// Check that the points aren't all held by one team...if they are
// this will reset the round and will reset all the points
int iWinners = TeamOwnsAllPoints();
if ( ( m_iInvalidCapWinner != 1 ) &&
( iWinners >= FIRST_GAME_TEAM ) &&
( iWinners != m_iInvalidCapWinner ) )
{
bool bWinner = true;
#if defined( TF_DLL)
if ( TFGameRules() && TFGameRules()->IsInKothMode() )
{
CTeamRoundTimer *pTimer = NULL;
if ( iWinners == TF_TEAM_RED )
{
pTimer = TFGameRules()->GetRedKothRoundTimer();
}
else if ( iWinners == TF_TEAM_BLUE )
{
pTimer = TFGameRules()->GetBlueKothRoundTimer();
}
if ( pTimer )
{
if ( pTimer->GetTimeRemaining() > 0 || TFGameRules()->TimerMayExpire() == false )
{
bWinner = false;
}
}
}
#endif
if ( bWinner )
{
TeamplayGameRules()->SetWinningTeam( iWinners, WINREASON_ALL_POINTS_CAPTURED, true, m_bSwitchTeamsOnWin );
FireTeamWinOutput( iWinners );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputSetWinner( inputdata_t &input )
{
int iTeam = input.value.Int();
InternalSetWinner( iTeam );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputSetWinnerAndForceCaps( inputdata_t &input )
{
int iTeam = input.value.Int();
// Set all cap points in the current round to be owned by the winning team
for ( unsigned int i = 0; i < m_ControlPoints.Count(); i++ )
{
CTeamControlPoint *pPoint = m_ControlPoints[i];
if ( pPoint && (!PlayingMiniRounds() || ObjectiveResource()->IsInMiniRound(pPoint->GetPointIndex()) ) )
{
pPoint->ForceOwner( iTeam );
}
}
InternalSetWinner( iTeam );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InternalSetWinner( int iTeam )
{
bool bForceMapReset = true;
if ( m_ControlPointRounds.Count() > 0 )
{
// if we're playing rounds and there are more to play, don't do a full reset
bForceMapReset = ( NumPlayableControlPointRounds() == 0 );
}
if ( iTeam == TEAM_UNASSIGNED )
{
TeamplayGameRules()->SetStalemate( STALEMATE_TIMER, bForceMapReset );
}
else
{
if ( !bForceMapReset )
{
TeamplayGameRules()->SetWinningTeam( iTeam, WINREASON_ALL_POINTS_CAPTURED, bForceMapReset );
}
else
{
TeamplayGameRules()->SetWinningTeam( iTeam, WINREASON_ALL_POINTS_CAPTURED, bForceMapReset, m_bSwitchTeamsOnWin );
}
FireTeamWinOutput( iTeam );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::HandleRandomOwnerControlPoints( void )
{
CUtlVector<CTeamControlPoint*> vecPoints;
CUtlVector<int> vecTeams;
int i = 0;
// loop through and find all of the points that want random owners after a full restart
for ( i = 0 ; i < (int)m_ControlPoints.Count() ; i++ )
{
CTeamControlPoint *pPoint = m_ControlPoints[i];
if ( pPoint && pPoint->RandomOwnerOnRestart() )
{
vecPoints.AddToHead( pPoint );
vecTeams.AddToHead( pPoint->GetTeamNumber() );
}
}
// now loop through and mix up the owners (if we found any points with this flag set)
for ( i = 0 ; i < vecPoints.Count() ; i++ )
{
CTeamControlPoint *pPoint = vecPoints[i];
if ( pPoint )
{
int index = random->RandomInt( 0, vecTeams.Count() - 1 );
pPoint->ForceOwner( vecTeams[index] );
vecTeams.Remove( index );
}
}
vecPoints.RemoveAll();
vecTeams.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputRoundSpawn( inputdata_t &input )
{
//clear out old control points
m_ControlPoints.RemoveAll();
//find the control points, and if successful, do CPMThink
if ( FindControlPoints() )
{
/* if ( m_bFirstRoundAfterRestart )
{
CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
if ( pRules && ( pRules->GetRoundToPlayNext() == NULL_STRING ) )
{
// we only want to handle the random points if we don't have a specific round to play next
// (prevents points being randomized again after "waiting for players" has finished and we're going to play the same round)
HandleRandomOwnerControlPoints();
}
}
*/
SetContextThink( &CTeamControlPointMaster::CPMThink, gpGlobals->curtime + 0.1, CPM_THINK );
}
// clear out the old rounds
m_ControlPointRounds.RemoveAll();
// find the rounds (if the map has any)
FindControlPointRounds();
SetBaseControlPoints();
ObjectiveResource()->ResetControlPoints();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputRoundActivate( inputdata_t &input )
{
// if we're using mini-rounds and haven't picked one yet, find one to play
if ( PlayingMiniRounds() && GetCurrentRound() == NULL )
{
GetControlPointRoundToPlay();
}
if ( PlayingMiniRounds() )
{
// Tell the objective resource what control points are in use in the selected mini-round
CTeamControlPointRound *pRound = GetCurrentRound();
if ( pRound )
{
for ( unsigned int i = 0; i < m_ControlPoints.Count(); i++ )
{
CTeamControlPoint *pPoint = m_ControlPoints[i];
if ( pPoint )
{
ObjectiveResource()->SetInMiniRound( pPoint->GetPointIndex(), pRound->IsControlPointInRound( pPoint ) );
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputSetCapLayout( inputdata_t &inputdata )
{
m_iszCapLayoutInHUD = inputdata.value.StringID();
g_pObjectiveResource->SetCapLayoutInHUD( STRING(m_iszCapLayoutInHUD) );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputSetCapLayoutCustomPositionX( inputdata_t &inputdata )
{
m_flCustomPositionX = inputdata.value.Float();
g_pObjectiveResource->SetCapLayoutCustomPosition( m_flCustomPositionX, m_flCustomPositionY );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputSetCapLayoutCustomPositionY( inputdata_t &inputdata )
{
m_flCustomPositionY = inputdata.value.Float();
g_pObjectiveResource->SetCapLayoutCustomPosition( m_flCustomPositionX, m_flCustomPositionY );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::FireTeamWinOutput( int iWinningTeam )
{
// Remap team so that first game team = 1
switch( iWinningTeam - FIRST_GAME_TEAM+1 )
{
case 1:
m_OnWonByTeam1.FireOutput(this,this);
break;
case 2:
m_OnWonByTeam2.FireOutput(this,this);
break;
default:
Assert(0);
break;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::FireRoundStartOutput( void )
{
CTeamControlPointRound *pRound = GetCurrentRound();
if ( pRound )
{
pRound->FireOnStartOutput();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::FireRoundEndOutput( void )
{
CTeamControlPointRound *pRound = GetCurrentRound();
if ( pRound )
{
pRound->FireOnEndOutput();
m_iCurrentRoundIndex = -1;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTeamControlPointMaster::PointLastContestedAt( int point )
{
CTeamControlPoint *pPoint = GetControlPoint(point);
if ( pPoint )
return pPoint->LastContestedAt();
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: This function returns the team that owns all the cap points.
// If its not the case that one team owns them all, it returns 0.
// CPs are broken into groups. A team can win by owning all flags within a single group.
//
// Can be passed an overriding team. If this is not null, the passed team
// number will be used for that cp. Used to predict if that CP changing would
// win the game.
//-----------------------------------------------------------------------------
int CTeamControlPointMaster::TeamOwnsAllPoints( CTeamControlPoint *pOverridePoint /* = NULL */, int iOverrideNewTeam /* = TEAM_UNASSIGNED */ )
{
unsigned int i;
int iWinningTeam[MAX_CONTROL_POINT_GROUPS];
for( i=0;i<MAX_CONTROL_POINT_GROUPS;i++ )
{
iWinningTeam[i] = TEAM_INVALID;
}
// if TEAM_INVALID, haven't found a flag for this group yet
// if TEAM_UNASSIGNED, the group is still being contested
// for each control point
for( i=0;i<m_ControlPoints.Count();i++ )
{
int group = m_ControlPoints[i]->GetCPGroup();
int owner = m_ControlPoints[i]->GetOwner();
if ( pOverridePoint == m_ControlPoints[i] )
{
owner = iOverrideNewTeam;
}
// the first one we find in this group, set the win to true
if ( iWinningTeam[group] == TEAM_INVALID )
{
iWinningTeam[group] = owner;
}
// unassigned means this group is already contested, move on
else if ( iWinningTeam[group] == TEAM_UNASSIGNED )
{
continue;
}
// if we find another one in the group that isn't the same owner, set the win to false
else if ( owner != iWinningTeam[group] )
{
iWinningTeam[group] = TEAM_UNASSIGNED;
}
}
// report the first win we find as the winner
for ( i=0;i<MAX_CONTROL_POINT_GROUPS;i++ )
{
if ( iWinningTeam[i] >= FIRST_GAME_TEAM )
return iWinningTeam[i];
}
// no wins yet
return TEAM_UNASSIGNED;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::WouldNewCPOwnerWinGame( CTeamControlPoint *pPoint, int iNewOwner )
{
return ( TeamOwnsAllPoints( pPoint, iNewOwner ) == iNewOwner );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamControlPointMaster::IsBaseControlPoint( int iPointIndex )
{
bool retVal = false;
for ( int iTeam = LAST_SHARED_TEAM+1; iTeam < GetNumberOfTeams(); iTeam++ )
{
if ( GetBaseControlPoint( iTeam ) == iPointIndex )
{
retVal = true;
break;
}
}
return retVal;
}
//-----------------------------------------------------------------------------
// Purpose: Get the control point for the specified team that's at their end of
// the control point chain.
//-----------------------------------------------------------------------------
int CTeamControlPointMaster::GetBaseControlPoint( int iTeam )
{
int iRetVal = -1;
int nLowestValue = 999, nHighestValue = -1;
int iLowestIndex = 0, iHighestIndex = 0;
for( int i = 0 ; i < (int)m_ControlPoints.Count() ; i++ )
{
CTeamControlPoint *pPoint = m_ControlPoints[i];
int iPointIndex = m_ControlPoints[i]->GetPointIndex();
if ( PlayingMiniRounds() && iTeam > LAST_SHARED_TEAM )
{
if ( IsInRound( pPoint ) ) // is this point in the current round?
{
if ( iPointIndex > nHighestValue )
{
nHighestValue = iPointIndex;
iHighestIndex = i;
}
if ( iPointIndex < nLowestValue )
{
nLowestValue = iPointIndex;
iLowestIndex = i;
}
}
}
else
{
if ( pPoint->GetDefaultOwner() != iTeam )
{
continue;
}
// If it's the first or the last point, it's their base
if ( iPointIndex == 0 || iPointIndex == (((int)m_ControlPoints.Count())-1) )
{
iRetVal = iPointIndex;
break;
}
}
}
if ( PlayingMiniRounds() && iTeam > LAST_SHARED_TEAM )
{
if ( nLowestValue != 999 && nHighestValue != -1 )
{
CTeamControlPoint *pLowestPoint = m_ControlPoints[iLowestIndex];
CTeamControlPoint *pHighestPoint = m_ControlPoints[iHighestIndex];
// which point is owned by this team?
if ( ( pLowestPoint->GetDefaultOwner() == iTeam && pHighestPoint->GetDefaultOwner() == iTeam ) || // if the same team owns both, take the highest value to be the last point
( pHighestPoint->GetDefaultOwner() == iTeam ) )
{
iRetVal = nHighestValue;
}
else if ( pLowestPoint->GetDefaultOwner() == iTeam )
{
iRetVal = nLowestValue;
}
}
}
return iRetVal;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputEnable( inputdata_t &input )
{
m_bDisabled = false;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::InputDisable( inputdata_t &input )
{
m_bDisabled = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTeamControlPointMaster::GetNumPointsOwnedByTeam( int iTeam )
{
int nCount = 0;
for( int i = 0 ; i < (int)m_ControlPoints.Count() ; i++ )
{
CTeamControlPoint *pPoint = m_ControlPoints[i];
if ( pPoint && ( pPoint->GetTeamNumber() == iTeam ) )
{
nCount++;
}
}
return nCount;
}
//-----------------------------------------------------------------------------
// Purpose: returns how many more mini-rounds it will take for specified team
// to win, if they keep winning every mini-round
//-----------------------------------------------------------------------------
int CTeamControlPointMaster::CalcNumRoundsRemaining( int iTeam )
{
// To determine how many rounds remain for a given team if it consistently wins mini-rounds, we have to
// simulate forward each mini-round and track the control point ownership that would result
// vector of control points the team owns in our forward-simulation
CUtlVector<CTeamControlPoint *> vecControlPointsOwned;
// start with all the control points the team currently owns
FOR_EACH_MAP_FAST( m_ControlPoints, iControlPoint )
{
if ( m_ControlPoints[iControlPoint]->GetOwner() == iTeam )
{
vecControlPointsOwned.AddToTail( m_ControlPoints[iControlPoint] );
}
}
int iRoundsRemaining = 0;
// keep simulating what will happen next if this team keeps winning, until
// it owns all the control points in the map
while ( vecControlPointsOwned.Count() < (int) m_ControlPoints.Count() )
{
iRoundsRemaining++;
// choose the next highest-priority round that is playable
for ( int i = 0 ; i < m_ControlPointRounds.Count() ; ++i )
{
CTeamControlPointRound *pRound = m_ControlPointRounds[i];
if ( !pRound )
continue;
// see if one team owns all control points in this round
int iRoundOwningTeam = TEAM_INVALID;
int iControlPoint;
for ( iControlPoint = 0; iControlPoint < pRound->m_ControlPoints.Count(); iControlPoint++ )
{
CTeamControlPoint *pControlPoint = pRound->m_ControlPoints[iControlPoint];
int iControlPointOwningTeam = TEAM_INVALID;
// determine who owns this control point.
// First, check our simulated ownership
if ( vecControlPointsOwned.InvalidIndex() != vecControlPointsOwned.Find( pControlPoint ) )
{
// This team has won this control point in forward simulation
iControlPointOwningTeam = iTeam;
}
else
{
// use actual control point ownership
iControlPointOwningTeam = pControlPoint->GetOwner();
}
if ( 0 == iControlPoint )
{
// if this is the first control point, assign ownership to the team that owns this control point
iRoundOwningTeam = iControlPointOwningTeam;
}
else
{
// for all other control points, if the control point ownership does not match other control points, reset
// round ownership to no team
if ( iRoundOwningTeam != iControlPointOwningTeam )
{
iRoundOwningTeam = TEAM_INVALID;
}
}
}
// this round is playable if all control points are not owned by one team (or owned by a team that can't win by capping them)
bool bPlayable = ( ( iRoundOwningTeam < FIRST_GAME_TEAM ) || ( pRound->GetInvalidCapWinner() == 1 ) || ( iRoundOwningTeam == pRound->GetInvalidCapWinner() ) );
if ( !bPlayable )
continue;
// Pretend this team played and won this round. It now owns all control points from this round. Add all the
// control points from this round that are not already own the owned list to the owned list
int iNewControlPointsOwned = 0;
FOR_EACH_VEC( pRound->m_ControlPoints, iControlPoint )
{
CTeamControlPoint *pControlPoint = pRound->m_ControlPoints[iControlPoint];
if ( vecControlPointsOwned.InvalidIndex() == vecControlPointsOwned.Find( pControlPoint ) )
{
vecControlPointsOwned.AddToTail( pControlPoint );
iNewControlPointsOwned++;
}
}
// sanity check: team being simulated should be owning at least one more new control point per round, or they're not making progress
Assert( iNewControlPointsOwned > 0 );
// now go back and pick the next playable round (if any) given the control points this team now owns,
// repeat until all control points are owned. The number of iterations it takes is the # of rounds remaining
// for this team to win.
break;
}
}
return iRoundsRemaining;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTeamControlPointMaster::GetPartialCapturePointRate( void )
{
return m_flPartialCapturePointsRate;
}
/*
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamControlPointMaster::ListRounds( void )
{
if ( PlayingMiniRounds() )
{
ConMsg( "Rounds in this map:\n\n" );
for ( int i = 0; i < m_ControlPointRounds.Count() ; ++i )
{
CTeamControlPointRound* pRound = m_ControlPointRounds[i];
if ( pRound )
{
const char *pszName = STRING( pRound->GetEntityName() );
ConMsg( "%s\n", pszName );
}
}
}
else
{
ConMsg( "* No rounds in this map *\n" );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void cc_ListRounds( void )
{
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
if ( pMaster )
{
pMaster->ListRounds();
}
}
static ConCommand listrounds( "listrounds", cc_ListRounds, "List the rounds for the current map", FCVAR_CHEAT );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void cc_PlayRound( const CCommand& args )
{
if ( args.ArgC() > 1 )
{
CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
if ( pRules && pMaster )
{
if ( pMaster->PlayingMiniRounds() )
{
// did we get the name of a round?
CTeamControlPointRound *pRound = dynamic_cast<CTeamControlPointRound*>( gEntList.FindEntityByName( NULL, args[1] ) );
if ( pRound )
{
pRules->SetRoundToPlayNext( pRound->GetEntityName() );
mp_restartgame.SetValue( 5 );
}
else
{
ConMsg( "* Round \"%s\" not found in this map *\n", args[1] );
}
}
}
}
else
{
ConMsg( "Usage: playround < round name >\n" );
}
}
static ConCommand playround( "playround", cc_PlayRound, "Play the selected round\n\tArgument: {round name given by \"listrounds\" command}", FCVAR_CHEAT );
*/