//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: CS's custom CPlayerResource // // $NoKeywords: $ //=============================================================================// #include "cbase.h" #include "cs_player.h" #include "cs_simple_hostage.h" #include "cs_player_resource.h" #include "econ_game_account_client.h" #include "weapon_c4.h" #include #include "cs_bot_manager.h" #include "cs_gamerules.h" #include "cs_bot.h" #include "cs_team.h" extern bool g_fGameOver; // Datatable IMPLEMENT_SERVERCLASS_ST(CCSPlayerResource, DT_CSPlayerResource) SendPropInt( SENDINFO( m_iPlayerC4 ), 8, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_iPlayerVIP ), 8, SPROP_UNSIGNED ), SendPropArray3( SENDINFO_ARRAY3(m_bHostageAlive), SendPropInt( SENDINFO_ARRAY(m_bHostageAlive), 1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_isHostageFollowingSomeone), SendPropInt( SENDINFO_ARRAY(m_isHostageFollowingSomeone), 1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_iHostageEntityIDs), SendPropInt( SENDINFO_ARRAY(m_iHostageEntityIDs), -1, SPROP_UNSIGNED ) ), SendPropVector( SENDINFO(m_bombsiteCenterA), -1, SPROP_COORD), SendPropVector( SENDINFO(m_bombsiteCenterB), -1, SPROP_COORD), SendPropArray3( SENDINFO_ARRAY3(m_hostageRescueX), SendPropInt( SENDINFO_ARRAY(m_hostageRescueX), COORD_INTEGER_BITS+1, 0 ) ), SendPropArray3( SENDINFO_ARRAY3(m_hostageRescueY), SendPropInt( SENDINFO_ARRAY(m_hostageRescueY), COORD_INTEGER_BITS+1, 0 ) ), SendPropArray3( SENDINFO_ARRAY3(m_hostageRescueZ), SendPropInt( SENDINFO_ARRAY(m_hostageRescueZ), COORD_INTEGER_BITS+1, 0 ) ), SendPropArray3( SENDINFO_ARRAY3(m_iMVPs), SendPropInt( SENDINFO_ARRAY(m_iMVPs), COORD_INTEGER_BITS+1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_iArmor), SendPropInt( SENDINFO_ARRAY(m_iArmor), COORD_INTEGER_BITS+1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_bHasDefuser), SendPropInt( SENDINFO_ARRAY(m_bHasDefuser), 1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_bHasHelmet), SendPropInt( SENDINFO_ARRAY(m_bHasHelmet), 1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_iScore), SendPropInt( SENDINFO_ARRAY(m_iScore), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_iCompetitiveRanking), SendPropInt( SENDINFO_ARRAY(m_iCompetitiveRanking), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_iCompetitiveWins), SendPropInt( SENDINFO_ARRAY(m_iCompetitiveWins), 32) ), SendPropArray3( SENDINFO_ARRAY3( m_iCompTeammateColor ), SendPropInt( SENDINFO_ARRAY( m_iCompTeammateColor ), 32 ) ), #if CS_CONTROLLABLE_BOTS_ENABLED SendPropArray3( SENDINFO_ARRAY3(m_bControllingBot), SendPropInt( SENDINFO_ARRAY(m_bControllingBot), 1, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_iControlledPlayer), SendPropInt( SENDINFO_ARRAY(m_iControlledPlayer), 8, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3(m_iControlledByPlayer), SendPropInt( SENDINFO_ARRAY(m_iControlledByPlayer), 8, SPROP_UNSIGNED ) ), #endif SendPropArray3( SENDINFO_ARRAY3(m_iBotDifficulty), SendPropInt( SENDINFO_ARRAY(m_iBotDifficulty), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_szClan), SendPropStringT( SENDINFO_ARRAY(m_szClan) ) ), SendPropArray3( SENDINFO_ARRAY3(m_iTotalCashSpent), SendPropInt( SENDINFO_ARRAY(m_iTotalCashSpent), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_iCashSpentThisRound), SendPropInt( SENDINFO_ARRAY(m_iCashSpentThisRound), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_nEndMatchNextMapVotes), SendPropInt( SENDINFO_ARRAY(m_nEndMatchNextMapVotes), 32) ), SendPropBool( SENDINFO( m_bEndMatchNextMapAllVoted ) ), SendPropArray3( SENDINFO_ARRAY3(m_nActiveCoinRank), SendPropInt( SENDINFO_ARRAY(m_nActiveCoinRank), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_nMusicID), SendPropInt( SENDINFO_ARRAY(m_nMusicID), 32) ), // SendPropArray3( SENDINFO_ARRAY3(m_bIsAssassinationTarget), SendPropBool( SENDINFO_ARRAY(m_bIsAssassinationTarget), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_nPersonaDataPublicLevel), SendPropInt( SENDINFO_ARRAY(m_nPersonaDataPublicLevel), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_nPersonaDataPublicCommendsLeader), SendPropInt( SENDINFO_ARRAY(m_nPersonaDataPublicCommendsLeader), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_nPersonaDataPublicCommendsTeacher), SendPropInt( SENDINFO_ARRAY(m_nPersonaDataPublicCommendsTeacher), 32) ), SendPropArray3( SENDINFO_ARRAY3(m_nPersonaDataPublicCommendsFriendly), SendPropInt( SENDINFO_ARRAY(m_nPersonaDataPublicCommendsFriendly), 32) ), END_SEND_TABLE() BEGIN_DATADESC( CCSPlayerResource ) // DEFINE_ARRAY( m_iPing, FIELD_INTEGER, MAX_PLAYERS+1 ), // DEFINE_ARRAY( m_iPacketloss, FIELD_INTEGER, MAX_PLAYERS+1 ), END_DATADESC() LINK_ENTITY_TO_CLASS( cs_player_manager, CCSPlayerResource ); CCSPlayerResource::CCSPlayerResource( void ) { m_bEndMatchNextMapAllVoted = false; m_bPreferencesAssigned_T = false; m_bPreferencesAssigned_CT = false; memset( m_nAttemptedToGetColor, false, sizeof( m_nAttemptedToGetColor ) ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCSPlayerResource::UpdatePlayerData( void ) { int i; m_iPlayerC4 = 0; m_iPlayerVIP = 0; int nTotalPlayingPlayers = 0; for ( i = 1; i <= gpGlobals->maxClients; i++ ) { int botDifficulty = -1; CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); bool bSetValidRanking = false; if ( pPlayer && pPlayer->IsConnected() ) { if ( pPlayer->IsVIP() ) { // we should only have one VIP Assert( m_iPlayerVIP == 0 ); m_iPlayerVIP = i; } if ( pPlayer->HasC4() ) { // we should only have one bomb m_iPlayerC4 = i; } m_iMVPs.Set( i, pPlayer->GetNumMVPs() ); m_bHasDefuser.Set( i, pPlayer->HasDefuser() ); m_bHasHelmet.Set( i, pPlayer->m_bHasHelmet ); m_iArmor.Set( i, pPlayer->ArmorValue() ); m_iScore.Set( i, pPlayer->GetScore() ); m_iTotalCashSpent.Set( i, pPlayer->GetTotalCashSpent() ); m_iCashSpentThisRound.Set( i, pPlayer->GetCashSpentThisRound() ); m_szClan.Set(i, AllocPooledString( pPlayer->GetClanTag() ) ); m_nEndMatchNextMapVotes.Set( i, pPlayer->GetEndMatchNextMapVote() ); m_nActiveCoinRank.Set( i, pPlayer->GetRank( MEDAL_CATEGORY_SEASON_COIN ) ); m_nMusicID.Set( i, pPlayer->GetMusicID() ); // UpdateAssassinationTargets(); if ( CEconPersonaDataPublic const *pPublic = pPlayer->GetPersonaDataPublic() ) { m_nPersonaDataPublicLevel.Set( i, pPublic->Obj().player_level() ); m_nPersonaDataPublicCommendsLeader.Set( i, pPublic->Obj().commendation().cmd_leader() ); m_nPersonaDataPublicCommendsTeacher.Set( i, pPublic->Obj().commendation().cmd_teaching() ); m_nPersonaDataPublicCommendsFriendly.Set( i, pPublic->Obj().commendation().cmd_friendly() ); } else { m_nPersonaDataPublicLevel.Set( i, -1 ); m_nPersonaDataPublicCommendsLeader.Set( i, -1 ); m_nPersonaDataPublicCommendsTeacher.Set( i, -1 ); m_nPersonaDataPublicCommendsFriendly.Set( i, -1 ); } if ( pPlayer->IsBot() ) { CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer ); if ( pBot ) { // Retrieve and store the bot's difficulty level const BotProfile* pProfile = pBot->GetProfile(); if ( pProfile ) { botDifficulty = pProfile->GetMaxDifficulty(); } m_iCompTeammateColor.Set( i, -2 ); m_nAttemptedToGetColor[i] = true; // SetPlayerTeammateColor( i, false ); } } else { if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) nTotalPlayingPlayers++; SetPlayerTeammateColor( i, false ); } if ( CSGameRules() && CSGameRules()->IsQueuedMatchmaking() ) { for ( int k = 0; k < CCSGameRules::sm_QueuedServerReservation.rankings().size(); ++ k ) { if ( CCSGameRules::sm_QueuedServerReservation.rankings( k ).account_id() == pPlayer->GetHumanPlayerAccountID() ) { m_iCompetitiveRanking.Set( i, CCSGameRules::sm_QueuedServerReservation.rankings( k ).rank_id() ); m_iCompetitiveWins.Set( i, CCSGameRules::sm_QueuedServerReservation.rankings( k ).wins() ); bSetValidRanking = true; break; } } } } else { m_iMVPs.Set( i, 0 ); m_bHasDefuser.Set( i, false ); m_bHasHelmet.Set( i, false ); m_iArmor.Set( i, 0 ); m_szClan.Set( i, MAKE_STRING( "" ) ); m_nEndMatchNextMapVotes.Set( i, -1 ); m_nActiveCoinRank.Set( i, -1 ); m_nMusicID.Set( i, -1 ); m_bIsAssassinationTarget.Set( i, 0 ); m_nPersonaDataPublicLevel.Set( i, -1 ); m_nPersonaDataPublicCommendsLeader.Set( i, -1 ); m_nPersonaDataPublicCommendsTeacher.Set( i, -1 ); m_nPersonaDataPublicCommendsFriendly.Set( i, -1 ); m_iCompTeammateColor.Set( i, -1 ); m_nAttemptedToGetColor[i] = false; } if ( !bSetValidRanking ) { m_iCompetitiveRanking.Set( i, 0 ); m_iCompetitiveWins.Set( i, 0 ); } m_iBotDifficulty.Set( i, botDifficulty ); } if ( g_fGameOver && nTotalPlayingPlayers > 0 && CSGameRules() && CSGameRules()->IsEndMatchVotingForNextMap() ) { if ( m_bEndMatchNextMapAllVoted == false ) { // ignore whether all players voted and just return whether or not we ran out of time if ( CSGameRules()->m_eEndMatchMapVoteState == CSGameRules()->k_EEndMatchMapVoteState_VoteTimeEnded || CSGameRules()->m_eEndMatchMapVoteState == CSGameRules()->k_EEndMatchMapVoteState_AllPlayersVoted || CSGameRules()->m_eEndMatchMapVoteState == CSGameRules()->k_EEndMatchMapVoteState_SelectingWinner || CSGameRules()->m_eEndMatchMapVoteState == CSGameRules()->k_EEndMatchMapVoteState_SettingNextLevel || CSGameRules()->m_eEndMatchMapVoteState == CSGameRules()->k_EEndMatchMapVoteState_VoteAllDone ) { m_bEndMatchNextMapAllVoted = true; } /* m_bEndMatchNextMapAllVoted = (CSGameRules()->m_eEndMatchMapVoteState == CSGameRules()->k_EEndMatchMapVoteState_VoteTimeEnded);*/ // int nNumVotes = 0; // int nHighestSingleVoteSlot = 0; // // // this is done in three different places // // TODO: make a function out of this // int nVotes[MAX_ENDMATCH_VOTE_PANELS]; // for ( int i = 0; i < MAX_ENDMATCH_VOTE_PANELS; i++ ) // nVotes[i] = 0; // // for ( int i = 1; i <= MAX_PLAYERS; i++ ) // { // int nMapSlot = m_nEndMatchNextMapVotes[i]; // if ( nMapSlot != -1 && nMapSlot < MAX_ENDMATCH_VOTE_PANELS ) // { // nVotes[nMapSlot] += 1; // nNumVotes += 1; // } // } // ////// // // for ( int i = 0; i < MAX_ENDMATCH_VOTE_PANELS + 1; i++ ) // { // if ( nVotes[nHighestSingleVoteSlot] < nVotes[i] ) // nHighestSingleVoteSlot = i; // } // // if ( nTotalPlayingPlayers > 0 ) // { // float flnVotesToSucceed = (float)nTotalPlayingPlayers * 0.501f; // int nVotesToSucceed = ceil( flnVotesToSucceed ); // // if ( nNumVotes >= nTotalPlayingPlayers || nVotes[nHighestSingleVoteSlot] >= nVotesToSucceed || ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) ) ) // m_bEndMatchNextMapAllVoted = true; // } } } else { // if we aren't at the end of the game or we don't have any players, no one has voted m_bEndMatchNextMapAllVoted = false; } int numHostages = g_Hostages.Count(); for ( i = 0; i < MAX_HOSTAGES; i++ ) { if ( i >= numHostages ) { // engine->Con_NPrintf( i, "Dead" ); m_bHostageAlive.Set( i, false ); m_isHostageFollowingSomeone.Set( i, false ); m_iHostageEntityIDs.Set( i, 0 ); continue; } CHostage* pHostage = g_Hostages[i]; m_bHostageAlive.Set( i, pHostage->IsRescuable() ); if ( pHostage->IsValid() ) { m_iHostageEntityIDs.Set( i, pHostage->entindex() ); m_isHostageFollowingSomeone.Set( i, pHostage->IsFollowingSomeone() ); // engine->Con_NPrintf( i, "ID:%d Pos:(%.0f,%.0f,%.0f)", pHostage->entindex(), pHostage->GetAbsOrigin().x, pHostage->GetAbsOrigin().y, pHostage->GetAbsOrigin().z ); } else { // engine->Con_NPrintf( i, "Invalid" ); } } if( !m_foundGoalPositions ) { // We only need to update these once a map, but we need the client to know about them. CBaseEntity* ent = NULL; while ( ( ent = gEntList.FindEntityByClassname( ent, "func_bomb_target" ) ) != NULL ) { const Vector &pos = ent->WorldSpaceCenter(); CNavArea *area = TheNavMesh->GetNearestNavArea( pos, true, 10000.0f, false, false ); const char *placeName = (area) ? TheNavMesh->PlaceToName( area->GetPlace() ) : NULL; if ( placeName == NULL ) { // The bomb site has no area or place name, so just choose A then B if ( m_bombsiteCenterA.Get().IsZero() ) { m_bombsiteCenterA = pos; } else { m_bombsiteCenterB = pos; } } else { // The bomb site has a place name, so choose accordingly if( FStrEq( placeName, "BombsiteA" ) || FStrEq( placeName, "Bombsite" ) ) { m_bombsiteCenterA = pos; } else { m_bombsiteCenterB = pos; } } m_foundGoalPositions = true; } int hostageRescue = 0; while ( (( ent = gEntList.FindEntityByClassname( ent, "func_hostage_rescue" ) ) != NULL) && (hostageRescue < MAX_HOSTAGE_RESCUES) ) { const Vector &pos = ent->WorldSpaceCenter(); m_hostageRescueX.Set( hostageRescue, (int) pos.x ); m_hostageRescueY.Set( hostageRescue, (int) pos.y ); m_hostageRescueZ.Set( hostageRescue, (int) pos.z ); hostageRescue++; m_foundGoalPositions = true; } } #if CS_CONTROLLABLE_BOTS_ENABLED for ( int i=0; i < MAX_PLAYERS+1; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); bool bControllingBot = false; CCSPlayer *pControlledPlayer = NULL; CCSPlayer *pControlledByPlayer = NULL; if ( pPlayer && pPlayer->IsConnected() ) { bControllingBot = pPlayer->IsControllingBot(); pControlledPlayer = pPlayer->GetControlledBot(); pControlledByPlayer = pPlayer->GetControlledByPlayer(); } m_bControllingBot.Set( i, bControllingBot ? 1 : 0 ); m_iControlledPlayer.Set( i, pControlledPlayer ? pControlledPlayer->entindex() : 0 ); m_iControlledByPlayer.Set( i, pControlledByPlayer ? pControlledByPlayer->entindex() : 0 ); } #endif BaseClass::UpdatePlayerData(); } void CCSPlayerResource::Spawn( void ) { m_iPlayerC4 = 0; m_iPlayerVIP = 0; m_bombsiteCenterA.Init(); m_bombsiteCenterB.Init(); m_foundGoalPositions = false; m_bPreferencesAssigned_CT = false; m_bPreferencesAssigned_T = false; memset( m_nAttemptedToGetColor, false, sizeof( m_nAttemptedToGetColor ) ); for ( int i=0; i < MAX_HOSTAGES; i++ ) { m_bHostageAlive.Set( i, 0 ); m_isHostageFollowingSomeone.Set( i, 0 ); m_iHostageEntityIDs.Set(i, 0); } for ( int i=0; i < MAX_HOSTAGE_RESCUES; i++ ) { m_hostageRescueX.Set( i, 0 ); m_hostageRescueY.Set( i, 0 ); m_hostageRescueZ.Set( i, 0 ); } for ( int i=0; i < MAX_PLAYERS+1; i++ ) { m_iMVPs.Set( i, 0 ); m_bHasDefuser.Set( i, false ); m_bHasHelmet.Set( i, false ); m_iArmor.Set( i, 0 ); m_iScore.Set( i, 0 ); m_iCompetitiveRanking.Set( i, 0 ); m_iCompetitiveWins.Set( i, 0 ); m_iCompTeammateColor.Set( i, -1 ); m_iBotDifficulty.Set( i, -1 ); m_szClan.Set( i, MAKE_STRING( "" ) ); m_nActiveCoinRank.Set( i, -1 ); m_nMusicID.Set( i, -1 ); m_bIsAssassinationTarget.Set( i, 0 ); m_nPersonaDataPublicLevel.Set( i, -1 ); m_nPersonaDataPublicCommendsLeader.Set( i, -1 ); m_nPersonaDataPublicCommendsTeacher.Set( i, -1 ); m_nPersonaDataPublicCommendsFriendly.Set( i, -1 ); } m_bEndMatchNextMapAllVoted = false; for ( int i=0; i < MAX_ENDMATCH_VOTE_PANELS+1; i++ ) { m_nEndMatchNextMapVotes.Set( i, 0 ); } BaseClass::Spawn(); } const Vector CCSPlayerResource::GetBombsiteAPosition() { return m_bombsiteCenterA; } const Vector CCSPlayerResource::GetBombsiteBPosition() { return m_bombsiteCenterB; } const Vector CCSPlayerResource::GetHostageRescuePosition( int iIndex ) { if ( iIndex < 0 || iIndex >= MAX_HOSTAGE_RESCUES ) return vec3_origin; Vector ret; ret.x = m_hostageRescueX[iIndex]; ret.y = m_hostageRescueY[iIndex]; ret.z = m_hostageRescueZ[iIndex]; return ret; } //-------------------------------------------------------------------------------------------------------- int CCSPlayerResource::GetCompTeammateColor( int iIndex ) { CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( iIndex ); if ( !pPlayer ) return -1; if ( pPlayer->IsBot() ) return -2; return m_iCompTeammateColor[iIndex]; } void CCSPlayerResource::ResetPlayerTeammateColor( int index ) { CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( index ); if ( !pPlayer ) return; if ( CSGameRules() && CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() && CSGameRules()->IsQueuedMatchmaking() ) return; int nTeamNum = pPlayer->GetTeamNumber(); if ( nTeamNum > TEAM_SPECTATOR ) { SetPlayerTeammateColor( index, true ); return; } m_iCompTeammateColor.Set( index, -1 ); } void CCSPlayerResource::ForcePlayersPickColors() { m_bPreferencesAssigned_CT = true; m_bPreferencesAssigned_T = true; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) m_nAttemptedToGetColor[i] = true; } void CCSPlayerResource::SetPlayerTeammateColor( int index, bool bReset ) { CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( index ); if ( !pPlayer ) return; m_nAttemptedToGetColor[index] = true; if ( !CSGameRules() || !CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() ) { m_iCompTeammateColor.Set( index, -1 ); return; } if ( pPlayer->IsBot() ) { m_iCompTeammateColor.Set( index, -2 ); return; } int nTeamNum = pPlayer->GetTeamNumber(); if ( nTeamNum > TEAM_SPECTATOR ) { if ( CSGameRules() && CSGameRules()->IsPlayingAnyCompetitiveStrictRuleset() ) { // check to see if we have a color already int idxThisPlayer = -1; // don't use the QMM code /* if ( CSGameRules()->IsQueuedMatchmaking() ) { CCSPlayer *pThisPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( index ); CSteamID steamID; pThisPlayer->GetSteamID( &steamID ); int numTotalPlayers = 0; static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" ); for ( char const *pszPrev = sv_mmqueue_reservation.GetString(), *pszNext = pszPrev; ( pszNext = strchr( pszPrev, '[' ) ) != NULL; pszPrev = pszNext + 1 ) { uint32 uiAccountId = 0; sscanf( pszNext, "[%x]", &uiAccountId ); if ( uiAccountId && ( steamID.GetAccountID() == uiAccountId ) ) { idxThisPlayer = numTotalPlayers; } ++numTotalPlayers; } } */ // let all players have at least one crack at getting their prefered color before we start assigning loser colors if ( (nTeamNum == TEAM_TERRORIST && m_bPreferencesAssigned_T == false) || (nTeamNum == TEAM_CT && m_bPreferencesAssigned_CT == false) ) { int nNumAttemptedToGetColor = 0; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pOtherPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( pOtherPlayer && pOtherPlayer->GetTeamNumber() == pPlayer->GetTeamNumber() ) { if ( m_nAttemptedToGetColor[i] == true ) nNumAttemptedToGetColor++; if ( nNumAttemptedToGetColor >= 5 ) { if ( nTeamNum == TEAM_TERRORIST ) m_bPreferencesAssigned_T = true; else m_bPreferencesAssigned_CT = true; break; } } } } // Valve MM gives us the player index - does this work? if ( idxThisPlayer > -1 ) { m_iCompTeammateColor.Set( index, ( idxThisPlayer % 5 ) ); } else if ( m_iCompTeammateColor[index] == -1 || bReset )//otherwise we have to do it ourselves { int nPreferredColor = pPlayer->GetTeammatePreferredColor( ); if ( nPreferredColor == -1 ) { pPlayer->InitTeammatePreferredColor( ); nPreferredColor = pPlayer->GetTeammatePreferredColor( ); } // we didn't initialize, so try again another time if ( nPreferredColor == -1 ) return; int nAssignedColor = m_iCompTeammateColor[index] > -1 ? m_iCompTeammateColor[index] : nPreferredColor; bool bColorInUse = false; for ( int ii = 0; ii < 5; ii++ ) { nAssignedColor = nAssignedColor % 5; bColorInUse = false; for ( int j = 1; j <= gpGlobals->maxClients; j++ ) { CCSPlayer *pOtherPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( j ); if ( pOtherPlayer && pOtherPlayer->GetTeamNumber( ) == pPlayer->GetTeamNumber( ) ) { if ( nAssignedColor == m_iCompTeammateColor[j] && pOtherPlayer != pPlayer ) { // All players should get a crack at getting their prefered color before a // previously connected player crawls up the color scale and nabs it first if ( ( nTeamNum == TEAM_TERRORIST && m_bPreferencesAssigned_T == false) || ( nTeamNum == TEAM_CT && m_bPreferencesAssigned_CT == false ) ) return; bColorInUse = true; nAssignedColor++; break; } } } if ( bColorInUse == false ) break; } // somehow this failed AssertMsg( !bColorInUse, "Trying to assign a color to a teammate, but all colors are already in use!" ); nAssignedColor = bColorInUse == false ? nAssignedColor : -1; m_iCompTeammateColor.Set( index, nAssignedColor ); } } else m_iCompTeammateColor.Set( index, -1 ); } } bool CCSPlayerResource::IsAssassinationTarget( int index ) const { return m_bIsAssassinationTarget[ index ]; } bool Helper_DoesPlayerHaveAssassinateQuestForTeam( const CCSPlayer *pPlayer, int iTeamNum ) { // If this player has an assassination quest targeting this team, prefer not to pick them as the target CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( pPlayer->Inventory()->GetActiveQuestID() ); return ( pQuest && IsAssassinationQuest( pQuest ) && ( ( int ) pQuest->GetTargetTeam() == iTeamNum ) ); } bool Helper_ValidateAssassinationTarget( const CCSPlayer *pCurrentAssassinationTarget, int iTeamNum ) { // Validate current assassination target, pick new one if needed if ( !pCurrentAssassinationTarget || !pCurrentAssassinationTarget->IsConnected() || pCurrentAssassinationTarget->GetTeamNumber() != iTeamNum || pCurrentAssassinationTarget->IsDead() || pCurrentAssassinationTarget->IsControllingBot() || Helper_DoesPlayerHaveAssassinateQuestForTeam( pCurrentAssassinationTarget, iTeamNum ) ) { return false; } return true; } ConVar sv_assassination_target_ratio( "sv_assassination_target_ratio", "5" ); void CCSPlayerResource::UpdateAssassinationTargets( const CEconQuestDefinition * pQuest ) { CCSTeam *pTeam = GetGlobalCSTeam( pQuest->GetTargetTeam() ); if ( !pTeam ) return; // 1 out of X players is an assassination target, no less than 1 and more more than MAX_ASSASSINATION_TARGETS. CUtlVector vecCandiates; auto iTargetsNeeded = Min( Max( 1, pTeam->GetHumanMembers( &vecCandiates ) / Max( 1, sv_assassination_target_ratio.GetInt() ) ), 3 ); CUtlVector< CCSPlayer* > vecNotIdealPlayers; FOR_EACH_VEC_BACK( vecCandiates, iter ) { CCSPlayer* pCur = vecCandiates[ iter ]; // Validate current assassination targets, remove from candidate list if ( pCur->IsAssassinationTarget() ) { // Still valid, then reduce count of needed targets if ( Helper_ValidateAssassinationTarget( pCur, pQuest->GetTargetTeam() ) ) { iTargetsNeeded--; } else { // Prefer not to pick recently invalidated players m_bIsAssassinationTarget.GetForModify( pCur->entindex() ) = false; vecNotIdealPlayers.AddToHead( pCur ); } vecCandiates.Remove( iter ); } else if ( Helper_DoesPlayerHaveAssassinateQuestForTeam( pCur, GetTeamNumber() ) ) { // Prefer not to pick players with this quest vecCandiates.Remove( iter ); vecNotIdealPlayers.AddToTail( pCur ); } } while ( iTargetsNeeded-- > 0 ) { CUtlVector< CCSPlayer* > &vecBucket = vecCandiates.Count() > 0 ? vecCandiates : vecNotIdealPlayers; CCSPlayer *pTarget = vecBucket[ RandomInt( 0, vecBucket.Count() - 1 ) ]; vecBucket.FindAndFastRemove( pTarget ); m_bIsAssassinationTarget.GetForModify( pTarget->entindex() ) = true; } }