2020-04-22 16:56:21 +00:00
//========= 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 ( " !!!! \n Multiple 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 ;
2022-03-01 20:00:42 +00:00
int nLowestValue = 999 , nHighestValue = - 1 ;
int iLowestIndex = 0 , iHighestIndex = 0 ;
2020-04-22 16:56:21 +00:00
2022-03-01 20:00:42 +00:00
for ( int i = 0 ; i < ( int ) m_ControlPoints . Count ( ) ; i + + )
2020-04-22 16:56:21 +00:00
{
CTeamControlPoint * pPoint = m_ControlPoints [ i ] ;
2022-03-01 20:00:42 +00:00
int iPointIndex = m_ControlPoints [ i ] - > GetPointIndex ( ) ;
if ( PlayingMiniRounds ( ) & & iTeam > LAST_SHARED_TEAM )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
if ( IsInRound ( pPoint ) ) // is this point in the current round?
{
if ( iPointIndex > nHighestValue )
{
nHighestValue = iPointIndex ;
iHighestIndex = i ;
}
2020-04-22 16:56:21 +00:00
2022-03-01 20:00:42 +00:00
if ( iPointIndex < nLowestValue )
{
nLowestValue = iPointIndex ;
iLowestIndex = i ;
}
}
}
else
{
if ( pPoint - > GetDefaultOwner ( ) ! = iTeam )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
continue ;
2020-04-22 16:56:21 +00:00
}
2022-03-01 20:00:42 +00:00
// If it's the first or the last point, it's their base
if ( iPointIndex = = 0 | | iPointIndex = = ( ( ( int ) m_ControlPoints . Count ( ) ) - 1 ) )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
iRetVal = iPointIndex ;
break ;
2020-04-22 16:56:21 +00:00
}
}
}
2022-03-01 20:00:42 +00:00
if ( PlayingMiniRounds ( ) & & iTeam > LAST_SHARED_TEAM )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
if ( nLowestValue ! = 999 & & nHighestValue ! = - 1 )
2020-04-22 16:56:21 +00:00
{
2022-03-01 20:00:42 +00:00
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 ;
}
2020-04-22 16:56:21 +00:00
}
}
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 ;
}
2022-03-01 20:00:42 +00:00
/*
2020-04-22 16:56:21 +00:00
//-----------------------------------------------------------------------------
// 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 \t Argument: {round name given by \" listrounds \" command} " , FCVAR_CHEAT ) ;
2022-03-01 20:00:42 +00:00
*/