//========= 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(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; jGetPointIndex() < 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( 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( GameRules() ); if ( pRules ) { if ( pRules->GetRoundToPlayNext() != NULL_STRING ) { // do we have the name of a round? pRound = dynamic_cast( 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( 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 nRounds; CTeamplayRoundBasedRules *pRules = dynamic_cast( 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 vecPoints; CUtlVector 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( 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;iGetCPGroup(); 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= 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 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( 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( 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 ); */