//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: The TF Game rules // // $NoKeywords: $ //=============================================================================// #ifdef POSIX // // Crazy compiler problems fix -- deque must be included first before any of the Valve game headers, which introduce // macro defines that completely break STL compile process. STL include dependencies happen as part of RSA crypto // library dependencies included at the bottom of this file. // #include #endif #include "cbase.h" #include "cs_gamerules.h" #include "cs_ammodef.h" #include "weapon_csbase.h" #include "basecsgrenade_projectile.h" #include "cs_shareddefs.h" #include "keyvalues.h" #include "cs_achievement_constants.h" #include "iachievementmgr.h" #include "matchmaking/imatchframework.h" #include "inputsystem/iinputsystem.h" #include "platforminputdevice.h" #include "engine/inetsupport.h" #include "usermessages.h" #include "hegrenade_projectile.h" #ifndef CLIENT_DLL #include "Effects/inferno.h" #endif #include "econ_item_view_helpers.h" #ifdef CLIENT_DLL #include "networkstringtable_clientdll.h" #include "c_cs_player.h" #include "fmtstr.h" #include "vgui/ILocalize.h" // temp - needed for GetFriendlyMapName() #include "c_team.h" #include "weapon_selection.h" #include "hud_macros.h" #include "c_cs_playerresource.h" #else #include "baseentity.h" #include "vote_controller.h" #include "cs_voteissues.h" #include "bot.h" #include "utldict.h" #include "cs_player.h" #include "cs_team.h" #include "cs_gamerules.h" #include "voice_gamemgr.h" #include "igamesystem.h" #include "weapon_c4.h" #include "mapinfo.h" #include "shake.h" #include "mapentities.h" #include "game.h" #include "cs_simple_hostage.h" #include "cs_gameinterface.h" #include "player_resource.h" #include "info_view_parameters.h" #include "cs_bot_manager.h" #include "cs_bot.h" #include "eventqueue.h" #include "fmtstr.h" #include "teamplayroundbased_gamerules.h" #include "gameweaponmanager.h" #include "cs_gamestats.h" #include "cs_urlretrieveprices.h" #include "networkstringtable_gamedll.h" #include "func_bomb_target.h" #include "func_hostage_rescue.h" #include "dedicated_server_ugc_manager.h" #include "vstdlib/vstrtools.h" #include "platforminputdevice.h" #include "cs_entity_spotting.h" #include "props.h" #include "hltvdirector.h" #include "econ_gcmessages.h" #include "env_cascade_light.h" #include "world.h" #include "items.h" #include "Effects/chicken.h" #endif #include "gametypes.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #ifndef CLIENT_DLL #define CS_GAME_STATS_UPDATE 79200 //22 hours #define CS_GAME_STATS_UPDATE_PERIOD 7200 // 2 hours #define ROUND_END_WARNING_TIME 10.0f static const float MAX_TIME_TO_WAIT_BEFORE_ENTERING = 5.0f; // # seconds to delay before displaying autobalance text to clients static const float AUTOBALANCE_TEXT_DELAY = 3.0f; extern IUploadGameStats *gamestatsuploader; extern bool Commentary_IsCommentaryEntity( CBaseEntity *pEntity ); ConVar spec_replay_round_delay( "spec_replay_round_delay", "0", FCVAR_RELEASE, "Round can be delayed by this much due to someone watching a replay; must be at least 3-4 seconds, otherwise the last replay will always be interrupted by round start, assuming normal pause between round_end and round_start events (7 seconds) and freezecam delay (2 seconds) and 7.4 second full replay (5.4 second pre-death and ~2 seconds post-death) and replay in/out switching (up to a second)" ); #endif // !CLIENT_DLL const float g_flWarmupToFreezetimeDelay = 4.0f; ConVar sv_server_graphic1( "sv_server_graphic1", "", FCVAR_REPLICATED | FCVAR_RELEASE, "A 360x60 (<16kb) image file in /csgo/ that will be displayed to spectators." ); ConVar sv_server_graphic2( "sv_server_graphic2", "", FCVAR_REPLICATED | FCVAR_RELEASE, "A 220x45 (<16kb) image file in /csgo/ that will be displayed to spectators." ); ConVar sv_disable_observer_interpolation( "sv_disable_observer_interpolation", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Disallow interpolating between observer targets on this server." ); ConVar sv_reward_drop_delay( "sv_reward_drop_delay", "3.0", FCVAR_REPLICATED, "Delay between the end match scoreboard being shown and the beginning of item drops." ); // to define which holiday it is ConVar sv_holiday_mode( "sv_holiday_mode", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "0 = OFF, 1 = Halloween, 2 = Winter" ); ConVar sv_teamid_overhead_always_prohibit( "sv_teamid_overhead_always_prohibit", "0", FCVAR_RELEASE | FCVAR_REPLICATED | FCVAR_NOTIFY, "Determines whether cl_teamid_overhead_always is prohibited." ); ConVar sv_show_team_equipment_prohibit( "sv_show_team_equipment_prohibit", "0", FCVAR_RELEASE | FCVAR_REPLICATED | FCVAR_NOTIFY, "Determines whether +cl_show_team_equipment is prohibited." ); #if defined( GAME_DLL ) ConVar sv_kick_players_with_cooldown( "sv_kick_players_with_cooldown", "1", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "(0: do not kick on insecure servers; 1: kick players with Untrusted status or convicted by Overwatch; 2: kick players with any cooldown)" ); ConVar sv_matchend_drops_enabled( "sv_matchend_drops_enabled", "1", FCVAR_RELEASE | FCVAR_GAMEDLL, "Rewards gameplay time is always accumulated for players, but drops at the end of the match can be prevented" ); ConVar sv_buy_status_override( "sv_buy_status_override", "-1", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "Override for buy status map info. 0 = everyone can buy, 1 = ct only, 2 = t only 3 = nobody" ); ConVar sv_ct_spawn_on_bombsite( "sv_ct_spawn_on_bombsite", "-1", FCVAR_RELEASE | FCVAR_GAMEDLL, "Force cts to spawn on a bombsite" ); ConVar sv_auto_adjust_bot_difficulty("sv_auto_adjust_bot_difficulty", "1", FCVAR_RELEASE | FCVAR_GAMEDLL, "Adjust the difficulty of bots each round based on contribution score." ); ConVar sv_bots_get_easier_each_win("sv_bots_get_easier_each_win", "0", FCVAR_RELEASE | FCVAR_GAMEDLL, "If > 0, some # of bots will lower thier difficulty each time they win. The argument defines how many will lower their difficulty each time." ); ConVar sv_bots_get_harder_after_each_wave( "sv_bots_get_harder_after_each_wave", "0", FCVAR_RELEASE | FCVAR_GAMEDLL, "If > 0, some # of bots will raise thier difficulty each time CTs beat a Guardian wave. The argument defines how many will raise their difficulty each time" ); ConVar sv_bots_force_rebuy_every_round( "sv_bots_force_rebuy_every_round", "0", FCVAR_RELEASE | FCVAR_GAMEDLL, "If set, this strips the bots of their weapons every round and forces them to rebuy." ); #endif ConVar mp_team_timeout_time( "mp_team_timeout_time", "60", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "Duration of each timeout." ); ConVar mp_team_timeout_max( "mp_team_timeout_max", "1", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_REPLICATED, "Number of timeouts each team gets per match." ); void MaxAllowedNetGraphCallback( IConVar *var, const char *pOldValue, float flOldValue ); ConVar sv_max_allowed_net_graph( "sv_max_allowed_net_graph", "1", FCVAR_NOTIFY | FCVAR_RELEASE | FCVAR_REPLICATED, "Determines max allowed net_graph value for clients.", MaxAllowedNetGraphCallback ); void MaxAllowedNetGraphCallback( IConVar *var, const char *pOldValue, float flOldValue ) { #ifdef CLIENT_DLL extern ConVar net_graph; if ( net_graph.GetInt() > sv_max_allowed_net_graph.GetInt() ) { net_graph.SetValue( sv_max_allowed_net_graph.GetInt() ); Msg( "Server does not allow net_graph values above %d\n", sv_max_allowed_net_graph.GetInt() ); } #endif } #define SV_QMM_MIN_PLAYERS_FOR_CANCEL_MATCH 0 extern ConVar mp_maxrounds; extern ConVar mp_match_restart_delay; ConVar sv_disable_show_team_select_menu( "sv_disable_show_team_select_menu", "0", FCVAR_REPLICATED|FCVAR_RELEASE, "Prevent the team select menu from showing." ); ConVar sv_disable_motd( "sv_disable_motd", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Prevent the motd from showing." ); #ifdef CLIENT_DLL CON_COMMAND_F( print_mapgroup, "Prints the current mapgroup and the contained maps", FCVAR_RELEASE ) #else CON_COMMAND_F( print_mapgroup_sv, "Prints the current mapgroup and the contained maps", FCVAR_RELEASE ) #endif { #if defined ( CLIENT_DLL ) const char* szMapGroup = engine->GetMapGroupName(); #else const char* szMapGroup = STRING( gpGlobals->mapGroupName ); #endif const CUtlStringList *pMaps = g_pGameTypes->GetMapGroupMapList( szMapGroup ); Msg( "Map group: %s\n", szMapGroup ); if ( pMaps ) { FOR_EACH_VEC( *pMaps, i ) { const char* szMap = (*pMaps)[i]; Msg( " %s\n", szMap ); } } else { Msg( "No maps in mapgroup map list!\n"); } } const float cInitialRestartRoundTime = 0.0f; /** * Player hull & eye position for standing, ducking, etc. This version has a taller * player height, but goldsrc-compatible collision bounds. */ static CViewVectors g_CSViewVectors( Vector( 0, 0, 64 ), // eye position Vector(-16, -16, 0 ), // hull min Vector( 16, 16, 72 ), // hull max Vector(-16, -16, 0 ), // duck hull min Vector( 16, 16, 54 ), // duck hull max Vector( 0, 0, 46 ), // duck view Vector(-10, -10, -10 ), // observer hull min Vector( 10, 10, 10 ), // observer hull max Vector( 0, 0, 14 ) // dead view height ); #ifndef CLIENT_DLL extern ConVar spec_replay_bot; BEGIN_DATADESC( SpawnPoint ) // Keyfields DEFINE_KEYFIELD( m_iPriority, FIELD_INTEGER, "priority" ), DEFINE_KEYFIELD( m_bEnabled, FIELD_BOOLEAN, "enabled" ), DEFINE_INPUTFUNC( FIELD_VOID, "SetEnabled", InputSetEnabled ), DEFINE_INPUTFUNC( FIELD_VOID, "SetDisabled", InputSetDisabled ), DEFINE_INPUTFUNC( FIELD_VOID, "ToggleEnabled", InputToggleEnabled ), END_DATADESC() LINK_ENTITY_TO_CLASS( info_player_terrorist, SpawnPoint ); LINK_ENTITY_TO_CLASS( info_player_counterterrorist, SpawnPoint ); LINK_ENTITY_TO_CLASS( info_player_logo, CPointEntity ); LINK_ENTITY_TO_CLASS( info_deathmatch_spawn, SpawnPoint ); LINK_ENTITY_TO_CLASS( info_armsrace_counterterrorist, SpawnPoint ); LINK_ENTITY_TO_CLASS( info_armsrace_terrorist, SpawnPoint ); template< typename TIssue > void NewTeamIssue() { new TIssue( g_voteControllerT ); new TIssue( g_voteControllerCT ); } template< typename TIssue > void NewGlobalIssue() { new TIssue( g_voteControllerGlobal ); } SpawnPoint::SpawnPoint() : m_bEnabled( true ), m_nType( 0 ) { } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void SpawnPoint::Spawn( void ) { BaseClass::Spawn(); if ( CSGameRules() ) CSGameRules()->AddSpawnPointToMasterList( this ); } void SpawnPoint::InputSetEnabled( inputdata_t &inputdata ) { SetSpawnEnabled( true ); } void SpawnPoint::InputSetDisabled( inputdata_t &inputdata ) { SetSpawnEnabled( false ); } void SpawnPoint::InputToggleEnabled( inputdata_t &inputdata ) { m_bEnabled = !m_bEnabled; if ( CSGameRules() && !CSGameRules()->IsPlayingCoopMission() ) { CSGameRules()->RefreshCurrentSpawnPointLists(); } } void SpawnPoint::SetSpawnEnabled( bool bEnabled ) { bool bChanged = (m_bEnabled != bEnabled); m_bEnabled = bEnabled; if ( CSGameRules() && bChanged && !CSGameRules()->IsPlayingCoopMission() ) { CSGameRules()->RefreshCurrentSpawnPointLists(); } } LINK_ENTITY_TO_CLASS( info_enemy_terrorist_spawn, SpawnPointCoopEnemy ); BEGIN_DATADESC( SpawnPointCoopEnemy ) DEFINE_KEYFIELD( m_szWeaponsToGive, FIELD_STRING, "weapons_to_give" ), DEFINE_KEYFIELD( m_szPlayerModelToUse, FIELD_STRING, "model_to_use" ), DEFINE_KEYFIELD( m_nArmorToSpawnWith, FIELD_INTEGER, "armor_to_give" ), DEFINE_KEYFIELD( m_nDefaultBehavior, FIELD_INTEGER, "default_behavior" ), DEFINE_KEYFIELD( m_nBotDifficulty, FIELD_INTEGER, "bot_difficulty" ), DEFINE_KEYFIELD( m_bIsAgressive, FIELD_BOOLEAN, "is_agressive" ), DEFINE_KEYFIELD( m_bStartAsleep, FIELD_BOOLEAN, "start_asleep" ), DEFINE_KEYFIELD( m_flHideRadius, FIELD_FLOAT, "hide_radius" ), END_DATADESC() SpawnPointCoopEnemy::SpawnPointCoopEnemy( void ) { //m_szWeaponsToGive = NULL_STRING; Assert( CSGameRules()->IsPlayingCoopMission() ); m_pMyArea = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void SpawnPointCoopEnemy::Spawn( void ) { BaseClass::Spawn(); Precache(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void SpawnPointCoopEnemy::Precache( void ) { if ( V_stricmp( GetPlayerModelToUse(), "" ) != 0 ) { PrecacheModel( GetPlayerModelToUse() ); } BaseClass::Precache(); } CNavArea * SpawnPointCoopEnemy::FindNearestArea( void ) { // These don't move so only do the expensive find the first time if ( !m_pMyArea ) m_pMyArea = TheNavMesh->GetNearestNavArea( this ); return m_pMyArea; } class CPointGiveAmmo : public CPointEntity { DECLARE_CLASS( CPointGiveAmmo, CPointEntity ); public: void Spawn( void ); void Precache( void ); // Input handlers void InputGiveAmmo( inputdata_t &inputdata ); DECLARE_DATADESC(); // int m_nDamage; // int m_bitsDamageType; // float m_flRadius; // float m_flDelay; // string_t m_strTarget; EHANDLE m_pActivator; }; BEGIN_DATADESC( CPointGiveAmmo ) // DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "DamageRadius" ), // DEFINE_KEYFIELD( m_nDamage, FIELD_INTEGER, "Damage" ), // DEFINE_KEYFIELD( m_flDelay, FIELD_FLOAT, "DamageDelay" ), // DEFINE_KEYFIELD( m_bitsDamageType, FIELD_INTEGER, "DamageType" ), // DEFINE_KEYFIELD( m_strTarget, FIELD_STRING, "DamageTarget" ), // Function Pointers // Inputs DEFINE_INPUTFUNC( FIELD_VOID, "GiveAmmo", InputGiveAmmo ), DEFINE_FIELD( m_pActivator, FIELD_EHANDLE ), END_DATADESC() LINK_ENTITY_TO_CLASS( point_give_ammo, CPointGiveAmmo ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointGiveAmmo::Spawn( void ) { SetThink( NULL ); SetUse( NULL ); m_pActivator = NULL; Precache(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPointGiveAmmo::Precache( void ) { BaseClass::Precache(); } //----------------------------------------------------------------------------- // Purpose: Input handler for instantaneously hurting whatever is near us. //----------------------------------------------------------------------------- void CPointGiveAmmo::InputGiveAmmo( inputdata_t &data ) { m_pActivator = data.pActivator; CBasePlayer *pPlayer = dynamic_cast< CBasePlayer* >( m_pActivator.Get() ); if ( pPlayer ) { for ( int i = 0; i < 2; ++i ) { CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pPlayer->Weapon_GetSlot( WEAPON_SLOT_RIFLE ) ); if ( i == 1 ) pWeapon = dynamic_cast< CWeaponCSBase* >( pPlayer->Weapon_GetSlot( WEAPON_SLOT_PISTOL ) ); if ( pWeapon ) { if ( pWeapon->UsesPrimaryAmmo() ) { int ammoIndex = pWeapon->GetPrimaryAmmoType(); if ( ammoIndex != -1 ) { int giveAmount; giveAmount = GetAmmoDef()->MaxCarry( ammoIndex, pPlayer ); pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex( ammoIndex )->pName ); } } if ( pWeapon->UsesSecondaryAmmo() && pWeapon->HasSecondaryAmmo() ) { // Give secondary ammo out, as long as the player already has some // from a presumeably natural source. This prevents players on XBox // having Combine Balls and so forth in areas of the game that // were not tested with these items. int ammoIndex = pWeapon->GetSecondaryAmmoType(); if ( ammoIndex != -1 ) { int giveAmount; giveAmount = GetAmmoDef()->MaxCarry( ammoIndex, pPlayer ); pPlayer->GiveAmmo( giveAmount, GetAmmoDef()->GetAmmoOfIndex( ammoIndex )->pName ); } } } } } } //----------------------------------------------------------------------------- class CCoopBonusCoin : public CDynamicProp { public: DECLARE_CLASS( CCoopBonusCoin, CDynamicProp ); DECLARE_DATADESC(); void CoinTouch( CBaseEntity *pOther ); void CoinFadeOut( void ); //void CoinThink( void ); void Precache( void ); void Spawn( void ) OVERRIDE; void StartFadeOut( float delay ); }; LINK_ENTITY_TO_CLASS( item_coop_coin, CCoopBonusCoin ); PRECACHE_REGISTER( item_coop_coin ); BEGIN_DATADESC( CCoopBonusCoin ) // Function Pointers DEFINE_ENTITYFUNC( CoinTouch ), DEFINE_THINKFUNC( CoinFadeOut ), END_DATADESC() void CCoopBonusCoin::CoinTouch( CBaseEntity *pOther ) { CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( pOther ); if ( !pPlayer || !CSGameRules() ) return; CSGameRules()->CoopCollectBonusCoin(); EmitSound( "CollectableCoin.Collect" ); ResetSequence( LookupSequence( "challenge_coin_collect" ) ); SetTouch( NULL ); StartFadeOut( 0.125 ); } // void CCoopBonusCoin::CoinThink( void ) // { // StudioFrameAdvance(); // } void CCoopBonusCoin::Precache( void ) { PrecacheModel( "models/coop/challenge_coin.mdl" ); PrecacheScriptSound( "CollectableCoin.Collect" ); } void CCoopBonusCoin::Spawn( void ) { Precache(); SetModel( "models/coop/challenge_coin.mdl" ); SetModelName( MAKE_STRING( "models/coop/challenge_coin.mdl" ) ); BaseClass::Spawn(); //m_bShouldGlow = true; SetMoveType( MOVETYPE_NONE ); SetSolid( SOLID_BBOX ); SetBlocksLOS( false ); AddSolidFlags( FSOLID_TRIGGER ); AddEFlags( EFL_NO_ROTORWASH_PUSH ); SetCollisionGroup( COLLISION_GROUP_WEAPON ); //if ( HasBloatedCollision() ) //{ //CollisionProp()->UseTriggerBounds( true, ITEM_PICKUP_BOX_BLOAT ); //} SetTouch( &CCoopBonusCoin::CoinTouch ); ResetSequence( LookupSequence( "idle" ) ); //SetThink( &CCoopBonusCoin::CoinThink ); } void CCoopBonusCoin::StartFadeOut( float delay ) { SetThink( &CCoopBonusCoin::CoinFadeOut ); SetNextThink( gpGlobals->curtime + delay ); SetRenderAlpha( 255 ); m_nRenderMode = kRenderNormal; } //----------------------------------------------------------------------------- // Purpose: Fade out slowly //----------------------------------------------------------------------------- void CCoopBonusCoin::CoinFadeOut( void ) { float dt = gpGlobals->frametime; if ( dt > 0.1f ) dt = 0.1f; m_nRenderMode = kRenderTransTexture; int speed = MAX( 0.35, 256 * dt ); // fade out SetRenderAlpha( UTIL_Approach( 0, m_clrRender->a, speed ) ); if ( m_clrRender->a == 0 ) { UTIL_Remove( this ); } else { SetNextThink( gpGlobals->curtime ); } } #endif #if defined ( CLIENT_DLL ) bool __MsgFunc_SendPlayerItemDrops( const CCSUsrMsg_SendPlayerItemDrops &msg ); bool __MsgFunc_SendPlayerItemFound( const CCSUsrMsg_SendPlayerItemFound &msg ); #endif REGISTER_GAMERULES_CLASS( CCSGameRules ); BEGIN_NETWORK_TABLE_NOBASE( CCSGameRules, DT_CSGameRules ) #ifdef CLIENT_DLL RecvPropBool( RECVINFO( m_bFreezePeriod ) ), RecvPropBool( RECVINFO( m_bMatchWaitingForResume ) ), RecvPropBool( RECVINFO( m_bWarmupPeriod ) ), RecvPropFloat( RECVINFO( m_fWarmupPeriodEnd ) ), // DUMMY VAR FOR DEMOS RecvPropFloat( RECVINFO( m_fWarmupPeriodStart ) ), RecvPropBool( RECVINFO( m_bTerroristTimeOutActive ) ), RecvPropBool( RECVINFO( m_bCTTimeOutActive ) ), RecvPropFloat( RECVINFO( m_flTerroristTimeOutRemaining ) ), RecvPropFloat( RECVINFO( m_flCTTimeOutRemaining ) ), RecvPropInt( RECVINFO( m_nTerroristTimeOuts ) ), RecvPropInt( RECVINFO( m_nCTTimeOuts ) ), RecvPropInt( RECVINFO( m_iRoundTime ) ), RecvPropInt( RECVINFO( m_gamePhase ) ), RecvPropInt( RECVINFO( m_totalRoundsPlayed ) ), RecvPropInt( RECVINFO( m_nOvertimePlaying ) ), RecvPropFloat( RECVINFO( m_timeUntilNextPhaseStarts ) ), RecvPropFloat( RECVINFO( m_flCMMItemDropRevealStartTime ) ), RecvPropFloat( RECVINFO( m_flCMMItemDropRevealEndTime ) ), RecvPropFloat( RECVINFO( m_fRoundStartTime ) ), RecvPropBool( RECVINFO( m_bGameRestart ) ), RecvPropFloat( RECVINFO( m_flRestartRoundTime ) ), RecvPropFloat( RECVINFO( m_flGameStartTime ) ), RecvPropInt( RECVINFO( m_iHostagesRemaining ) ), RecvPropBool( RECVINFO( m_bAnyHostageReached ) ), RecvPropBool( RECVINFO( m_bMapHasBombTarget ) ), RecvPropBool( RECVINFO( m_bMapHasRescueZone ) ), RecvPropBool( RECVINFO( m_bMapHasBuyZone ) ), RecvPropBool( RECVINFO( m_bIsQueuedMatchmaking ) ), RecvPropBool( RECVINFO( m_bIsValveDS ) ), RecvPropBool( RECVINFO( m_bIsQuestEligible ) ), RecvPropBool( RECVINFO( m_bLogoMap ) ), RecvPropInt( RECVINFO( m_iNumGunGameProgressiveWeaponsCT ) ), RecvPropInt( RECVINFO( m_iNumGunGameProgressiveWeaponsT ) ), RecvPropInt( RECVINFO( m_iSpectatorSlotCount ) ), RecvPropBool( RECVINFO( m_bBombDropped ) ), RecvPropBool( RECVINFO( m_bBombPlanted ) ), RecvPropInt( RECVINFO( m_iRoundWinStatus ) ), RecvPropInt( RECVINFO( m_eRoundWinReason ) ), RecvPropFloat( RECVINFO( m_flDMBonusStartTime ) ), RecvPropFloat( RECVINFO( m_flDMBonusTimeLength ) ), RecvPropInt( RECVINFO( m_unDMBonusWeaponLoadoutSlot ) ), RecvPropBool( RECVINFO( m_bDMBonusActive ) ), RecvPropBool( RECVINFO( m_bTCantBuy ) ), RecvPropBool( RECVINFO( m_bCTCantBuy ) ), RecvPropFloat( RECVINFO( m_flGuardianBuyUntilTime ) ), RecvPropArray3( RECVINFO_ARRAY( m_iMatchStats_RoundResults ), RecvPropInt( RECVINFO( m_iMatchStats_RoundResults[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_iMatchStats_PlayersAlive_T ), RecvPropInt( RECVINFO( m_iMatchStats_PlayersAlive_T[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_iMatchStats_PlayersAlive_CT ), RecvPropInt( RECVINFO( m_iMatchStats_PlayersAlive_CT[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponOrderCT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponOrderCT[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponOrderT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponOrderT[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderCT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponKillUpgradeOrderCT[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderT ), RecvPropInt( RECVINFO( m_GGProgressiveWeaponKillUpgradeOrderT[0] ) ) ), RecvPropInt( RECVINFO( m_MatchDevice ) ), RecvPropBool( RECVINFO( m_bHasMatchStarted ) ), RecvPropArray3( RECVINFO_ARRAY(m_TeamRespawnWaveTimes), RecvPropFloat( RECVINFO(m_TeamRespawnWaveTimes[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_flNextRespawnWave), RecvPropTime( RECVINFO(m_flNextRespawnWave[0]) ) ), RecvPropInt( RECVINFO( m_nNextMapInMapgroup ) ), RecvPropArray3( RECVINFO_ARRAY( m_nEndMatchMapGroupVoteOptions ), RecvPropInt( RECVINFO( m_nEndMatchMapGroupVoteOptions[0] ) ) ), RecvPropBool( RECVINFO( m_bIsDroppingItems ) ), RecvPropInt( RECVINFO( m_iActiveAssassinationTargetMissionID ) ), RecvPropFloat( RECVINFO( m_fMatchStartTime ) ), RecvPropString( RECVINFO( m_szTournamentEventName ) ), RecvPropString( RECVINFO( m_szTournamentEventStage ) ), RecvPropString( RECVINFO( m_szTournamentPredictionsTxt ) ), RecvPropInt( RECVINFO( m_nTournamentPredictionsPct ) ), RecvPropString( RECVINFO( m_szMatchStatTxt ) ), // guardian mode RecvPropInt( RECVINFO( m_nGuardianModeWaveNumber ) ), RecvPropInt( RECVINFO( m_nGuardianModeSpecialKillsRemaining ) ), RecvPropInt( RECVINFO( m_nGuardianModeSpecialWeaponNeeded ) ), // halloween RecvPropInt( RECVINFO( m_nHalloweenMaskListSeed ) ), // Gifts global info RecvPropInt( RECVINFO( m_numGlobalGiftsGiven ) ), RecvPropInt( RECVINFO( m_numGlobalGifters ) ), RecvPropInt( RECVINFO( m_numGlobalGiftsPeriodSeconds ) ), RecvPropArray3( RECVINFO_ARRAY( m_arrFeaturedGiftersAccounts ), RecvPropInt (RECVINFO( m_arrFeaturedGiftersAccounts[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_arrFeaturedGiftersGifts ), RecvPropInt (RECVINFO( m_arrFeaturedGiftersGifts[0] ) ) ), RecvPropArray3( RECVINFO_ARRAY( m_arrProhibitedItemIndices ), RecvPropInt( RECVINFO( m_arrProhibitedItemIndices[ 0 ] ) ) ), // Tournament Casters RecvPropInt( RECVINFO( m_numBestOfMaps ) ), RecvPropArray3( RECVINFO_ARRAY( m_arrTournamentActiveCasterAccounts ), RecvPropInt ( RECVINFO( m_arrTournamentActiveCasterAccounts[0] ), 0, CCSGameRules::RecvProxy_TournamentActiveCasterAccounts ) ) #else SendPropBool( SENDINFO( m_bFreezePeriod ) ), SendPropBool( SENDINFO( m_bMatchWaitingForResume ) ), SendPropBool( SENDINFO( m_bWarmupPeriod ) ), SendPropFloat( SENDINFO( m_fWarmupPeriodEnd ) ), // DUMMY VAR FOR DEMOS SendPropFloat( SENDINFO( m_fWarmupPeriodStart ) ), SendPropBool( SENDINFO( m_bTerroristTimeOutActive ) ), SendPropBool( SENDINFO( m_bCTTimeOutActive ) ), SendPropFloat( SENDINFO( m_flTerroristTimeOutRemaining ) ), SendPropFloat( SENDINFO( m_flCTTimeOutRemaining ) ), SendPropInt( SENDINFO( m_nTerroristTimeOuts ) ), SendPropInt( SENDINFO( m_nCTTimeOuts ) ), SendPropInt( SENDINFO( m_iRoundTime ), 16 ), SendPropInt( SENDINFO( m_gamePhase ), 4, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_totalRoundsPlayed ), 16 ), SendPropInt( SENDINFO( m_nOvertimePlaying ), 16 ), SendPropFloat( SENDINFO( m_timeUntilNextPhaseStarts ), 32, SPROP_NOSCALE ), SendPropFloat( SENDINFO( m_flCMMItemDropRevealStartTime ), 32, SPROP_NOSCALE ), SendPropFloat( SENDINFO( m_flCMMItemDropRevealEndTime ), 32, SPROP_NOSCALE ), SendPropFloat( SENDINFO( m_fRoundStartTime ), 32, SPROP_NOSCALE ), SendPropFloat( SENDINFO( m_flRestartRoundTime ) ), SendPropBool( SENDINFO( m_bGameRestart ) ), SendPropFloat( SENDINFO( m_flGameStartTime ), 32, SPROP_NOSCALE ), SendPropInt( SENDINFO( m_iHostagesRemaining ), 4 ), SendPropBool( SENDINFO( m_bAnyHostageReached ) ), SendPropBool( SENDINFO( m_bMapHasBombTarget ) ), SendPropBool( SENDINFO( m_bMapHasRescueZone ) ), SendPropBool( SENDINFO( m_bMapHasBuyZone ) ), SendPropBool( SENDINFO( m_bIsQueuedMatchmaking ) ), SendPropBool( SENDINFO( m_bIsValveDS ) ), SendPropBool( SENDINFO( m_bIsQuestEligible ) ), SendPropBool( SENDINFO( m_bLogoMap ) ), SendPropInt( SENDINFO( m_iNumGunGameProgressiveWeaponsCT ) ), SendPropInt( SENDINFO( m_iNumGunGameProgressiveWeaponsT ) ), SendPropInt( SENDINFO( m_iSpectatorSlotCount ) ), SendPropBool( SENDINFO( m_bBombDropped ) ), SendPropBool( SENDINFO( m_bBombPlanted ) ), SendPropInt( SENDINFO( m_iRoundWinStatus ) ), SendPropInt( SENDINFO( m_eRoundWinReason ) ), SendPropFloat( SENDINFO( m_flDMBonusStartTime ) ), SendPropFloat( SENDINFO( m_flDMBonusTimeLength ) ), SendPropInt( SENDINFO( m_unDMBonusWeaponLoadoutSlot ) ), SendPropBool( SENDINFO( m_bDMBonusActive ) ), SendPropBool( SENDINFO( m_bTCantBuy ) ), SendPropBool( SENDINFO( m_bCTCantBuy ) ), SendPropFloat( SENDINFO( m_flGuardianBuyUntilTime ) ), SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_RoundResults ), SendPropInt (SENDINFO_ARRAY( m_iMatchStats_RoundResults ), 8, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_PlayersAlive_T ), SendPropInt (SENDINFO_ARRAY( m_iMatchStats_PlayersAlive_T ), 6, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_iMatchStats_PlayersAlive_CT ), SendPropInt (SENDINFO_ARRAY( m_iMatchStats_PlayersAlive_CT ), 6, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponOrderCT ), SendPropInt (SENDINFO_ARRAY( m_GGProgressiveWeaponOrderCT ), 0, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponOrderT ), SendPropInt( SENDINFO_ARRAY( m_GGProgressiveWeaponOrderT ), 0, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponKillUpgradeOrderCT ), SendPropInt( SENDINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderCT ), 0, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_GGProgressiveWeaponKillUpgradeOrderT ), SendPropInt( SENDINFO_ARRAY( m_GGProgressiveWeaponKillUpgradeOrderT ), 0, SPROP_UNSIGNED ) ), SendPropInt( SENDINFO( m_MatchDevice ) ), SendPropBool( SENDINFO( m_bHasMatchStarted ) ), SendPropArray3( SENDINFO_ARRAY3(m_TeamRespawnWaveTimes), SendPropFloat( SENDINFO_ARRAY(m_TeamRespawnWaveTimes) ) ), SendPropArray3( SENDINFO_ARRAY3(m_flNextRespawnWave), SendPropTime( SENDINFO_ARRAY(m_flNextRespawnWave) ) ), SendPropInt( SENDINFO( m_nNextMapInMapgroup ) ), SendPropArray3( SENDINFO_ARRAY3( m_nEndMatchMapGroupVoteOptions ), SendPropInt (SENDINFO_ARRAY( m_nEndMatchMapGroupVoteOptions ) ) ), SendPropBool( SENDINFO( m_bIsDroppingItems ) ), SendPropInt( SENDINFO( m_iActiveAssassinationTargetMissionID ) ), SendPropFloat( SENDINFO( m_fMatchStartTime ), 32, SPROP_NOSCALE ), SendPropString( SENDINFO( m_szTournamentEventName ) ), SendPropString( SENDINFO( m_szTournamentEventStage ) ), SendPropString( SENDINFO( m_szTournamentPredictionsTxt ) ), SendPropInt( SENDINFO( m_nTournamentPredictionsPct ) ), SendPropString( SENDINFO( m_szMatchStatTxt ) ), // guardian mode SendPropInt( SENDINFO( m_nGuardianModeWaveNumber ) ), SendPropInt( SENDINFO( m_nGuardianModeSpecialKillsRemaining ) ), SendPropInt( SENDINFO( m_nGuardianModeSpecialWeaponNeeded ) ), // Halloween SendPropInt( SENDINFO( m_nHalloweenMaskListSeed ) ), // Gifts global info SendPropInt( SENDINFO( m_numGlobalGiftsGiven ), 0, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_numGlobalGifters ), 0, SPROP_UNSIGNED ), SendPropInt( SENDINFO( m_numGlobalGiftsPeriodSeconds ), 0, SPROP_UNSIGNED ), SendPropArray3( SENDINFO_ARRAY3( m_arrFeaturedGiftersAccounts ), SendPropInt (SENDINFO_ARRAY( m_arrFeaturedGiftersAccounts ), 0, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_arrFeaturedGiftersGifts ), SendPropInt (SENDINFO_ARRAY( m_arrFeaturedGiftersGifts ), 0, SPROP_UNSIGNED ) ), SendPropArray3( SENDINFO_ARRAY3( m_arrProhibitedItemIndices ), SendPropInt( SENDINFO_ARRAY( m_arrProhibitedItemIndices ), 0, SPROP_UNSIGNED ) ), // Tournament Casters SendPropInt( SENDINFO( m_numBestOfMaps ), 4, SPROP_UNSIGNED ), // supporting no more than best-of-7 (1+2+4) SendPropArray3( SENDINFO_ARRAY3( m_arrTournamentActiveCasterAccounts ), SendPropInt (SENDINFO_ARRAY( m_arrTournamentActiveCasterAccounts ), 0, SPROP_UNSIGNED ) ) #endif END_NETWORK_TABLE() IMPLEMENT_NETWORKCLASS_ALIASED( CSGameRulesProxy, DT_CSGameRulesProxy ) LINK_ENTITY_TO_CLASS_ALIASED( cs_gamerules, CSGameRulesProxy ); #ifdef GAME_DLL ConVar mp_teamname_1( "mp_teamname_1", "", FCVAR_RELEASE, "A non-empty string overrides the first team's name." ); ConVar mp_teamname_2( "mp_teamname_2", "", FCVAR_RELEASE, "A non-empty string overrides the second team's name." ); ConVar mp_teamflag_1( "mp_teamflag_1", "", FCVAR_RELEASE, "Enter a country's alpha 2 code to show that flag next to team 1's name in the spectator scoreboard." ); ConVar mp_teamflag_2( "mp_teamflag_2", "", FCVAR_RELEASE, "Enter a country's alpha 2 code to show that flag next to team 2's name in the spectator scoreboard." ); ConVar mp_teamlogo_1( "mp_teamlogo_1", "", FCVAR_RELEASE, "Enter a team's shorthand image name to display their logo. Images can be found here: 'resource/flash/econ/tournaments/teams'" ); ConVar mp_teamlogo_2( "mp_teamlogo_2", "", FCVAR_RELEASE, "Enter a team's shorthand image name to display their logo. Images can be found here: 'resource/flash/econ/tournaments/teams'" ); ConVar mp_teamprediction_txt( "mp_teamprediction_txt", "#SFUIHUD_Spectate_Predictions", FCVAR_RELEASE, "A value between 1 and 99 will set predictions in favor of first team." ); ConVar mp_teamprediction_pct( "mp_teamprediction_pct", "0", FCVAR_RELEASE, "A value between 1 and 99 will show predictions in favor of CT team." ); ConVar mp_teammatchstat_txt( "mp_teammatchstat_txt", "", FCVAR_RELEASE, "A non-empty string sets the match stat description, e.g. 'Match 2 of 3'." ); ConVar mp_teammatchstat_1( "mp_teammatchstat_1", "", FCVAR_RELEASE, "A non-empty string sets first team's match stat." ); ConVar mp_teammatchstat_2( "mp_teammatchstat_2", "", FCVAR_RELEASE, "A non-empty string sets second team's match stat." ); ConVar mp_teamscore_1( "mp_teamscore_1", "", FCVAR_RELEASE, "A non-empty string for best-of-N maps won by the first team." ); ConVar mp_teamscore_2( "mp_teamscore_2", "", FCVAR_RELEASE, "A non-empty string for best-of-N maps won by the second team." ); void FnChangeCallback_mp_teamscore_max( IConVar *var, const char *pOldValue, float flOldValue ) { ConVarRef ref( var ); int nVal = ref.GetInt(); if ( CCSGameRules *pCSR = CSGameRules() ) { if ( nVal != pCSR->m_numBestOfMaps ) pCSR->m_numBestOfMaps = nVal; } } ConVar mp_teamscore_max( "mp_teamscore_max", "0", FCVAR_RELEASE, "How many maps to win the series (bo3 max=2; bo5 max=3; bo7 max=4)", true, 0, true, 7, FnChangeCallback_mp_teamscore_max ); ConVar mp_teammatchstat_holdtime( "mp_teammatchstat_holdtime", "5", FCVAR_RELEASE, "Decide on a match stat and hold it additionally for at least so many seconds" ); ConVar mp_teammatchstat_cycletime( "mp_teammatchstat_cycletime", "45", FCVAR_RELEASE, "Cycle match stats after so many seconds" ); #endif // COOP #define COOPMISSION_SCORE_MULTIPLIER_TIMELEFT 40 #define COOPMISSION_SCORE_MULTIPLIER_DAMTAKEN -10 #define COOPMISSION_SCORE_MULTIPLIER_ROUNDSFAILED -1000 static uint32 Helper_ScoreLeaderboardData_FindEntryValue( uint32 nTag, const ::google::protobuf::RepeatedPtrField< ::ScoreLeaderboardData_Entry >&arr ) { for ( int i = 0; i < arr.size(); ++ i ) { if ( arr.Get( i ).tag() == nTag ) return arr.Get( i ).val(); } return 0; } static uint32 Helper_ScoreLeaderboardData_FindEntryValueSum( uint32 nTag, const ::google::protobuf::RepeatedPtrField< ::ScoreLeaderboardData_AccountEntries >&arr ) { uint32 val = 0; for ( int i = 0; i < arr.size(); ++i ) { val += Helper_ScoreLeaderboardData_FindEntryValue( nTag, arr.Get( i ).entries() ); } return val; } int32 CoopScoreGetRatingEntryFromLeaderboardData( ScoreLeaderboardData &sld, bool bBonus, int nIndex, bool bAsScore ) { // Note: this function should not be referencing gamerules because we can be looking at scores from Steam friends leaderboards bool bGuardian = false; bool bCoopMission = true; if ( sld.quest_id() ) { const CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( sld.quest_id() ); if ( !pQuest ) return 0.0f; if ( !V_stricmp( pQuest->GetGameMode(), "cooperative" ) ) { bGuardian = true; bCoopMission = false; } // otherwise assume coopmission } else if ( CSGameRules() ) { // keeping this around for local compatibility testing with listenservers bGuardian = CSGameRules()->IsPlayingCoopGuardian(); bCoopMission = CSGameRules()->IsPlayingCoopMission(); // otherwise assume coopmission } else return 0.0f; int32 nResult = 0; // // Base scorechart // if ( !bBonus ) { if ( bGuardian ) { if ( nIndex == 0 ) { // Damage to enemies ratio uint32 numDmgInflicted = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_HpDmgInflicted, sld.accountentries() ); uint32 numDmgSuffered = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_HpDmgSuffered, sld.accountentries() ); if ( !numDmgInflicted ) nResult = 0; else if ( numDmgSuffered ) { nResult = int( 10000.0f * float( numDmgInflicted ) / float( numDmgInflicted + numDmgSuffered ) ); if ( numDmgInflicted && ( nResult <= 0 ) ) nResult = 1; } else nResult = 10000; //if ( bAsScore ) //{ // nResult = nResult; //} } else if ( nIndex == 3 ) { // Rounds failed penalty nResult = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_RoundsPlayed, sld.matchentries() ); nResult = ( nResult > 1 ) ? ( nResult - 1 ) : 0; if ( bAsScore ) { nResult = COOPMISSION_SCORE_MULTIPLIER_ROUNDSFAILED*nResult; } } } else { if ( nIndex == 0 ) { // Time Remaining on the clock nResult = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_TimeRemaining, sld.matchentries() ); if ( bAsScore ) { nResult = COOPMISSION_SCORE_MULTIPLIER_TIMELEFT*nResult; } } else if ( nIndex == 3 ) { // Total damage taken nResult = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_HpDmgSuffered, sld.accountentries() ); if ( bAsScore ) { nResult = COOPMISSION_SCORE_MULTIPLIER_DAMTAKEN*nResult; } } } if ( bGuardian || bCoopMission ) { // Shared categories for Guardian and Coop Mission if ( nIndex == 1 ) { // Bullets Accuracy uint32 numBulletsFired = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_ShotsFired, sld.accountentries() ); uint32 numBulletsOnTarget = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_ShotsOnTarget, sld.accountentries() ); if ( !numBulletsFired ) nResult = 0; else if ( numBulletsFired > 0 && numBulletsOnTarget < numBulletsFired ) { nResult = int( 10000.0f * float( numBulletsOnTarget ) / float( numBulletsFired ) ); if ( numBulletsOnTarget && ( nResult <= 0 ) ) nResult = 1; } else nResult = 10000; //if ( bAsScore ) //{ // nResult = nResult; //} } else if ( nIndex == 2 ) { // Headshots kill percentage uint32 numHeadshots = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_Headshots, sld.accountentries() ); uint32 numKills = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_Kills, sld.accountentries() ); if ( !numKills ) nResult = 0; else if ( numKills > 0 && numHeadshots < numKills ) { nResult = int( 10000.0f * float( numHeadshots ) / float( numKills ) ); if ( numHeadshots && ( nResult <= 0 ) ) nResult = 1; } else nResult = 10000; //if ( bAsScore ) //{ // nResult = nResult; //} } } } // // Scorechart for bonuses! // else { if ( bGuardian ) { if ( nIndex == 0 ) { // Under 3 rounds? uint32 numRoundsPlayed = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_RoundsPlayed, sld.matchentries() ); nResult = numRoundsPlayed; if ( bAsScore ) { nResult = ( numRoundsPlayed <= 3 ) ? 5000 : 0; } } } if ( bCoopMission ) { if ( nIndex == 0 ) { // No Deaths? uint32 numDeaths = Helper_ScoreLeaderboardData_FindEntryValueSum( k_EScoreLeaderboardDataEntryTag_Deaths, sld.accountentries() ); nResult = numDeaths; if ( bAsScore ) { nResult = ( numDeaths == 0 ) ? 5000 : 0; } } else if ( nIndex == 1 ) { // All Challenge Coins? uint32 numChallenge = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_BonusChallenge, sld.matchentries() ); nResult = numChallenge; if ( bAsScore ) { nResult = ( numChallenge ) ? 5000 : 0; } } else if ( nIndex == 2 ) { // Pistols Only? uint32 numPistolsOnly = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_BonusPistolOnly, sld.matchentries() ); nResult = numPistolsOnly; if ( bAsScore ) { nResult = ( numPistolsOnly ) ? 10000 : 0; } } else if ( nIndex == 3 ) { // Hard Mode? uint32 numHardMode = Helper_ScoreLeaderboardData_FindEntryValue( k_EScoreLeaderboardDataEntryTag_BonusHardMode, sld.matchentries() ); nResult = numHardMode; if ( bAsScore ) { nResult = ( numHardMode ) ? 25000 : 0; } } } } return nResult; } #ifndef CLIENT_DLL ConVar mp_backup_round_auto( "mp_backup_round_auto", "1", FCVAR_RELEASE, "If enabled will keep in-memory backups to handle reconnecting players even if the backup files aren't written to disk" ); ConVar mp_backup_round_file( "mp_backup_round_file", "backup", FCVAR_RELEASE, "If set then server will save all played rounds information to files filename_date_time_team1_team2_mapname_roundnum_score1_score2.txt" ); ConVar mp_backup_round_file_pattern( "mp_backup_round_file_pattern", "%prefix%_round%round%.txt", FCVAR_RELEASE, "If set then server will save all played rounds information to files named by this pattern, e.g.'%prefix%_%date%_%time%_%team1%_%team2%_%map%_round%round%_score_%score1%_%score2%.txt'" ); ConVar mp_backup_round_file_last( "mp_backup_round_file_last", "", FCVAR_RELEASE, "Every time a backup file is written the value of this convar gets updated to hold the name of the backup file." ); static bool Helper_mp_backup_round_IsEnabled() { if ( g_pGameTypes->GetCurrentGameType() == CS_GameType_GunGame ) { switch ( g_pGameTypes->GetCurrentGameMode() ) { case CS_GameMode::GunGame_Progressive: case CS_GameMode::GunGame_Bomb: return !!*mp_backup_round_file.GetString(); // round backups aren't supported in 'auto' mode in ArmsRace/Demolition, but we'll honor server config to write backup file } } return mp_backup_round_auto.GetBool() || *mp_backup_round_file.GetString(); } static int Helper_mp_backup_restore_from_file_sorter( char const * const *a, char const * const *b ) { return -Q_stricmp( *a, *b ); // sort more recent files earlier } static void Helper_FilenameTokenReplace( char* pchBufFilename, int nSize, const char *aReplacePattern, const char *theActualValue ) { char const *pchReplacePattern, *pchActualValue; char const *pchEndFilename = ( char * ) pchBufFilename + nSize; pchReplacePattern = aReplacePattern; pchActualValue = theActualValue; while ( char *pchReplace = Q_strstr( pchBufFilename, pchReplacePattern ) ) { int lenReplace = Q_strlen( pchReplacePattern ), lenValue = Q_strlen( pchActualValue ); if ( pchReplace + lenValue >= pchEndFilename ) { break; } Q_memmove( pchReplace + lenValue, pchReplace + lenReplace, pchEndFilename - pchReplace - MAX( lenReplace, lenValue ) ); Q_memmove( pchReplace, pchActualValue, lenValue ); } } static bool Helper_ShouldBroadcastCoopScoreLeaderboardData() { if ( !CSGameRules() ) return false; if ( CSGameRules()->IsPlayingCoopGuardian() || CSGameRules()->IsPlayingCoopMission() ) { // Check if the humans won the match int numHumanRoundsWon = CSGameRules()->IsPlayingCoopMission() ? CSGameRules()->m_match.GetCTScore() : ( CSGameRules()->IsHostageRescueMap() ? CSGameRules()->m_match.GetTerroristScore() : CSGameRules()->m_match.GetCTScore() ); if ( numHumanRoundsWon > 0 ) return true; } return false; } static void Helper_FillScoreLeaderboardData( ScoreLeaderboardData &sld ) { // // This function is used in both official and community server build // In official build it will deliver the leaderboard data to GC // in both official and community build this data is also replicated to clients for the end of match scoreboard // if ( !Helper_ShouldBroadcastCoopScoreLeaderboardData() ) return; // // Per player stats // FOR_EACH_MAP( CSGameRules()->m_mapQueuedMatchmakingPlayersData, i ) { CCSGameRules::CQMMPlayerData_t const &qmm = *CSGameRules()->m_mapQueuedMatchmakingPlayersData.Element( i ); ScoreLeaderboardData_AccountEntries *pAcc = sld.add_accountentries(); pAcc->set_accountid( qmm.m_uiPlayerAccountId ); if ( int n = qmm.m_numEnemyKills ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_Kills ); pEnt->set_val( n ); } if ( int n = qmm.m_numEnemyKillHeadshots ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_Headshots ); pEnt->set_val( n ); } if ( int n = qmm.m_numDeaths ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_Deaths ); pEnt->set_val( n ); } if ( int n = qmm.m_numHealthPointsRemovedTotal ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_HpDmgSuffered ); pEnt->set_val( n ); } if ( int n = qmm.m_numHealthPointsDealtTotal ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_HpDmgInflicted ); pEnt->set_val( n ); } if ( int n = qmm.m_numShotsFiredTotal ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_ShotsFired ); pEnt->set_val( n ); } if ( int n = qmm.m_numShotsOnTargetTotal ) { ScoreLeaderboardData_Entry *pEnt = pAcc->add_entries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_ShotsOnTarget ); pEnt->set_val( n ); } } // // Match stats // if ( int n = CSGameRules()->GetTotalRoundsPlayed() ) { ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_RoundsPlayed ); pEnt->set_val( n ); } if ( CSGameRules()->IsPlayingCoopMission() ) { int nRemainingTime = CSGameRules()->GetRoundRemainingTime(); if ( nRemainingTime < 0 ) nRemainingTime = 0; if ( int n = nRemainingTime ) { ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_TimeRemaining ); pEnt->set_val( n ); } static ConVarRef mp_coopmission_bot_difficulty_offset( "mp_coopmission_bot_difficulty_offset" ); int nHardMode = ( mp_coopmission_bot_difficulty_offset.GetInt() >= 3 ) ? 1 : 0; if ( int n = nHardMode ) { ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_BonusHardMode ); pEnt->set_val( n ); } if ( int n = CSGameRules()->m_coopBonusPistolsOnly ? 1 : 0 ) { ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_BonusPistolOnly ); pEnt->set_val( n ); } if ( int n = ( CSGameRules()->m_coopBonusCoinsFound == 3 ) ? 1 : 0 ) { ScoreLeaderboardData_Entry *pEnt = sld.add_matchentries(); pEnt->set_tag( k_EScoreLeaderboardDataEntryTag_BonusChallenge ); pEnt->set_val( n ); } } // // Set the final score and the questid if applicable // int32 nTotalScore = 0; for ( int nBonus = 0; nBonus <= 1; ++ nBonus ) { int32 nScoreTier = 0; for ( int iCategory = 0; iCategory < 5; ++ iCategory ) { nScoreTier += CoopScoreGetRatingEntryFromLeaderboardData( sld, !!nBonus, iCategory, true ); } if ( nScoreTier < 0 ) nScoreTier = 0; nTotalScore += nScoreTier; } sld.set_score( nTotalScore ); } bool IsAssassinationQuest( const CEconQuestDefinition *pQuest ) { if ( pQuest && ( V_stristr( pQuest->GetQuestExpression(), "act_kill_target" ) || V_stristr( pQuest->GetQuestExpression(), "act_pick_up_trophy" ) ) ) return true; return false; } bool IsAssassinationQuest( uint32 questID ) { const CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( questID ); return IsAssassinationQuest( pQuest ); } // Checks basic conditions for a quest (mapgroup, mode, etc) to see if a quest is possible to complete bool Helper_CheckQuestMapAndMode( const CEconQuestDefinition *pQuest ) { const char *szMapName = NULL; const char *szMapGroupName = NULL; #if defined ( CLIENT_DLL ) szMapName = engine->GetLevelNameShort(); szMapGroupName = engine->GetMapGroupName(); #else szMapName = V_UnqualifiedFileName( STRING( gpGlobals->mapname ) ); szMapGroupName = STRING( gpGlobals->mapGroupName ); #endif // Wrong map if ( !StringIsEmpty( pQuest->GetMap() ) && V_strcmp( szMapName, pQuest->GetMap() ) ) return false; // Unless the map group is named after our map (so queued for a single map) also confirm we're using the right map group if ( V_strcmp( szMapGroupName, CFmtStr( "mg_%s", szMapName ) ) ) { if ( !StringIsEmpty( pQuest->GetMapGroup() ) && V_strcmp( szMapGroupName, pQuest->GetMapGroup() ) ) { return false; } } const char *szCurrentModeAsString = g_pGameTypes->GetGameModeFromInt( g_pGameTypes->GetCurrentGameType(), g_pGameTypes->GetCurrentGameMode() ); // Mode doesn't match if ( V_strcmp( pQuest->GetGameMode(), szCurrentModeAsString ) ) return false; return true; } bool IsAssassinationQuestActive( const CEconQuestDefinition *pQuest ) { if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() ) return false; // We need to have an active quest with the 'act_kill_target' requirement if ( !pQuest || !IsAssassinationQuest( pQuest ) ) return false; // Validate target team if ( pQuest->GetTargetTeam() != TEAM_TERRORIST && pQuest->GetTargetTeam() != TEAM_CT ) return false; if ( !Helper_CheckQuestMapAndMode( pQuest ) ) return false; return true; } #if BACKUPSUPPORTZEROZERO static char const * const g_szRoundBackupZeroZero = "0:0"; static char const * const g_szRoundBackupZeroZeroFileName = "00restart"; #endif class CBackupFilesEnumerator : public CUtlVector< char const * > { public: CBackupFilesEnumerator() { if ( !mp_backup_round_file.GetString()[0] ) { Warning( "Current backup file prefix is not set, use mp_backup_round_file to set it.\n" ); return; } Warning( "Listing backup files with prefix: %s\n", mp_backup_round_file.GetString() ); FileFindHandle_t hFind = NULL; CUtlBuffer bufFilename; int nBufSize = 1024; bufFilename.EnsureCapacity( nBufSize ); char *pchBufFilename = ( char * ) bufFilename.Base(); Q_memset( pchBufFilename, 0, nBufSize ); Q_snprintf( pchBufFilename, nBufSize, "%s", mp_backup_round_file_pattern.GetString() ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%prefix%", mp_backup_round_file.GetString() ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%date%", "*" ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%time%", "*" ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%round%", "*" ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%score1%", "*" ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%score2%", "*" ); char chTeam1[64] = {0}; Q_snprintf( chTeam1, sizeof( chTeam1 ), "%s", GetGlobalTeam( CSGameRules()->AreTeamsPlayingSwitchedSides() ? TEAM_TERRORIST : TEAM_CT )->GetClanName() ); for ( char *pch = chTeam1; *pch; ++ pch ) { if ( ( ( pch[0] >= 'a' ) && ( pch[0] <= 'z' ) ) || ( ( pch[0] >= 'A' ) && ( pch[0] <= 'Z' ) ) || ( ( pch[0] >= '0' ) && ( pch[0] <= '9' ) ) ) ; else { Q_memmove( pch, pch + 1, sizeof( chTeam1 ) - ( pch - chTeam1 ) - 1 ); -- pch; } } char chTeam2[64] = {0}; Q_snprintf( chTeam2, sizeof( chTeam2 ), "%s", GetGlobalTeam( CSGameRules()->AreTeamsPlayingSwitchedSides() ? TEAM_CT : TEAM_TERRORIST )->GetClanName() ); for ( char *pch = chTeam2; *pch; ++ pch ) { if ( ( ( pch[0] >= 'a' ) && ( pch[0] <= 'z' ) ) || ( ( pch[0] >= 'A' ) && ( pch[0] <= 'Z' ) ) || ( ( pch[0] >= '0' ) && ( pch[0] <= '9' ) ) ) ; else { Q_memmove( pch, pch + 1, sizeof( chTeam2 ) - ( pch - chTeam2 ) - 1 ); -- pch; } } Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%team1%", chTeam1 ); Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%team2%", chTeam2 ); { // Map might have a path in it, make sure we strip it const char *szMapNameToken = STRING( gpGlobals->mapname ); if ( const char *pchSlash = strrchr( szMapNameToken, '/' ) ) szMapNameToken = pchSlash + 1; if ( const char *pchSlash2 = strrchr( szMapNameToken, '\\' ) ) szMapNameToken = pchSlash2 + 1; Helper_FilenameTokenReplace( pchBufFilename, nBufSize, "%map%", szMapNameToken ); } for ( char const *szFileName = filesystem->FindFirst( pchBufFilename, &hFind ); szFileName && *szFileName; szFileName = filesystem->FindNext( hFind ) ) { m_mapFiles.AddString( szFileName ); } filesystem->FindClose( hFind ); for ( int iStr = 0; iStr < m_mapFiles.GetNumStrings(); ++ iStr ) { AddToTail( m_mapFiles.String( iStr ) ); } Sort( Helper_mp_backup_restore_from_file_sorter ); #if BACKUPSUPPORTZEROZERO // add 0:0 backup static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL ); if ( s_pchTournamentServer ) AddToTail( g_szRoundBackupZeroZeroFileName ); #endif } private: CUtlStringMap< bool > m_mapFiles; }; #if defined( _DEBUG ) #define ROUND_BACKUP_REQUEST_RATE_LIMIT 5 #else #define ROUND_BACKUP_REQUEST_RATE_LIMIT 10 #endif // concommand that requests a user message with the round backup filenames in it. CON_COMMAND_F ( send_round_backup_file_list, "", FCVAR_GAMEDLL | FCVAR_RELEASE |FCVAR_HIDDEN|FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS ) { CBasePlayer* pPlayer = UTIL_GetCommandClient(); if ( !pPlayer ) return; static CUtlMap s_mapLastRequestTime; if ( s_mapLastRequestTime.Count() == 0 ) { s_mapLastRequestTime.SetLessFunc( DefLessFunc( int ) ); } // prevent spamming of the command. store each user's previous request time. int nIndex = s_mapLastRequestTime.Find( pPlayer->entindex() ); if ( ( s_mapLastRequestTime.IsValidIndex( nIndex ) ) && ( gpGlobals->curtime > s_mapLastRequestTime[ nIndex ] ) && ( ( gpGlobals->curtime - s_mapLastRequestTime[ nIndex ] ) <= ROUND_BACKUP_REQUEST_RATE_LIMIT ) ) return; s_mapLastRequestTime.InsertOrReplace( pPlayer->entindex(), ( int )gpGlobals->curtime ); CCSUsrMsg_RoundBackupFilenames msg; CBackupFilesEnumerator arrStrings; // to avoid the proto message limit we're sending these in individual messages rather than one. for ( int idx = 0; idx < Min( 10, arrStrings.Count() ); idx++ ) { CCSUsrMsg_RoundBackupFilenames msg; msg.set_count( Min( 10, arrStrings.Count() ) ); msg.set_index( idx ); msg.set_filename( arrStrings[ idx ] ); // create human readable name KeyValues *kvSaveFile = new KeyValues( "" ); KeyValues::AutoDelete autodelete_kvSaveFile( kvSaveFile ); autodelete_kvSaveFile->UsesEscapeSequences( true ); CUtlString nicefilename = arrStrings[ idx ]; #if BACKUPSUPPORTZEROZERO if ( !V_strcmp( arrStrings[ idx ], g_szRoundBackupZeroZeroFileName ) ) { nicefilename = g_szRoundBackupZeroZero; } else #endif if ( kvSaveFile->LoadFromFile( filesystem, arrStrings[ idx ] ) ) { nicefilename = CFmtStr( "%s score %d:%d", kvSaveFile->GetString( "timestamp" ), kvSaveFile->GetInt( "FirstHalfScore/team1" ) + kvSaveFile->GetInt( "SecondHalfScore/team1" ) + kvSaveFile->GetInt( "OvertimeScore/team1" ), kvSaveFile->GetInt( "FirstHalfScore/team2" ) + kvSaveFile->GetInt( "SecondHalfScore/team2" ) + kvSaveFile->GetInt( "OvertimeScore/team2" )); } msg.set_nicename( nicefilename ); CSingleUserRecipientFilter filter( pPlayer ); SendUserMessage( filter, CS_UM_RoundBackupFilenames, msg ); } } CON_COMMAND_F ( mp_backup_restore_list_files, "Lists recent backup round files matching the prefix, most recent files first, accepts a numeric parameter to limit the number of files displayed", FCVAR_GAMEDLL | FCVAR_RELEASE ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; int numFilesToList = 5; if ( args.ArgC() >= 2 ) { numFilesToList = Q_atoi( args.Arg( 1 ) ); } CBackupFilesEnumerator arrStrings; FOR_EACH_VEC( arrStrings, idxString ) { if ( idxString >= numFilesToList ) break; Warning( " %s\n", arrStrings[idxString] ); } if ( !arrStrings.Count() ) { Warning( "No matching backup files found.\n" ); } else if ( numFilesToList < arrStrings.Count() ) { Warning( "%d backup files found, %d most recent listed, use 'mp_backup_restore_list_files %d' to list all.\n", arrStrings.Count(), numFilesToList, arrStrings.Count() ); } else { Warning( "%d backup files listed.\n", arrStrings.Count() ); } } ConVar mp_backup_restore_load_autopause( "mp_backup_restore_load_autopause", "1", FCVAR_RELEASE, "Whether to automatically pause the match after restoring round data from backup" ); CON_COMMAND_F ( mp_backup_restore_load_file, "Loads player cash, KDA, scores and team scores; resets to the next round after the backup", FCVAR_GAMEDLL | FCVAR_RELEASE ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; if ( args.ArgC() < 2 ) { Warning( "Usage: mp_backup_restore_load_file [filename], use 'mp_backup_restore_list_files' to list matching backup files.\n" ); return; } CSGameRules()->LoadRoundDataInformation( args.Arg( 1 ) ); } CMsgGCCStrike15_v2_MatchmakingGC2ServerReserve CCSGameRules::sm_QueuedServerReservation; #endif #ifdef CLIENT_DLL void RecvProxy_CSGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID ) { CCSGameRules *pRules = CSGameRules(); Assert( pRules ); *pOut = pRules; } BEGIN_RECV_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy ) RecvPropDataTable( "cs_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_CSGameRules ), RecvProxy_CSGameRules ) END_RECV_TABLE() #else void* SendProxy_CSGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID ) { CCSGameRules *pRules = CSGameRules(); Assert( pRules ); return pRules; } BEGIN_SEND_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy ) SendPropDataTable( "cs_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_CSGameRules ), SendProxy_CSGameRules ) END_SEND_TABLE() #endif // MOSTLY DECPRECATED. // All weapons ( except exhaustibles, ITEM_FLAG_EXHAUSTIBLE, such as grenades ) store their reserve ammo on the weapon entity // and no longer on the player. The reserve amount is specified in schema as attributes "primary reserve ammo max" and "secondary reserve ammo max" ConVar ammo_50AE_max( "ammo_50AE_max", "35", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_762mm_max( "ammo_762mm_max", "90", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_556mm_max( "ammo_556mm_max", "90", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_556mm_small_max( "ammo_556mm_small_max", "40", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_556mm_box_max( "ammo_556mm_box_max", "200", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_338mag_max( "ammo_338mag_max", "30", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_9mm_max( "ammo_9mm_max", "120", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_buckshot_max( "ammo_buckshot_max", "32", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_45acp_max( "ammo_45acp_max", "100", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_357sig_max( "ammo_357sig_max", "52", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_357sig_p250_max( "ammo_357sig_p250_max", "26", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_357sig_small_max( "ammo_357sig_small_max", "24", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_357sig_min_max( "ammo_357sig_min_max", "12", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_57mm_max( "ammo_57mm_max", "100", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_grenade_limit_default( "ammo_grenade_limit_default", "1", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_grenade_limit_flashbang( "ammo_grenade_limit_flashbang", "1", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_grenade_limit_total( "ammo_grenade_limit_total", "3", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_item_limit_healthshot( "ammo_item_limit_healthshot", "4", FCVAR_REPLICATED | FCVAR_RELEASE ); ConVar ammo_50AE_impulse( "ammo_50AE_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_762mm_impulse( "ammo_762mm_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_556mm_impulse( "ammo_556mm_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_556mm_box_impulse( "ammo_556mm_box_impulse", "2400", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_338mag_impulse( "ammo_338mag_impulse", "2800", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_9mm_impulse( "ammo_9mm_impulse", "2000", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_buckshot_impulse( "ammo_buckshot_impulse", "600", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_45acp_impulse( "ammo_45acp_impulse", "2100", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_357sig_impulse( "ammo_357sig_impulse", "2000", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_57mm_impulse( "ammo_57mm_impulse", "2000", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_50AE_headshot_mult( "ammo_50AE_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_762mm_headshot_mult( "ammo_762mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_556mm_headshot_mult( "ammo_556mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_556mm_box_headshot_mult( "ammo_556mm_box_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_338mag_headshot_mult( "ammo_338mag_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_9mm_headshot_mult( "ammo_9mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_buckshot_headshot_mult( "ammo_buckshot_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_45acp_headshot_mult( "ammo_45acp_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_357sig_headshot_mult( "ammo_357sig_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); ConVar ammo_57mm_headshot_mult( "ammo_57mm_headshot_mult", "1.0", FCVAR_REPLICATED, "You must enable tweaking via tweak_ammo_impulses to use this value." ); //ConVar mp_dynamicpricing( "mp_dynamicpricing", "0", FCVAR_REPLICATED, "Enables or Disables the dynamic weapon prices" ); ConVar mp_spec_swapplayersides( "mp_spec_swapplayersides", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Toggle set the player names and team names to the opposite side in which they are are on the spectator panel.", true, 0, true, 1 ); ConVar mp_force_assign_teams( "mp_force_assign_teams", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Players don't get to choose what team they are on, it is auto assinged.", true, 0, true, 1 ); #if defined( GAME_DLL ) // need this defined for the server so it can load from game.cfg ConVar sv_gameinstructor_disable( "sv_gameinstructor_disable", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Force all clients to disable their game instructors." ); ConVar cs_AssistDamageThreshold( "cs_AssistDamageThreshold", "40.0", FCVAR_DEVELOPMENTONLY, "cs_AssistDamageThreshold defines the amount of damage needed to score an assist" ); #endif ConVar sv_matchpause_auto_5v5( "sv_matchpause_auto_5v5", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "When enabled will automatically pause the match at next freeze time if less than 5 players are connected on each team." ); extern ConVar sv_stopspeed; extern ConVar mp_randomspawn; extern ConVar mp_randomspawn_los; extern ConVar mp_teammates_are_enemies; extern ConVar mp_respawnwavetime; extern ConVar mp_hostages_max; extern ConVar mp_hostages_spawn_farthest; extern ConVar mp_hostages_spawn_force_positions; extern ConVar mp_hostages_spawn_same_every_round; extern ConVar weapon_max_before_cleanup; ConVar mp_spectators_max( "mp_spectators_max", "2", FCVAR_REPLICATED | FCVAR_RELEASE, "How many spectators are allowed in a match.", true, 0, false, 0 ); ConVar mp_buytime( "mp_buytime", "90", FCVAR_REPLICATED | FCVAR_RELEASE, "How many seconds after round start players can buy items for.", true, 0, false, 0 ); ConVar mp_buy_allow_grenades( "mp_buy_allow_grenades", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether players can purchase grenades from the buy menu or not.", true, 0, true, 1 ); ConVar mp_do_warmup_period( "mp_do_warmup_period", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether or not to do a warmup period at the start of a match.", true, 0, true, 1 ); ConVar mp_do_warmup_offine( "mp_do_warmup_offine", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether or not to do a warmup period at the start of a match in an offline (bot) match.", true, 0, true, 1 ); ConVar mp_startmoney( "mp_startmoney", "800", FCVAR_REPLICATED | FCVAR_RELEASE, "amount of money each player gets when they reset", true, 0, false, 0 ); ConVar mp_maxmoney( "mp_maxmoney", "16000", FCVAR_REPLICATED | FCVAR_RELEASE, "maximum amount of money allowed in a player's account", true, 0, false, 0 ); ConVar mp_afterroundmoney( "mp_afterroundmoney", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "amount of money awared to every player after each round" ); ConVar mp_playercashawards( "mp_playercashawards", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Players can earn money by performing in-game actions" ); ConVar mp_teamcashawards( "mp_teamcashawards", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Teams can earn money by performing in-game actions" ); ConVar mp_overtime_enable( "mp_overtime_enable", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If a match ends in a tie, use overtime rules to determine winner" ); ConVar mp_overtime_maxrounds( "mp_overtime_maxrounds", "6", FCVAR_REPLICATED | FCVAR_RELEASE, "When overtime is enabled play additional rounds to determine winner" ); ConVar mp_overtime_startmoney( "mp_overtime_startmoney", "10000", FCVAR_REPLICATED | FCVAR_RELEASE, "Money assigned to all players at start of every overtime half" ); ConVar mp_hostages_takedamage( "mp_hostages_takedamage", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether or not hostages can be hurt." ); ConVar mp_hostages_rescuetowin( "mp_hostages_rescuetowin", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "0 == all alive, any other number is the number the CT's need to rescue to win the round." ); ConVar mp_hostages_rescuetime( "mp_hostages_rescuetime", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Additional time added to round time if a hostage is reached by a CT." ); ConVar mp_anyone_can_pickup_c4( "mp_anyone_can_pickup_c4", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If set, everyone can pick up the c4, not just Ts." ); ConVar mp_c4_cannot_be_defused( "mp_c4_cannot_be_defused", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If set, the planted c4 cannot be defused." ); ConVar sv_coaching_enabled( "sv_coaching_enabled", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Allows spectating and communicating with a team ( 'coach t' or 'coach ct' )" ); ConVar sv_allow_thirdperson( "sv_allow_thirdperson", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Allows the server set players in third person mode without the client slamming it back (if cheats are on, all clients can set thirdperson without this convar being set)" ); ConVar sv_party_mode( "sv_party_mode", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Party!!" ); // ConVar mp_hostages_canpickup( // "mp_hostages_canpickup", // "1", // FCVAR_REPLICATED | FCVAR_RELEASE, // "1 = hostages are picked up when used, 0 == hostages uyse the old method of following behind players." ); #ifndef CLIENT_DLL CON_COMMAND( timeout_terrorist_start, "" ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; CSGameRules()->StartTerroristTimeOut(); } CON_COMMAND( timeout_ct_start, "" ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; CSGameRules()->StartCTTimeOut(); } CON_COMMAND( mp_warmup_start, "Start warmup." ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; if ( CSGameRules() ) { if ( CSGameRules()->IsQueuedMatchmaking() ) { Msg( "mp_warmup_start cannot be used on official servers, use a longer mp_warmuptime instead!\n" ); } else { CSGameRules()->StartWarmup(); } } } CON_COMMAND( mp_warmup_end, "End warmup immediately." ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) { // // Special handling here allowing players to unpause warmup timer // // if it is in mode #2 where it is paused until getting unpaused by players // extern ConVar mp_warmup_pausetimer; // extern ConVar mp_warmuptime; // if ( CSGameRules() && CSGameRules()->IsWarmupPeriod() && ( mp_warmup_pausetimer.GetInt() == 2 ) ) // { // int nNewWarmupTime = mp_warmuptime.GetInt(); // if ( nNewWarmupTime < 15 ) // { // mp_warmuptime.SetValue( 15 ); // } // mp_warmup_pausetimer.SetValue( 0 ); // // CSGameRules()->SetWarmupPeriodStartTime( gpGlobals->curtime ); // CSGameRules()->m_fWarmupNextChatNoticeTime = gpGlobals->curtime + 10; // // CBroadcastRecipientFilter filter; // int issuingPlayerIndex = UTIL_GetCommandClientIndex(); // if ( CCSPlayer *pPlayer = ToCSPlayer( UTIL_EntityByIndex( issuingPlayerIndex ) ) ) // { // Warning( "mp_warmup_end issued by player#%d(%s), unpausing warmup...\n", issuingPlayerIndex, pPlayer->GetPlayerName() ); // UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Player_Wants_Restart", pPlayer->GetPlayerName() ); // } // else // { // Warning( "mp_warmup_end issued by player#%d with no player info, unpausing warmup...\n", issuingPlayerIndex ); // } // UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Match_Will_Start_Chat" ); // } return; } if ( CSGameRules() ) { if ( CSGameRules()->IsQueuedMatchmaking() ) { Msg( "mp_warmup_start cannot be used on official servers, use a shorter mp_warmuptime instead!\n" ); } else { CSGameRules()->EndWarmup(); } } } #endif static void mpwarmuptime_f( IConVar *pConVar, const char *pOldString, float flOldValue ) { if ( CSGameRules() ) { CSGameRules()->SetWarmupPeriodStartTime( gpGlobals->curtime ); } } ConVar mp_verbose_changelevel_spew( "mp_verbose_changelevel_spew", "1", FCVAR_RELEASE ); ConVar mp_warmuptime( "mp_warmuptime", "30", FCVAR_REPLICATED | FCVAR_RELEASE, "How long the warmup period lasts. Changing this value resets warmup.", true, 5, false, 0, mpwarmuptime_f ); ConVar mp_warmuptime_all_players_connected( "mp_warmuptime_all_players_connected", "60", FCVAR_REPLICATED | FCVAR_RELEASE, "Warmup time to use when all players have connected in official competitive. 0 to disable." ); ConVar mp_warmup_pausetimer( "mp_warmup_pausetimer", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Set to 1 to stay in warmup indefinitely. Set to 0 to resume the timer." ); ConVar mp_halftime_pausetimer( "mp_halftime_pausetimer", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Set to 1 to stay in halftime indefinitely. Set to 0 to resume the timer." ); ConVar mp_halftime_pausematch( "mp_halftime_pausematch", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Set to 1 to pause match after halftime countdown elapses. Match must be resumed by vote or admin." ); ConVar mp_overtime_halftime_pausetimer( "mp_overtime_halftime_pausetimer", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If set to 1 will set mp_halftime_pausetimer to 1 before every half of overtime. Set mp_halftime_pausetimer to 0 to resume the timer." ); ConVar mp_respawn_immunitytime("mp_respawn_immunitytime", "4.0", FCVAR_RELEASE | FCVAR_REPLICATED, "How many seconds after respawn immunity lasts." ); ConVar mp_playerid( "mp_playerid", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Controls what information player see in the status bar: 0 all names; 1 team names; 2 no names", true, 0, true, 2 ); ConVar mp_playerid_delay( "mp_playerid_delay", "0.4", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to delay showing information in the status bar", true, 0, true, 1 ); ConVar mp_playerid_hold( "mp_playerid_hold", "0.2", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to keep showing old information in the status bar", true, 0, true, 1 ); ConVar mp_round_restart_delay( "mp_round_restart_delay", "7.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to delay before restarting a round after a win", true, 0.0f, true, 14.0f ); // 10 - 11 seconds is a comfortable minimum to fit the end of round replay ConVar mp_halftime_duration( "mp_halftime_duration", "15.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds that halftime lasts", true, 0.0f, true, 300.0f ); ConVar mp_match_can_clinch( "mp_match_can_clinch", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Can a team clinch and end the match by being so far ahead that the other team has no way to catching up?" ); ConVar mp_ggtr_end_round_kill_bonus( "mp_ggtr_end_round_kill_bonus", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of bonus points awarded in Demolition Mode when knife kill ends round", true, 0, true, 10 ); ConVar mp_ggtr_last_weapon_kill_ends_half( "mp_ggtr_last_weapon_kill_ends_half", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "End the half and give a team round point when a player makes a kill using the final weapon", true, 0, true, 1 ); ConVar mp_ggprogressive_round_restart_delay( "mp_ggprogressive_round_restart_delay", "15.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to delay before restarting a round after a win in gungame progessive", true, 0.0f, true, 90.0f ); ConVar mp_ggprogressive_use_random_weapons( "mp_ggprogressive_use_random_weapons", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "If set, selects random weapons from set categories for the progression order" ); ConVar mp_ggprogressive_random_weapon_kills_needed( "mp_ggprogressive_random_weapon_kills_needed", "2", FCVAR_REPLICATED | FCVAR_RELEASE, "If mp_ggprogressive_use_random_weapons is set, this is the number of kills needed with each weapon" ); ConVar mp_ggtr_num_rounds_autoprogress( "mp_ggtr_num_rounds_autoprogress", "3", FCVAR_REPLICATED | FCVAR_RELEASE, "Upgrade the player's weapon after this number of rounds without upgrading" ); ConVar mp_ct_default_melee( "mp_ct_default_melee", "weapon_knife", FCVAR_REPLICATED | FCVAR_RELEASE, "The default melee weapon that the CTs will spawn with. Even if this is blank, a knife will be given. To give a taser, it should look like this: 'weapon_knife weapon_taser'. Remember to set mp_weapons_allow_zeus to 1 if you want to give a taser!" ); ConVar mp_ct_default_secondary( "mp_ct_default_secondary", "weapon_hkp2000", FCVAR_REPLICATED | FCVAR_RELEASE, "The default secondary (pistol) weapon that the CTs will spawn with" ); ConVar mp_ct_default_primary( "mp_ct_default_primary", "", FCVAR_REPLICATED | FCVAR_RELEASE, "The default primary (rifle) weapon that the CTs will spawn with" ); ConVar mp_ct_default_grenades( "mp_ct_default_grenades", "", FCVAR_REPLICATED | FCVAR_RELEASE, "The default grenades that the CTs will spawn with. To give multiple grenades, separate each weapon class with a space like this: 'weapon_molotov weapon_hegrenade'" ); ConVar mp_t_default_melee( "mp_t_default_melee", "weapon_knife", FCVAR_REPLICATED | FCVAR_RELEASE, "The default melee weapon that the Ts will spawn with" ); ConVar mp_t_default_secondary( "mp_t_default_secondary", "weapon_glock", FCVAR_REPLICATED | FCVAR_RELEASE, "The default secondary (pistol) weapon that the Ts will spawn with" ); ConVar mp_t_default_primary( "mp_t_default_primary", "", FCVAR_REPLICATED | FCVAR_RELEASE, "The default primary (rifle) weapon that the Ts will spawn with" ); ConVar mp_t_default_grenades( "mp_t_default_grenades", "", FCVAR_REPLICATED | FCVAR_RELEASE, "The default grenades that the Ts will spawn with. To give multiple grenades, separate each weapon class with a space like this: 'weapon_molotov weapon_hegrenade'" ); ConVar mp_join_grace_time( "mp_join_grace_time", "0.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds after round start to allow a player to join a game", true, 0.0f, true, 30.0f ); ConVar mp_win_panel_display_time( "mp_win_panel_display_time", "3", FCVAR_REPLICATED | FCVAR_RELEASE, "The amount of time to show the win panel between matches / halfs" ); ConVar mp_ggtr_bomb_pts_for_upgrade( "mp_ggtr_bomb_pts_for_upgrade", "2.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Kill points required to upgrade a player's weapon", true, 1.0, true, 10.0 ); ConVar mp_ggtr_bomb_pts_for_he( "mp_ggtr_bomb_pts_for_he", "3", FCVAR_REPLICATED | FCVAR_RELEASE, "Kill points required in a round to get a bonus HE grenade", true, 1, true, 5); ConVar mp_ggtr_bomb_pts_for_flash( "mp_ggtr_bomb_pts_for_flash", "4", FCVAR_REPLICATED | FCVAR_RELEASE, "Kill points required in a round to get a bonus flash grenade", true, 1, true, 5); ConVar mp_ggtr_bomb_pts_for_molotov( "mp_ggtr_bomb_pts_for_molotov", "5", FCVAR_REPLICATED | FCVAR_RELEASE, "Kill points required in a round to get a bonus molotov cocktail", true, 1, true, 5); ConVar mp_molotovusedelay( "mp_molotovusedelay", "15.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to delay before the molotov can be used after acquiring it", true, 0.0, true, 30.0 ); ConVar mp_ggtr_halftime_delay( "mp_ggtr_halftime_delay", "0.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to delay during TR Mode halftime", true, 0.0, true, 30.0 ); ConVar mp_ggtr_bomb_respawn_delay( "mp_ggtr_bomb_respawn_delay", "0.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of seconds to delay before making the bomb available to a respawner in gun game", true, 0.0, true, 30.0 ); ConVar mp_ggtr_bomb_defuse_bonus( "mp_ggtr_bomb_defuse_bonus", "1.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of bonus upgrades to award the CTs when they defuse a gun game bomb", true, 1.0, true, 10.0 ); ConVar mp_ggtr_bomb_detonation_bonus( "mp_ggtr_bomb_detonation_bonus", "1.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Number of bonus upgrades to award the Ts when they detonate a gun game bomb", true, 1.0, true, 10.0 ); ConVar mp_dm_bonus_percent( "mp_dm_bonus_percent", "50", FCVAR_REPLICATED | FCVAR_RELEASE, "Percent of points additionally awarded when someone gets a kill with the bonus weapon during the bonus period." ); ConVar mp_display_kill_assists( "mp_display_kill_assists", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether to display and score player assists", true, 0, true, 1 ); ConVar mp_match_end_restart( "mp_match_end_restart", #if defined (CSTRIKE15) "0", #else "0", #endif FCVAR_REPLICATED | FCVAR_RELEASE, "At the end of the match, perform a restart instead of loading a new map", true, 0, true, 1 ); ConVar mp_match_end_changelevel( "mp_match_end_changelevel", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "At the end of the match, perform a changelevel even if next map is the same", true, 0, true, 1 ); ConVar mp_defuser_allocation( "mp_defuser_allocation", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "How to allocate defusers to CTs at start or round: 0=none, 1=random, 2=everyone", true, 0, true, 2 ); ConVar mp_give_player_c4( "mp_give_player_c4", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether this map should spawn a c4 bomb for a player or not.", true, 0, true, 1 ); ConVar mp_death_drop_gun( "mp_death_drop_gun", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Which gun to drop on player death: 0=none, 1=best, 2=current or best", true, 0, true, 2 ); ConVar mp_death_drop_c4( "mp_death_drop_c4", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether c4 is droppable" ); ConVar mp_death_drop_grenade( "mp_death_drop_grenade", "2", FCVAR_REPLICATED | FCVAR_RELEASE, "Which grenade to drop on player death: 0=none, 1=best, 2=current or best, 3=all grenades", true, 0, true, 3 ); ConVar mp_death_drop_defuser( "mp_death_drop_defuser", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Drop defuser on player death", true, 0, true, 1 ); ConVar mp_coop_force_join_ct( "mp_coop_force_join_ct", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If set, real players will auto join CT on join." ); ConVar mp_coopmission_mission_number( "mp_coopmission_mission_number", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Which mission the map should run after it loads." ); ConVar mp_force_pick_time( "mp_force_pick_time", "15", FCVAR_REPLICATED | FCVAR_RELEASE, "The amount of time a player has on the team screen to make a selection before being auto-teamed" ); ConVar bot_autodifficulty_threshold_low( "bot_autodifficulty_threshold_low", "-2.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Lower bound below Average Human Contribution Score that a bot must be below to change its difficulty", true, -20.0, true, 20.0 ); ConVar bot_autodifficulty_threshold_high( "bot_autodifficulty_threshold_high", "5.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Upper bound above Average Human Contribution Score that a bot must be above to change its difficulty", true, -20.0, true, 20.0 ); ConVar mp_weapons_allow_zeus( "mp_weapons_allow_zeus", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines how many Zeus purchases a player can make per round (0 to disallow, -1 to have no limit)." ); ConVar mp_weapons_allow_typecount( "mp_weapons_allow_typecount", "5", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines how many purchases of each weapon type allowed per player per round (0 to disallow purchasing, -1 to have no limit)." ); ConVar mp_weapons_allow_map_placed( "mp_weapons_allow_map_placed", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If this convar is set, when a match starts, the game will not delete weapons placed in the map." ); ConVar mp_default_team_winner_no_objective( "mp_default_team_winner_no_objective", "-1", FCVAR_REPLICATED | FCVAR_RELEASE, "If the map doesn't define an objective (bomb, hostage, etc), the value of this convar will declare the winner when the time runs out in the round." ); ConVar mp_weapons_glow_on_ground( "mp_weapons_glow_on_ground", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If this convar is set, weapons on the ground will have a glow around them." ); ConVar mp_respawn_on_death_t( "mp_respawn_on_death_t", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "When set to 1, terrorists will respawn after dying." ); ConVar mp_respawn_on_death_ct( "mp_respawn_on_death_ct", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "When set to 1, counter-terrorists will respawn after dying." ); ConVar mp_use_respawn_waves( "mp_use_respawn_waves", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "When set to 1, and that player's team is set to respawn, they will respawn in waves. If set to 2, teams will respawn when the whole team is dead." ); void ProhibitedItemsCallback( IConVar *var, const char *pOldValue, float flOldValue ) { #ifdef GAME_DLL if ( !CSGameRules() ) return; ConVar *pCvar = static_cast(var); CUtlStringList pProhibitedWeapons( pCvar->GetString(), "," ); for( int i = 0; i < MAX_PROHIBITED_ITEMS; i++ ) { if ( i < (pProhibitedWeapons.Count()) && ( GetItemSchema()->GetItemDefinition( i ) ) ) { int nDefIndex = V_atoi( pProhibitedWeapons[ i ] ); CSGameRules()->m_arrProhibitedItemIndices.Set( i, nDefIndex ); DevMsg( "Prohibiting %s\n", GetItemSchema()->GetItemDefinition( nDefIndex )->GetDefinitionName() ); } else { CSGameRules()->m_arrProhibitedItemIndices.Set( i, 0 ); } } #endif // GAME_DLL } ConVar mp_items_prohibited( "mp_items_prohibited", "", FCVAR_REPLICATED | FCVAR_RELEASE, "Set this convar to a comma-delimited list of definition indices of weapons that should be prohibited from use.", ProhibitedItemsCallback ); void RespawnWaveTimeCTCallback( IConVar *var, const char *pOldValue, float flOldValue ); void RespawnWaveTimeTCallback( IConVar *var, const char *pOldValue, float flOldValue ); void RespawnWaveTimeCTCallback( IConVar *var, const char *pOldValue, float flOldValue ) { #ifdef CLIENT_DLL extern ConVar mp_respawnwavetime_ct; float flTime = mp_respawnwavetime_ct.GetFloat(); if ( CSGameRules() ) { float flNextRespawn = gpGlobals->curtime + flTime; CSGameRules()->SetNextTeamRespawnWaveDelay( TEAM_CT, flNextRespawn ); } #endif } void RespawnWaveTimeTCallback( IConVar *var, const char *pOldValue, float flOldValue ) { #ifdef CLIENT_DLL extern ConVar mp_respawnwavetime_t; float flTime = mp_respawnwavetime_t.GetFloat(); if ( CSGameRules() ) { float flNextRespawn = gpGlobals->curtime + flTime; CSGameRules()->SetNextTeamRespawnWaveDelay( TEAM_TERRORIST, flNextRespawn ); } #endif } ConVar mp_respawnwavetime_ct( "mp_respawnwavetime_ct", "10.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Time between respawn waves for CTs.", RespawnWaveTimeCTCallback ); ConVar mp_respawnwavetime_t( "mp_respawnwavetime_t", "10.0", FCVAR_REPLICATED | FCVAR_RELEASE, "Time between respawn waves for Terrorists.", RespawnWaveTimeTCallback ); #ifndef CLIENT_DLL // Announcing is always on in REL build // training ConVar tr_completed_training( "tr_completed_training", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS, "Whether the local player has completed the initial training portion of the training map" ); ConVar tr_best_course_time( "tr_best_course_time", "0", FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE | FCVAR_SS, "The player's best time for the timed obstacle course" ); ConVar tr_valve_course_time( "tr_valve_course_time", "352", FCVAR_DEVELOPMENTONLY, "Valve's best time for the timed obstacle course" ); ConVar mp_competitive_endofmatch_extra_time( "mp_competitive_endofmatch_extra_time", "15", FCVAR_RELEASE, "After a competitive match finishes rematch voting extra time is given for rankings." ); #endif ConVar mp_endmatch_votenextmap( "mp_endmatch_votenextmap", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Whether or not players vote for the next map at the end of the match when the final scoreboard comes up" ); ConVar mp_endmatch_votenextmap_keepcurrent( "mp_endmatch_votenextmap_keepcurrent", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "If set, keeps the current map in the list of voting options. If not set, the current map will not appear in the list of voting options." ); ConVar mp_endmatch_votenextleveltime( "mp_endmatch_votenextleveltime", "20", FCVAR_RELEASE, "If mp_endmatch_votenextmap is set, players have this much time to vote on the next map at match end." ); // music controls ConVar snd_music_boost( "snd_music_boost", "0", FCVAR_REPLICATED, "Specifies an amount to boost music volume by" ); #ifdef CLIENT_DLL ConVar snd_music_selection( "snd_music_selection", "1", FCVAR_ARCHIVE, "Tracking rotating music for players with no music packs equipped."); extern ConVar cl_borrow_music_from_player_index; #endif ConVar sv_endmatch_item_drop_interval( "sv_endmatch_item_drop_interval", "1.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The time between drops on the end match scoreboard " ); ConVar sv_endmatch_item_drop_interval_rare( "sv_endmatch_item_drop_interval_rare", "1.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The time between drops on the end match scoreboard for rare items " ); ConVar sv_endmatch_item_drop_interval_mythical( "sv_endmatch_item_drop_interval_mythical", "1.25", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The time between drops on the end match scoreboard for mythical items " ); ConVar sv_endmatch_item_drop_interval_legendary( "sv_endmatch_item_drop_interval_legendary", "2.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The time between drops on the end match scoreboard for legendary items " ); ConVar sv_endmatch_item_drop_interval_ancient( "sv_endmatch_item_drop_interval_ancient", "3.5", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "The time between drops on the end match scoreboard for ancient items " ); // bot difficulty tracking per user input device ConVar sv_compute_per_bot_difficulty( "sv_compute_per_bot_difficulty", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "0 = compute all bot difficulties equally, 1 = compute unique bot difficulty for each bot " ); ConVar sv_show_bot_difficulty_in_name( "sv_show_bot_difficulty_in_name", "0", FCVAR_REPLICATED, "0 = hide bot difficulty in bot name, 1 = show bot difficulty in bot name" ); ConVar sv_bot_difficulty_kbm( "sv_bot_difficulty_kbm", "0", FCVAR_REPLICATED | FCVAR_HIDDEN, "Bot difficulty while playing with Keyboard/Mouse device" ); ConVar sv_bot_difficulty_gamepad( "sv_bot_difficulty_gamepad", "0", FCVAR_REPLICATED | FCVAR_HIDDEN, "Bot difficulty while playing with Gamepad device" ); ConVar sv_bot_difficulty_ps3move( "sv_bot_difficulty_ps3move", "0", FCVAR_REPLICATED | FCVAR_HIDDEN, "Bot difficulty while playing with PS3Move device" ); ConVar sv_bot_difficulty_hydra( "sv_bot_difficulty_hydra", "0", FCVAR_REPLICATED | FCVAR_HIDDEN, "Bot difficulty while playing with Hydra device" ); ConVar sv_bot_difficulty_sharpshooter( "sv_bot_difficulty_sharpshooter", "0", FCVAR_REPLICATED | FCVAR_HIDDEN, "Bot difficulty while playing with SharpShooter device" ); ConVar sv_competitive_official_5v5( "sv_competitive_official_5v5", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Enable to force the server to show 5v5 scoreboards and allows spectators to see characters through walls." ); ConVar sv_kick_ban_duration( "sv_kick_ban_duration", "15", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "How long should a kick ban from the server should last (in minutes)" ); ConVar sv_disable_immunity_alpha( "sv_disable_immunity_alpha", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If set, clients won't slam the player model render settings each frame for immunity [mod authors use this]" ); #ifdef CLIENT_DLL ConVar cl_bot_difficulty_kbm( "cl_bot_difficulty_kbm", "0", FCVAR_HIDDEN, "Bot difficulty while playing with Keyboard/Mouse device" ); ConVar cl_bot_difficulty_gamepad( "cl_bot_difficulty_gamepad", "0", FCVAR_HIDDEN, "Bot difficulty while playing with Gamepad device" ); ConVar cl_bot_difficulty_ps3move( "cl_bot_difficulty_ps3move", "0", FCVAR_HIDDEN, "Bot difficulty while playing with PS3Move device" ); ConVar cl_bot_difficulty_hydra( "cl_bot_difficulty_hydra", "0", FCVAR_HIDDEN, "Bot difficulty while playing with Hydra device" ); ConVar cl_bot_difficulty_sharpshooter( "cl_bot_difficulty_sharpshooter", "0", FCVAR_HIDDEN, "Bot difficulty while playing with SharpShooter device" ); #endif // Set game rules to allow all clients to talk to each other. // Muted players still can't talk to each other. ConVar sv_alltalk( "sv_alltalk", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Deprecated. Replaced with sv_talk_enemy_dead and sv_talk_enemy_living." ); // [jason] Can the dead speak to the living? ConVar sv_deadtalk( "sv_deadtalk", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Dead players can speak (voice, text) to the living" ); // [jason] Override that removes all chat restrictions, including those for spectators ConVar sv_full_alltalk( "sv_full_alltalk", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Any player (including Spectator team) can speak to any other player" ); ConVar sv_talk_enemy_dead( "sv_talk_enemy_dead", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Dead players can hear all dead enemy communication (voice, chat)" ); ConVar sv_talk_enemy_living( "sv_talk_enemy_living", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Living players can hear all living enemy communication (voice, chat)" ); #ifdef GAME_DLL ConVar sv_auto_full_alltalk_during_warmup_half_end( "sv_auto_full_alltalk_during_warmup_half_end", "1", FCVAR_RELEASE, "When enabled will automatically turn on full all talk mode in warmup, at halftime and at the end of the match" ); #endif ConVar sv_spec_hear( "sv_spec_hear", "1", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Determines who spectators can hear: 0: only spectators; 1: all players; 2: spectated team; 3: self only; 4: nobody" ); ConVar mp_c4timer( "mp_c4timer", "40", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "how long from when the C4 is armed until it blows", true, 10, // min value false, 0 // max value ); namespace SpecHear { enum Type { OnlySpectators = 0, AllPlayers = 1, SpectatedTeam = 2, Self = 3, Nobody = 4, }; } // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc. char *sTeamNames[] = { "Unassigned", "Spectator", "TERRORIST", "CT" }; #ifdef CLIENT_DLL ConVar cl_autowepswitch( "cl_autowepswitch", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE| FCVAR_SS | FCVAR_USERINFO, "Automatically switch to picked up weapons (if more powerful)" ); ConVar cl_use_opens_buy_menu( "cl_use_opens_buy_menu", "1", FCVAR_ARCHIVE | FCVAR_ARCHIVE_GAMECONSOLE| FCVAR_SS | FCVAR_USERINFO, "Pressing the +use key will open the buy menu if in a buy zone (just as if you pressed the 'buy' key)." ); ConVar cl_autohelp( "cl_autohelp", "1", FCVAR_ARCHIVE | FCVAR_USERINFO, "Auto-help" ); #else // longest the intermission can last, in seconds #define MAX_INTERMISSION_TIME 120 // Falling damage stuff. #define CS_PLAYER_FATAL_FALL_SPEED 1000 // approx 60 feet #define CS_PLAYER_MAX_SAFE_FALL_SPEED 580 // approx 20 feet #define CS_DAMAGE_FOR_FALL_SPEED ((float)100 / ( CS_PLAYER_FATAL_FALL_SPEED - CS_PLAYER_MAX_SAFE_FALL_SPEED )) // damage per unit per second. // These entities are preserved each round restart. The rest are removed and recreated. static const char *s_PreserveEnts[] = { "ai_network", "ai_hint", "cs_gamerules", "cs_team_manager", "cs_player_manager", "env_soundscape", "env_soundscape_proxy", "env_soundscape_triggerable", "env_sun", "env_wind", "env_fog_controller", "env_tonemap_controller", "env_cascade_light", "func_brush", "func_wall", "func_buyzone", "func_illusionary", "func_hostage_rescue", "func_bomb_target", "infodecal", "info_projecteddecal", "info_node", "info_target", "info_node_hint", "info_player_counterterrorist", "info_player_terrorist", "info_enemy_terrorist_spawn", "info_deathmatch_spawn", "info_armsrace_counterterrorist", "info_armsrace_terrorist", "info_map_parameters", "keyframe_rope", "move_rope", "info_ladder", "player", "point_viewcontrol", "point_viewcontrol_multiplayer", "scene_manager", "shadow_control", "sky_camera", "soundent", "trigger_soundscape", "viewmodel", "predicted_viewmodel", "worldspawn", "point_devshot_camera", "logic_choreographed_scene", "cfe_player_decal", // persistent player spray decals must be preserved //"logic_auto", // preserving this will break all of the maps who currently rely on it getting destroyed each time the map entities are recreated "info_bomb_target_hint_A", "info_bomb_target_hint_B", "info_hostage_rescue_zone_hint", // for the training map "generic_actor", "vote_controller", "wearable_item", "point_hiding_spot", "game_coopmission_manager", "chicken", "", // END Marker }; // --------------------------------------------------------------------------------------------------- // // Voice helper // --------------------------------------------------------------------------------------------------- // class CVoiceGameMgrHelper : public IVoiceGameMgrHelper { public: virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity ) { if ( pListener == NULL || pTalker == NULL ) return false; if ( !CSGameRules() ) return false; return CSGameRules()->CanPlayerHearTalker( pListener, pTalker, false ); } }; CVoiceGameMgrHelper g_VoiceGameMgrHelper; IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper; // --------------------------------------------------------------------------------------------------- // // Globals. // --------------------------------------------------------------------------------------------------- // ConVar dev_reportmoneychanges( "dev_reportmoneychanges", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Displays money account changes for players in the console" ); ConVar mp_roundtime( "mp_roundtime", "5", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "How many minutes each round takes.", true, 1, // min value true, 60 // max value ); ConVar mp_roundtime_deployment( "mp_roundtime_deployment", "5", FCVAR_RELEASE, "How many minutes deployment for coop mission takes.", true, 1, // min value true, 15 // max value ); ConVar mp_roundtime_hostage( "mp_roundtime_hostage", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "How many minutes each round of Hostage Rescue takes. If 0 then use mp_roundtime instead.", true, 0, // min value true, 60 // max value ); ConVar mp_roundtime_defuse( "mp_roundtime_defuse", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "How many minutes each round of Bomb Defuse takes. If 0 then use mp_roundtime instead.", true, 0, // min value true, 60 // max value ); ConVar mp_freezetime( "mp_freezetime", "6", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "how many seconds to keep players frozen when the round starts", true, 0, // min value true, 60 // max value ); ConVar mp_limitteams( "mp_limitteams", "2", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Max # of players 1 team can have over another (0 disables check)", true, 0, // min value true, 30 // max value ); ConVar mp_tkpunish( "mp_tkpunish", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Will TK'ers and team damagers be punished in the next round? {0=no, 1=yes}" ); ConVar mp_autokick( "mp_autokick", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Kick idle/team-killing/team-damaging players" ); ConVar mp_spawnprotectiontime( "mp_spawnprotectiontime", "5", FCVAR_REPLICATED | FCVAR_RELEASE, "Kick players who team-kill within this many seconds of a round restart." ); ConVar mp_td_spawndmgthreshold( "mp_td_spawndmgthreshold", "50", FCVAR_REPLICATED | FCVAR_RELEASE, "The damage threshold players have to exceed at the start of the round to be warned/kick." ); ConVar mp_td_dmgtowarn( "mp_td_dmgtowarn", "200", FCVAR_REPLICATED | FCVAR_RELEASE, "The damage threshhold players have to exceed in a match to get warned that they are about to be kicked." ); ConVar mp_td_dmgtokick( "mp_td_dmgtokick", "300", FCVAR_REPLICATED | FCVAR_RELEASE, "The damage threshhold players have to exceed in a match to get kicked." ); ConVar mp_humanteam( "mp_humanteam", "any", FCVAR_REPLICATED | FCVAR_RELEASE, "Restricts human players to a single team {any, CT, T}" ); ConVar mp_guardian_special_kills_needed( "mp_guardian_special_kills_needed", "10", FCVAR_REPLICATED | FCVAR_RELEASE, "The number of kills needed with a specific weapon." ); ConVar mp_guardian_special_weapon_needed( "mp_guardian_special_weapon_needed", "awp", FCVAR_REPLICATED | FCVAR_RELEASE, "The weapon that needs to be used to increment the kills needed to complete the mission." ); ConVar mp_guardian_player_dist_min( "mp_guardian_player_dist_min", "1300", FCVAR_REPLICATED | FCVAR_RELEASE, "The distance at which we start to warn a player when they are too far from the guarded bombsite." ); ConVar mp_guardian_player_dist_max( "mp_guardian_player_dist_max", "2000", FCVAR_REPLICATED | FCVAR_RELEASE, "The maximum distance a player is allowed to get from the bombsite before they're killed." ); ConVar mp_guardian_bot_money_per_wave( "mp_guardian_bot_money_per_wave", "800", FCVAR_REPLICATED | FCVAR_RELEASE, "The amount of money bots get time each wave the players complete. This # is absolute and not additive, the money is set to (this)x(wave#) for each bot on each wave." ); ConVar mp_ignore_round_win_conditions( "mp_ignore_round_win_conditions", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Ignore conditions which would end the current round" ); ConVar mp_dm_time_between_bonus_min( "mp_dm_time_between_bonus_min", "30", FCVAR_REPLICATED | FCVAR_RELEASE, "Minimum time a bonus time will start after the round start or after the last bonus (in seconds)" ); ConVar mp_dm_time_between_bonus_max( "mp_dm_time_between_bonus_max", "40", FCVAR_REPLICATED | FCVAR_RELEASE, "Maximum time a bonus time will start after the round start or after the last bonus (in seconds)" ); ConVar mp_dm_bonus_length_min( "mp_dm_bonus_length_min", "30", FCVAR_REPLICATED | FCVAR_RELEASE, "Minimum time the bonus time will last (in seconds)" ); ConVar mp_dm_bonus_length_max( "mp_dm_bonus_length_max", "30", FCVAR_REPLICATED | FCVAR_RELEASE, "Maximum time the bonus time will last (in seconds)" ); ConVar mp_damage_scale_ct_body( "mp_damage_scale_ct_body", "1.0", FCVAR_REPLICATED, "Scales the damage a CT player takes by this much when they take damage in the body. (1 == 100%, 0.5 == 50%)" ); ConVar mp_damage_scale_ct_head( "mp_damage_scale_ct_head", "1.0", FCVAR_REPLICATED, "Scales the damage a CT player takes by this much when they take damage in the head (1 == 100%, 0.5 == 50%). REMEMBER! headshots do 4x the damage of the body before this scaler is applied." ); ConVar mp_damage_scale_t_body( "mp_damage_scale_t_body", "1.0", FCVAR_REPLICATED, "Scales the damage a T player takes by this much when they take damage in the body. (1 == 100%, 0.5 == 50%)" ); ConVar mp_damage_scale_t_head( "mp_damage_scale_t_head", "1.0", FCVAR_REPLICATED, "Scales the damage a T player takes by this much when they take damage in the head (1 == 100%, 0.5 == 50%). REMEMBER! headshots do 4x the damage of the body before this scaler is applied." ); ConVar mp_player_healthbuffer_decay_rate( "mp_player_healthbuffer_decay_rate", "0", FCVAR_REPLICATED, "When a player has buffer health, this is how fast it ticks down." ); ConCommand EndRound( "endround", &CCSGameRules::EndRound, "End the current round.", FCVAR_CHEAT ); void cc_ReportEntitiesInEntList( const CCommand& args ) { //int nNumEnts = gEntList.NumberOfEntities(); for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt( pClass ) ) { if ( pClass /*&& !pClass->IsDormant()*/ ) { Msg( "%s\n", pClass->GetClassname() ); } } } static ConCommand ent_list_report( "ent_list_report", cc_ReportEntitiesInEntList, "Reports all list of all entities in a map, one by one" ); //number_of_entities = gEntList.NumberOfEntities(); CON_COMMAND_F ( tv_time_remaining, "Print remaining tv broadcast time", FCVAR_RELEASE | FCVAR_GAMEDLL | FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS ) { #ifdef GAME_DLL if ( HLTVDirector() && HLTVDirector()->IsActive() ) { CEngineHltvInfo_t engineHltv; if ( engine->GetEngineHltvInfo( engineHltv ) && engineHltv.m_bBroadcastActive && ( engineHltv.m_numClients > 0 ) ) { if ( CSGameRules()->GetMatch()->GetPhase() != GAMEPHASE_MATCH_ENDED ) { ConMsg( "GOTV spectators are attached. Match is still in progress.\n" ); } else { float flTimeRemaining = ( CSGameRules()->GetIntermissionStartTime() + HLTVDirector()->GetDelay() + 5.0 ) - gpGlobals->curtime; if ( flTimeRemaining > 0 ) { ConMsg("GOTV spectators are attached. %f seconds remaining to broadcast.\n", ( CSGameRules()->GetIntermissionStartTime() + HLTVDirector()->GetDelay() + 5.0 ) - gpGlobals->curtime ); } else { ConMsg( "GOTV spectators are attached. GOTV Broadcast is complete.\n" ); } } } else { ConMsg( "There are no GOTV spectators attached.\n" ); } } else #endif ConMsg( "GOTV is not active.\n" ); } CON_COMMAND_F ( reset_expo, "Reset player scores, player controls, team scores, and end the round", FCVAR_CHEAT | FCVAR_GAMEDLL ) { CSGameRules()->ResetForTradeshow(); } CON_COMMAND_F ( tweak_ammo_impulses, "Allow real-time tweaking of the ammo impulse values.", FCVAR_CHEAT | FCVAR_GAMEDLL) { for (int ii=0; iim_AmmoType[ii].pPhysicsForceImpulse = USE_CVAR; } } // --------------------------------------------------------------------------------------------------- // // Contribution score control values // --------------------------------------------------------------------------------------------------- // ConVar score_default( "score_default", "1000", FCVAR_NONE, "Default points for a new user" ); ConVar score_kill_enemy_bonus( "score_kill_enemy_bonus", "0", FCVAR_NONE, "Points awarded for killing an enemy" ); ConVar score_damage( "score_damage", "1", FCVAR_NONE, "Points awarded for each point of damage to an enemy" ); ConVar score_ff_damage( "score_ff_damage", "1", FCVAR_NONE, "Penalty awarded for each point of damage to a teammate" ); ConVar score_team_damage_bonus( "score_team_damage_bonus", "1", FCVAR_NONE, "Points awarded for each point of damage a nearby (in same zone) teammate does to enemies" ); ConVar score_planted_bomb_proximity_damage_bonus( "score_planted_bomb_proximity_damage_bonus", "1", FCVAR_NONE, "Points awarded for damaging enemy near planted bomb" ); ConVar score_planted_bomb_proximity_damage_radius_inner( "score_planted_bomb_proximity_damage_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for doing damage near planted bomb" ); ConVar score_planted_bomb_proximity_damage_radius_outer( "score_planted_bomb_proximity_damage_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for doing damage near planted bomb" ); ConVar score_hostage_proximity_damage_bonus( "score_hostage_proximity_damage_bonus", "1", FCVAR_NONE, "Points awarded for damaging enemy near live hostage" ); ConVar score_hostage_proximity_damage_radius_inner( "score_hostage_proximity_damage_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for doing damage near hostage" ); ConVar score_hostage_proximity_damage_radius_outer( "score_hostage_proximity_damage_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for doing damage near hostage" ); ConVar score_dropped_bomb_proximity_damage_bonus( "score_dropped_bomb_proximity_damage_bonus", "1", FCVAR_NONE, "Points awarded for damaging enemy near dropped bomb" ); ConVar score_dropped_bomb_proximity_damage_bonus_radius_inner( "score_dropped_bomb_proximity_damage_bonus_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for doing damage near dropped bomb" ); ConVar score_dropped_bomb_proximity_damage_bonus_radius_outer( "score_dropped_bomb_proximity_damage_bonus_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for doing damage near dropped bomb" ); ConVar score_dropped_defuser_proximity_damage_bonus( "score_dropped_defuser_proximity_damage_bonus", "1", FCVAR_NONE, "Points awarded for damaging enemy near dropped defuser" ); ConVar score_dropped_defuser_proximity_damage_radius_inner( "score_dropped_defuser_proximity_damage_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for doing damage near dropped defuser" ); ConVar score_dropped_defuser_proximity_damage_radius_outer( "score_dropped_defuser_proximity_damage_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for doing damage near dropped defuser" ); ConVar score_bomb_plant_bonus( "score_bomb_plant_bonus", "200", FCVAR_NONE, "Points awarded for planting or assisting with planting the bomb" ); ConVar score_bomb_plant_radius_inner( "score_bomb_plant_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for planting or assisting with planting the bomb" ); ConVar score_bomb_plant_radius_outer( "score_bomb_plant_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for planting or assisting with planting the bomb" ); ConVar score_bomb_defuse_bonus( "score_bomb_defuse_bonus", "400", FCVAR_NONE, "Points awarded for defusing or assisting with defuse of bomb" ); ConVar score_bomb_defuse_radius_inner( "score_bomb_defuse_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for defusing or assisting with defusing the bomb" ); ConVar score_bomb_defuse_radius_outer( "score_bomb_defuse_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for defusing or assisting with defseing the bomb" ); ConVar score_hostage_rescue_bonus( "score_hostage_rescue_bonus", "100", FCVAR_NONE, "Points awarded for rescuing a hostage" ); ConVar score_hostage_rescue_radius_inner( "score_hostage_rescue_radius_inner", "120", FCVAR_NONE, "Inner radius (full bonus) for rescuing hostage" ); ConVar score_hostage_rescue_radius_outer( "score_hostage_rescue_radius_outer", "600", FCVAR_NONE, "Outer radius (zero bonus) for rescuing hostage" ); ConVar score_hostage_damage_penalty( "score_hostage_damage_penalty", "2", FCVAR_NONE, "Penalty for damaging a hostage" ); ConVar score_blind_enemy_bonus( "score_blind_enemy_bonus", "10", FCVAR_NONE, "Bonus for blinding enemy players" ); ConVar score_blind_friendly_penalty( "score_blind_friendly_penalty", "10", FCVAR_NONE, "Penalty for blinding friendly players" ); ConVar score_typical_good_score( "score_typical_good_score", "5", FCVAR_NONE, "An average good score for use in funfacts" ); ConVar contributionscore_assist( "contributionscore_assist", "1", FCVAR_NONE, "amount of contribution score added for an assist" ); ConVar contributionscore_kill( "contributionscore_kill", "2", FCVAR_NONE, "amount of contribution score added for a kill" ); ConVar contributionscore_objective_kill( "contributionscore_objective_kill", "3", FCVAR_NONE, "amount of contribution score added for an objective related kill" ); ConVar contributionscore_hostage_rescue_minor( "contributionscore_hostage_rescue_minor", "1", FCVAR_NONE, "amount of contribution score added to all alive CTs per hostage rescued" ); ConVar contributionscore_hostage_rescue_major( "contributionscore_hostage_rescue_major", "3", FCVAR_NONE, "amount of contribution score added to rescuer per hostage rescued" ); ConVar contributionscore_bomb_defuse_minor( "contributionscore_bomb_defuse_minor", "1", FCVAR_NONE, "amount of contribution score for defusing a bomb after eliminating enemy team" ); ConVar contributionscore_bomb_defuse_major( "contributionscore_bomb_defuse_major", "3", FCVAR_NONE, "amount of contribution score for defusing a bomb while at least one enemy remains alive" ); ConVar contributionscore_bomb_planted( "contributionscore_bomb_planted", "2", FCVAR_NONE, "amount of contribution score for planting a bomb" ); ConVar contributionscore_bomb_exploded( "contributionscore_bomb_exploded", "1", FCVAR_NONE, "amount of contribution score awarded to bomb planter and terrorists remaining alive if bomb explosion wins the round" ); ConVar contributionscore_suicide( "contributionscore_suicide", "-2", FCVAR_NONE, "amount of contribution score for a suicide, normally negative" ); ConVar contributionscore_team_kill( "contributionscore_team_kill", "-2", FCVAR_NONE, "amount of contribution score for a team kill, normally negative" ); ConVar contributionscore_hostage_kill( "contributionscore_hostage_kill", "-2", FCVAR_NONE, "amount of contribution score for killing a hostage, normally negative" ); // --------------------------------------------------------------------------------------------------- // // Global helper functions. // --------------------------------------------------------------------------------------------------- // void InitBodyQue(void) { // FIXME: Make this work } Vector DropToGround( CBaseEntity *pMainEnt, const Vector &vPos, const Vector &vMins, const Vector &vMaxs ) { trace_t trace; UTIL_TraceHull( vPos, vPos + Vector( 0, 0, -500 ), vMins, vMaxs, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace ); return trace.endpos; } //----------------------------------------------------------------------------- // Purpose: This function can be used to find a valid placement location for an entity. // Given an origin to start looking from and a minimum radius to place the entity at, // it will sweep out a circle around vOrigin and try to find a valid spot (on the ground) // where mins and maxs will fit. // Input : *pMainEnt - Entity to place // &vOrigin - Point to search around // fRadius - Radius to search within // nTries - Number of tries to attempt // &mins - mins of the Entity // &maxs - maxs of the Entity // &outPos - Return point // Output : Returns true and fills in outPos if it found a spot. //----------------------------------------------------------------------------- bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround, unsigned int mask, ITraceFilter *pFilter ) { // This function moves the box out in each dimension in each step trying to find empty space like this: // // X // X X // Step 1: X Step 2: XXX Step 3: XXXXX // X X // X // CTraceFilterSimple defaultFilter( pMainEnt, COLLISION_GROUP_NONE ); if ( !pFilter ) { pFilter = &defaultFilter; } Vector mins, maxs; if ( pMainEnt ) { pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs ); mins -= pMainEnt->GetAbsOrigin(); maxs -= pMainEnt->GetAbsOrigin(); } else { mins = VEC_HULL_MIN; maxs = VEC_HULL_MAX; } // Put some padding on their bbox. float flPadSize = 5; Vector vTestMins = mins - Vector( flPadSize, flPadSize, flPadSize ); Vector vTestMaxs = maxs + Vector( flPadSize, flPadSize, flPadSize ); // First test the starting origin. if ( UTIL_IsSpaceEmpty( pMainEnt, vOrigin + vTestMins, vOrigin + vTestMaxs, mask, pFilter ) ) { if ( bDropToGround ) { outPos = DropToGround( pMainEnt, vOrigin, vTestMins, vTestMaxs ); } else { outPos = vOrigin; } return true; } Vector vDims = vTestMaxs - vTestMins; // Keep branching out until we get too far. int iCurIteration = 0; int nMaxIterations = 15; int offset = 0; do { for ( int iDim=0; iDim < 3; iDim++ ) { float flCurOffset = offset * vDims[iDim]; for ( int iSign=0; iSign < 2; iSign++ ) { Vector vBase = vOrigin; vBase[iDim] += (iSign*2-1) * flCurOffset; if ( UTIL_IsSpaceEmpty( pMainEnt, vBase + vTestMins, vBase + vTestMaxs, mask, pFilter ) ) { // Ensure that there is a clear line of sight from the spawnpoint entity to the actual spawn point. // (Useful for keeping things from spawning behind walls near a spawn point) trace_t tr; UTIL_TraceLine( vOrigin, vBase, mask, pFilter, &tr ); if ( tr.fraction != 1.0 ) { continue; } if ( bDropToGround ) outPos = DropToGround( pMainEnt, vBase, vTestMins, vTestMaxs ); else outPos = vBase; return true; } } } ++offset; } while ( iCurIteration++ < nMaxIterations ); // Warning( "EntityPlacementTest for ent %d:%s failed!\n", pMainEnt->entindex(), pMainEnt->GetClassname() ); return false; } // Returns the number of human spectators in the game int UTIL_SpectatorsInGame( void ) { int iCount = 0; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *entity = CCSPlayer::Instance( i ); if ( entity && !FNullEnt( entity->edict() ) ) { if ( FStrEq( entity->GetPlayerName(), "" ) ) continue; if ( FBitSet( entity->GetFlags(), FL_FAKECLIENT ) ) continue; if ( entity->IsBot() ) continue; if ( entity->GetTeamNumber() == TEAM_SPECTATOR ) { iCount++; } } } return iCount; } int UTIL_HumansInGame( bool ignoreSpectators, bool ignoreUnassigned ) { int iCount = 0; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *entity = CCSPlayer::Instance( i ); if ( entity && !FNullEnt( entity->edict() ) ) { if ( FStrEq( entity->GetPlayerName(), "" ) ) continue; if ( FBitSet( entity->GetFlags(), FL_FAKECLIENT ) ) continue; if ( ignoreSpectators && entity->GetTeamNumber() != TEAM_TERRORIST && entity->GetTeamNumber() != TEAM_CT ) continue; if ( ignoreSpectators && entity->State_Get() == STATE_PICKINGCLASS ) continue; if ( ignoreUnassigned && entity->GetTeamNumber() == TEAM_UNASSIGNED ) continue; iCount++; } } return iCount; } #if defined ( GAME_DLL ) bool CCSGameRules::CheckGotGuardianModeSpecialKill( CWeaponCSBase* pAttackerWeapon ) { if ( IsPlayingCoopGuardian() == false ) return false; if ( IsWarmupPeriod() ) return false; if ( m_nGuardianModeSpecialWeaponNeeded != 0 ) { if ( !pAttackerWeapon ) return false; const CEconItemView *pEconItemViewWeapon = pAttackerWeapon->GetEconItemView(); if ( !pEconItemViewWeapon || !pEconItemViewWeapon->GetItemDefinition() ) return false; if ( m_nGuardianModeSpecialWeaponNeeded != pEconItemViewWeapon->GetItemDefinition()->GetDefinitionIndex() ) return false; } // reduce # of kills needed m_nGuardianModeSpecialKillsRemaining = MAX( m_nGuardianModeSpecialKillsRemaining - 1, 0 ); // REI: Should we send a game event message here? Right now we rely on network synchronization of // m_nGuardianModeSpecialKillsRemaining and notice when it changes on the client to update // their UI. if ( m_nGuardianModeSpecialKillsRemaining <= 0 ) GuardianAllKillsAchievedCheck(); return true; } #endif #if CS_CONTROLLABLE_BOTS_ENABLED // DK TODO: Make a similar method run AFTER all loops of this to look for orphaned bots that think they are still player controlled class RevertBotsFunctor { public: bool operator()( CBasePlayer *basePlayer ) { CCSPlayer *pPlayer = ToCSPlayer( basePlayer ); if ( !pPlayer ) return true; if ( !pPlayer->IsControllingBot() ) return true; // this will properly handle restoring money, frag counts, etc pPlayer->ReleaseControlOfBot(); return true; } }; #endif CCSMatch::CCSMatch() { Reset(); } void CCSMatch::Reset( void ) { m_actualRoundsPlayed = 0; CSGameRules()->SetTotalRoundsPlayed( 0 ); m_nOvertimePlaying = 0; CSGameRules()->SetOvertimePlaying( 0 ); m_ctScoreFirstHalf = 0; m_ctScoreSecondHalf = 0; m_ctScoreOvertime = 0; m_ctScoreTotal = 0; m_terroristScoreFirstHalf = 0; m_terroristScoreSecondHalf = 0; m_terroristScoreOvertime = 0; m_terroristScoreTotal = 0; if ( CSGameRules()->HasHalfTime() ) { SetPhase( GAMEPHASE_PLAYING_FIRST_HALF ); } else { SetPhase( GAMEPHASE_PLAYING_STANDARD ); } UpdateTeamScores(); } void CCSMatch::SetPhase( GamePhase phase ) { CCSGameRules *pRules = CSGameRules(); if ( ( m_phase == GAMEPHASE_HALFTIME ) && mp_halftime_pausematch.GetInt() && pRules ) { // when halftime is over, we pause the match if needed if ( !pRules->IsMatchWaitingForResume() ) { UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" ); } pRules->SetMatchWaitingForResume( true ); } m_phase = phase; // When going to overtime halftime pause the timer if requested if ( ( m_phase == GAMEPHASE_HALFTIME ) && m_nOvertimePlaying && mp_overtime_halftime_pausetimer.GetInt() ) mp_halftime_pausetimer.SetValue( mp_overtime_halftime_pausetimer.GetInt() ); EnableFullAlltalk( CSGameRules()->IsWarmupPeriod() || m_phase == GAMEPHASE_HALFTIME || m_phase == GAMEPHASE_MATCH_ENDED ); CSGameRules()->SetGamePhase( phase ); } void CCSMatch::AddTerroristWins( int numWins ) { m_actualRoundsPlayed += numWins; CSGameRules()->SetTotalRoundsPlayed( m_actualRoundsPlayed ); AddTerroristScore( numWins ); } void CCSMatch::AddCTWins( int numWins ) { m_actualRoundsPlayed += numWins; CSGameRules()->SetTotalRoundsPlayed( m_actualRoundsPlayed ); AddCTScore( numWins ); } void CCSMatch::IncrementRound( int nNumRounds ) { m_actualRoundsPlayed += nNumRounds; CSGameRules()->SetTotalRoundsPlayed( m_actualRoundsPlayed ); } void CCSMatch::AddTerroristBonusPoints( int points ) { AddTerroristScore( points ); } void CCSMatch::AddCTBonusPoints( int points) { AddCTScore( points ); } void CCSMatch::AddTerroristScore( int score ) { m_terroristScoreTotal += score; if ( m_nOvertimePlaying > 0 ) { m_terroristScoreOvertime += score; } else if ( m_phase == GAMEPHASE_PLAYING_FIRST_HALF ) { m_terroristScoreFirstHalf += score; } else if ( m_phase == GAMEPHASE_PLAYING_SECOND_HALF ) { m_terroristScoreSecondHalf += score; } UpdateTeamScores(); } void CCSMatch::AddCTScore( int score ) { m_ctScoreTotal += score; if ( m_nOvertimePlaying > 0 ) { m_ctScoreOvertime += score; } else if ( m_phase == GAMEPHASE_PLAYING_FIRST_HALF ) { m_ctScoreFirstHalf += score; } else if ( m_phase == GAMEPHASE_PLAYING_SECOND_HALF ) { m_ctScoreSecondHalf += score; } UpdateTeamScores(); } void CCSMatch::GoToOvertime( int numOvertimesToAdd ) { m_nOvertimePlaying += numOvertimesToAdd; CSGameRules()->SetOvertimePlaying( m_nOvertimePlaying ); } void CCSMatch::SwapTeamScores( void ) { short temp = m_terroristScoreFirstHalf; m_terroristScoreFirstHalf = m_ctScoreFirstHalf; m_ctScoreFirstHalf = temp; temp = m_terroristScoreSecondHalf; m_terroristScoreSecondHalf = m_ctScoreSecondHalf; m_ctScoreSecondHalf = temp; temp = m_terroristScoreOvertime; m_terroristScoreOvertime = m_ctScoreOvertime; m_ctScoreOvertime = temp; temp = m_terroristScoreTotal; m_terroristScoreTotal = m_ctScoreTotal; m_ctScoreTotal = temp; UpdateTeamScores(); } void CCSMatch::UpdateTeamScores( void ) { CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST ); CTeam *pCTs = GetGlobalTeam( TEAM_CT ); if ( pTerrorists ) { pTerrorists->SetScore( m_terroristScoreTotal ); pTerrorists->SetScoreFirstHalf( m_terroristScoreFirstHalf ); pTerrorists->SetScoreSecondHalf( m_terroristScoreSecondHalf ); pTerrorists->SetScoreOvertime( m_terroristScoreOvertime ); } if ( pCTs ) { pCTs->SetScore( m_ctScoreTotal); pCTs->SetScoreFirstHalf( m_ctScoreFirstHalf ); pCTs->SetScoreSecondHalf( m_ctScoreSecondHalf ); pCTs->SetScoreOvertime( m_ctScoreOvertime ); } } void CCSMatch::EnableFullAlltalk( bool bEnable ) { if ( !sv_auto_full_alltalk_during_warmup_half_end.GetBool() ) bEnable = false; static ConVarRef sv_full_alltalk( "sv_full_alltalk" ); sv_full_alltalk.SetValue( bEnable ); } int CCSMatch::GetWinningTeam( void ) { CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST ); CTeam *pCTs = GetGlobalTeam( TEAM_CT ); if ( pTerrorists && pTerrorists->m_bSurrendered ) { return TEAM_CT; } else if ( pCTs && pCTs->m_bSurrendered ) { return TEAM_TERRORIST; } else if ( m_terroristScoreTotal > m_ctScoreTotal ) { return TEAM_TERRORIST; } else if ( m_terroristScoreTotal < m_ctScoreTotal ) { return TEAM_CT; } else { return WINNER_NONE; } } template < class T > void VectorShuffle( CUtlVector< T > &arrayToShuffle ) { int numEntries = arrayToShuffle.Count(); // Shuffle entries for ( int i = 0; i < numEntries - 1; ++i ) { int randVal = RandomInt( i, numEntries - 1 ); if ( randVal != i ) { // Swap values V_swap( arrayToShuffle[ i ], arrayToShuffle[ randVal ] ); } } } // --------------------------------------------------------------------------------------------------- // // CCSGameRules implementation. // --------------------------------------------------------------------------------------------------- // CCSGameRules::GcBanInformationMap_t CCSGameRules::sm_mapGcBanInformation; CCSGameRules::CCSGameRules() { m_flLastThinkTime = gpGlobals->curtime; m_iRoundTime = 0; m_gamePhase = GAMEPHASE_PLAYING_STANDARD; m_iRoundWinStatus = WINNER_NONE; m_eRoundWinReason = RoundEndReason_StillInProgress; m_iFreezeTime = 0; m_totalRoundsPlayed = 0; m_nOvertimePlaying = 0; m_fMatchStartTime = gpGlobals->curtime; m_fRoundStartTime = 0; m_bAllowWeaponSwitch = true; m_bFreezePeriod = true; m_bMatchWaitingForResume = false; m_nTerroristTimeOuts = mp_team_timeout_max.GetInt(); m_nCTTimeOuts = mp_team_timeout_max.GetInt(); m_flTerroristTimeOutRemaining = mp_team_timeout_time.GetInt(); m_flCTTimeOutRemaining = mp_team_timeout_time.GetInt(); m_bTerroristTimeOutActive = false; m_bCTTimeOutActive = false; m_iNumTerrorist = m_iNumCT = 0; // number of players per team m_flRestartRoundTime = cInitialRestartRoundTime; // restart first round as soon as possible m_timeUntilNextPhaseStarts = 0.0f; m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0; m_bFirstConnected = false; m_bCompleteReset = true; m_bPickNewTeamsOnReset = true; m_bScrambleTeamsOnRestart = false; m_bSwapTeamsOnRestart = false; m_iAccountTerrorist = m_iAccountCT = 0; m_iNumConsecutiveCTLoses = 0; m_iNumConsecutiveTerroristLoses = 0; m_bTargetBombed = false; m_bBombDefused = false; m_iTotalRoundsPlayed = -1; m_endMatchOnRoundReset = false; m_endMatchOnThink = false; m_iUnBalancedRounds = 0; m_flGameStartTime = 0; m_iHostagesRemaining = 0; m_bAnyHostageReached = false; m_bLevelInitialized = false; m_flCoopRespawnAndHealTime = -1; m_bLogoMap = false; m_tmNextPeriodicThink = 0; m_bPlayerItemsHaveBeenDisplayed = false; m_flDMBonusStartTime = 0; m_flDMBonusTimeLength = 0; m_unDMBonusWeaponLoadoutSlot = 0; m_bDMBonusActive = false; m_bIsDroppingItems = false; m_iActiveAssassinationTargetMissionID = 0; m_flGuardianBuyUntilTime = -1; m_bCTCantBuy = false; m_bTCantBuy = false; m_bForceTeamChangeSilent = false; m_bLoadingRoundBackupData = false; m_pfnCalculateEndOfRoundMVPHook = NULL; m_bMapHasBombTarget = false; m_bMapHasRescueZone = false; m_iSpawnPointCount_Terrorist = 0; m_iSpawnPointCount_CT = 0; m_bBuyTimeEnded = false; m_bHasMatchStarted = false; m_nLastFreezeEndBeep = -1; m_iMaxNumTerrorists = 0; m_iMaxNumCTs = 0; m_bMapHasBuyZone = false; m_nNextMapInMapgroup = -1; m_bVoiceWonMatchBragFired = false; m_iLoserBonus = 0; m_iHostagesRescued = 0; m_iHostagesTouched = 0; m_flNextHostageAnnouncement = 0.0f; m_MatchDevice = BotProfileInputDevice::KB_MOUSE; // map vote for "season" map groups m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_MatchInProgress; // clear the next level nextlevel.SetValue( "" ); // Set the bestof maps state m_numBestOfMaps = mp_teamscore_max.GetInt(); // Set global gifts state m_numGlobalGiftsGiven = 0; m_numGlobalGifters = 0; m_numGlobalGiftsPeriodSeconds = 0; for ( int j = 0; j < MAX_GIFT_GIVERS_FEATURED_COUNT; ++ j ) { m_arrFeaturedGiftersAccounts.Set( j, 0 ); m_arrFeaturedGiftersGifts.Set( j, 0 ); } CheckForGiftsLeaderboardUpdate(); for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; ++ j ) { m_arrTournamentActiveCasterAccounts.Set( j, 0 ); } // Configure QMM settings m_bIsQuestEligible = IsQuestEligible(); m_bIsQueuedMatchmaking = IsQueuedMatchmaking(); m_bIsValveDS = IsValveDS(); m_pQueuedMatchmakingReservationString = NULL; m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_MatchInProgress; m_bNeedToAskPlayersForContinueVote = false; m_pQueuedMatchmakingReportedRoundStats = NULL; m_numTotalTournamentDrops = 0; m_numSpectatorsCountMax = 0; m_numSpectatorsCountMaxTV = 0; m_numSpectatorsCountMaxLnk = 0; m_numQueuedMatchmakingAccounts = 0; m_szTournamentEventName.GetForModify()[0] = 0; m_szTournamentEventStage.GetForModify()[0] = 0; Q_strncpy( m_szTournamentPredictionsTxt.GetForModify(), mp_teamprediction_txt.GetString(), MAX_PATH ); Q_strncpy( m_szMatchStatTxt.GetForModify(), mp_teammatchstat_txt.GetString(), MAX_PATH ); m_nTournamentPredictionsPct = 0; m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None; m_flMatchInfoDecidedTime = gpGlobals->curtime; m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements(); for ( int j = 0; j < MAX_MATCH_STATS_ROUNDS; ++ j ) { m_iMatchStats_PlayersAlive_T.GetForModify(j) = 0x3F; m_iMatchStats_PlayersAlive_CT.GetForModify(j) = 0x3F; } // Halloween m_nHalloweenMaskListSeed = RandomInt( 0, 30 ); #if 0 // // Setting a bunch of overrides // sm_QueuedServerReservation.mutable_tournament_event()->set_event_name( "ESL One Katowice 2015 Vitaliy Test Championship" ); sm_QueuedServerReservation.mutable_tournament_event()->set_event_stage_name( "Group Stage | Decider Match" ); TournamentTeam *pTeam; pTeam = sm_QueuedServerReservation.add_tournament_teams(); pTeam->set_team_tag( "NiP" ); pTeam->set_team_flag( "SE" ); pTeam->set_team_name( "Ninjas in Pyjamas" ); // pTeam->set_team_clantag( "NiP.Trig" ); if ( TournamentPlayer *pPlayer = pTeam->add_players() ) { pPlayer->set_account_id( 102003 ); pPlayer->set_player_nick( "GeT_RiGhT" ); pPlayer->set_player_name( "Vitaliy Genkin" ); } pTeam = sm_QueuedServerReservation.add_tournament_teams(); pTeam->set_team_tag( "NAVI" ); pTeam->set_team_flag( "UA" ); pTeam->set_team_name( "Natus Vincere" ); // pTeam->set_team_clantag( "NA'VI" ); if ( TournamentPlayer *pPlayer = pTeam->add_players() ) { pPlayer->set_account_id( 102003 ); pPlayer->set_player_nick( "GeT_RiGhT" ); pPlayer->set_player_name( "Vitaliy Genkin" ); } sm_QueuedServerReservation.mutable_pre_match_data()->set_predictions_pct( 72 ); CPreMatchInfoData_TeamStats *pTS; #if 0 // Group A | Decider Match pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossVs{team=#CSGO_TeamID_24}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossVs{team=#CSGO_TeamID_31}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Group2{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Group2{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" ); #endif #if 0 // Quarterfinal | Match 1 of 3 pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Series2{name=#CSGO_MatchInfo_Stage_Quarterfinal}{idx=1}{count=3}" ); pTS->add_match_info_teams()->assign( "0" ); pTS->add_match_info_teams()->assign( "0" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos1{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos2{name=#CSGO_MatchInfo_Stage_GroupB}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" ); #endif #if 0 // Quarterfinal | Match 2 of 3 pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Series2{name=#CSGO_MatchInfo_Stage_Quarterfinal}{idx=2}{count=3}" ); pTS->add_match_info_teams()->assign( "1" ); pTS->add_match_info_teams()->assign( "0" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_JustPlayedMap{map=#SFUI_Map_de_cbble}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinScoreMap{map=#SFUI_Map_de_cbble}{high=16}{low=3}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_Loss{map=#SFUI_Map_de_cbble}{high=16}{low=3}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos1{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos2{name=#CSGO_MatchInfo_Stage_GroupB}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_Series2{name=#CSGO_MatchInfo_Stage_Quarterfinal}{idx=2}{count=3}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" ); #endif #if 1 // Quarterfinal | Match 3 of 3 pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_BracketDecider{name=#CSGO_MatchInfo_Stage_Quarterfinal}" ); pTS->add_match_info_teams()->assign( "1" ); pTS->add_match_info_teams()->assign( "1" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_JustPlayedMaps" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinScoreMap{map=#SFUI_Map_de_cbble}{high=16}{low=3}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinScoreMap{map=#SFUI_Map_de_mirage}{high=23}{low=21}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos1{name=#CSGO_MatchInfo_Stage_GroupA}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_QualPos2{name=#CSGO_MatchInfo_Stage_GroupB}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_PreviouslyIn{name=#CSGO_MatchInfo_Stage_Groups}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_37}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinVs{team=#CSGO_TeamID_26}" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_BracketDecider{name=#CSGO_MatchInfo_Stage_Quarterfinal}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" ); pTS = sm_QueuedServerReservation.mutable_pre_match_data()->add_stats(); pTS->set_match_info_txt( "#CSGO_MatchInfoTxt_BracketDecider{name=#CSGO_MatchInfo_Stage_Quarterfinal}" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_LossElim" ); pTS->add_match_info_teams()->assign( "#CSGO_MatchInfoTeam_WinAdvan" ); #endif #endif if ( m_bIsQueuedMatchmaking ) { static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" ); m_pQueuedMatchmakingReservationString = new char[ Q_strlen( sv_mmqueue_reservation.GetString() ) + 1 ]; Q_strcpy( m_pQueuedMatchmakingReservationString, sv_mmqueue_reservation.GetString() ); int iDraftIndex = 0; for ( char const *pszPrev = sv_mmqueue_reservation.GetString(), *pszNext = pszPrev; ( pszNext = strchr( pszPrev, '[' ) ) != NULL; ( pszPrev = pszNext + 1 ), ( ++ iDraftIndex ) ) { uint32 uiAccountId = 0; sscanf( pszNext, "[%x]", &uiAccountId ); if ( uiAccountId ) { ++ m_numQueuedMatchmakingAccounts; CQMMPlayerData_t *pqmmPlayerData = new CQMMPlayerData_t; pqmmPlayerData->m_uiPlayerAccountId = uiAccountId; pqmmPlayerData->m_iDraftIndex = iDraftIndex; if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( uiAccountId ) ) delete pQMM; m_mapQueuedMatchmakingPlayersData.InsertOrReplace( uiAccountId, pqmmPlayerData ); } } // Set next level to the current level for potential rematch nextlevel.SetValue( STRING( gpGlobals->mapname ) ); // Set the tournament settings if ( sm_QueuedServerReservation.has_tournament_event() && sm_QueuedServerReservation.tournament_event().has_event_name() ) Q_strncpy( m_szTournamentEventName.GetForModify(), sm_QueuedServerReservation.tournament_event().event_name().c_str(), MAX_PATH ); if ( sm_QueuedServerReservation.has_tournament_event() && sm_QueuedServerReservation.tournament_event().has_event_stage_name() ) Q_strncpy( m_szTournamentEventStage.GetForModify(), sm_QueuedServerReservation.tournament_event().event_stage_name().c_str(), MAX_PATH ); } // [tj] reset flawless and lossless round related flags m_bNoTerroristsKilled = true; m_bNoCTsKilled = true; m_bNoTerroristsDamaged = true; m_bNoCTsDamaged = true; m_bNoEnemiesKilled = true; m_pFirstKill = NULL; m_firstKillTime = 0; // [menglish] Reset fun fact values m_pFirstBlood = NULL; m_firstBloodTime = 0; m_pMVP = NULL; m_bCanDonateWeapons = true; // [dwenger] Reset rescue-related achievement values m_arrRescuers.RemoveAll(); m_hostageWasInjured = false; m_hostageWasKilled = false; m_pFunFactManager = new CCSFunFactMgr(); m_pFunFactManager->Init(); m_iHaveEscaped = 0; m_bMapHasEscapeZone = false; m_iNumEscapers = 0; m_iNumEscapeRounds = 0; m_bMapHasBombZone = false; m_bBombDropped = false; m_bBombPlanted = false; m_bHasHostageBeenTouched = false; m_bDontIncrementCoopWave = false; m_bGunGameRespawnWithBomb = false; m_fGunGameBombRespawnTimer = 0.0f; m_bRoundTimeWarningTriggered = false; m_iNumGunGameProgressiveWeaponsCT = 0; m_iNumGunGameProgressiveWeaponsT = 0; m_bAllowWeaponSwitch = true; m_iSpectatorSlotCount = 0; m_nGuardianModeWaveNumber = 1; m_nGuardianModeSpecialKillsRemaining = -1; m_nGuardianModeSpecialWeaponNeeded = -1; m_nGuardianGrenadesToGiveBots = 0; m_nNumHeaviesToSpawn = 0; m_flNextHostageAnnouncement = gpGlobals->curtime; // asap. m_phaseChangeAnnouncementTime = 0.0f; m_fNextUpdateTeamClanNamesTime = 0.0f; // Create the team managers for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ ) { CTeam *pTeam = static_cast(CreateEntityByName( "cs_team_manager" )); pTeam->Init( sTeamNames[i], i ); g_Teams.AddToTail( pTeam ); } m_bHasTriggeredRoundStartMusic = false; m_bHasTriggeredCoopSpawnReset = false; InitializeGameTypeAndMode(); #ifndef VALVE_DEDICATED_SERVER if ( const char* szMapNameBase = V_GetFileName( STRING(gpGlobals->mapname) ) ) { if ( (IsPlayingCustomGametype() ) && filesystem->FileExists( UTIL_VarArgs( "maps/cfg/%s.cfg", szMapNameBase ) ) ) { // Execute a map specific cfg file to define the rules engine->ServerCommand( UTIL_VarArgs( "execwithwhitelist %s.cfg */maps\n", szMapNameBase ) ); engine->ServerExecute(); } else if ( IsPlayingCoopMission() ) { // Execute a map specific cfg file to define the rules int nMissionNumber = 1; for ( KeyValues *kvLaunchOptions = engine->GetLaunchOptions()->GetFirstSubKey(); kvLaunchOptions; kvLaunchOptions = kvLaunchOptions->GetNextKey() ) { if ( char const *szValue = StringAfterPrefix( kvLaunchOptions->GetString(), "mission" ) ) { nMissionNumber = V_atoi( szValue ); DevMsg( "Coop mission number = %d (parsed from %s)\n", nMissionNumber, kvLaunchOptions->GetString() ); break; } } engine->ServerCommand( UTIL_VarArgs( "execwithwhitelist %s_%d.cfg */maps\n", szMapNameBase, nMissionNumber ) ); engine->ServerExecute(); } else if ( IsPlayingCoopGuardian() ) { char szCfgName[ MAX_PATH ]; if ( filesystem->FileExists( UTIL_VarArgs( "maps/cfg/guardian_%s.cfg", szMapNameBase ) ) ) V_snprintf( szCfgName, sizeof( szCfgName ), "guardian_%s", szMapNameBase ); else V_snprintf( szCfgName, sizeof( szCfgName ), "guardian_defaultmap" ); // Execute a map specific cfg file to define the rules engine->ServerCommand( UTIL_VarArgs( "execwithwhitelist %s.cfg */maps\n", szCfgName ) ); engine->ServerExecute(); } } #endif ReadMultiplayCvars(); m_bVoteCalled = false; m_bServerVoteOnReset = false; m_flVoteCheckThrottle = 0; m_bGameRestart = false; m_bSwitchingTeamsAtRoundReset = false; m_fAutobalanceDisplayTime = 0.0f; m_AutobalanceStatus = AutobalanceStatus::NONE; m_iNextCTSpawnPoint = 0; m_iNextTerroristSpawnPoint = 0; m_iMaxGunGameProgressiveWeaponIndex = 0; // m_flDeferredCallDispatchTime = 0.0f; m_bWarmupPeriod = mp_do_warmup_period.GetBool(); m_fWarmupNextChatNoticeTime = 0; m_fWarmupPeriodStart = gpGlobals->curtime; m_coopMissionManager = NULL; for ( int i = 0; i < MAX_TEAMS; i++ ) { m_flNextRespawnWave.Set( i, 0 ); m_TeamRespawnWaveTimes.Set( i, -1.0f ); } for ( int iVoteOption = 0; iVoteOption < MAX_ENDMATCH_VOTE_PANELS; ++ iVoteOption ) m_nEndMatchMapGroupVoteOptions.Set( iVoteOption, -1 ); m_coopBonusCoinsFound = 0; m_coopBonusPistolsOnly = true; m_coopPlayersInDeploymentZone = false; //m_pSun = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCSGameRules::~CCSGameRules() { // Note, don't delete each team since they are in the gEntList and will // automatically be deleted from there, instead. g_Teams.Purge(); delete m_pFunFactManager; m_pFunFactManager = NULL; delete m_pQueuedMatchmakingReportedRoundStats; m_pQueuedMatchmakingReportedRoundStats = NULL; delete m_pQueuedMatchmakingReservationString; m_pQueuedMatchmakingReservationString = NULL; m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements(); ClearItemsDroppedDuringMatch(); } void CCSGameRules::RefreshSkillData( bool /* forceUpdate */ ) { // NOTE[pmf]: The base class loads skill configuration files which we // do not want for CS:GO } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCSGameRules::UpdateClientData( CBasePlayer *player ) { } int CCSGameRules::GetMaxHumanPlayers() const { int iGameType = g_pGameTypes->GetCurrentGameType(); int iGameMode = g_pGameTypes->GetCurrentGameMode(); return g_pGameTypes->GetMaxPlayersForTypeAndMode( iGameType, iGameMode ); } void CCSGameRules::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues ) { CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pEntity ) ); if ( !pPlayer ) return; #if 0 // If a client sends up their medal rankings (based on achievements completed so far) // store them in CSPlayer for display on the scoreboard. if ( 0 == Q_strcmp( pKeyValues->GetName(), "player_medal_ranking" ) ) { pPlayer->UpdateRankFromKV( pKeyValues ); } else #endif if ( ( 0 == Q_strcmp( pKeyValues->GetName(), "ClanTagChanged" ) ) && // When we have tournament system enabled then players cannot change their clan tags from client CanClientCustomizeOwnIdentity() ) { pPlayer->SetClanTag( pKeyValues->GetString( "tag", "" ) ); const char *szClanName = pKeyValues->GetString( "name", "" ); pPlayer->SetClanName( szClanName ); //if ( !V_strcmp( team->Get_Name(), CSGameRules()->GetDefaultTeamName(TEAM_TERRORIST) ) ) UpdateTeamClanNames( TEAM_TERRORIST ); UpdateTeamClanNames( TEAM_CT ); UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"clantag\" (value \"%s\")\n", pPlayer->GetPlayerName(), pPlayer->GetUserID(), pPlayer->GetNetworkIDString(), pPlayer->GetTeam() ? pPlayer->GetTeam()->GetName() : "UNKNOWN", pKeyValues->GetString( "tag", "unknown" ) ); } else if ( 0 == Q_strcmp( pKeyValues->GetName(), "InvalidSteamLogon" ) ) { if ( IsWarmupPeriod() || IsFreezePeriod() || !pPlayer->IsAlive() ) pKeyValues->SetBool( "disconnect", true ); else pPlayer->m_bInvalidSteamLogonDelayed = true; } } static bool Helper_CheckFieldAppliesToTeam( char const *szField, int nTeam ) { if ( StringHasPrefix( szField, "#CSGO_MatchInfoTeam_WinAdvan" ) ) { // Make sure that this team is winning int nOtherTeam = ( ( nTeam == TEAM_CT ) ? TEAM_TERRORIST : ( ( nTeam == TEAM_TERRORIST ) ? TEAM_CT : nTeam ) ); return ( GetGlobalTeam( nTeam ) ? GetGlobalTeam( nTeam )->GetScore() : 0 ) > ( GetGlobalTeam( nOtherTeam ) ? GetGlobalTeam( nOtherTeam )->GetScore() : 0 ); } else if ( StringHasPrefix( szField, "#CSGO_MatchInfoTeam_LossElim" ) ) { // Make sure that this team is losing int nOtherTeam = ( ( nTeam == TEAM_CT ) ? TEAM_TERRORIST : ( ( nTeam == TEAM_TERRORIST ) ? TEAM_CT : nTeam ) ); return ( GetGlobalTeam( nTeam ) ? GetGlobalTeam( nTeam )->GetScore() : 0 ) < ( GetGlobalTeam( nOtherTeam ) ? GetGlobalTeam( nOtherTeam )->GetScore() : 0 ); } else if ( ( szField[0] >= '0' ) && ( szField[0] <= '9' ) && ( szField[1] == 0 ) && CSGameRules() && ( ( CSGameRules()->m_match.GetPhase() == GAMEPHASE_HALFTIME ) || ( CSGameRules()->m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) ) ) return false; else return true; } void CCSGameRules::UpdateTeamPredictions() { int nWantPrediction = 0; if ( ( sm_QueuedServerReservation.pre_match_data().predictions_pct() >= 1 ) && ( sm_QueuedServerReservation.pre_match_data().predictions_pct() <= 99 ) ) nWantPrediction = int( sm_QueuedServerReservation.pre_match_data().predictions_pct() ); // but convar can override if ( ( mp_teamprediction_pct.GetInt() >= 1 ) && ( mp_teamprediction_pct.GetInt() <= 99 ) ) nWantPrediction = mp_teamprediction_pct.GetInt(); // Prediction UI component cannot be displayed at certain times if ( ( m_match.GetPhase() == GAMEPHASE_HALFTIME ) || ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) ) nWantPrediction = 0; if ( Q_strncmp( m_szTournamentPredictionsTxt, mp_teamprediction_txt.GetString(), MAX_PATH - 1 ) ) Q_strncpy( m_szTournamentPredictionsTxt.GetForModify(), mp_teamprediction_txt.GetString(), MAX_PATH ); char const *szWantMatchStatTxt = mp_teammatchstat_txt.GetString(); // can override from reservation later if ( sm_QueuedServerReservation.pre_match_data().stats().size() ) szWantMatchStatTxt = sm_QueuedServerReservation.pre_match_data().stats( 0 ).match_info_txt().c_str(); // to ensure that it is eligible for a pick // // Here we must determine which statistics we are going to be showing // if ( IsWarmupPeriod() ) { // we are going to show the draft here if ( sm_QueuedServerReservation.pre_match_data().stats().size() ) m_nMatchInfoShowType = 0; else if ( *szWantMatchStatTxt ) m_nMatchInfoShowType = 0; else if ( nWantPrediction ) m_nMatchInfoShowType = k_MapMatchInfoShownCounts_Predictions; else m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None; m_flMatchInfoDecidedTime = gpGlobals->curtime; } else if ( IsFreezePeriod() || ( m_iRoundWinStatus != WINNER_NONE ) ) { if ( ( m_nMatchInfoShowType == k_MapMatchInfoShownCounts_None ) || ( gpGlobals->curtime - m_flMatchInfoDecidedTime > mp_teammatchstat_cycletime.GetFloat() ) ) { m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None; CUtlVectorFixedGrowable< int32, 10 > arrOptions; bool bTeamsAreSwitched = AreTeamsPlayingSwitchedSides(); if ( nWantPrediction ) arrOptions.AddToTail( k_MapMatchInfoShownCounts_Predictions ); for ( int j = 0; j < sm_QueuedServerReservation.pre_match_data().stats().size(); ++ j ) { if ( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams().size() < 2 ) continue; if ( ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) && ( m_nMatchInfoShowType == k_MapMatchInfoShownCounts_None ) ) { if ( ( ( StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), "#CSGO_MatchInfoTeam_WinAdvan" ) || StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), "#CSGO_MatchInfoTeam_LossElim" ) ) && Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), bTeamsAreSwitched ? TEAM_TERRORIST : TEAM_CT ) ) || ( ( StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), "#CSGO_MatchInfoTeam_WinAdvan" ) || StringHasPrefix( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), "#CSGO_MatchInfoTeam_LossElim" ) ) && Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), bTeamsAreSwitched ? TEAM_CT : TEAM_TERRORIST ) ) ) { // always conclude the match with advances/eliminated notification if such is applicable m_nMatchInfoShowType = j; } } if ( Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(0).c_str(), bTeamsAreSwitched ? TEAM_TERRORIST : TEAM_CT ) || Helper_CheckFieldAppliesToTeam( sm_QueuedServerReservation.pre_match_data().stats( j ).match_info_teams(1).c_str(), bTeamsAreSwitched ? TEAM_CT : TEAM_TERRORIST ) ) { arrOptions.AddToTail( j ); } } if ( ( !arrOptions.Count() || !sm_QueuedServerReservation.pre_match_data().stats().size() ) && *szWantMatchStatTxt ) arrOptions.AddToTail( 0 ); if ( arrOptions.Count() && ( m_nMatchInfoShowType == k_MapMatchInfoShownCounts_None ) ) { // Pick the option that was shown the least number of times uint32 nLeastNumberOfTimesShown = ~uint32( 0 ); FOR_EACH_VEC( arrOptions, iOption ) { MapMatchInfoShownCounts::IndexType_t lookupIdx = m_mapMatchInfoShownCounts.Find( arrOptions[iOption] ); uint32 nThisNumberOfTimesShown = 0; if ( lookupIdx != m_mapMatchInfoShownCounts.InvalidIndex() ) nThisNumberOfTimesShown = m_mapMatchInfoShownCounts.Element( lookupIdx ); if ( nThisNumberOfTimesShown < nLeastNumberOfTimesShown ) { nLeastNumberOfTimesShown = nThisNumberOfTimesShown; m_nMatchInfoShowType = arrOptions[iOption]; } } } if ( m_nMatchInfoShowType != k_MapMatchInfoShownCounts_None ) { // Even if some stat was never picked prevent it from showing more than twice in a row uint32 numShown = 1; MapMatchInfoShownCounts::IndexType_t lookupIdx = m_mapMatchInfoShownCounts.Find( m_nMatchInfoShowType ); if ( lookupIdx != m_mapMatchInfoShownCounts.InvalidIndex() ) numShown = m_mapMatchInfoShownCounts.Element( lookupIdx ) + 1; FOR_EACH_MAP_FAST( m_mapMatchInfoShownCounts, iSCFast ) { uint32 numOtherShown = m_mapMatchInfoShownCounts.Element( iSCFast ); if ( numOtherShown > numShown + 1 ) numShown = numOtherShown - 1; } // Track the option that was picked m_mapMatchInfoShownCounts.InsertOrReplace( m_nMatchInfoShowType, numShown ); } m_flMatchInfoDecidedTime = gpGlobals->curtime; } } else if ( m_nMatchInfoShowType != k_MapMatchInfoShownCounts_None ) { if ( m_flMatchInfoDecidedTime <= gpGlobals->curtime ) { // hold it a little longer to cover brief intermittent gamestate blips m_flMatchInfoDecidedTime = gpGlobals->curtime + mp_teammatchstat_holdtime.GetFloat(); } else if ( m_flMatchInfoDecidedTime <= gpGlobals->curtime + 1.0f ) { // reset when < 1 sec remaining on hold timer m_nMatchInfoShowType = k_MapMatchInfoShownCounts_None; } } // // Kill the desire for things that weren't picked // if ( m_nMatchInfoShowType != k_MapMatchInfoShownCounts_Predictions ) nWantPrediction = 0; if ( m_nMatchInfoShowType >= k_MapMatchInfoShownCounts_None ) szWantMatchStatTxt = ""; // // Set desired values // if ( nWantPrediction != m_nTournamentPredictionsPct ) m_nTournamentPredictionsPct = nWantPrediction; if ( m_nMatchInfoShowType < k_MapMatchInfoShownCounts_None ) { if ( sm_QueuedServerReservation.pre_match_data().stats().size() > m_nMatchInfoShowType ) { int idxMatchTxt = m_nMatchInfoShowType; if ( sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).has_match_info_idxtxt() ) idxMatchTxt = sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_idxtxt(); szWantMatchStatTxt = sm_QueuedServerReservation.pre_match_data().stats( idxMatchTxt ).match_info_txt().c_str(); } } if ( Q_strncmp( m_szMatchStatTxt, szWantMatchStatTxt, MAX_PATH - 1 ) ) Q_strncpy( m_szMatchStatTxt.GetForModify(), szWantMatchStatTxt, MAX_PATH ); } void CCSGameRules::UpdateTeamClanNames( int nTeam ) { Assert( ( nTeam == TEAM_CT ) || ( nTeam == TEAM_TERRORIST ) ); CTeam *pTeam = GetGlobalTeam( nTeam ); //pTeam->SetName( GetDefaultTeamName(nTeam) ); bool bTeamsAreSwitched = AreTeamsPlayingSwitchedSides(); const char *(pTeamNames[ 2 ]) = { mp_teamname_2.GetString(), mp_teamname_1.GetString() }; // If we have a competitive reservation then override team names from it if ( ( sm_QueuedServerReservation.tournament_teams().size() > 0 ) && sm_QueuedServerReservation.tournament_teams(0).has_team_name() && * sm_QueuedServerReservation.tournament_teams(0).team_name().c_str() ) pTeamNames[1] = sm_QueuedServerReservation.tournament_teams(0).team_name().c_str(); if ( ( sm_QueuedServerReservation.tournament_teams().size() > 1 ) && sm_QueuedServerReservation.tournament_teams(1).has_team_name() && * sm_QueuedServerReservation.tournament_teams(1).team_name().c_str() ) pTeamNames[0] = sm_QueuedServerReservation.tournament_teams(1).team_name().c_str(); int nTeamIndex = ( nTeam - TEAM_TERRORIST ); // nTeamIndex == 0 if Terrorist, 1 if CT const char *pClanName = ""; uint32 uiClanID = 0; // Set the team names to the convars depending on what half phase it is. if ( !bTeamsAreSwitched ) pClanName = pTeamNames[ nTeamIndex ]; else pClanName = pTeamNames[ 1 - nTeamIndex ]; // The teamname convar was empty so differ to the team's clan name, if it exists. if ( StringIsEmpty( pClanName ) && IsClanTeam( pTeam ) ) { for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); if ( pPlayer && !pPlayer->IsBot() ) { pClanName = pPlayer->GetClanName(); const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" ); uiClanID = Q_atoi( pClanID ); break; } } } pTeam->SetClanName( pClanName ); pTeam->SetClanID( uiClanID ); // // Team flags processing // const char *pFlag = ""; const char *(pTeamFlags[ 2 ]) = { mp_teamflag_2.GetString(), mp_teamflag_1.GetString() }; const char *pLogo = ""; const char *(pTeamLogos[ 2 ]) = { mp_teamlogo_2.GetString(), mp_teamlogo_1.GetString() }; int numMapsWon = 0; int arrMapsWon[ 2 ] = { mp_teamscore_2.GetInt(), mp_teamscore_1.GetInt() }; // If we have a competitive reservation then override team flags from it if ( ( sm_QueuedServerReservation.tournament_teams().size() > 0 ) && sm_QueuedServerReservation.tournament_teams(0).has_team_flag() && * sm_QueuedServerReservation.tournament_teams(0).team_flag().c_str() ) pTeamFlags[1] = sm_QueuedServerReservation.tournament_teams(0).team_flag().c_str(); if ( ( sm_QueuedServerReservation.tournament_teams().size() > 1 ) && sm_QueuedServerReservation.tournament_teams(1).has_team_flag() && * sm_QueuedServerReservation.tournament_teams(1).team_flag().c_str() ) pTeamFlags[0] = sm_QueuedServerReservation.tournament_teams(1).team_flag().c_str(); // get the logos if ( ( sm_QueuedServerReservation.tournament_teams().size() > 0 ) && sm_QueuedServerReservation.tournament_teams( 0 ).has_team_tag() && * sm_QueuedServerReservation.tournament_teams( 0 ).team_tag().c_str() ) pTeamLogos[1] = sm_QueuedServerReservation.tournament_teams( 0 ).team_tag().c_str() ; if ( ( sm_QueuedServerReservation.tournament_teams().size() > 01 ) && sm_QueuedServerReservation.tournament_teams( 1 ).has_team_tag() && * sm_QueuedServerReservation.tournament_teams( 1 ).team_tag().c_str() ) pTeamLogos[0] = sm_QueuedServerReservation.tournament_teams( 1 ).team_tag().c_str(); // Set the team names to the convars depending on what half phase it is. if ( !bTeamsAreSwitched ) { pFlag = pTeamFlags[ nTeamIndex ]; pLogo = pTeamLogos[ nTeamIndex ]; numMapsWon = arrMapsWon[ nTeamIndex ]; } else { pFlag = pTeamFlags[ 1 - nTeamIndex ]; pLogo = pTeamLogos[ 1 - nTeamIndex ]; numMapsWon = arrMapsWon[ 1 - nTeamIndex ]; } pTeam->SetFlagImageString( pFlag ); pTeam->SetLogoImageString( pLogo ); pTeam->SetNumMapVictories( numMapsWon ); // // Team match stat processing // const char *pMatchStat = ""; const char *(pTeamMatchStats[ 2 ]) = { mp_teammatchstat_2.GetString(), mp_teammatchstat_1.GetString() }; // If we have a competitive reservation then override team match stats from it bool bUsingReservation = false; if ( m_nMatchInfoShowType < k_MapMatchInfoShownCounts_None ) { if ( ( sm_QueuedServerReservation.pre_match_data().stats().size() > m_nMatchInfoShowType ) && ( sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_teams().size() >= 2 ) ) { bUsingReservation = true; pTeamMatchStats[1] = sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_teams(0).c_str(); pTeamMatchStats[0] = sm_QueuedServerReservation.pre_match_data().stats( m_nMatchInfoShowType ).match_info_teams(1).c_str(); } } else { pTeamMatchStats[0] = pTeamMatchStats[1] = ""; } // Set the team match stats to the convars depending on what half phase it is. if ( !bTeamsAreSwitched ) pMatchStat = pTeamMatchStats[ nTeamIndex ]; else pMatchStat = pTeamMatchStats[ 1 - nTeamIndex ]; if ( bUsingReservation && !Helper_CheckFieldAppliesToTeam( pMatchStat, nTeam ) ) pMatchStat = ""; Q_strncpy( pTeam->m_szTeamMatchStat.GetForModify(), pMatchStat, MAX_PATH ); } // registered VSCRIPT functions void CCSGameRules::SetPlayerCompletedTraining( bool bCompleted ) { #ifndef CLIENT_DLL int nComplete = 0; if ( bCompleted ) nComplete = 1; tr_completed_training.SetValue( nComplete ); #endif } bool CCSGameRules::GetPlayerCompletedTraining( void ) { #ifndef CLIENT_DLL return tr_completed_training.GetBool(); #else return 0; #endif } void CCSGameRules::SetBestTrainingCourseTime( int nTime ) { #ifndef CLIENT_DLL tr_best_course_time.SetValue( nTime ); #endif } int CCSGameRules::GetBestTrainingCourseTime( void ) { #ifndef CLIENT_DLL return tr_best_course_time.GetInt(); #else return 0; #endif } int CCSGameRules::GetValveTrainingCourseTime( void ) { #ifndef CLIENT_DLL return tr_valve_course_time.GetInt(); #else return 0; #endif } bool CCSGameRules::IsLocalPlayerUsingController( void ) { CCSPlayer *pPlayer = static_cast( UTIL_PlayerByIndex(1) ); if ( pPlayer ) { if ( pPlayer->GetPlayerInputDevice() == INPUT_DEVICE_GAMEPAD ) { return true; } } return false; } void CCSGameRules::TrainingGivePlayerAmmo( void ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); if( pWeapon && pWeapon->UsesPrimaryAmmo() ) { pWeapon->SetReserveAmmoCount( AMMO_POSITION_PRIMARY, pWeapon->GetReserveAmmoMax( AMMO_POSITION_PRIMARY ) ); } } } void CCSGameRules::TrainingSetRadarHidden( bool bHide ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { CCSPlayer *pCSPlayer = static_cast( pPlayer ); if ( pCSPlayer ) pCSPlayer->SetRadarHidden( bHide ); } } void CCSGameRules::TrainingSetMiniScoreHidden( bool bHide ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { CCSPlayer *pCSPlayer = static_cast( pPlayer ); if ( pCSPlayer ) pCSPlayer->SetMiniScoreHidden( bHide ); } } // not used yet because this relied on vgui panels, scaleform isn't supported yet void CCSGameRules::TrainingHighlightAmmoCounter( void ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { #ifndef CLIENT_DLL IGameEvent * event = gameeventmanager->CreateEvent( "tr_highlight_ammo", true ); if ( event ) { event->SetInt( "userid", pPlayer->GetUserID() ); gameeventmanager->FireEvent( event ); } #endif } } void CCSGameRules::TrainingShowFinishMsgBox( void ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { IGameEvent * event = gameeventmanager->CreateEvent( "tr_show_finish_msgbox", true ); if ( event ) { gameeventmanager->FireEvent( event ); } } } void CCSGameRules::TrainingShowExitDoorMsg( void ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); if ( pPlayer ) { IGameEvent * event = gameeventmanager->CreateEvent( "tr_show_exit_msgbox", true ); if ( event ) { gameeventmanager->FireEvent( event ); } } } void ScriptCoopResetRoundStartTime( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; pRules->CoopResetRoundStartTime(); } void ScriptCoopGiveC4sToCTs( int nNumC4ToGive ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; pRules->CoopGiveC4sToCTs( nNumC4ToGive ); } void ScriptSetPlayerCompletedTraining( bool bCompleted ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; pRules->SetPlayerCompletedTraining( bCompleted ); } bool ScriptGetPlayerCompletedTraining( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return false; return pRules->GetPlayerCompletedTraining(); } void ScriptSetBestTrainingCourseTime( int nTime ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; pRules->SetBestTrainingCourseTime( nTime ); } int ScriptGetBestTrainingCourseTime( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return false; return pRules->GetBestTrainingCourseTime(); } int ScriptGetValveTrainingCourseTime( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return false; return pRules->GetValveTrainingCourseTime(); } void ScriptTrainingGivePlayerAmmo( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules || !pRules->IsPlayingTraining() ) return; pRules->TrainingGivePlayerAmmo(); } void ScriptSetMiniScoreHidden( bool nHide ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules || !pRules->IsPlayingTraining() ) return; pRules->TrainingSetMiniScoreHidden( nHide ); } void ScriptSetRadarHidden( bool nHide ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules || !pRules->IsPlayingTraining() ) return; pRules->TrainingSetRadarHidden( nHide ); } void ScriptHighlightAmmoCounter( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules || !pRules->IsPlayingTraining() ) return; pRules->TrainingHighlightAmmoCounter(); } void ScriptShowFinishMsgBox( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules || !pRules->IsPlayingTraining() ) return; pRules->TrainingShowFinishMsgBox(); } void ScriptShowExitDoorMsg( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules || !pRules->IsPlayingTraining() ) return; pRules->TrainingShowExitDoorMsg(); } bool ScriptIsLocalPlayerUsingController( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return false; return pRules->IsLocalPlayerUsingController(); } void ScriptPrintMessageCenterAll( const char *pszMessage ) { UTIL_ClientPrintAll( HUD_PRINTCENTER, pszMessage ); } void ScriptPrintMessageChatAll( const char *pszMessage ) { UTIL_ClientPrintAll( HUD_PRINTTALK, pszMessage ); } void ScriptPrintMessageCenterTeam( int nTeamNumber, const char *pszMessage ) { CRecipientFilter filter; filter.MakeReliable(); filter.AddRecipientsByTeam( GetGlobalTeam(nTeamNumber) ); UTIL_ClientPrintFilter( filter, HUD_PRINTCENTER, pszMessage ); } void ScriptPrintMessageChatTeam( int nTeamNumber, const char *pszMessage ) { CRecipientFilter filter; filter.MakeReliable(); filter.AddRecipientsByTeam( GetGlobalTeam(nTeamNumber) ); UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, pszMessage ); } int ScriptGetGameMode( void ) { return g_pGameTypes->GetCurrentGameMode(); } int ScriptGetGameType( void ) { return g_pGameTypes->GetCurrentGameType(); } int ScriptGetRoundsPlayed( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return false; return pRules->GetRoundsPlayed(); } bool ScriptIsWarmupPeriod( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return false; return pRules->IsWarmupPeriod(); } void ScriptCoopSetBotQuotaAndRefreshSpawns( int nMaxEnemiesToSpawn ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; return pRules->CoopSetBotQuotaAndRefreshSpawns( nMaxEnemiesToSpawn ); } void ScriptCoopMissionSpawnFirstEnemies( int nMaxEnemiesToSpawn ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; return pRules->CoopMissionSpawnFirstEnemies( nMaxEnemiesToSpawn ); } void ScriptCoopMissionSetNextRespawnIn( float flSeconds, bool bIncrementWaveNumber ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; return pRules->CoopMissionSetNextRespawnIn( flSeconds, bIncrementWaveNumber ); } void ScriptCoopMissionSpawnNextWave( int nMaxEnemiesToSpawn ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; return pRules->CoopMissionSpawnNextWave( nMaxEnemiesToSpawn ); } void ScriptCoopMissionRespawnDeadPlayers( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; return pRules->CoopMissionRespawnDeadPlayers(); } int ScriptCoopMissionGetMissionNumber( void ) { return mp_coopmission_mission_number.GetInt(); } void ScriptCoopCollectBonusCoin( void ) { CCSGameRules *pRules = CSGameRules(); if ( !pRules ) return; return pRules->CoopCollectBonusCoin(); } void CCSGameRules::RegisterScriptFunctions( void ) { ScriptRegisterFunction( g_pScriptVM, ScriptSetPlayerCompletedTraining, "Sets whether the player has completed the initial portion of the training map." ); ScriptRegisterFunction( g_pScriptVM, ScriptGetPlayerCompletedTraining, "Returns true if the player has completed the initial portion of the training map." ); ScriptRegisterFunction( g_pScriptVM, ScriptSetBestTrainingCourseTime, "Sets the player's best time for completing the timed course." ); ScriptRegisterFunction( g_pScriptVM, ScriptGetBestTrainingCourseTime, "Gets the player's best time for completing the timed course." ); ScriptRegisterFunction( g_pScriptVM, ScriptGetValveTrainingCourseTime, "Gets Valve's best time for completing the timed course." ); ScriptRegisterFunction( g_pScriptVM, ScriptTrainingGivePlayerAmmo, "Refills ammo to max for all weapons the player has (only works in training)." ); ScriptRegisterFunction( g_pScriptVM, ScriptSetMiniScoreHidden, "Toggles the visibility of the miniscoreboard hud element." ); ScriptRegisterFunction( g_pScriptVM, ScriptSetRadarHidden, "Toggles the visibility of the radar hud element." ); ScriptRegisterFunction( g_pScriptVM, ScriptHighlightAmmoCounter, "Sends an event that is just used by the instructor system to show a hint highlighting the ammo counter." ); ScriptRegisterFunction( g_pScriptVM, ScriptShowFinishMsgBox, "Shows a message box to let players know what to do next after finishing the training course." ); ScriptRegisterFunction( g_pScriptVM, ScriptShowExitDoorMsg, "Shows a message box in trainign when the player exits through the exit door" ); ScriptRegisterFunction( g_pScriptVM, ScriptIsLocalPlayerUsingController, "Returns whether the player is playing with a controller or not." ); ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageCenterAll, "Prints an alert message in the center print method to all players." ); ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageChatAll, "Prints a message in chat to all players." ); ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageCenterTeam, "Prints an alert message in the center print method to the specified team." ); ScriptRegisterFunction( g_pScriptVM, ScriptPrintMessageChatTeam, "Prints a message in chat to the specified team." ); ScriptRegisterFunction( g_pScriptVM, ScriptGetGameMode, "Gets the current game mode." ); ScriptRegisterFunction( g_pScriptVM, ScriptGetGameType, "Gets the current game type." ); ScriptRegisterFunction( g_pScriptVM, ScriptGetRoundsPlayed, "Get the number of rounds played so far." ); ScriptRegisterFunction( g_pScriptVM, ScriptIsWarmupPeriod, "Is it warmup or not." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopSetBotQuotaAndRefreshSpawns, "Sets the bot quota considering the # of players connected and refreshes the spawns." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionSpawnFirstEnemies, "Spawns the first wave of enemies in coop." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionSetNextRespawnIn, "Set the next respawn wave to happen in this many seconds." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionSpawnNextWave, "Tells the next wave of enemies to spawn in coop. Also respawns player." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionRespawnDeadPlayers, "Respawns players only." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopMissionGetMissionNumber, "Gets the mission number for the current map - maps can have multiple missions on them." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopResetRoundStartTime, "Resets the round time and starts the mission." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopGiveC4sToCTs, "Will give the number of specified C4s to all alive CT players." ); ScriptRegisterFunction( g_pScriptVM, ScriptCoopCollectBonusCoin, "Marks one of the bonus coins as collected." ); } //----------------------------------------------------------------------------- // Purpose: TF2 Specific Client Commands // Input : // Output : //----------------------------------------------------------------------------- bool CCSGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args ) { CCSPlayer *pPlayer = ToCSPlayer( pEdict ); if ( FStrEq( args[0], "changeteam" ) ) { return true; } else if ( FStrEq( args[0], "nextmap" ) ) { // catch corrupted command if ( pPlayer == NULL ) return true; extern ConVar nextmap_print_enabled; if ( !nextmap_print_enabled.GetBool() ) return true; if ( pPlayer->m_iNextTimeCheck < gpGlobals->curtime ) { char szNextMap[MAX_PATH]; if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) ) { Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) ); } else { GetNextLevelName( szNextMap, sizeof( szNextMap ) ); } if ( IsGameConsole() || engine->IsDedicatedServerForXbox() || engine->IsDedicatedServerForPS3() ) { // we know all the map names on console so we can safely assume we have a token for all maps const int nMaxLength = MAX_MAP_NAME+10+1; char szToken[nMaxLength]; CreateFriendlyMapNameToken(szNextMap, szToken, nMaxLength); ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szToken ); } else { ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", V_GetFileName( szNextMap ) ); } pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1; } return true; } else if ( FStrEq( args[0], "tr_map_show_exit_door_msg" ) ) { IGameEvent * event = gameeventmanager->CreateEvent( "tr_exit_hint_trigger" ); if ( event ) { gameeventmanager->FireEvent( event ); } //engine->ServerCommand( "ent_fire @tr_exit_hint trigger\n" ); return true; } else if( pPlayer != NULL && pPlayer->ClientCommand( args ) ) { return true; } else if( BaseClass::ClientCommand( pEdict, args ) ) { return true; } else if ( TheBots->ServerCommand( args.GetCommandString() ) ) { return true; } else { return TheBots->ClientCommand( pPlayer, args ); } } //----------------------------------------------------------------------------- // Purpose: Player has just spawned. Equip them. //----------------------------------------------------------------------------- void CCSGameRules::PlayerSpawn( CBasePlayer *pBasePlayer ) { CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer ); if ( !pPlayer ) Error( "PlayerSpawn" ); if ( pPlayer->State_Get() != STATE_ACTIVE ) return; if ( CSGameRules()->IsPlayingCoopGuardian()/* || CSGameRules()->IsPlayingCoopMission()*/ ) CSGameRules()->GuardianUpdateBotAccountAndWeapons( pPlayer ); pPlayer->EquipSuit(); // remove any defusers left over from previous random if there is just one random one if ( mp_defuser_allocation.GetInt() == DefuserAllocation::Random ) pPlayer->RemoveDefuser(); bool addDefault = (pPlayer->GetTeamNumber() > TEAM_SPECTATOR); CBaseEntity *pWeaponEntity = NULL; while ( ( pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL ) { CGamePlayerEquip *pEquip = dynamic_cast( pWeaponEntity ); if ( pEquip && !pEquip->UseOnly() ) { if ( addDefault && pEquip->StripFirst() ) { // remove all our weapons and armor before touching the first game_player_equip pPlayer->RemoveAllItems( true ); } pWeaponEntity->Touch( pPlayer ); addDefault = false; } } if ( addDefault ) pPlayer->GiveDefaultItems(); if ( !IsWarmupPeriod() && IsPlayingCooperativeGametype() && !IsQueuedMatchmaking() ) { switch ( pBasePlayer->GetTeamNumber() ) { case TEAM_CT: case TEAM_TERRORIST: // Ensure that all QMM structures are always initialized for players in coop game type ( void ) QueuedMatchmakingPlayersDataFindOrCreate( pPlayer ); break; } } } void CCSGameRules::BroadcastSound( const char *sound, int team ) { CBroadcastRecipientFilter filter; filter.MakeReliable(); if( team != -1 ) { filter.RemoveAllRecipients(); filter.AddRecipientsByTeam( GetGlobalTeam(team) ); } CCSUsrMsg_SendAudio msg; msg.set_radio_sound( sound ) ; SendUserMessage( filter, CS_UM_SendAudio, msg ); } //----------------------------------------------------------------------------- // Purpose: Player has just spawned. Equip them. //----------------------------------------------------------------------------- // return a multiplier that should adjust the damage done by a blast at position vecSrc to something at the position // vecEnd. This will take into account the density of an entity that blocks the line of sight from one position to // the other. // // this algorithm was taken from the HL2 version of RadiusDamage. float CCSGameRules::GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pEntityToIgnore) { float retval = 0.0; trace_t tr; UTIL_TraceLine(vecSrc, vecEnd, MASK_SHOT, pEntityToIgnore, COLLISION_GROUP_NONE, &tr); if (tr.fraction == 1.0) { retval = 1.0; } else if (!(tr.DidHitWorld()) && (tr.m_pEnt != NULL) && (tr.m_pEnt != pEntityToIgnore) && (tr.m_pEnt->GetOwnerEntity() != pEntityToIgnore)) { // if we didn't hit world geometry perhaps there's still damage to be done here. CBaseEntity *blockingEntity = tr.m_pEnt; // check to see if this part of the player is visible if entities are ignored. UTIL_TraceLine(vecSrc, vecEnd, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &tr); if (tr.fraction == 1.0) { if ((blockingEntity != NULL) && (blockingEntity->VPhysicsGetObject() != NULL)) { int nMaterialIndex = blockingEntity->VPhysicsGetObject()->GetMaterialIndex(); float flDensity; float flThickness; float flFriction; float flElasticity; physprops->GetPhysicsProperties( nMaterialIndex, &flDensity, &flThickness, &flFriction, &flElasticity ); const float DENSITY_ABSORB_ALL_DAMAGE = 3000.0; float scale = flDensity / DENSITY_ABSORB_ALL_DAMAGE; if ((scale >= 0.0) && (scale < 1.0)) { retval = 1.0 - scale; } else if (scale < 0.0) { // should never happen, but just in case. retval = 1.0; } } else { retval = 0.75; // we're blocked by something that isn't an entity with a physics module or world geometry, just cut damage in half for now. } } } return retval; } // returns the percentage of the player that is visible from the given point in the world. // return value is between 0 and 1. float CCSGameRules::GetAmountOfEntityVisible(Vector & vecSrc, CBaseEntity *entity) { float retval = 0.0; const float damagePercentageChest = 0.40; const float damagePercentageHead = 0.20; const float damagePercentageFeet = 0.20; const float damagePercentageRightSide = 0.10; const float damagePercentageLeftSide = 0.10; if (!(entity->IsPlayer())) { // the entity is not a player, so the damage is all or nothing. Vector vecTarget; vecTarget = entity->BodyTarget(vecSrc, false); return GetExplosionDamageAdjustment(vecSrc, vecTarget, entity); } CCSPlayer *player = (CCSPlayer *)entity; // check what parts of the player we can see from this point and modify the return value accordingly. float chestHeightFromFeet; float armDistanceFromChest = HalfHumanWidth; // calculate positions of various points on the target player's body Vector vecFeet = player->GetAbsOrigin(); Vector vecChest = player->BodyTarget(vecSrc, false); chestHeightFromFeet = vecChest.z - vecFeet.z; // compute the distance from the chest to the feet. (this accounts for ducking and the like) Vector vecHead = player->GetAbsOrigin(); vecHead.z += HumanHeight; Vector vecRightFacing; AngleVectors(player->GetAbsAngles(), NULL, &vecRightFacing, NULL); vecRightFacing.NormalizeInPlace(); vecRightFacing = vecRightFacing * armDistanceFromChest; Vector vecLeftSide = player->GetAbsOrigin(); vecLeftSide.x -= vecRightFacing.x; vecLeftSide.y -= vecRightFacing.y; vecLeftSide.z += chestHeightFromFeet; Vector vecRightSide = player->GetAbsOrigin(); vecRightSide.x += vecRightFacing.x; vecRightSide.y += vecRightFacing.y; vecRightSide.z += chestHeightFromFeet; // check chest float damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecChest, entity); retval += (damagePercentageChest * damageAdjustment); // check top of head damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecHead, entity); retval += (damagePercentageHead * damageAdjustment); // check feet damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecFeet, entity); retval += (damagePercentageFeet * damageAdjustment); // check left "edge" damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecLeftSide, entity); retval += (damagePercentageLeftSide * damageAdjustment); // check right "edge" damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRightSide, entity); retval += (damagePercentageRightSide * damageAdjustment); return retval; } void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity * pEntityIgnore ) { RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, false ); } // Add the ability to ignore the world trace void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld ) { CBaseEntity *pEntity = NULL; trace_t tr; float falloff, damagePercentage; Vector vecSpot; Vector vecToTarget; Vector vecEndPos; // [tj] The number of enemy players this explosion killed int numberOfEnemyPlayersKilledByThisExplosion = 0; // [tj] who we award the achievement to if enough players are killed CCSPlayer* pCSExplosionAttacker = ToCSPlayer(info.GetAttacker()); // [tj] used to determine which achievement to award for sufficient kills CBaseEntity* pInflictor = info.GetInflictor(); bool isGrenade = dynamic_cast< CHEGrenadeProjectile* >( pInflictor ) != NULL; bool isBomb = pInflictor && V_strcmp(pInflictor->GetClassname(), "planted_c4") == 0; vecEndPos.Init(); Vector vecSrc = vecSrcIn; damagePercentage = 1.0; if ( flRadius ) falloff = info.GetDamage() / flRadius; else falloff = 1.0; //int bInWater = (UTIL_PointContents ( vecSrc, MASK_WATER ) & MASK_WATER) ? true : false; vecSrc.z += 1;// in case grenade is lying on the ground // iterate on all entities in the vicinity. for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) { // [tj] We have to save whether or not the player is killed so we don't give credit // for pre-dead players. bool wasAliveBeforeExplosion = false; CCSPlayer* pCSExplosionVictim = ToCSPlayer(pEntity); if (pCSExplosionVictim) { wasAliveBeforeExplosion = pCSExplosionVictim->IsAlive(); } bool bIgnoreDamageToThisEntity = ( pEntity->m_takedamage == DAMAGE_NO ) || ( pCSExplosionVictim && !wasAliveBeforeExplosion ); if ( !bIgnoreDamageToThisEntity ) { // UNDONE: this should check a damage mask, not an ignore if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore ) {// houndeyes don't hurt other houndeyes with their attack continue; } //// blasts used to not travel into or out of water, users assumed it was a bug. Fix is not to run this check -wills //if ( !bIgnoreWorld ) //{ // if (bInWater && pEntity->GetWaterLevel() == WL_NotInWater) // continue; // if (!bInWater && pEntity->GetWaterLevel() == WL_Eyes) // continue; //} // radius damage can only be blocked by the world vecSpot = pEntity->BodyTarget( vecSrc ); bool bHit = false; if( bIgnoreWorld ) { vecEndPos = vecSpot; bHit = true; } else { // get the percentage of the target entity that is visible from the // explosion position. damagePercentage = GetAmountOfEntityVisible(vecSrc, pEntity); if (damagePercentage > 0.0) { vecEndPos = vecSpot; bHit = true; } } if ( bHit ) { // the explosion can 'see' this entity, so hurt them! //vecToTarget = ( vecSrc - vecEndPos ); vecToTarget = ( vecEndPos - vecSrc ); // use a Gaussian function to describe the damage falloff over distance, with flRadius equal to 3 * sigma // this results in the following values: // // Range Fraction Damage // 0.0 100% // 0.1 96% // 0.2 84% // 0.3 67% // 0.4 49% // 0.5 32% // 0.6 20% // 0.7 11% // 0.8 6% // 0.9 3% // 1.0 1% float fDist = vecToTarget.Length(); float fSigma = flRadius / 3.0f; // flRadius specifies 3rd standard deviation (0.0111 damage at this range) float fGaussianFalloff = exp(-fDist * fDist / (2.0f * fSigma * fSigma)); float flAdjustedDamage = info.GetDamage() * fGaussianFalloff * damagePercentage; if ( flAdjustedDamage > 0 ) { CTakeDamageInfo adjustedInfo = info; adjustedInfo.SetRadius( flRadius ); adjustedInfo.SetDamage( flAdjustedDamage ); Vector dir = vecToTarget; VectorNormalize( dir ); // If we don't have a damage force, manufacture one if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin ) { CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ ); } else { // Assume the force passed in is the maximum force. Decay it based on falloff. float flForce = adjustedInfo.GetDamageForce().Length() * falloff; adjustedInfo.SetDamageForce( dir * flForce ); adjustedInfo.SetDamagePosition( vecSrc ); } Vector vecTarget; vecTarget = pEntity->BodyTarget(vecSrc, false); UTIL_TraceLine(vecSrc, vecTarget, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr); // blasts always hit chest tr.hitgroup = HITGROUP_GENERIC; if (tr.fraction != 1.0) { // this has to be done to make breakable glass work. ClearMultiDamage( ); pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr ); ApplyMultiDamage(); } else { pEntity->TakeDamage( adjustedInfo ); } // Now hit all triggers along the way that respond to damage... pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecEndPos, dir ); // [sbodenbender] Increment grenade damage stat if (pCSExplosionVictim && pCSExplosionAttacker && isGrenade && pCSExplosionVictim->GetTeamNumber() != pCSExplosionAttacker->GetTeamNumber() ) { CCS_GameStats.IncrementStat(pCSExplosionAttacker, CSSTAT_GRENADE_DAMAGE, static_cast(adjustedInfo.GetDamage())); } } } } // [tj] Count up victims of area of effect damage for achievement purposes if ( pCSExplosionVictim ) { //If the bomb is exploding, set the attacker to the planter (we can't put this in the CTakeDamageInfo, since //players aren't supposed to get credit for bomb kills) if ( isBomb ) { CPlantedC4* bomb = static_cast( pInflictor ); if ( bomb ) { pCSExplosionAttacker = bomb->GetPlanter(); } } //Count check to make sure we killed an enemy player if( pCSExplosionAttacker && !pCSExplosionVictim->IsAlive() && wasAliveBeforeExplosion && pCSExplosionVictim->GetTeamNumber() != pCSExplosionAttacker->GetTeamNumber()) { numberOfEnemyPlayersKilledByThisExplosion++; } } } // [tj] Depending on which type of explosion it was, award the appropriate achievement. if ( pCSExplosionAttacker && isGrenade && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::GrenadeMultiKill_MinKills ) { pCSExplosionAttacker->AwardAchievement( CSGrenadeMultikill ); pCSExplosionAttacker->CheckMaxGrenadeKills( numberOfEnemyPlayersKilledByThisExplosion ); } if ( pCSExplosionAttacker && isBomb && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::BombMultiKill_MinKills ) { pCSExplosionAttacker->AwardAchievement( CSBombMultikill ); } } CCSPlayer* CCSGameRules::CheckAndAwardAssists( CCSPlayer* pCSVictim, CCSPlayer* pKiller ) { CUtlLinkedList< CDamageRecord *, int >& victimDamageTakenList = pCSVictim->GetDamageList(); float maxDamage = 0.0f; CCSPlayer* maxDamagePlayer = NULL; FOR_EACH_LL( victimDamageTakenList, ii ) { if ( victimDamageTakenList[ii]->GetPlayerRecipientPtr() == pCSVictim ) { CCSPlayer* pAttackerPlayer = victimDamageTakenList[ii]->GetPlayerDamagerPtr(); if ( pAttackerPlayer ) { if ( (victimDamageTakenList[ii]->GetDamage() > maxDamage) && (pAttackerPlayer != pKiller) && ( pAttackerPlayer != pCSVictim ) ) { maxDamage = victimDamageTakenList[ii]->GetDamage(); maxDamagePlayer = pAttackerPlayer; } } } } // note, only the highest damaging player can be awarded an assist if ( maxDamagePlayer && (maxDamage > cs_AssistDamageThreshold.GetFloat( )) ) { ScorePlayerAssist( maxDamagePlayer, pCSVictim ); return maxDamagePlayer; } return NULL; } //----------------------------------------------------------------------------- // Purpose: // Input : *pVictim - // *pKiller - // *pInflictor - //----------------------------------------------------------------------------- IGameEvent * CCSGameRules::CreateWeaponKillGameEvent( char const *szEventName, const CTakeDamageInfo &info ) { IGameEvent * event = gameeventmanager->CreateEvent( szEventName ); if ( !event ) return NULL; // Work out what killed the player, and prepare to send a message to all clients about it const char *killer_weapon_name = "world"; // by default, the player is killed by the world int killer_ID = 0; char killer_XUID[64] = { '\0' }; char killer_weapon_itemid[64] = { '\0' }; char killer_weapon_fauxitemid[64] = { '\0' }; char kill_weapon_originalowner_xuid[64] = { '\0' }; // Find the killer & the scorer CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); bool bHeadshot = false; if ( pScorer ) // Is the killer a client? { killer_ID = pScorer->GetUserID(); V_sprintf_safe( killer_XUID, "%llu", pScorer->GetSteamIDAsUInt64() ); if( info.GetDamageType() & DMG_HEADSHOT ) { //to enable drawing the headshot icon as well as the weapon icon, bHeadshot = true; } if ( pInflictor ) { if ( pInflictor == pScorer ) { // If the inflictor is the killer, then it must be their current weapon doing the damage if ( pScorer->GetActiveWeapon() ) { CEconItemView *pItem = pScorer->GetActiveWeapon()->GetEconItemView(); killer_weapon_name = ( ( pItem && pItem->IsValid() && pItem->GetItemIndex() && pItem->GetItemDefinition() ) ? pItem->GetItemDefinition()->GetDefinitionName() : pScorer->GetActiveWeapon()->GetClassname() ); //GetDeathNoticeName(); if ( pItem && pItem->IsValid() ) { V_sprintf_safe( killer_weapon_itemid, "%llu", pItem->GetItemID() ); V_sprintf_safe( killer_weapon_fauxitemid, "%llu", CombinedItemIdMakeFromDefIndexAndPaint( pItem->GetItemDefinition()->GetDefinitionIndex(), pItem->GetCustomPaintKitIndex() ) ); } //the default weapon knife looks different in the kill feed depending on faction if ( !V_strcmp( killer_weapon_name, "weapon_knife" ) ) { killer_weapon_name = "weapon_knife_default_t"; if ( pScorer->GetTeamNumber() == TEAM_CT ) killer_weapon_name = "weapon_knife_default_ct"; } } } else { killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy } } } else { killer_weapon_name = STRING( pInflictor->m_iClassname ); } // strip the NPC_* or weapon_* from the inflictor's classname if ( IsWeaponClassname( killer_weapon_name ) ) { killer_weapon_name += WEAPON_CLASSNAME_PREFIX_LENGTH; } else if ( StringHasPrefix( killer_weapon_name, "NPC_" ) ) { killer_weapon_name += V_strlen( "NPC_" ); } else if ( StringHasPrefixCaseSensitive( killer_weapon_name, "func_" ) ) { killer_weapon_name += V_strlen( "func_" ); } else if( StringHasPrefixCaseSensitive( killer_weapon_name, "hegrenade" ) ) //"hegrenade_projectile" { killer_weapon_name = "hegrenade"; } else if( StringHasPrefixCaseSensitive( killer_weapon_name, "flashbang" ) ) //"flashbang_projectile" { killer_weapon_name = "flashbang"; } else if( StringHasPrefixCaseSensitive( killer_weapon_name, "decoy" ) ) //"decoy_projectile" { killer_weapon_name = "decoy"; } else if( StringHasPrefixCaseSensitive( killer_weapon_name, "smokegrenade" ) ) //"smokegrenade_projectile" { killer_weapon_name = "smokegrenade"; } event->SetInt("attacker", killer_ID ); event->SetString("weapon", killer_weapon_name ); event->SetString("weapon_itemid", killer_weapon_itemid ); event->SetString("weapon_fauxitemid", killer_weapon_fauxitemid ); event->SetBool("headshot", bHeadshot ); event->SetString("weapon_originalowner_xuid", kill_weapon_originalowner_xuid ); // If the weapon has a silencer but it isn't currently attached, add "_off" suffix to the weapon name so hud can find an alternate icon if ( pInflictor && pScorer && (pInflictor == pScorer) ) { CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pScorer->GetActiveWeapon() ); if ( pWeapon && pWeapon->HasSilencer() && !pWeapon->IsSilenced() ) { if ( V_strEndsWith( killer_weapon_name, "silencer" ) ) { char szTempWeaponNameWithOFFsuffix[64]; V_snprintf( szTempWeaponNameWithOFFsuffix, sizeof(szTempWeaponNameWithOFFsuffix), "%s_off", killer_weapon_name ); event->SetString("weapon", szTempWeaponNameWithOFFsuffix ); } } } event->SetInt( "penetrated", info.GetObjectsPenetrated() ); return event; } //----------------------------------------------------------------------------- // Purpose: // Input : *pVictim - // *pKiller - // *pInflictor - //----------------------------------------------------------------------------- void CCSGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) { // Find the killer & the scorer CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); CCSPlayer *pCSVictim = (CCSPlayer*)(pVictim); CCSPlayer *pAssiter = CheckAndAwardAssists( pCSVictim, (CCSPlayer *)pKiller ); if ( IGameEvent * event = CreateWeaponKillGameEvent( "player_death", info ) ) { event->SetInt("userid", pVictim->GetUserID() ); event->SetInt("assister", pAssiter ? pAssiter->GetUserID() : 0 ); if( !OnReplayPrompt( pVictim, pScorer ) ) event->SetBool( "noreplay", true ); // prevent UI display of "press to replay" message if HLTV data is unavailable bool bHeadshot = ( pScorer ) && ( info.GetDamageType() & DMG_HEADSHOT ); int priority = bHeadshot ? 8 : 7; #ifdef GAME_DLL CCSPlayer* player = ( dynamic_cast< CCSPlayer* >(pScorer) ); if ( player ) { priority += player->GetKillStreak(); } #endif event->SetInt( "priority", priority ); // player_death if ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION ) { event->SetInt( "dominated", 1 ); } else if ( pCSVictim->GetDeathFlags() & CS_DEATH_REVENGE ) { event->SetInt( "revenge", 1 ); } gameeventmanager->FireEvent( event ); } } bool EconEntity_OnOwnerKillEaterEvent( CEconItemView *pEconItemView, CCSPlayer *pOwner, CCSPlayer *pVictim, kill_eater_event_t eEventType, int iAmount /*= 1*/, uint32 *pNewValue /* = NULL */ ) { if ( pNewValue ) *pNewValue = 0; // Kill-eater weapons. if ( !pEconItemView ) return false; if ( !pOwner ) return false; // Ignore events where we're affecting ourself. if ( pOwner == pVictim ) return false; // Always require that we have at least the base kill eater attribute before sending any messages // to the GC. static CSchemaAttributeDefHandle pAttr_KillEater( "kill eater" ); if ( !pEconItemView->FindAttribute( pAttr_KillEater ) ) return false; // Don't bother sending a message to the GC if either party is a bot, unless we're tracking events against // bots specifically. CSteamID KillerSteamID, VictimSteamID; if ( !pOwner->GetSteamID( &KillerSteamID ) ) return false; // comment this out to have StatTrak count bot kills if ( pVictim && !pVictim->GetSteamID( &VictimSteamID ) ) return false; // we don't want to increment the killeater if the weapon owner is not the killer if ( pEconItemView->GetAccountID() != KillerSteamID.GetAccountID() ) return false; // comment this out to have StatTrak count bot kills if ( pVictim && pVictim->IsFakeClient() ) return false; // Also require that we have whatever event type we're looking for, unless we're looking for regular // player kills in which case we may or may not have a field to describe that. AssertMsg( GetKillEaterAttrPairCount() == 1, "This function is now assuming there is only one stattrak value being updated when called. If we're adding multiple killeaters per item, this needs to be revisited." ); const CEconItemAttributeDefinition *pScoreAttribDef = GetKillEaterAttrPair_Score(0); if ( !pScoreAttribDef ) return false; // If we don't have this attribute, move on. It's possible to be missing this attribute but still // have the next one in the list if we have user-customized tracking types. uint32 unCurrent; if ( !FindAttribute_UnsafeBitwiseCast( pEconItemView, pScoreAttribDef, &unCurrent ) ) return false; const CEconItemAttributeDefinition *pScoreTypeAttribDef = GetKillEaterAttrPair_Type(0); if ( !pScoreTypeAttribDef ) return false; unCurrent += iAmount; pEconItemView->UpdateNetworkedDynamicAttributesForDemos( pScoreAttribDef->GetDefinitionIndex(), *(float*)&unCurrent ); if ( pNewValue ) *pNewValue = unCurrent; return true; } //========================================================= //========================================================= void CCSGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info ) { CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); CCSPlayer *pCSVictim = (CCSPlayer *)pVictim; CCSPlayer *pCSScorer = (CCSPlayer *)pScorer; // determine whether this is a positive or negative kill int numPointsForPlayerKill = IPointsForKill( pScorer, pVictim ); CWeaponCSBase* pWeapon = dynamic_cast( info.GetWeapon() ); // [tj] Flag the round as non-lossless for the appropriate team. // [menglish] Set the death flags depending on a nemesis system if ( pVictim->GetTeamNumber() == TEAM_TERRORIST ) { m_bNoTerroristsKilled = false; m_bNoTerroristsDamaged = false; } if ( pVictim->GetTeamNumber() == TEAM_CT ) { m_bNoCTsKilled = false; m_bNoCTsDamaged = false; } m_bCanDonateWeapons = false; if ( m_pFirstKill == NULL && pCSScorer != pVictim ) { m_pFirstKill = pCSScorer; m_firstKillTime = gpGlobals->curtime - m_fRoundStartTime; } // determine if this kill affected a nemesis relationship int iDeathFlags = 0; if ( pScorer ) { CCS_GameStats.CalculateOverkill( pCSScorer, pCSVictim); CCS_GameStats.CalcDominationAndRevenge( pCSScorer, pCSVictim, &iDeathFlags ); } pCSVictim->SetDeathFlags( iDeathFlags ); pCSVictim->SetLastKillerIndex( pCSScorer ? pCSScorer->entindex() : 0 ); // If we're killed by the C4, we do a subset of BaseClass::PlayerKilled() // Specifically, we shouldn't lose any points or show death notices, to match goldsrc if ( Q_strcmp(pKiller->GetClassname(), "planted_c4" ) == 0 ) { // dvsents2: uncomment when removing all FireTargets // variant_t value; // g_EventQueue.AddEvent( "game_playerdie", "Use", value, 0, pVictim, pVictim ); FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 ); UTIL_LogPrintf( "\"%s<%i><%s><%s>\" [%.0f %.0f %.0f] was killed by the bomb.\n", pCSVictim->GetPlayerName(), pCSVictim->GetUserID(), pCSVictim->GetNetworkIDString(), pCSVictim->GetTeam()->GetName(), pCSVictim->GetAbsOrigin().x, pCSVictim->GetAbsOrigin().y, pCSVictim->GetAbsOrigin().z ); } else { if ( ( !pCSScorer && !dynamic_cast< CBaseGrenade * >( pInflictor ) && !dynamic_cast< CInferno * >( pInflictor ) ) || // special case for suicide tracking: exclude grenade exploding when thrower disconnected ( pCSVictim == pCSScorer ) ) { if ( UseSuicidePenalty() && !pCSVictim->IsControllingBot() && mp_autokick.GetBool() && !CSGameRules()->IsPlayingOffline() && !CSGameRules()->IsWarmupPeriod() && ( pVictim->GetPendingTeamNumber() == pVictim->GetTeamNumber() ) ) // don't punish suicides due to team change { pVictim->m_nSuicides++; if ( pVictim->m_nSuicides >= 5 ) { if ( sv_kick_ban_duration.GetInt() > 0 ) { engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pVictim->GetUserID() ) ); } engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For suiciding too many times\n", pVictim->GetUserID(), 1 ) ); } } } if( pCSVictim == pCSScorer ) { CSGameRules()->ScorePlayerSuicide( pCSVictim ); } else if ( numPointsForPlayerKill >= 0 ) { // Track kills per weapon before increment frag count call if ( pWeapon && pCSScorer ) { switch ( pWeapon->GetWeaponType() ) { case WEAPONTYPE_PISTOL: { int numPistolKills, numSniperKills; pCSScorer->GetEnemyWeaponKills( numPistolKills, numSniperKills ); pCSScorer->SetEnemyWeaponKills( numPistolKills + 1, numSniperKills ); } break; case WEAPONTYPE_SNIPER_RIFLE: { int numPistolKills, numSniperKills; pCSScorer->GetEnemyWeaponKills( numPistolKills, numSniperKills ); pCSScorer->SetEnemyWeaponKills( numPistolKills, numSniperKills + 1 ); } break; } } } BaseClass::PlayerKilled( pVictim, info ); } // check for team-killing, and give monetary rewards/penalties // Find the killer & the scorer if ( !pScorer ) { pCSVictim->SetLastConcurrentKilled( 0 ); return; } if ( numPointsForPlayerKill < 0 ) { // team-killer! pCSScorer->AddAccountAward( PlayerCashAward::KILL_TEAMMATE ); // m_DeferredCallQueue.QueueCall( pCSScorer, &CCSPlayer::AddAccountAward, PlayerCashAward::KILL_TEAMMATE ); pCSScorer->IncrementTeamKillsCount( 1 ); pCSScorer->m_bJustKilledTeammate = true; ClientPrint( pCSScorer, HUD_PRINTCENTER, "#SFUI_Notice_Killed_Teammate" ); if ( mp_autokick.GetBool() ) { if ( !IsPlayingOffline() ) { char strTeamKills[64]; Q_snprintf( strTeamKills, sizeof( strTeamKills ), "%d", (3 - pCSScorer->m_iTeamKills) ); ClientPrint( pCSScorer, HUD_PRINTTALK, "#SFUI_Notice_Game_teammate_kills", strTeamKills ); // this includes a " of 3" in it } if ( pCSScorer->m_iTeamKills >= 3 ) { if ( !IsPlayingOffline() ) { ClientPrint( pCSScorer, HUD_PRINTTALK, "#SFUI_Notice_Banned_For_Killing_Teammates" ); if ( sv_kick_ban_duration.GetInt() > 0 ) { SendKickBanToGC( pCSScorer, k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_TKLimit ); // don't roll the kick command into this, it will fail on a lan, where kickid will go through engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pCSScorer->GetUserID() ) ); } } engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For killing too many teammates\n", pCSScorer->GetUserID(), IsPlayingOffline() ? 0 : 1 ) ); } else if ( ( mp_spawnprotectiontime.GetInt() > 0 ) && ( GetRoundElapsedTime() < mp_spawnprotectiontime.GetInt() ) ) { const CCSWeaponInfo* pWeaponInfo = CCSPlayer::GetWeaponInfoFromDamageInfo( info ); // Special case here: TK at round start using some weapons* only counts towards team damage // and team kills amount, but doesn't instantly ban the player due to common occurrence // of teammates crossing in front of AWP'er on de_dust2 // Weapons that will not instantly ban for TK at round start must have zoom levels and // team damage from headshot must kill with a single kill extern ConVar ff_damage_reduction_bullets; if ( !pWeaponInfo || !pWeapon || ( pWeapon->GetZoomLevels() < 2 ) || // weapon doesn't have 2 zoom levels, worth the ban ( float( pWeapon->GetDamage() ) * 4.0f * ff_damage_reduction_bullets.GetFloat() <= 99.0f ) || // single friendly headshot will not kill, worth the ban !pCSVictim || ( pCSVictim->GetNumAttackersFromDamageList() > 1 ) || // victim already took damage from multiple players, TK should be banned ( pCSVictim->GetMostNumHitsDamageRecordFrom(pCSScorer) > 1 ) // TK'er shot the victim multiple times, worth the ban ) { if ( !IsPlayingOffline() ) { ClientPrint( pCSScorer, HUD_PRINTTALK, "#SFUI_Notice_Banned_For_TK_Start" ); if ( sv_kick_ban_duration.GetInt() > 0 ) { SendKickBanToGC( pCSScorer, k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_TKSpawn ); // don't roll the kick command into this, it will fail on a lan, where kickid will go through engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pCSScorer->GetUserID() ) ); } } engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d For killing a teammate at round start\n", pCSScorer->GetUserID(), IsPlayingOffline() ? 0 : 1 ) ); } } } if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) ) { pCSScorer->m_iDisplayHistoryBits |= DHF_FRIEND_KILLED; pCSScorer->HintMessage( "#SFUI_Notice_Hint_careful_around_teammates", false ); } } else { // [tj] Added a check to make sure we don't get money for suicides. if (pCSScorer != pCSVictim) { // Get the cash award from the weapon and scale it by the gamemode factor // const CCSWeaponInfo* pWeaponInfo = CCSPlayer::GetWeaponInfoFromDamageInfo(info); if ( pWeapon ) { pCSScorer->AddAccountAward( PlayerCashAward::KILLED_ENEMY, pWeapon->GetKillAward(), pWeapon ); // m_DeferredCallQueue.QueueCall( pCSScorer, &CCSPlayer::AddAccountAward, PlayerCashAward::KILLED_ENEMY, award, pWeaponInfo ); } else { pCSScorer->AddAccountAward( PlayerCashAward::KILLED_ENEMY ); // m_DeferredCallQueue.QueueCall( pCSScorer, &CCSPlayer::AddAccountAward, PlayerCashAward::KILLED_ENEMY ); } } CWeaponCSBase* pWeapon = dynamic_cast( info.GetWeapon() ); CEconItemView *pEconWeapon = pWeapon ? pWeapon->GetEconItemView() : NULL ; EconEntity_OnOwnerKillEaterEvent( pEconWeapon, pCSScorer, pCSVictim, kKillEaterEvent_PlayerKill ); /* if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_ENEMY_KILLED) ) { pCSScorer->m_iDisplayHistoryBits |= DHF_ENEMY_KILLED; pCSScorer->HintMessage( "#SFUI_Notice_Hint_win_round_by_killing_enemy", false ); } */ } if ( CSGameRules()->IsPlayingCoopMission() && mp_use_respawn_waves.GetInt() == 2 ) { if ( pCSVictim->IsBot() && pCSVictim->GetTeamNumber() == TEAM_TERRORIST ) { // get the number of enemies remaining and have one of the surviving CTs call it out int count = 0; for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { CBaseEntity *player = UTIL_PlayerByIndex( i ); if ( player == NULL || player->GetTeamNumber() == TEAM_CT || !player->IsAlive() ) continue; count++; } CCSPlayer *pCTPlayer = NULL; for ( int i = 1; i <= gpGlobals->maxClients; ++i ) { pCTPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pCTPlayer && pCTPlayer->GetTeamNumber() == TEAM_CT ) break; } if ( pCTPlayer ) { switch ( count ) { case 3: pCTPlayer->Radio("ThreeEnemiesLeft", "", true); break; case 2: pCTPlayer->Radio("TwoEnemiesLeft", "", true); break; case 1: pCTPlayer->Radio("OneEnemyLeft", "", true); break; } } } } } void CCSGameRules::SendKickBanToGC( CCSPlayer *pPlayer, EMsgGCCStrike15_v2_MatchmakingKickBanReason_t eReason ) { /** Removed for partner depot **/ } void CCSGameRules::SendKickBanToGCforAccountId( uint32 uiAccountId, EMsgGCCStrike15_v2_MatchmakingKickBanReason_t eReason ) { /** Removed for partner depot **/ } void CCSGameRules::InitDefaultAIRelationships() { // Allocate memory for default relationships CBaseCombatCharacter::AllocateDefaultRelationships(); // -------------------------------------------------------------- // First initialize table so we can report missing relationships // -------------------------------------------------------------- int i, j; int iNumClasses = GameRules() ? GameRules()->NumEntityClasses() : LAST_SHARED_ENTITY_CLASS; for (i=0;iIsBeingGivenItem() ) bIsBeingGivenItem = true; if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() && !bIsBeingGivenItem ) { // Player has an active item, so let's check cl_autowepswitch. const char *cl_autowepswitch = engine->GetClientConVarValue( ENTINDEX( pPlayer->edict() ), "cl_autowepswitch" ); if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 ) { return false; } } if ( pPlayer->IsBot() && !bIsBeingGivenItem ) { return false; } if ( !GetAllowWeaponSwitch() ) { return false; } return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon ); } //----------------------------------------------------------------------------- // Purpose: // Input : allow - //----------------------------------------------------------------------------- void CCSGameRules::SetAllowWeaponSwitch( bool allow ) { m_bAllowWeaponSwitch = allow; } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CCSGameRules::GetAllowWeaponSwitch() { return m_bAllowWeaponSwitch; } //----------------------------------------------------------------------------- // Purpose: // Input : *pPlayer - // Output : const char //----------------------------------------------------------------------------- const char *CCSGameRules::SetDefaultPlayerTeam( CBasePlayer *pPlayer ) { Assert( pPlayer ); return BaseClass::SetDefaultPlayerTeam( pPlayer ); } void CCSGameRules::LevelInitPreEntity() { BaseClass::LevelInitPreEntity(); // TODO for CZ-style hostages: TheHostageChatter->Precache(); } void CCSGameRules::LevelInitPostEntity() { BaseClass::LevelInitPostEntity(); m_bLevelInitialized = false; // re-count CT and T start spots now that they exist // Figure out from the entities in the map what kind of map this is (bomb run, prison escape, etc). CheckMapConditions(); } float CCSGameRules::FlPlayerFallDamage( CBasePlayer *pPlayer ) { float fFallVelocity = pPlayer->m_Local.m_flFallVelocity - CS_PLAYER_MAX_SAFE_FALL_SPEED; float fallDamage = fFallVelocity * CS_DAMAGE_FOR_FALL_SPEED; if ( fallDamage > 0.0f ) { // let the bots know IGameEvent * event = gameeventmanager->CreateEvent( "player_falldamage" ); if ( event ) { event->SetInt( "userid", pPlayer->GetUserID() ); event->SetFloat( "damage", fallDamage ); event->SetInt( "priority", 4 ); // player_falldamage gameeventmanager->FireEvent( event ); } } return fallDamage; } bool CCSGameRules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen ) { if ( !BaseClass::ClientConnected( pEntity, pszName, pszAddress, reject, maxrejectlen ) ) return false; return true; } void CCSGameRules::ClientDisconnected( edict_t *pClient ) { // [vitaliy] Players who disconnect while alive and reconnect in competitive mode before the end of the round will not receive win money just like suicide if ( CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) ) ) { if ( pPlayer->IsAlive() ) { pPlayer->ProcessSuicideAsKillReward(); } } BaseClass::ClientDisconnected( pClient ); // [tj] Clear domination data when a player disconnects if ( CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) ) ) { pPlayer->RemoveNemesisRelationships(); } UpdateTeamClanNames( TEAM_TERRORIST ); UpdateTeamClanNames( TEAM_CT ); if ( !IsPlayingCoopMission() ) CheckWinConditions(); } // Called when game rules are destroyed by CWorld void CCSGameRules::LevelShutdown() { BaseClass::LevelShutdown(); } //--------------------------------------------------------------------------------------------------- /** * Check if the scenario has been won/lost. * Return true if the scenario is over, false if the scenario is still in progress */ bool CCSGameRules::CheckWinConditions( void ) { if ( mp_ignore_round_win_conditions.GetBool() || m_bLoadingRoundBackupData ) return false; // If a winner has already been determined.. then get the heck out of here if ( IsWarmupPeriod() || ( m_iRoundWinStatus != WINNER_NONE ) ) { // still check if we lost players to where we need to do a full reset next round... int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); bool bNeededPlayers = false; NeededPlayersCheck( bNeededPlayers ); return true; } // Initialize the player counts.. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); /*********************************** GUN GAME PROGRESSIVE CHECK *******************************************************/ if ( IsPlayingGunGame() ) { if ( GunGameProgressiveEndCheck( ) ) { return true; } if ( IsPlayingGunGameProgressive() ) { return false; } } /***************************** OTHER PLAYER's CHECK *********************************************************/ bool bNeededPlayers = false; if ( NeededPlayersCheck( bNeededPlayers ) ) return false; /****************************** PRISON ESCAPE CHECK *******************************************************/ if ( PrisonRoundEndCheck() ) return true; /****************************** BOMB CHECK ********************************************************/ if ( BombRoundEndCheck( bNeededPlayers ) ) return true; /***************************** TEAM EXTERMINATION CHECK!! *********************************************************/ // CounterTerrorists won by virture of elimination if ( TeamExterminationCheck( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT, bNeededPlayers ) ) return true; /******************************** HOSTAGE RESCUE CHECK ******************************************************/ if ( HostageRescueRoundEndCheck( bNeededPlayers ) ) return true; /******************************** GUARDIAN MODE CHECK ******************************************************/ if ( IsPlayingCoopGuardian() && ( BombPlantedRoundEndCheck() || CTsReachedHostageRoundEndCheck() ) ) return true; // scenario not won - still in progress return false; } bool CCSGameRules::NeededPlayersCheck( bool &bNeededPlayers ) { if ( IsPlayingTraining() ) return false; // When we silently are performing team manipulations then // don't run this players check logic which might cause // round to restart ignoring all team scores and player stats // assignments. if ( m_bLoadingRoundBackupData ) return false; // Run this check differently in queue matchmaking mode if ( IsQueuedMatchmaking() ) { if ( !m_bFirstConnected && ( m_iNumSpawnableTerrorist || m_iNumSpawnableCT ) ) { m_bFreezePeriod = false; //Make sure we are not on the FreezePeriod. m_bCompleteReset = true; TerminateRound( 0.5f, Game_Commencing ); m_bFirstConnected = true; return true; } return false; } // We needed players to start scoring // Do we have them now? if( !m_iNumSpawnableTerrorist && !m_iNumSpawnableCT ) { Msg( "Game will not start until both teams have players.\n" ); UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#SFUI_Notice_Game_scoring" ); bNeededPlayers = true; m_bFirstConnected = false; } if ( !m_bFirstConnected && ( m_iNumSpawnableTerrorist || m_iNumSpawnableCT ) ) { // Start the round immediately when the first person joins // UTIL_LogPrintf( "World triggered \"Game_Commencing\"\n" ); m_bFreezePeriod = false; //Make sure we are not on the FreezePeriod. m_bCompleteReset = true; TerminateRound( 0.5f, Game_Commencing ); m_bFirstConnected = true; return true; } return false; } void CCSGameRules::InitializePlayerCounts( int &NumAliveTerrorist, int &NumAliveCT, int &NumDeadTerrorist, int &NumDeadCT ) { NumAliveTerrorist = NumAliveCT = NumDeadCT = NumDeadTerrorist = 0; m_iNumTerrorist = m_iNumCT = m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0; m_iHaveEscaped = 0; // Set team filters m_filterCT.Reset(); m_filterCT.MakeReliable(); m_filterCT.AddRecipientsByTeam( GetGlobalTeam(TEAM_CT) ); m_filterTerrorist.Reset(); m_filterTerrorist.MakeReliable(); m_filterTerrorist.AddRecipientsByTeam( GetGlobalTeam(TEAM_TERRORIST) ); // Count how many dead players there are on each team. for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ ) { CTeam *pTeam = GetGlobalTeam( iTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; Assert( pPlayer->GetTeamNumber() == pTeam->GetTeamNumber() ); switch ( pTeam->GetTeamNumber() ) { case TEAM_CT: m_iNumCT++; if ( pPlayer->State_Get() != STATE_PICKINGCLASS ) m_iNumSpawnableCT++; if ( pPlayer->m_lifeState != LIFE_ALIVE ) NumDeadCT++; else NumAliveCT++; break; case TEAM_TERRORIST: m_iNumTerrorist++; if ( pPlayer->State_Get() != STATE_PICKINGCLASS ) m_iNumSpawnableTerrorist++; if ( pPlayer->m_lifeState != LIFE_ALIVE ) NumDeadTerrorist++; else NumAliveTerrorist++; // Check to see if this guy escaped. if ( pPlayer->m_bEscaped == true ) m_iHaveEscaped++; break; } } } } void CCSGameRules::AddHostageRescueTime( void ) { if ( m_bAnyHostageReached ) return; m_bAnyHostageReached = true; // If the round is already over don't add additional time bool roundIsAlreadyOver = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE); if ( roundIsAlreadyOver ) return; m_iRoundTime += (int)( mp_hostages_rescuetime.GetFloat() * 60 ); UTIL_ClientPrintAll( HUD_PRINTTALK, "#hostagerescuetime" ); } bool CCSGameRules::HostageRescueRoundEndCheck( bool bNeededPlayers ) { // Check to see if 50% of the hostages have been rescued. CHostage* hostage = NULL; int iNumHostages = g_Hostages.Count(); int iNumLeftToRescue = 0; int i; for ( i=0; im_iHealth > 0 && !hostage->IsRescued() ) // We've found a live hostage. don't end the round iNumLeftToRescue++; } // the number of hostages that can be left un rescued, but still win int iNumRescuedToWin = mp_hostages_rescuetowin.GetInt() == 0 ? iNumHostages : MIN( iNumHostages, mp_hostages_rescuetowin.GetInt() ); int iNumLeftCanWin = MAX( 0, iNumHostages - iNumRescuedToWin ); m_iHostagesRemaining = iNumLeftToRescue; if ( (iNumLeftToRescue >= iNumLeftCanWin) && (iNumHostages > 0) ) { if ( m_iHostagesRescued >= iNumRescuedToWin ) { if ( !bNeededPlayers ) { m_match.AddCTWins( 1 ); } AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_HOSTAGE_RESCUE ); CCS_GameStats.Event_AllHostagesRescued(); // tell the bots all the hostages have been rescued IGameEvent * event = gameeventmanager->CreateEvent( "hostage_rescued_all" ); if ( event ) { gameeventmanager->FireEvent( event ); } CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ); if ( IsPlayingCoopMission() && pTeam ) pTeam->MarkSurrendered(); TerminateRound( mp_round_restart_delay.GetFloat(), All_Hostages_Rescued ); return true; } } return false; } bool CCSGameRules::PrisonRoundEndCheck() { //MIKETODO: get this working when working on prison escape /* if (m_bMapHasEscapeZone == true) { float flEscapeRatio; flEscapeRatio = (float) m_iHaveEscaped / (float) m_iNumEscapers; if (flEscapeRatio >= m_flRequiredEscapeRatio) { BroadcastSound( "Event.TERWin" ); m_iAccountTerrorist += 3150; if ( !bNeededPlayers ) { m_iNumTerroristWins ++; // Update the clients team score UpdateTeamScores(); } EndRoundMessage( "#Terrorists_Escaped", Terrorists_Escaped ); TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_TER ); return; } else if ( NumAliveTerrorist == 0 && flEscapeRatio < m_flRequiredEscapeRatio) { BroadcastSound( "Event.CTWin" ); m_iAccountCT += (1 - flEscapeRatio) * 3500; // CTs are rewarded based on how many terrorists have escaped... if ( !bNeededPlayers ) { m_iNumCTWins++; // Update the clients team score UpdateTeamScores(); } EndRoundMessage( "#CTs_PreventEscape", CTs_PreventEscape ); TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT ); return; } else if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && m_iNumSpawnableCT > 0 ) { BroadcastSound( "Event.CTWin" ); m_iAccountCT += (1 - flEscapeRatio) * 3250; // CTs are rewarded based on how many terrorists have escaped... if ( !bNeededPlayers ) { m_iNumCTWins++; // Update the clients team score UpdateTeamScores(); } EndRoundMessage( "#Escaping_Terrorists_Neutralized", Escaping_Terrorists_Neutralized ); TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT ); return; } // else return; } */ return false; } bool CCSGameRules::BombPlantedRoundEndCheck( void ) { if ( !IsPlayingCoopGuardian() ) return false; bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE ); if ( roundIsAlreadyOver ) return false; if ( m_bBombPlanted ) { m_match.AddTerroristWins( 1 ); TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Planted ); return true; } return false; } bool CCSGameRules::CTsReachedHostageRoundEndCheck( void ) { if ( !IsPlayingCoopGuardian() ) return false; bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE ); if ( roundIsAlreadyOver ) return false; if ( m_bHasHostageBeenTouched ) { m_match.AddCTWins( 1 ); TerminateRound( mp_round_restart_delay.GetFloat(), CTs_ReachedHostage ); return true; } return false; } bool CCSGameRules::GuardianAllKillsAchievedCheck( void ) { if ( !IsPlayingCoopGuardian() ) return false; bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE ); if ( roundIsAlreadyOver ) return false; if ( m_nGuardianModeSpecialKillsRemaining <= 0 ) { if ( IsHostageRescueMap() ) { m_match.AddTerroristWins( 1 ); if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) ) pTeam->MarkSurrendered(); TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); } else { m_match.AddCTWins( 1 ); if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) ) pTeam->MarkSurrendered(); TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win ); } return true; } return false; } void CCSGameRules::IncrementGunGameTerroristWeapons( void ) { if ( IsPlayingGunGameTRBomb() ) { for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ ) { CTeam *pTeam = GetGlobalTeam( iTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; if ( pTeam->GetTeamNumber() == TEAM_TERRORIST ) { // Increment the player's gun game progressive weapon pPlayer->IncrementGunGameProgressiveWeapon( mp_ggtr_bomb_detonation_bonus.GetInt() ); } } } } } void CCSGameRules::IncrementGunGameCTWeapons( void ) { if ( IsPlayingGunGameTRBomb() ) { for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ ) { CTeam *pTeam = GetGlobalTeam( iTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; if ( pTeam->GetTeamNumber() == TEAM_CT ) { // Increment the player's gun game progressive weapon pPlayer->IncrementGunGameProgressiveWeapon( mp_ggtr_bomb_defuse_bonus.GetInt() ); } } } } } bool CCSGameRules::IsBotOnlyTeam( int nTeamNumber ) { CTeam *pTeam = GetGlobalTeam( nTeamNumber ); if ( pTeam ) { // Loop through all players on team and determine if they are all bots for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); if ( !pPlayer ) { continue; } if ( !pPlayer->IsBot() ) { return false; } } } return true; } bool CCSGameRules::GunGameProgressiveEndCheck( void ) { bool bDidEnd = false; if ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) { // No need to perform the check if the match has ended return false; } if ( IsPlayingGunGameTRBomb() && !mp_ggtr_last_weapon_kill_ends_half.GetBool() ) { // ignore kills made with the final weapon and let the round end naturally. return false; } if ( !IsPlayingGunGame() ) return false; CCSPlayer *pWinner = NULL; // Test if a player made a kill with the final gun game weapon for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ ) { CTeam *pTeam = GetGlobalTeam( iTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; if ( pPlayer->MadeFinalGunGameProgressiveKill() ) { pWinner = pPlayer; float delayTime; if ( IsPlayingGunGameProgressive() || IsPlayingGunGameTRBomb() ) { delayTime = mp_ggprogressive_round_restart_delay.GetFloat(); } else { delayTime = mp_round_restart_delay.GetFloat(); } // Award bonus points because the kill was made with the final progression weapon if ( IsPlayingGunGameTRBomb() ) { if ( pTeam->GetTeamNumber() == TEAM_CT ) { m_match.AddCTWins( 1 ); m_match.AddCTBonusPoints( mp_ggtr_end_round_kill_bonus.GetInt() ); } else if ( pTeam->GetTeamNumber() == TEAM_TERRORIST ) { m_match.AddTerroristWins( 1 ); m_match.AddTerroristBonusPoints( mp_ggtr_end_round_kill_bonus.GetInt() ); } } if ( IsPlayingGunGameProgressive() ) { m_bCompleteReset = true; bDidEnd = true; m_match.SetPhase( GAMEPHASE_MATCH_ENDED ); m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); GoToIntermission(); } TerminateRound( delayTime, ( pTeam->GetTeamNumber() == TEAM_CT ) ? CTs_Win : Terrorists_Win ); break; } } if ( bDidEnd ) break; } if ( bDidEnd && IsPlayingGunGameProgressive() ) { for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->AddFlag( FL_FROZEN ); pPlayer->Unblind(); } } } return bDidEnd; } bool CCSGameRules::BombRoundEndCheck( bool bNeededPlayers ) { // Check to see if the bomb target was hit or the bomb defused.. if so, then let's end the round! if ( ( m_bTargetBombed == true ) && ( m_bMapHasBombTarget == true ) ) { if ( !bNeededPlayers ) { m_match.AddTerroristWins( 1 ); } AddTeamAccount( TEAM_TERRORIST, TeamCashAward::TERRORIST_WIN_BOMB ); TerminateRound( mp_round_restart_delay.GetFloat(), Target_Bombed ); return true; } else if ( ( m_bBombDefused == true ) && ( m_bMapHasBombTarget == true ) ) { if ( !bNeededPlayers ) { m_match.AddCTWins( 1 ); } AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_DEFUSING_BOMB ); AddTeamAccount( TEAM_TERRORIST, TeamCashAward::PLANTED_BOMB_BUT_DEFUSED ); // give the T's a little bonus for planting the bomb even though it was defused. TerminateRound( mp_round_restart_delay.GetFloat(), Bomb_Defused ); return true; } return false; } // [dkorus] note, this is the standard "end of round mvp" for the case where a more specific MVP condition has not been met // examples of more specific conditions: Planting the bomb, defusing the bomb, rescuing the hostages, escaping as the VIP, etc CCSPlayer * CCSGameRules::CalculateEndOfRoundMVP() { CCSPlayer* pMVP = NULL; int maxKills = 0; int maxDamage = 0; CSMvpReason_t mvpReason = CSMVP_ELIMINATION; if ( CSGameRules()->IsPlayingGunGameProgressive() ) { // Handle winner of gun game progressive for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { if ( pPlayer->MadeFinalGunGameProgressiveKill() ) { pMVP = pPlayer; mvpReason = CSMVP_GUNGAMEWINNER; break; } } } } else { int nBestScore = 0; // Handle non-gun game progressive mvp for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { if ( pPlayer->HasBeenControlledThisRound() ) continue; if ( CSGameRules()->IsPlayingGunGameDeathmatch() ) { int nScore = pPlayer->GetScore(); int nEntindex = pPlayer->entindex(); if ( nScore > nBestScore || (nScore == nBestScore && nEntindex < maxKills) ) { pMVP = pPlayer; maxKills = nEntindex; nBestScore = nScore; mvpReason = CSMVP_GUNGAMEWINNER; } } else { // only consider players on the winning team if ( CSGameRules()->m_iRoundWinStatus != WINNER_DRAW && pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus ) continue; int nKills = pPlayer->GetNumRoundKills(); // - pPlayer->m_iNumRoundTKs; - for most eliminations count only enemies killed int nDamage = pPlayer->GetTotalActualHealthRemovedFromEnemies(); if ( nKills > maxKills || ( nKills == maxKills && nDamage > maxDamage ) ) { pMVP = pPlayer; maxKills = nKills; maxDamage = nDamage; } } } } } if ( pMVP ) { pMVP->IncrementNumMVPs( mvpReason ); } return pMVP; } bool CCSGameRules::TeamExterminationCheck( int NumAliveTerrorist, int NumAliveCT, int NumDeadTerrorist, int NumDeadCT, bool bNeededPlayers ) { bool bCTsRespawn = mp_respawn_on_death_ct.GetBool(); bool bTsRespawn = mp_respawn_on_death_t.GetBool(); if ( ( m_iNumCT > 0 && m_iNumSpawnableCT > 0 ) && ( m_iNumTerrorist > 0 && m_iNumSpawnableTerrorist > 0 ) ) { // this checks for last man standing rules if ( mp_teammates_are_enemies.GetBool() ) { // last CT alive if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && !bTsRespawn && NumAliveCT == 1 ) { m_match.AddCTWins( 1 ); TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win ); return true; } if ( NumAliveCT == 0 && NumDeadCT != 0 && !bCTsRespawn && NumAliveTerrorist == 1 ) { m_match.AddTerroristWins( 1 ); TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); return true; } if ( NumAliveCT == 0 && !bCTsRespawn && NumAliveTerrorist == 0 && !bTsRespawn && ( m_iNumTerrorist > 0 || m_iNumCT > 0 ) ) { TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw ); return true; } } else { // CTs WON (if they don't respawn) if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && !bTsRespawn && m_iNumSpawnableCT > 0 ) { bool nowin = false; for ( int iGrenade=0; iGrenade < g_PlantedC4s.Count(); iGrenade++ ) { CPlantedC4 *pC4 = g_PlantedC4s[iGrenade]; if ( pC4->IsBombActive() ) nowin = true; } if ( !nowin ) { if ( !bNeededPlayers ) { m_match.AddCTWins( 1 ); } if ( m_bMapHasBombTarget ) AddTeamAccount( TEAM_CT, TeamCashAward::ELIMINATION_BOMB_MAP ); else AddTeamAccount( TEAM_CT, TeamCashAward::ELIMINATION_HOSTAGE_MAP_CT ); TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win ); return true; } } else if ( mp_use_respawn_waves.GetInt() == 2 && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) ) { int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT; int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST; if ( IsPlayingCoopMission() ) { nTeam = TEAM_CT; nOtherTeam = TEAM_TERRORIST; } if ( (IsHostageRescueMap() && NumAliveCT == 0 && NumDeadCT != 0 && bCTsRespawn) || ((IsBombDefuseMap() || IsPlayingCoopMission()) && NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && bTsRespawn) ) { // HACK //play a sound here for all players on other team for ( int j = 1; j <= MAX_PLAYERS; j++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) ); if ( pPlayer && pPlayer->GetTeamNumber() == nTeam ) { CSingleUserRecipientFilter filter( pPlayer ); pPlayer->EmitSound( filter, pPlayer->entindex(), "UI.ArmsRace.BecomeMatchLeader" ); ConVarRef mp_buy_anywhere( "mp_buy_anywhere" ); if ( IsPlayingCoopGuardian() && pPlayer->IsAlive() && mp_buy_anywhere.GetBool() ) { // force the player to get more ammo pPlayer->GuardianForceFillAmmo(); } } } if ( IsPlayingCoopGuardian() ) { // if T's are supposed to respawn float flNextRespawn = gpGlobals->curtime + GetRespawnWaveMaxLength( nOtherTeam ); m_flNextRespawnWave.Set( nOtherTeam, flNextRespawn ); m_flCoopRespawnAndHealTime = gpGlobals->curtime + MIN( 2.0f, GetRespawnWaveMaxLength( nOtherTeam ) ); // decide if the players can buy now int iBuyStatus = sv_buy_status_override.GetInt(); if ( iBuyStatus > 0 && ((nTeam == TEAM_CT && iBuyStatus != 1) || (nTeam == TEAM_TERRORIST && iBuyStatus != 2)) ) { m_flGuardianBuyUntilTime = -1; } else { m_flGuardianBuyUntilTime = flNextRespawn + 7.0f; } char szSec[32]; Q_snprintf(szSec, sizeof(szSec), "%d", (int)GetRespawnWaveMaxLength( nOtherTeam )); CBroadcastRecipientFilter filter; UTIL_ClientPrintFilter(filter, HUD_PRINTCENTER, "#SFUI_Notice_NextWaveIn", szSec); // give CTs a bonus for surving the round AddTeamAccount( nTeam, TeamCashAward::SURVIVE_GUARDIAN_WAVE ); } // COOP MISSION if ( IsPlayingCoopMission() ) { // output to the game entity here that says, we completed the room // and open the door to the next room CGameCoopMissionManager *pManager = GetCoopMissionManager(); if ( !pManager ) { DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't keep track of completed waves without it!" ); } else { pManager->SetWaveCompleted(); m_flCoopRespawnAndHealTime = gpGlobals->curtime + 3.0f; } } // give Ts something so they aren't always using pistols //AddTeamAccount( nOtherTeam, TeamCashAward::CUSTOM_AWARD, 1800, "" ); // Super-specific hack for coop. After CTs clear a round, the easiest bot on that team // gets one level harder. CTeam *pTeam = GetGlobalTeam( nOtherTeam ); if ( pTeam && sv_bots_get_harder_after_each_wave.GetInt() > 0 ) { // increase difficulty of 2 bots for ( int i=0; i < sv_bots_get_harder_after_each_wave.GetInt(); i++ ) { CUtlVector < CCSBot* > vecBotsOnTeam; if ( pTeam->GetBotMembers( &vecBotsOnTeam ) ) { CCSBot *pLeadBot = NULL; BotDifficultyType botHighDifficulty = BOT_EXPERT; FOR_EACH_VEC( vecBotsOnTeam, i ) { const BotProfile *pProfile = vecBotsOnTeam[i]->GetProfile(); if ( pProfile->GetMaxDifficulty() < botHighDifficulty ) { pLeadBot = vecBotsOnTeam[i]; botHighDifficulty = ( BotDifficultyType )pProfile->GetMaxDifficulty(); } } if ( pLeadBot ) { const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( MIN( ( BotDifficultyType )( botHighDifficulty + 1 ), BOT_EXPERT ), pLeadBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true ); if ( pNewProfileData ) { pLeadBot->Initialize( pNewProfileData, pLeadBot->GetTeamNumber() ); } } } } // now just randomize all of the profiles CUtlVector < CCSBot* > vecBotsOnTeam; if ( pTeam->GetBotMembers( &vecBotsOnTeam ) ) { FOR_EACH_VEC( vecBotsOnTeam, i ) { const BotProfile *pProfile = vecBotsOnTeam[i]->GetProfile(); const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )( pProfile->GetMaxDifficulty() ), vecBotsOnTeam[i]->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true ); if ( pNewProfileData ) { vecBotsOnTeam[i]->Initialize( pNewProfileData, vecBotsOnTeam[i]->GetTeamNumber() ); } } } } // // let players buy // int iBuyStatus = -1; // if ( sv_buy_status_override.GetInt() >= 0 ) // { // iBuyStatus = sv_buy_status_override.GetInt(); // } // else if ( g_pMapInfo ) // { // // Check to see if there's a mapping info parameter entity // iBuyStatus = g_pMapInfo->m_iBuyingStatus; // } // // if ( iBuyStatus >= 0 ) // { // switch ( iBuyStatus ) // { // case 1: // m_bCTCantBuy = false; // m_bTCantBuy = true; // Msg( "Only CT's can buy!!\n" ); // break; // // case 2: // m_bCTCantBuy = true; // m_bTCantBuy = false; // Msg( "Only T's can buy!!\n" ); // break; // // case 3: // m_bCTCantBuy = true; // m_bTCantBuy = true; // Msg( "No one can buy!!\n" ); // break; // // default: // m_bCTCantBuy = false; // m_bTCantBuy = false; // break; // } // } // // // if ( nTeam == TEAM_CT ) // // m_bCTCantBuy = false; // // else // // m_bTCantBuy = false; } } // Terrorists WON (if they don't respawn) if ( NumAliveCT == 0 && NumDeadCT != 0 && !bCTsRespawn && m_iNumSpawnableTerrorist > 0 ) { if ( !bNeededPlayers ) { m_match.AddTerroristWins( 1 ); } if ( m_bMapHasBombTarget ) AddTeamAccount( TEAM_TERRORIST, TeamCashAward::ELIMINATION_BOMB_MAP ); else AddTeamAccount( TEAM_TERRORIST, TeamCashAward::ELIMINATION_HOSTAGE_MAP_T ); TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); return true; } else if ( mp_use_respawn_waves.GetInt() == 2 && NumAliveCT == 0 && NumDeadCT != 0 && bCTsRespawn ) { // if T's are supposed to respawn m_flNextRespawnWave.Set( TEAM_CT, gpGlobals->curtime + GetRespawnWaveMaxLength( TEAM_CT ) ); } } } else if ( NumAliveCT == 0 && !bCTsRespawn && NumAliveTerrorist == 0 && !bTsRespawn && ( m_iNumTerrorist > 0 || m_iNumCT > 0 ) ) { TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw ); return true; } return false; } void CCSGameRules::ReadMultiplayCvars() { float flRoundTime = 0; if ( IsPlayingClassic() && IsHostageRescueMap() && ( mp_roundtime_hostage.GetFloat() > 0 ) ) { flRoundTime = mp_roundtime_hostage.GetFloat(); } else if ( IsPlayingClassic() && IsBombDefuseMap() && ( mp_roundtime_defuse.GetFloat() > 0 ) ) { flRoundTime = mp_roundtime_defuse.GetFloat(); } else if ( IsPlayingCoopMission() ) { flRoundTime = mp_roundtime_deployment.GetFloat(); } else { flRoundTime = mp_roundtime.GetFloat(); } m_iRoundTime = IsWarmupPeriod() ? 999 : (int)( flRoundTime * 60 ); m_iFreezeTime = IsWarmupPeriod() ? 2 : mp_freezetime.GetInt(); } static int BombSortPredicate(CCSPlayer * const *left, CCSPlayer * const *right) { // should we prioritize humans over bots? if (cv_bot_defer_to_human_items.GetBool() ) { if ( (*left)->IsBot() && !(*right)->IsBot() ) return 1; if ( !(*left)->IsBot() && (*right)->IsBot() ) return -1; } if ( (*left)->m_fLastGivenBombTime < (*right)->m_fLastGivenBombTime ) return -1; if ( (*left)->m_fLastGivenBombTime > (*right)->m_fLastGivenBombTime ) return +1; return 0; } static int SpawnPointSortFunction( SpawnPoint* const *left, SpawnPoint* const *right ) { // Sort 2 spawn points against each other using their priority values return ( *left )->m_iPriority - ( *right )->m_iPriority; } static int CoopEnemySpawnSortFunction( SpawnPoint* const *left, SpawnPoint* const *right ) { if ( !CSGameRules() ) return 0; // Sort the spawn point lists // just use the first overlapping nav area as a reasonable approximation ShortestPathCost cost = ShortestPathCost(); float dist_left = NavAreaTravelDistance( TheNavMesh->GetNearestNavArea( ( *left )->GetAbsOrigin() ), TheNavMesh->GetNearestNavArea( CSGameRules()->m_vecMainCTSpawnPos ), cost ); float dist_right = NavAreaTravelDistance( TheNavMesh->GetNearestNavArea( ( *right )->GetAbsOrigin() ), TheNavMesh->GetNearestNavArea( CSGameRules()->m_vecMainCTSpawnPos ), cost ); return dist_left - dist_right; } static int ArmsRaceSpawnPointSortFunction( SpawnPoint* const *left, SpawnPoint* const *right ) { if ( ( *left )->m_nType != SpawnPoint::ArmsRace && ( *right )->m_nType == SpawnPoint::ArmsRace ) return 1; if ( ( *left )->m_nType == SpawnPoint::ArmsRace && ( *right )->m_nType != SpawnPoint::ArmsRace ) return -1; return 0; } void CCSGameRules::ResetMasterSpawnPointsForCoop( void ) { RefreshCurrentSpawnPointLists(); } // Perform round-related processing at the point when there is less than 1 second of "free play" to go before the round officially ends // At this point the round winner has been determined and displayed to the players void CCSGameRules::PreRestartRound( void ) { IGameEvent *restartEvent = gameeventmanager->CreateEvent( "cs_pre_restart" ); gameeventmanager->FireEvent( restartEvent ); m_bHasTriggeredRoundStartMusic = true; if ( IsPlayingCoopMission() ) { // make sure all active spawn points are enabled again ResetMasterSpawnPointsForCoop(); } else { // reshuffle spawns and then sort by priority for the next round. ShuffleSpawnPointLists(); SortSpawnPointLists(); } // TEMP // for ( int i=0; i < WEAPON_LAST; i++ ) // { // const CCSWeaponInfo *pCSWeaponInfo = GetWeaponInfo( (CSWeaponID)i ); // if ( pCSWeaponInfo ) // Msg( "%s is worth %d points.\n", pCSWeaponInfo->szPrintName, GetWeaponScoreForDeathmatch( (CSWeaponID)i ) ); // } } // Perform round-related processing at the point where the round winner has been determined, and free-play has begun void CCSGameRules::RoundWin( void ) { // Update accounts based on number of hostages remaining.. int iRescuedHostageBonus = 0; for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ ) { CHostage *pHostage = g_Hostages[iHostage]; if( pHostage->IsRescuable() ) //Alive and not rescued { iRescuedHostageBonus += TeamCashAwardValue( TeamCashAward::HOSTAGE_ALIVE ); } if ( iRescuedHostageBonus >= 2000 ) break; } // Super-specific hack for coop. After a team wins, the hardest bot on that team // gets easier. if ( m_iRoundWinStatus == TEAM_TERRORIST || m_iRoundWinStatus == TEAM_CT ) { CTeam *pTeam = GetGlobalTeam( m_iRoundWinStatus ); if ( pTeam && IsPlayingCoopGuardian() ) { for ( int j = 0; j < m_nGuardianModeWaveNumber; j++ ) { for ( int i = 0; i < sv_bots_get_easier_each_win.GetInt(); i++ ) { CUtlVector < CCSBot* > vecBotsOnTeam; if ( pTeam->GetBotMembers( &vecBotsOnTeam ) ) { CCSBot *pLeadBot = NULL; BotDifficultyType botHighDifficulty = BOT_EASY; FOR_EACH_VEC( vecBotsOnTeam, i ) { const BotProfile *pProfile = vecBotsOnTeam[i]->GetProfile(); if ( pProfile->GetMaxDifficulty() > botHighDifficulty ) { pLeadBot = vecBotsOnTeam[i]; botHighDifficulty = ( BotDifficultyType )pProfile->GetMaxDifficulty(); } } if ( pLeadBot ) { // dont go lower than the map set bot difficulty ConVarRef bot_difficulty( "bot_difficulty" ); const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( MAX( ( BotDifficultyType )( botHighDifficulty - 1 ), ( BotDifficultyType )bot_difficulty.GetInt() ), pLeadBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true ); if ( pNewProfileData ) { pLeadBot->Initialize( pNewProfileData, pLeadBot->GetTeamNumber() ); } } } } } int nRoundsPlayed = GetTotalRoundsPlayed(); if ( nRoundsPlayed > 3 ) { int nShouldReport = nRoundsPlayed % 2; if ( nShouldReport == 0 ) { int nHumanGuardianTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT; if ( m_iRoundWinStatus != nHumanGuardianTeam ) { // Only print the message if the humans didn't complete the mission yet CBroadcastRecipientFilter filter; UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_GuardianModeLowerDifficultyNextRound" ); } ConVarRef sv_bots_get_harder_after_each_wave( "sv_bots_get_harder_after_each_wave" ); sv_bots_get_harder_after_each_wave.SetValue( MAX( 1, sv_bots_get_harder_after_each_wave.GetInt()-1) ); ConVarRef bot_difficulty( "bot_difficulty" ); int nBotDifficulty = bot_difficulty.GetInt(); if ( nRoundsPlayed > 9 ) nBotDifficulty = 0; else if ( nRoundsPlayed > 5 ) nBotDifficulty = MIN( nBotDifficulty, 1 ); else if ( nRoundsPlayed > 3 ) nBotDifficulty = MIN( nBotDifficulty, 2 ); bot_difficulty.SetValue( MAX( 0, sv_bots_get_harder_after_each_wave.GetInt()-1) ); } } } } //*******Catch up code by SupraFiend. Scale up the loser bonus when teams fall into losing streaks if (m_iRoundWinStatus == WINNER_TER) // terrorists won { //check to see if they just broke a losing streak if ( m_iNumConsecutiveTerroristLoses > 0 ) { // reset the loser bonus m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS ); m_iNumConsecutiveTerroristLoses = 0; } m_iNumConsecutiveCTLoses++;//increment the number of wins the CTs have had } else if (m_iRoundWinStatus == WINNER_CT) // CT Won { //check to see if they just broke a losing streak if ( m_iNumConsecutiveCTLoses > 0 ) { // reset the loser bonus m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS ); m_iNumConsecutiveCTLoses = 0; } m_iNumConsecutiveTerroristLoses++;//increment the number of wins the Terrorists have had } //check if the losing team is in a losing streak & that the loser bonus hasn't maxed out. if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000)) m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak else if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000)) m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak // assign the wining and losing bonuses if (m_iRoundWinStatus == WINNER_TER) // terrorists won { AddTeamAccount( TEAM_TERRORIST, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus ); AddTeamAccount( TEAM_CT, TeamCashAward::LOSER_BONUS, m_iLoserBonus ); } else if (m_iRoundWinStatus == WINNER_CT) // CT Won { AddTeamAccount( TEAM_CT, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus); AddTeamAccount( TEAM_TERRORIST, TeamCashAward::LOSER_BONUS, m_iLoserBonus ); } //Update CT account based on number of hostages rescued AddTeamAccount( TEAM_CT, TeamCashAward::RESCUED_HOSTAGE, m_iHostagesRescued * TeamCashAwardValue( TeamCashAward::RESCUED_HOSTAGE )); // store win reason in match stats if ( ShouldRecordMatchStats() ) { int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); bool bCTsRespawn = mp_respawn_on_death_ct.GetBool(); bool bTsRespawn = mp_respawn_on_death_t.GetBool(); int iNumRescuedToWin = mp_hostages_rescuetowin.GetInt() == 0 ? g_Hostages.Count() : MIN( g_Hostages.Count(), mp_hostages_rescuetowin.GetInt() ); int nStatToModify = GetTotalRoundsPlayed() - 1; if ( nStatToModify >= 0 ) { nStatToModify = MIN( nStatToModify, m_iMatchStats_RoundResults.Count() - 1 ); m_iMatchStats_PlayersAlive_T.GetForModify( nStatToModify ) = NumAliveTerrorist; DevMsg( "NumAliveTerrorist = %d", NumAliveTerrorist ); m_iMatchStats_PlayersAlive_CT.GetForModify( nStatToModify ) = NumAliveCT; DevMsg( "NumAliveCT = %d", NumAliveCT ); if (m_iRoundWinStatus == WINNER_TER) // terrorists won { // T_WIN_BOMB if ( m_bTargetBombed == true ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::T_WIN_BOMB; DevMsg( "Ts won by BOMB\n" ); } // T_WIN_ELIMINATION else if ( NumAliveCT == 0 && NumDeadCT != 0 && !bCTsRespawn && m_iNumSpawnableTerrorist > 0 ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::T_WIN_ELIMINATION; DevMsg( "Ts won by ELIMINATION\n" ); } // T_WIN_TIME else if ( GetRoundRemainingTime() <= 0 ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::T_WIN_TIME; DevMsg( "Ts won by TIME\n" ); } else { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::UNKNOWN; DevMsg( "Ts won, but the reason is UNKNOWN!!!\n" ); } } else if (m_iRoundWinStatus == WINNER_CT) // cts won { // CT_WIN_DEFUSE if ( m_bBombDefused == true ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_DEFUSE; DevMsg( "CTs won by DEFUSE\n" ); } // CT_WIN_ELIMINATION else if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && !bTsRespawn && m_iNumSpawnableCT > 0 ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_ELIMINATION; DevMsg( "CTs won by ELIMINATION\n" ); } // CT_WIN_TIME else if ( GetRoundRemainingTime() <= 0 ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_TIME; DevMsg( "CTs won by TIME\n" ); } // CT_WIN_RESCUE else if ( iNumRescuedToWin > 0 && m_iHostagesRescued >= iNumRescuedToWin ) { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::CT_WIN_RESCUE; DevMsg( "CTs won by RESCUE\n" ); } else { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::UNKNOWN; DevMsg( "CTs won, but the reason is UNKNOWN!!!\n" ); } } else { m_iMatchStats_RoundResults.GetForModify( nStatToModify ) = RoundResult::UNKNOWN; } } } } // Perform round-related processing at the official end of round void CCSGameRules::RoundEnd( void ) { IGameEvent * event = gameeventmanager->CreateEvent( "round_officially_ended" ); if( event ) { gameeventmanager->FireEvent( event ); } if ( m_bPlayerItemsHaveBeenDisplayed ) ClearItemsDroppedDuringMatch(); } // Perform round-related processing at the point when the next round is beginning void CCSGameRules::RestartRound() { // save off how many players were alive at the very end of this round if ( ShouldRecordMatchStats() ) { int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); int nStatToModify = GetTotalRoundsPlayed() - 1; if ( nStatToModify >= 0 ) { nStatToModify = MIN( nStatToModify, m_iMatchStats_PlayersAlive_T.Count() - 1 ); m_iMatchStats_PlayersAlive_T.GetForModify( nStatToModify ) = NumAliveTerrorist; DevMsg( "NumAliveTerrorist = %d", NumAliveTerrorist ); m_iMatchStats_PlayersAlive_CT.GetForModify( nStatToModify ) = NumAliveCT; DevMsg( "NumAliveCT = %d", NumAliveCT ); } } m_iNextCTSpawnPoint = 0; m_iNextTerroristSpawnPoint = 0; // fire a round_prestart event IGameEvent * event = gameeventmanager->CreateEvent( "round_prestart" ); if( event ) { gameeventmanager->FireEvent( event ); } // FIXME[pmf]: move this up to the pre-restart? // [tj] Notify players that the round is about to be reset int playerCount = 0; for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex ); if ( pPlayer ) { playerCount++; pPlayer->OnPreResetRound(); } } GetGlobalTeam( TEAM_CT )->ResetTeamLeaders(); GetGlobalTeam( TEAM_TERRORIST )->ResetTeamLeaders(); // If the pre-reset round was causing team changes then make sure we stop silencing them from now on m_bForceTeamChangeSilent = false; // Also after the round restarts we mark ourselves as no longer loading round backup data m_bLoadingRoundBackupData = false; // Kick any bots flagged for removal for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex ); if ( pPlayer && pPlayer->IsBot() && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetPendingTeamNumber() == TEAM_SPECTATOR ) ) { engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", engine->GetPlayerUserId( pPlayer->edict() ) ) ); } } // Perform any queued team moves for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex ); if ( pPlayer && ( pPlayer->GetPendingTeamNumber() != pPlayer->GetTeamNumber() ) ) { pPlayer->HandleCommand_JoinTeam( pPlayer->GetPendingTeamNumber() ) ; } } // Team pre-round setup. Do this after players have finished switching teams. GetGlobalCSTeam( TEAM_CT )->OnRoundPreStart(); GetGlobalCSTeam( TEAM_TERRORIST )->OnRoundPreStart(); if ( m_endMatchOnRoundReset ) { m_endMatchOnRoundReset = false; m_endMatchOnThink = true; } #if defined ( _GAMECONSOLE ) bool isReallyEndOfRound = false; if ((m_iRoundWinStatus == WINNER_TER) || (m_iRoundWinStatus == WINNER_CT)) isReallyEndOfRound = true; if (playerCount > 1 && isReallyEndOfRound) { IGameEvent * updateMatchStatsEvent = gameeventmanager->CreateEvent( "update_matchmaking_stats" ); if (updateMatchStatsEvent) { gameeventmanager->FireEvent( updateMatchStatsEvent); } IGameEvent * writeProfileEvent = gameeventmanager->CreateEvent( "write_profile_data" ); if ( writeProfileEvent ) { gameeventmanager->FireEvent( writeProfileEvent ); } } #endif if ( !IsFinite( gpGlobals->curtime ) ) { Warning( "NaN curtime in RestartRound\n" ); gpGlobals->curtime = 0.0f; } // Brock H. - TR - 03/31/09 // Revert any player controlled bots #if CS_CONTROLLABLE_BOTS_ENABLED RevertBotsFunctor revertBots; ForEachPlayer( revertBots ); #endif m_iTotalRoundsPlayed++; //ClearBodyQue(); // Tabulate the number of players on each team. int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); m_bBombDropped = false; m_bBombPlanted = false; m_bHasHostageBeenTouched = false; if ( GetHumanTeam() != TEAM_UNASSIGNED ) { MoveHumansToHumanTeam(); } #if !defined ( _GAMECONSOLE ) ProcessAutoBalance(); #endif //If this is the first restart since halftime, do the appropriate bookkeeping. bool bClearAccountsAfterHalftime = false; if ( IsPlayingGunGameProgressive() ) { ClearGunGameData(); } else if ( m_match.GetPhase() == GAMEPHASE_HALFTIME ) { if ( GetOvertimePlaying() && ( m_match.GetRoundsPlayed() <= ( mp_maxrounds.GetInt() + ( GetOvertimePlaying() - 1 )*mp_overtime_maxrounds.GetInt() ) ) ) { // This is the overtime halftime at the end of a tied regulation time or at the end of a previous overtime that // failed to determine the winner, we will not be switching teams at this time and we proceed into first half // of next overtime period m_match.SetPhase( GAMEPHASE_PLAYING_FIRST_HALF ); } else { // Regulation halftime or 1st half of overtime finished, swap the CT and T scores so the scoreboard will be correct m_match.SwapTeamScores(); m_match.SetPhase( GAMEPHASE_PLAYING_SECOND_HALF ); } if ( IsPlayingGunGameTRBomb() ) { ClearGunGameData(); } else { // Ensure everyone is given only the starting money bClearAccountsAfterHalftime = true; } // Remove all items at halftime or before overtime when teams aren't switching sides for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; pPlayer->RemoveAllItems( true ); } } // Check to see if TR Gun Game match has ended, and player data should be reset else if ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) { if ( IsPlayingGunGameTRBomb() ) { ClearGunGameData(); } } if ( m_bCompleteReset ) { // reset timeouts EndTerroristTimeOut(); EndCTTimeOut(); m_nTerroristTimeOuts = mp_team_timeout_max.GetInt(); m_nCTTimeOuts = mp_team_timeout_max.GetInt(); m_flTerroristTimeOutRemaining = mp_team_timeout_time.GetInt(); m_flCTTimeOutRemaining = mp_team_timeout_time.GetInt(); // bounds check if ( mp_timelimit.GetInt() < 0 ) { mp_timelimit.SetValue( 0 ); } if ( m_bScrambleTeamsOnRestart ) { HandleScrambleTeams(); m_bScrambleTeamsOnRestart = false; if ( IsPlayingGunGameTRBomb() ) { ClearGunGameData(); } } if ( m_bSwapTeamsOnRestart ) { HandleSwapTeams(); m_bSwapTeamsOnRestart = false; } m_flGameStartTime = gpGlobals->curtime; if ( !IsFinite( m_flGameStartTime.Get() ) ) { Warning( "Trying to set a NaN game start time\n" ); m_flGameStartTime.GetForModify() = 0.0f; } m_iTotalRoundsPlayed = 0; // Reset score info m_match.Reset(); m_iNumConsecutiveTerroristLoses = 0; m_iNumConsecutiveCTLoses = 0; m_iAccountTerrorist = m_iAccountCT = 0; //No extra cash!. //We are starting fresh. So it's like no one has ever won or lost. m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS ); } if ( m_bCompleteReset || // player scores and QMM fully reset when players lost coop mission round! IsPlayingCoopMission() ) { // Reset the player stats for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = CCSPlayer::Instance( i ); if ( pPlayer && !FNullEnt( pPlayer->edict() ) ) pPlayer->Reset( true ); } // For queued matchmaking mode reset KDA/MVPs as well and initialize money for start if ( IsQueuedMatchmaking() ) { FOR_EACH_MAP( m_mapQueuedMatchmakingPlayersData, idxQueuedPlayer ) { m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer )->Reset(); m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer )->m_cash = GetStartMoney(); } } else { m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements(); } // Save a zeroeth round backup if ( Helper_mp_backup_round_IsEnabled() && !IsWarmupPeriod() ) { SaveRoundDataInformation(); } } m_bFreezePeriod = true; m_bGameRestart = false; UTIL_LogPrintf( "Starting Freeze period\n" ); ReadMultiplayCvars(); int iBuyStatus = -1; if ( sv_buy_status_override.GetInt() >= 0 ) { iBuyStatus = sv_buy_status_override.GetInt(); } else if ( g_pMapInfo ) { // Check to see if there's a mapping info parameter entity iBuyStatus = g_pMapInfo->m_iBuyingStatus; } if ( iBuyStatus >= 0 ) { switch ( iBuyStatus ) { case 0: m_bCTCantBuy = false; m_bTCantBuy = false; Msg( "EVERYONE CAN BUY!\n" ); break; case 1: m_bCTCantBuy = false; m_bTCantBuy = true; Msg( "Only CT's can buy!!\n" ); break; case 2: m_bCTCantBuy = true; m_bTCantBuy = false; Msg( "Only T's can buy!!\n" ); break; case 3: m_bCTCantBuy = true; m_bTCantBuy = true; Msg( "No one can buy!!\n" ); break; default: m_bCTCantBuy = false; m_bTCantBuy = false; break; } } else { // by default everyone can buy m_bCTCantBuy = false; m_bTCantBuy = false; } // Check to see if this map has a bomb target in it if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) ) { // this is a bit hacky, but it makes it so the bomb stuff only shows up on mission 3 of the coop mission if ( IsPlayingCoopMission() && mp_anyone_can_pickup_c4.GetBool() == false ) { m_bMapHasBombTarget = false; m_bMapHasBombZone = false; } else { m_bMapHasBombTarget = true; m_bMapHasBombZone = true; } } else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) ) { m_bMapHasBombTarget = true; m_bMapHasBombZone = false; } else { m_bMapHasBombTarget = false; m_bMapHasBombZone = false; } // Check to see if this map has hostage rescue zones if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) ) m_bMapHasRescueZone = true; else m_bMapHasRescueZone = false; // See if the map has func_buyzone entities // Used by CBasePlayer::HandleSignals() to support maps without these entities if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) ) m_bMapHasBuyZone = true; else m_bMapHasBuyZone = false; // GOOSEMAN : See if this map has func_escapezone entities if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) ) { m_bMapHasEscapeZone = true; m_iHaveEscaped = 0; m_iNumEscapers = 0; // Will increase this later when we count how many Ts are starting if (m_iNumEscapeRounds >= 3) { SwapAllPlayers(); m_iNumEscapeRounds = 0; } m_iNumEscapeRounds++; // Increment the number of rounds played... After 8 rounds, the players will do a whole sale switch.. } else m_bMapHasEscapeZone = false; /* // Update accounts based on number of hostages remaining.. int iRescuedHostageBonus = 0; for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ ) { CHostage *pHostage = g_Hostages[iHostage]; if( pHostage->IsRescuable() ) //Alive and not rescued { iRescuedHostageBonus += TeamCashAwardValue( TeamCashAward::HOSTAGE_ALIVE ); } if ( iRescuedHostageBonus >= 2000 ) break; } // *******Catch up code by SupraFiend. Scale up the loser bonus when teams fall into losing streaks if (m_iRoundWinStatus == WINNER_TER) // terrorists won { //check to see if they just broke a losing streak if ( m_iNumConsecutiveTerroristLoses > 0 ) { // reset the loser bonus m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS ); m_iNumConsecutiveTerroristLoses = 0; } m_iNumConsecutiveCTLoses++;//increment the number of wins the CTs have had } else if (m_iRoundWinStatus == WINNER_CT) // CT Won { //check to see if they just broke a losing streak if ( m_iNumConsecutiveCTLoses > 0 ) { // reset the loser bonus m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS ); m_iNumConsecutiveCTLoses = 0; } m_iNumConsecutiveTerroristLoses++;//increment the number of wins the Terrorists have had } //check if the losing team is in a losing streak & that the loser bonus hasn't maxed out. if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000)) m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak else if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000)) m_iLoserBonus += TeamCashAwardValue( TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS );//help out the team in the losing streak // assign the wining and losing bonuses if (m_iRoundWinStatus == WINNER_TER) // terrorists won { AddTeamAccount( TEAM_TERRORIST, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus ); AddTeamAccount( TEAM_CT, TeamCashAward::LOSER_BONUS, m_iLoserBonus ); } else if (m_iRoundWinStatus == WINNER_CT) // CT Won { AddTeamAccount( TEAM_CT, TeamCashAward::HOSTAGE_ALIVE, iRescuedHostageBonus); if (m_bMapHasEscapeZone == false) // only give them the bonus if this isn't an escape map AddTeamAccount( TEAM_TERRORIST, TeamCashAward::LOSER_BONUS, m_iLoserBonus); } //Update CT account based on number of hostages rescued AddTeamAccount( TEAM_CT, TeamCashAward::RESCUED_HOSTAGE, m_iHostagesRescued * TeamCashAwardValue( TeamCashAward::RESCUED_HOSTAGE )); */ // Update individual players accounts and respawn players //**********new code by SupraFiend //##########code changed by MartinO //the round time stamp must be set before players are spawned m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime; if ( !IsFinite( m_fRoundStartTime.Get() ) ) { Warning( "Trying to set a NaN round start time\n" ); m_fRoundStartTime.GetForModify() = 0.0f; } m_bRoundTimeWarningTriggered = false; if ( IsPlayingCoopGuardian() ) { if ( CanSpendMoneyInMap() ) m_flGuardianBuyUntilTime = GetRoundStartTime(); } //Adrian - No cash for anyone at first rounds! ( well, only the default. ) // Get the cash bonus awarded for completing the round int RoundBonus = m_bCompleteReset ? 0 : mp_afterroundmoney.GetInt(); for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; pPlayer->m_iNumSpawns = 0; pPlayer->m_bTeamChanged = false; // Award between round auto bonuses if ( RoundBonus > 0 ) { pPlayer->AddAccount( RoundBonus, false ); } // tricky, make players non solid while moving to their spawn points if ( (pPlayer->GetTeamNumber() == TEAM_CT) || (pPlayer->GetTeamNumber() == TEAM_TERRORIST) ) { pPlayer->AddSolidFlags( FSOLID_NOT_SOLID ); } } // Respawn entities (glass, doors, etc..). NOTE this also KILLS any map entities that the players happen to be pointing to. // For this reason, there is a UpdateMapEntityPointers() call to each player after the restart_round event is issued and processed. CleanUpMap(); if ( IsPlayingCoopMission() ) { m_coopPlayersInDeploymentZone = true; // output to the game entity here that says, we completed the room // and open the door to the next room CGameCoopMissionManager *pManager = GetCoopMissionManager(); // we really dont have one if ( !pManager ) { DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't keep track of completed waves without it!\n" ); } else { pManager->SetRoundReset(); // we do it here after we set a bunch or things for the map, // because coop might want a certain map type even if they aren't spawned right now } } // Reduce hostage count to desired number int iHostageCount = mp_hostages_max.GetInt(); if ( ( mp_hostages_max.GetInt() != atoi( mp_hostages_max.GetDefault() ) ) && ( g_pMapInfo ) && ( g_pMapInfo->m_iHostageCount != 0 ) ) { iHostageCount = g_pMapInfo->m_iHostageCount; } if ( g_Hostages.Count() > iHostageCount ) { CUtlVector< CHostage * > arrCopyOfOriginalHostageIndices; arrCopyOfOriginalHostageIndices.AddMultipleToTail( g_Hostages.Count(), g_Hostages.Base() ); if ( !mp_hostages_spawn_same_every_round.GetBool() ) m_arrSelectedHostageSpawnIndices.RemoveAll(); if ( m_arrSelectedHostageSpawnIndices.Count() ) { // We have pre-selected hostage indices, keep only them FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage ) { if ( m_arrSelectedHostageSpawnIndices.Find( idxGlobalHostage ) != m_arrSelectedHostageSpawnIndices.InvalidIndex() ) continue; CHostage *pHostage = g_Hostages[ idxGlobalHostage ]; UTIL_Remove( pHostage ); g_Hostages.Remove( idxGlobalHostage ); } } else if ( mp_hostages_spawn_force_positions.GetString()[0] ) { CUtlVector< int > arrBestHostageIdx; CUtlVector< char* > tagStrings; V_SplitString( mp_hostages_spawn_force_positions.GetString(), ",", tagStrings ); arrBestHostageIdx.EnsureCapacity( tagStrings.Count() ); FOR_EACH_VEC( tagStrings, iTagString ) { arrBestHostageIdx.AddToTail( Q_atoi( tagStrings[iTagString] ) ); } tagStrings.PurgeAndDeleteElements(); // Now we have selected best hostage indices, keep only them FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage ) { if ( arrBestHostageIdx.Find( idxGlobalHostage ) != arrBestHostageIdx.InvalidIndex() ) continue; CHostage *pHostage = g_Hostages[ idxGlobalHostage ]; UTIL_Remove( pHostage ); g_Hostages.Remove( idxGlobalHostage ); } } else if ( mp_hostages_spawn_farthest.GetBool() ) { CUtlVector< int > arrBestHostageIdx; vec_t bestMetric = 0; CUtlVector< int > arrTryHostageIdx; for ( int iStartIdx = 0; iStartIdx < mp_hostages_max.GetInt(); ++ iStartIdx ) arrTryHostageIdx.AddToTail( iStartIdx ); arrBestHostageIdx.AddMultipleToTail( arrTryHostageIdx.Count(), arrTryHostageIdx.Base() ); while ( 1 ) { vec_t metricThisCombo = 0; for ( int iFirstHostage = 0; iFirstHostage < arrTryHostageIdx.Count(); ++ iFirstHostage ) { for ( int iSecondHostage = iFirstHostage + 1; iSecondHostage < arrTryHostageIdx.Count(); ++ iSecondHostage ) { vec_t len2Dsq = ( g_Hostages[ arrTryHostageIdx[iFirstHostage] ]->GetAbsOrigin() - g_Hostages[ arrTryHostageIdx[iSecondHostage] ]->GetAbsOrigin() ).Length2DSqr(); metricThisCombo += len2Dsq; } } if ( metricThisCombo > bestMetric ) { arrBestHostageIdx.RemoveAll(); arrBestHostageIdx.AddMultipleToTail( arrTryHostageIdx.Count(), arrTryHostageIdx.Base() ); bestMetric = metricThisCombo; } // Advance to next permutation int iAdvanceIdx = 0; while ( ( iAdvanceIdx < arrTryHostageIdx.Count() ) && ( arrTryHostageIdx[ arrTryHostageIdx.Count() - 1 - iAdvanceIdx ] >= g_Hostages.Count() - 1 - iAdvanceIdx ) ) iAdvanceIdx ++; if ( iAdvanceIdx >= arrTryHostageIdx.Count() ) break; // Cannot set a valid permutation // Increment the index arrTryHostageIdx[ arrTryHostageIdx.Count() - 1 - iAdvanceIdx ] ++; // Set all the following indices for ( int iFollowingIdx = arrTryHostageIdx.Count() - iAdvanceIdx; iFollowingIdx < arrTryHostageIdx.Count(); ++ iFollowingIdx ) arrTryHostageIdx[iFollowingIdx] = arrTryHostageIdx[ arrTryHostageIdx.Count() - 1 - iAdvanceIdx ] + ( iFollowingIdx - ( arrTryHostageIdx.Count() - iAdvanceIdx ) + 1 ); } // Now we have selected best hostage indices, keep only them FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage ) { if ( arrBestHostageIdx.Find( idxGlobalHostage ) != arrBestHostageIdx.InvalidIndex() ) continue; CHostage *pHostage = g_Hostages[ idxGlobalHostage ]; UTIL_Remove( pHostage ); g_Hostages.Remove( idxGlobalHostage ); } } else { // Enforce spawn exclusion groups CUtlVector< CHostage * > arrSelectedSpawns; while ( ( arrSelectedSpawns.Count() < mp_hostages_max.GetInt() ) && g_Hostages.Count() ) { uint32 uiTotalSpawnWeightFactor = 0; FOR_EACH_VEC( g_Hostages, idxGlobalHostage ) { if ( CHostage *pCheckHostage = g_Hostages[idxGlobalHostage] ) uiTotalSpawnWeightFactor += pCheckHostage->GetHostageSpawnRandomFactor(); } if ( !uiTotalSpawnWeightFactor ) break; uint32 iKeepHostage = ( uint32 ) RandomInt( 0, uiTotalSpawnWeightFactor - 1 ); CHostage *pKeepHostage = NULL; FOR_EACH_VEC( g_Hostages, idxGlobalHostage ) { if ( CHostage *pCheckHostage = g_Hostages[idxGlobalHostage] ) { uint32 uiThisFactor = pCheckHostage->GetHostageSpawnRandomFactor(); if ( iKeepHostage < uiThisFactor ) { pKeepHostage = pCheckHostage; g_Hostages.Remove( idxGlobalHostage ); break; } else { iKeepHostage -= uiThisFactor; } } } if ( !pKeepHostage ) break; uint32 uiHostageSpawnExclusionGroup = pKeepHostage->GetHostageSpawnExclusionGroup(); arrSelectedSpawns.AddToTail( pKeepHostage ); if ( uiHostageSpawnExclusionGroup ) { FOR_EACH_VEC_BACK( g_Hostages, idxGlobalHostage ) { CHostage *pCheckHostage = g_Hostages[ idxGlobalHostage ]; if ( ( pCheckHostage != pKeepHostage ) && !!( pCheckHostage->GetHostageSpawnExclusionGroup() & uiHostageSpawnExclusionGroup ) ) { // They share the same exclusion group UTIL_Remove( pCheckHostage ); g_Hostages.Remove( idxGlobalHostage ); } } } } // Remove all the remaining hostages that we didn't pick while ( g_Hostages.Count() ) { CHostage *pHostage = g_Hostages.Tail(); UTIL_Remove( pHostage ); g_Hostages.RemoveMultipleFromTail( 1 ); } // Add back the hostages that we decided to keep g_Hostages.AddMultipleToTail( arrSelectedSpawns.Count(), arrSelectedSpawns.Base() ); } // Keep removing randomly now until we reach needed number of hostages remaining while ( g_Hostages.Count() > mp_hostages_max.GetInt() ) { int randHostage = RandomInt( 0, g_Hostages.Count() - 1 ); CHostage *pHostage = g_Hostages[ randHostage ]; UTIL_Remove( pHostage ); g_Hostages.Remove( randHostage ); } // Remember which spots ended up picked, so that players could disable randomization and keep the spots if ( !m_arrSelectedHostageSpawnIndices.Count() ) { FOR_EACH_VEC( g_Hostages, iPickedHostage ) { int idxOriginalSpawnPoint = arrCopyOfOriginalHostageIndices.Find( g_Hostages[iPickedHostage] ); Assert( idxOriginalSpawnPoint != arrCopyOfOriginalHostageIndices.InvalidIndex() ); m_arrSelectedHostageSpawnIndices.AddToTail( idxOriginalSpawnPoint ); } } // Show information about which hostage positions were selected for the round CFmtStr fmtHostagePositions; fmtHostagePositions.AppendFormat( "Selected %d hostage positions '", g_Hostages.Count() ); FOR_EACH_VEC( g_Hostages, iPickedHostage ) { int idxOriginalSpawnPoint = arrCopyOfOriginalHostageIndices.Find( g_Hostages[iPickedHostage] ); Assert( idxOriginalSpawnPoint != arrCopyOfOriginalHostageIndices.InvalidIndex() ); fmtHostagePositions.AppendFormat( "%d,", idxOriginalSpawnPoint ); } fmtHostagePositions.Access()[ fmtHostagePositions.Length() - 1 ] = '\''; fmtHostagePositions.AppendFormat( "\n" ); ConMsg( "%s", fmtHostagePositions.Access() ); } // this needs to happen before we cleanup the map or we won't have a mission manager! if ( IsPlayingCoopMission() ) { m_nGuardianModeWaveNumber = 1; m_nGuardianGrenadesToGiveBots = 0; m_coopPlayersInDeploymentZone = true; m_coopBonusCoinsFound = 0; m_coopBonusPistolsOnly = true; } if ( IsPlayingCoopGuardian() ) { m_nGuardianModeWaveNumber = 1; m_nGuardianGrenadesToGiveBots = 0; extern ConVar sv_guardian_heavy_count; m_nNumHeaviesToSpawn = sv_guardian_heavy_count.GetInt(); m_nGuardianModeSpecialKillsRemaining = mp_guardian_special_kills_needed.GetInt(); char szWepShortName[MAX_PATH]; V_strcpy_safe( szWepShortName, mp_guardian_special_weapon_needed.GetString() ); if ( V_strcmp( szWepShortName, "any" ) == 0 ) { m_nGuardianModeSpecialWeaponNeeded = 0; } else { char pszWeaponClassname[MAX_PATH]; V_sprintf_safe( pszWeaponClassname, "weapon_%s", szWepShortName ); CEconItemDefinition *pItemDef = GetItemSchema()->GetItemDefinitionByName( pszWeaponClassname ); if ( pItemDef && pItemDef->GetDefinitionIndex() != 0 ) { m_nGuardianModeSpecialWeaponNeeded = pItemDef->GetDefinitionIndex(); } else { // REI: This code-path doesn't seem to be used in the latest operation, and I'm not sure this is the behavior we want. // The quest HUD doesn't handle this path, so leave a message in chat for it in case it is used. // But I suggest that maybe in this case we should just fall back on the 'any weapon' code. // we didn't find the weapon specified or it was intentially left blank // send a message instead that says we need to survive the round CBroadcastRecipientFilter filter; if ( IsHostageRescueMap() ) UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_GuardianModeSurviveRoundHostage" ); else UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_GuardianModeSurviveRound" ); } } } // [tj] Keep track of number of players per side and if they have the same uniform int terroristUniform = -1; bool allTerroristsWearingSameUniform = true; int numberOfTerrorists = 0; int ctUniform = -1; bool allCtsWearingSameUniform = true; int numberOfCts = 0; // Now respawn all players CUtlVector< CCSPlayer* > respawningPlayersList; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; if ( pPlayer->GetTeamNumber() == TEAM_CT && PlayerModelInfo::GetPtr()->IsCTClass( pPlayer->PlayerClass() ) ) { // [tj] Increment CT count and check CT uniforms. numberOfCts++; if ( ctUniform == -1 ) { ctUniform = pPlayer->PlayerClass(); } else if ( pPlayer->PlayerClass() != ctUniform ) { allCtsWearingSameUniform = false; } respawningPlayersList.AddToTail( pPlayer ); } else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && PlayerModelInfo::GetPtr()->IsTClass( pPlayer->PlayerClass() ) ) { // [tj] Increment terrorist count and check terrorist uniforms numberOfTerrorists++; if ( terroristUniform == -1 ) { terroristUniform = pPlayer->PlayerClass(); } else if ( pPlayer->PlayerClass() != terroristUniform ) { allTerroristsWearingSameUniform = false; } respawningPlayersList.AddToTail( pPlayer ); } else { pPlayer->ObserverRoundRespawn(); } } // Shuffle spawning players list (this is done to ensure that spawning players don't always spawn in at the same spawn point) ShufflePlayerList( respawningPlayersList ); // [menglish] reset per-round achievement variables for each player for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if( pPlayer ) { pPlayer->ResetRoundBasedAchievementVariables(); } } // Spawn the players for ( int i = 0; i < respawningPlayersList.Count(); ++i ) { respawningPlayersList[ i ]->RoundRespawn(); } // move follower chickens CBaseEntity *pNextChicken = NULL; while ( ( pNextChicken = gEntList.FindEntityByClassname( pNextChicken, "chicken" ) ) != NULL ) { CChicken * pChicken = dynamic_cast< CChicken* >( pNextChicken ); if ( pChicken && pChicken->GetLeader( ) ) { if ( TheNavMesh ) { CNavArea *pPlayerNav = TheNavMesh->GetNearestNavArea( pChicken->GetLeader( ) ); const float tooSmall = 15.0f; if ( pPlayerNav && pPlayerNav->GetSizeX() > tooSmall && pPlayerNav->GetSizeY() > tooSmall ) { { pChicken->SetAbsOrigin( pPlayerNav->GetRandomPoint() ); } } } pChicken->GetLeader( )->IncrementNumFollowers( ); // redo since this got cleared on player respawn } } // Mark all QMM records as eligible for receiving money next round FOR_EACH_MAP_FAST( m_mapQueuedMatchmakingPlayersData, idxQMMEntry ) { m_mapQueuedMatchmakingPlayersData.Element( idxQMMEntry )->m_bReceiveNoMoneyNextRound = false; } // [tj] Award same uniform achievement for qualifying teams for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; #if(ALL_WEARING_SAME_UNIFORM_ACHIEVEMENT) if ( pPlayer->GetTeamNumber() == TEAM_CT && allCtsWearingSameUniform && numberOfCts >= AchievementConsts::SameUniform_MinPlayers) { pPlayer->AwardAchievement(CSSameUniform); } if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && allTerroristsWearingSameUniform && numberOfTerrorists >= AchievementConsts::SameUniform_MinPlayers) { pPlayer->AwardAchievement(CSSameUniform); } #endif } // [pfreese] Reset all round or match stats, depending on type of restart if ( m_bCompleteReset ) { CCS_GameStats.ResetAllStats(); CCS_GameStats.ResetPlayerClassMatchStats(); } else { CCS_GameStats.ResetRoundStats(); } // reset Match Stats if ( m_bCompleteReset ) { for ( int r = 0; r < MAX_MATCH_STATS_ROUNDS; r++ ) { m_iMatchStats_RoundResults.GetForModify( r ) = 0; m_iMatchStats_PlayersAlive_T.GetForModify( r ) = 0x3F; m_iMatchStats_PlayersAlive_CT.GetForModify( r ) = 0x3F; } for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if( pPlayer ) { for ( int r = 0; r < MAX_MATCH_STATS_ROUNDS; r++ ) { pPlayer->m_iMatchStats_Damage.GetForModify( r ) = 0; pPlayer->m_iMatchStats_EquipmentValue.GetForModify( r ) = 0; pPlayer->m_iMatchStats_MoneySaved.GetForModify( r ) = 0; pPlayer->m_iMatchStats_KillReward.GetForModify( r ) = 0; pPlayer->m_iMatchStats_LiveTime.GetForModify( r ) = 0; pPlayer->m_iMatchStats_Deaths.GetForModify( r ) = 0; pPlayer->m_iMatchStats_Assists.GetForModify( r ) = 0; pPlayer->m_iMatchStats_HeadShotKills.GetForModify( r ) = 0; pPlayer->m_iMatchStats_Objective.GetForModify( r ) = 0; pPlayer->m_iMatchStats_CashEarned.GetForModify( r ) = 0; pPlayer->m_iMatchStats_UtilityDamage.GetForModify( r ) = 0; pPlayer->m_iMatchStats_EnemiesFlashed.GetForModify( r ) = 0; } } } // Log the match starting event into server log UTIL_LogPrintf( "World triggered \"Match_Start\" on \"%s\"\n", STRING( gpGlobals->mapname ) ); if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) ) { char const *szName = pTeam->GetClanName(); if ( szName && *szName ) { UTIL_LogPrintf( "Team playing \"CT\": %s\n", szName ); } } if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) ) { char const *szName = pTeam->GetClanName(); if ( szName && *szName ) { UTIL_LogPrintf( "Team playing \"TERRORIST\": %s\n", szName ); } } } // now run a tkpunish check, after the map has been cleaned up for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; if ( ( pPlayer->GetTeamNumber() == TEAM_CT && PlayerModelInfo::GetPtr()->IsCTClass( pPlayer->PlayerClass() )) || ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && PlayerModelInfo::GetPtr()->IsTClass( pPlayer->PlayerClass() ) ) ) { pPlayer->CheckTKPunishment(); } } if (m_bMapHasBombTarget == true ) { if ( !IsPlayingTraining() && !IsWarmupPeriod() && !IsPlayingGunGameProgressive() && !IsPlayingGunGameDeathmatch() && !CSGameRules()->IsPlayingCoopMission() ) { GiveC4ToRandomPlayer(); // Give C4 to the terrorists if ( mp_defuser_allocation.GetInt() == DefuserAllocation::Random ) GiveDefuserToRandomPlayer(); } } // Reset game variables m_flIntermissionStartTime = 0; m_flRestartRoundTime = 0.0; m_timeUntilNextPhaseStarts = 0.0f; m_iAccountTerrorist = m_iAccountCT = 0; m_iHostagesRescued = 0; m_iHostagesTouched = 0; m_flCMMItemDropRevealStartTime = 0; m_flCMMItemDropRevealEndTime = 0; // [tj] reset flawless and lossless round related flags m_bNoTerroristsKilled = true; m_bNoCTsKilled = true; m_bNoTerroristsDamaged = true; m_bNoCTsDamaged = true; m_bNoEnemiesKilled = true; m_pFirstKill = NULL; m_pFirstBlood = NULL; m_pMVP = NULL; m_bCanDonateWeapons = true; // [dwenger] Reset rescue-related achievement values m_iHostagesRemaining = 0; m_bAnyHostageReached = false; m_arrRescuers.RemoveAll(); m_hostageWasInjured = false; m_hostageWasKilled = false; m_iRoundWinStatus = WINNER_NONE; m_eRoundWinReason = RoundEndReason_StillInProgress; m_bTargetBombed = m_bBombDefused = false; m_bCompleteReset = false; m_flNextHostageAnnouncement = gpGlobals->curtime; m_iHostagesRemaining = g_Hostages.Count(); m_flDMBonusStartTime = gpGlobals->curtime + random->RandomFloat( mp_dm_time_between_bonus_min.GetFloat(), mp_dm_time_between_bonus_max.GetFloat() ); m_flDMBonusTimeLength = random->RandomFloat( mp_dm_bonus_length_min.GetFloat(), mp_dm_bonus_length_max.GetFloat() ); m_unDMBonusWeaponLoadoutSlot = PickRandomWeaponForDMBonus(); m_bDMBonusActive = false; m_bIsDroppingItems = false; // fire global game event event = gameeventmanager->CreateEvent( "round_start" ); if ( event ) { event->SetInt("timelimit", m_iRoundTime ); event->SetInt("fraglimit", 0 ); event->SetInt( "priority", 6 ); // round_start if ( m_bMapHasRescueZone ) { event->SetString("objective","HOSTAGE RESCUE"); } else if ( m_bMapHasEscapeZone ) { event->SetString("objective","PRISON ESCAPE"); } else if ( m_bMapHasBombTarget || m_bMapHasBombZone ) { event->SetString("objective","BOMB TARGET"); } else { event->SetString("objective","DEATHMATCH"); } gameeventmanager->FireEvent( event ); } // clear out hits data for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( pPlayer ) pPlayer->m_totalHitsOnServer = 0; } if ( IsWarmupPeriod() ) { IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_warmup" ); if ( event ) gameeventmanager->FireEvent( event ); #ifndef CLIENT_DLL CheckForGiftsLeaderboardUpdate(); #endif } #ifndef CLIENT_DLL if ( m_match.GetRoundsPlayed() <= 0 ) { CheckForGiftsLeaderboardUpdate(); } #endif // We need to reassign the player's pointers to entities that were killed during the map clean up but have been recreated since the round_start event was called. for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; pPlayer->UpdateMapEntityPointers(); } m_iActiveAssassinationTargetMissionID = 0; // [pfreese] I commented out this call to CreateWeaponManager, as the // CGameWeaponManager object doesn't appear to be actually used by the CSS // code, and in any case, the weapon manager does not support wildcards in // entity names (as seemingly indicated) below. When the manager fails to // create its factory, it removes itself in any case. // CreateWeaponManager( "weapon_*", gpGlobals->maxClients * 2 ); g_pPlayerResource->UpdatePlayerData(); g_EntitySpotting->UpdateSpottedEntities(); if ( bClearAccountsAfterHalftime && IsPlayingClassic() && HasHalfTime() ) { AssignStartingMoneyToAllPlayers(); m_iNumConsecutiveTerroristLoses = 0; m_iNumConsecutiveCTLoses = 0; m_iLoserBonus = TeamCashAwardValue( TeamCashAward::LOSER_BONUS ); } if ( !IsPlayingTraining() ) { // should we show an announcement to declare that this round might be the last round? if ( IsLastRoundBeforeHalfTime() ) { IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_last_round_half" ); if ( event ) gameeventmanager->FireEvent( event ); } else if ( IsLastRoundOfMatch() ) { // don't send the final round event if one of the teams just won the round by clinching int iNumWinsToClinch = GetNumWinsToClinch(); if ( m_match.GetCTScore() != iNumWinsToClinch && m_match.GetTerroristScore() != iNumWinsToClinch ) { IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_final" ); if ( event ) gameeventmanager->FireEvent( event ); } } else if ( IsMatchPoint() ) { IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_match_point" ); if ( event ) gameeventmanager->FireEvent( event ); } } // if a team voted to surrender and it passed at the end of a round and we went into halftime, // switch the teams that need to surrender if ( m_bSwitchingTeamsAtRoundReset ) { OnTeamsSwappedAtRoundReset(); } m_bSwitchingTeamsAtRoundReset = false; // Unfreeze all players now that the round is starting UnfreezeAllPlayers(); if ( m_bPlayerItemsHaveBeenDisplayed ) ClearItemsDroppedDuringMatch(); // Perform round-related processing at the point when the next round has just restarted // (This line should be last in this function) PostRestartRound(); } // Perform round-related processing at the point when the next round has just restarted void CCSGameRules::PostRestartRound( void ) { if ( m_match.GetRoundsPlayed() < 1 ) { // Ensure all spectating players are in correct mode at the beginning of the match for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->IsObserver() ) { // Only process players in observer mode int nMode = pPlayer->GetObserverMode(); if ( nMode != OBS_MODE_CHASE && nMode != OBS_MODE_IN_EYE ) { // If the player is not in the chase or in-eye mode then force them to chase mode nMode = OBS_MODE_CHASE; } // Build and send the command to ensure player is in a valid observer mode char szCommand[ 32 ]; V_snprintf( szCommand, sizeof( szCommand ), "spec_mode %i", nMode ); CCommand cmd; cmd.Tokenize( szCommand, kCommandSrcCode ); ClientCommand( pPlayer, cmd ); } } } IGameEvent * event = gameeventmanager->CreateEvent( "round_poststart" ); if ( event ) { gameeventmanager->FireEvent( event ); } } void CCSGameRules::UnfreezeAllPlayers( void ) { for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->RemoveFlag( FL_FROZEN ); } } } loadout_positions_t CCSGameRules::PickRandomWeaponForDMBonus( void ) { return LOADOUT_POSITION_INVALID; } void CCSGameRules::AssignStartingMoneyToAllPlayers( void ) { // Loop through all players and give them only the starting money for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; if ( pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST ) { int amount_to_assign = -pPlayer->GetAccountBalance() + GetStartMoney(); pPlayer->AddAccount( amount_to_assign, false ); } } } void CCSGameRules::GiveC4ToRandomPlayer() { // Don't give C4 if not everyone is in the game, we are going to restart or if the convar says we should not bool bNeeded = false; NeededPlayersCheck( bNeeded ); float timeToRestart = GetRoundRestartTime() - gpGlobals->curtime; if ( !mp_give_player_c4.GetBool() || timeToRestart > 0.001f ) { return; } CUtlVector candidates; candidates.EnsureCapacity(MAX_PLAYERS); // add all eligible terrorist candidates to a list for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_TERRORIST ) { candidates.AddToTail(pPlayer); } } // randomly shuffle the list; this will keep the selection random in case of ties FOR_EACH_VEC(candidates, i) { V_swap(candidates[i], candidates[random->RandomInt( 0, candidates.Count() - 1)] ); } // now sort the list candidates.Sort(BombSortPredicate); // give bomb to the first candidate if ( candidates.Count() > 0 ) { CCSPlayer *pPlayer = candidates[0]; Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->IsAlive() ); pPlayer->GiveNamedItem( WEAPON_C4_CLASSNAME ); pPlayer->SelectItem( "weapon_c4" ); pPlayer->m_fLastGivenBombTime = gpGlobals->curtime; //ClientPrint( pPlayer, HUD_PRINTCENTER, "#SFUI_Notice_Have_Bomb" ); } m_bBombDropped = false; } static int DefuserSortPredicate(CCSPlayer * const *left, CCSPlayer * const *right) { // should we prioritize humans over bots? if (cv_bot_defer_to_human_items.GetBool() ) { if ( (*left)->IsBot() && !(*right)->IsBot() ) return 1; if ( !(*left)->IsBot() && (*right)->IsBot() ) return -1; } if ( (*left)->m_fLastGivenDefuserTime < (*right)->m_fLastGivenDefuserTime ) return -1; if ( (*left)->m_fLastGivenDefuserTime > (*right)->m_fLastGivenDefuserTime ) return +1; return 0; } void CCSGameRules::GiveDefuserToRandomPlayer() { int iDefusersToGive = 2; CUtlVector candidates; candidates.EnsureCapacity(MAX_PLAYERS); // add all CT candidates to a list for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_CT ) { candidates.AddToTail(pPlayer); } } // randomly shuffle the list; this will keep the selection random in case of ties FOR_EACH_VEC(candidates, i) { V_swap(candidates[i], candidates[random->RandomInt( 0, candidates.Count() - 1)] ); } // now sort the shuffled list into subgroups candidates.Sort(DefuserSortPredicate); // give defusers to the first N candidates for ( int i = 0; i < iDefusersToGive && i < candidates.Count(); ++i ) { CCSPlayer *pPlayer = candidates[i]; Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->IsAlive() ); pPlayer->GiveDefuser(false); pPlayer->HintMessage( "#Hint_you_have_the_defuser", false, true ); } } void CCSGameRules::Think() { // NOTE: We are skipping calling CTeamplayRules::Think() and CMultiplayRules::Think() // by calling CGameRules directly. Be aware of this. CGameRules::Think(); // if ( m_DeferredCallQueue.Count() > 0 && gpGlobals->curtime > m_flDeferredCallDispatchTime ) // { // m_DeferredCallQueue.CallQueued(); // } // // Check if connected players have bans on record // int nCooldownMode = sv_kick_players_with_cooldown.GetInt(); if ( ( nCooldownMode <= 0 ) && steamgameserverapicontext && steamgameserverapicontext->SteamGameServer() && steamgameserverapicontext->SteamGameServer()->BSecure() ) nCooldownMode = 1; // On VAC secure servers enforce global cooldowns if ( ( nCooldownMode > 0 ) && CCSGameRules::sm_mapGcBanInformation.Count() ) { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pBasePlayer = UTIL_PlayerByIndex( i ); if ( !pBasePlayer ) continue; CSteamID steamID; if ( pBasePlayer->GetSteamID( &steamID ) && steamID.IsValid() && steamID.GetAccountID() ) { CCSGameRules::GcBanInformationMap_t::IndexType_t idx = CCSGameRules::sm_mapGcBanInformation.Find( steamID.GetAccountID() ); if ( idx != CCSGameRules::sm_mapGcBanInformation.InvalidIndex() ) { CCSGameRules::CGcBanInformation_t &banInfo = CCSGameRules::sm_mapGcBanInformation.Element( idx ); if ((banInfo.m_dblExpiration > Plat_FloatTime()) && !EMsgGCCStrike15_v2_MatchmakingKickBanReason_IsGreen(banInfo.m_uiReason) && ( ( nCooldownMode > 1 ) || EMsgGCCStrike15_v2_MatchmakingKickBanReason_IsGlobal( banInfo.m_uiReason ) ) ) { // Kick this guy Msg( "Kicking user %s (sv_kick_players_with_cooldown=%d)\n", pBasePlayer->GetPlayerName(), nCooldownMode ); if ( sv_kick_ban_duration.GetInt() > 0 ) { // don't roll the kick command into this, it will fail on a lan, where kickid will go through engine->ServerCommand( CFmtStr( "banid %d %d;", sv_kick_ban_duration.GetInt(), pBasePlayer->GetUserID() ) ); } char const *szReasonForKick = "Player has competitive matchmaking cooldown"; switch ( banInfo.m_uiReason ) { case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_OfficialBan: case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_ChallengeNotification: szReasonForKick = "Account is Untrusted"; break; case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_GsltViolation: case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_ConvictedForBehavior: case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_ConvictedForCheating: szReasonForKick = "Account is Convicted"; break; case k_EMsgGCCStrike15_v2_MatchmakingKickBanReason_NoUserSession: szReasonForKick = INVALID_STEAM_TICKET; break; } engine->ServerCommand( UTIL_VarArgs( "kickid_ex %d %d %s\n", pBasePlayer->GetUserID(), ( g_pGameRules && ((CCSGameRules *) g_pGameRules)->IsPlayingOffline() ) ? 0 : 1, szReasonForKick ) ); } } } } } extern void ServerThinkReplayUploader(); ServerThinkReplayUploader(); if ( IsQueuedMatchmaking() ) { CEngineHltvInfo_t engineHltv; if ( engine->GetEngineHltvInfo( engineHltv ) && engineHltv.m_bBroadcastActive && engineHltv.m_bMasterProxy ) { int numCurrentSpectators = engineHltv.m_numClients - engineHltv.m_numProxies + engineHltv.m_numExternalTotalViewers; int numCurrentSpectatorsTV = engineHltv.m_numClients - engineHltv.m_numProxies; int numCurrentSpectatorsLnk = engineHltv.m_numExternalLinkedViewers; if ( numCurrentSpectators > int( m_numSpectatorsCountMax ) ) m_numSpectatorsCountMax = numCurrentSpectators; if ( numCurrentSpectatorsTV > int( m_numSpectatorsCountMaxTV ) ) m_numSpectatorsCountMaxTV = numCurrentSpectatorsTV; if ( numCurrentSpectatorsLnk > int( m_numSpectatorsCountMaxLnk ) ) m_numSpectatorsCountMaxLnk = numCurrentSpectatorsLnk; } } // This fires begin_new_match once when a new match starts... there are other similar game events // but they all get fired multiple times between ending and starting a new match. Since we're using // this event to restart an OGS session we can't let it spam like that. if ( !m_bHasMatchStarted && // Turned off when we go to intermission, turned on once we fire the even m_match.GetPhase() != GAMEPHASE_MATCH_ENDED && // Don't try to run this while we're still in intermission !IsWarmupPeriod() && // Don't start the match until the first round after warmup m_flRestartRoundTime == 0.0f && // This awkwardly ensures we've done a RestartRound() and are currently still waiting on one UTIL_HumansInGame( true, true ) > 0 ) // don't start until there is somebody on a team { IGameEvent * newMatch = gameeventmanager->CreateEvent( "begin_new_match" ); if( newMatch ) { gameeventmanager->FireEvent( newMatch ); } m_bHasMatchStarted = true; m_fMatchStartTime = gpGlobals->curtime; if ( m_bPlayerItemsHaveBeenDisplayed ) ClearItemsDroppedDuringMatch(); CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource ); if ( pResource ) pResource->ForcePlayersPickColors(); } // Display autobalance messages if necessary if ( m_fAutobalanceDisplayTime > 0.0f && gpGlobals->curtime > m_fAutobalanceDisplayTime ) { if ( m_AutobalanceStatus == AutobalanceStatus::THIS_ROUND ) { UTIL_ClientPrintFilter( m_AutoBalanceTraitors, HUD_PRINTCENTER, "#SFUI_Notice_Player_Balanced" ); UTIL_ClientPrintFilter( m_AutoBalanceLoyalists, HUD_PRINTCENTER, "#SFUI_Notice_Teams_Balanced" ); m_AutoBalanceTraitors.RemoveAllRecipients(); m_AutoBalanceLoyalists.RemoveAllRecipients(); } else if ( m_AutobalanceStatus == AutobalanceStatus::NEXT_ROUND ) { UTIL_ClientPrintAll( HUD_PRINTCENTER,"#SFUI_Notice_Auto_Team_Balance_Next_Round"); } m_fAutobalanceDisplayTime = 0.0f; m_AutobalanceStatus = AutobalanceStatus::NONE; } //Update replicated variable for time till next match or half if ( m_match.GetPhase() == GAMEPHASE_HALFTIME ) { if ( mp_halftime_pausetimer.GetBool() ) { //Delay m_flRestartRoundTime for as long as we're paused. m_flRestartRoundTime += gpGlobals->curtime - m_flLastThinkTime; } m_timeUntilNextPhaseStarts = m_flRestartRoundTime - gpGlobals->curtime; m_bIsDroppingItems = false; // Can also implement mp_halftime_pausematch here } else if ( m_match.GetPhase() == GAMEPHASE_MATCH_ENDED ) { float flIntermissionDuration = IsQueuedMatchmaking() ? MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ) : GetIntermissionDuration(); if ( m_ItemsPtrDroppedDuringMatch.Count() > 0 ) { // synch up the server's list of items recieved during this match to the ones on every client if ( !m_bPlayerItemsHaveBeenDisplayed && ( m_phaseChangeAnnouncementTime > 0 && gpGlobals->curtime > m_phaseChangeAnnouncementTime ) ) { SendPlayerItemDropsToClient(); IGameEvent * event = gameeventmanager->CreateEvent( "endmatch_cmm_start_reveal_items" ); if( event ) { gameeventmanager->FireEvent( event ); } //now delay the rematch/failed vote/etc stuff until we are done revealing the items dropped // 1 second per drop + 2 extra seconds for looking m_flCMMItemDropRevealStartTime = gpGlobals->curtime; } // make sure that the intermission time accounts for the number of items we're giving out if ( m_flIntermissionStartTime && ( (m_flIntermissionStartTime + flIntermissionDuration) < m_flCMMItemDropRevealStartTime + (GetCMMItemDropRevealDuration() + 4.0f) ) ) { m_flIntermissionStartTime = ( m_flCMMItemDropRevealStartTime + (GetCMMItemDropRevealDuration() + 4.0f) ) - flIntermissionDuration; } } if (m_bIsDroppingItems && (m_flIntermissionStartTime + mp_win_panel_display_time.GetInt() + 5.0f + sv_reward_drop_delay.GetFloat()) < gpGlobals->curtime && m_flCMMItemDropRevealEndTime < gpGlobals->curtime) { m_bIsDroppingItems = false; CheckSetVoteTime(); } if ( IsPlayingGunGameProgressive() ) { m_timeUntilNextPhaseStarts = (m_flRestartRoundTime + flIntermissionDuration + GetCMMItemDropRevealDuration()) - gpGlobals->curtime; } else { m_timeUntilNextPhaseStarts = m_flIntermissionStartTime + flIntermissionDuration - gpGlobals->curtime; } if ( m_flRestartRoundTime > 0.0f && ( ( m_flRestartRoundTime - 3.0 ) <= gpGlobals->curtime ) && !m_bVoiceWonMatchBragFired ) { // [tj] Inform players that the round is over for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == m_match.GetWinningTeam() ) { // have someone on the winning team brag about winning over the radio pPlayer->Radio( "WonRound", "", true ); break; } } m_bVoiceWonMatchBragFired = true; } } else { m_timeUntilNextPhaseStarts = 0.0f; m_bVoiceWonMatchBragFired = false; m_bIsDroppingItems = false; } //Check if it is time to make the phase change announcement if ( m_phaseChangeAnnouncementTime > 0 && gpGlobals->curtime > m_phaseChangeAnnouncementTime ) { IGameEvent * event = gameeventmanager->CreateEvent( "announce_phase_end" ); if ( event ) { gameeventmanager->FireEvent( event ); } m_phaseChangeAnnouncementTime = 0.0f; } //Let all teams process for ( int i = 0; i < GetNumberOfTeams(); i++ ) { GetGlobalTeam( i )->Think(); } // Update Team Clan Names periodically if ( m_fNextUpdateTeamClanNamesTime <= gpGlobals->curtime ) { m_fNextUpdateTeamClanNamesTime = gpGlobals->curtime + 2; UpdateTeamPredictions(); UpdateTeamClanNames( TEAM_CT ); UpdateTeamClanNames( TEAM_TERRORIST ); } ///// Check game rules ///// if ( CheckGameOver() ) { return; } // did somebody hit the fraglimit ? if ( CheckFragLimit() ) { return; } //Restart if we were flagged to do so if ( m_endMatchOnThink ) { m_endMatchOnThink = false; GoToIntermission(); return; } if ( !m_bBuyTimeEnded && IsBuyTimeElapsed() ) { m_bBuyTimeEnded = true; IGameEvent * event = gameeventmanager->CreateEvent( "buytime_ended" ); if ( event ) { gameeventmanager->FireEvent( event ); } } //Check for clinch int iNumWinsToClinch = GetNumWinsToClinch(); bool bTeamHasClinchedVictory = false; bool bMatchHasEnded = false; //Check for halftime switching if ( m_match.GetPhase() == GAMEPHASE_PLAYING_FIRST_HALF ) { //The number of rounds before halftime depends on the mode and the associated convar int numRoundsBeforeHalftime = GetOvertimePlaying() ? ( mp_maxrounds.GetInt() + ( 2*GetOvertimePlaying() - 1 )*( mp_overtime_maxrounds.GetInt() / 2 ) ) : ( mp_maxrounds.GetInt() / 2 ); //Finally, check for halftime bool bhalftime = false; if ( numRoundsBeforeHalftime > 0 ) { if ( m_match.GetRoundsPlayed() >= numRoundsBeforeHalftime ) { bhalftime = true; } } // if maxrounds is 0 then the server is relying on mp_timelimit rather than mp_maxrounds. else if ( ( GetMapRemainingTime() <= ( ( mp_timelimit.GetInt() * 60 ) / 2 ) ) && IsRoundOver() ) { bhalftime = true; } if ( bhalftime ) { m_match.SetPhase( GAMEPHASE_HALFTIME ); m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); m_flRestartRoundTime = gpGlobals->curtime + mp_halftime_duration.GetFloat(); SwitchTeamsAtRoundReset(); FreezePlayers(); } } //Check for end of half-time match else if ( m_match.GetPhase() == GAMEPHASE_PLAYING_SECOND_HALF ) { //Check for clinch if ( iNumWinsToClinch > 0 && ( HasHalfTime() && !IsPlayingTraining()) ) { bTeamHasClinchedVictory = ( m_match.GetCTScore() >= iNumWinsToClinch ) || ( m_match.GetTerroristScore() >= iNumWinsToClinch ); } //Finally, if there have enough rounds played, end the match bool bEndMatch = false; int numRoundToEndMatch = mp_maxrounds.GetInt() + GetOvertimePlaying()*mp_overtime_maxrounds.GetInt(); if ( numRoundToEndMatch > 0 ) { if ( m_match.GetRoundsPlayed() >= numRoundToEndMatch || bTeamHasClinchedVictory ) { bEndMatch = true; } } else if ( GetMapRemainingTime() <= 0 && IsRoundOver() ) { bEndMatch = true; } // Check if the match ended in a tie and needs overtime if ( bEndMatch && mp_overtime_enable.GetBool() && !bTeamHasClinchedVictory ) { bEndMatch = false; m_match.GoToOvertime( 1 ); m_match.SetPhase( GAMEPHASE_HALFTIME ); m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); m_flRestartRoundTime = gpGlobals->curtime + mp_halftime_duration.GetFloat(); // SwitchTeamsAtRoundReset(); -- don't switch teams, only switch at true halftimes FreezePlayers(); } if ( bEndMatch ) { m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); GoToIntermission(); bMatchHasEnded = true; if ( bTeamHasClinchedVictory && m_match.GetRoundsPlayed() < numRoundToEndMatch ) { // Send chat message to let players know why match is ending early CRecipientFilter filter; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST ) ) { filter.AddRecipient( pPlayer ); } } filter.MakeReliable(); if ( m_match.GetCTScore() > m_match.GetTerroristScore() ) { // CTs have clinched the match UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_CTs_Clinched_Match" ); } else { // Ts have clinched the match UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Ts_Clinched_Match" ); } } } } //If playing a non-halftime game, check the max rounds else if ( m_match.GetPhase() == GAMEPHASE_PLAYING_STANDARD ) { // Check for a clinch if ( mp_maxrounds.GetInt() > 0 && !IsPlayingTraining() && mp_match_can_clinch.GetBool() ) { bTeamHasClinchedVictory = ( m_match.GetCTScore() >= iNumWinsToClinch ) || ( m_match.GetTerroristScore() >= iNumWinsToClinch ); } // End the match if ( ( maxrounds are used ) and ( we've reached maxrounds or clinched the game ) ) or ( we've exceeded timelimit ) if ( mp_maxrounds.GetInt() > 0 && !IsPlayingTraining() ) { if ( m_match.GetRoundsPlayed() >= mp_maxrounds.GetInt() || bTeamHasClinchedVictory ) { bMatchHasEnded = true; } } else if ( GetMapRemainingTime() <= 0 && !IsPlayingTraining() && IsRoundOver() ) { bMatchHasEnded = true; } if ( bMatchHasEnded == true ) { m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); GoToIntermission(); } } if ( IsWarmupPeriod() ) { #ifdef GAME_DLL if ( IsQueuedMatchmaking() ) { // if all humans are present and warmup time left is greater than mp_warmuptime_all_players_connected, reduce warmup time to mp_warmuptime_all_players_connected if ( ( UTIL_HumansInGame( true, false ) == ( int ) MatchmakingGameTypeGameMaxPlayers( MatchmakingGameTypeToGame( sm_QueuedServerReservation.game_type() ) ) ) && ( mp_warmuptime_all_players_connected.GetFloat() > 0 ) && ( GetWarmupPeriodEndTime() - mp_warmuptime_all_players_connected.GetFloat() >= gpGlobals->curtime ) ) { m_fWarmupPeriodStart = gpGlobals->curtime; mp_warmuptime.SetValue( mp_warmuptime_all_players_connected.GetFloat() ); // notify players CBroadcastRecipientFilter filter; UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_All_Players_Connected", mp_warmuptime_all_players_connected.GetString() ); } } if ( IsWarmupPeriodPaused() && ( GetWarmupPeriodEndTime() - 6 >= gpGlobals->curtime) ) // Ignore warmup pause if within 6s of end. { // push out the timers indefinitely. m_fWarmupPeriodStart += gpGlobals->curtime - m_flLastThinkTime; m_fWarmupNextChatNoticeTime += gpGlobals->curtime - m_flLastThinkTime; } if ( m_fWarmupNextChatNoticeTime < gpGlobals->curtime ) { m_fWarmupNextChatNoticeTime = gpGlobals->curtime + 10; CBroadcastRecipientFilter filter; if ( IsQueuedMatchmaking() && ( UTIL_HumansInGame(true, false) < ( int ) MatchmakingGameTypeGameMaxPlayers( MatchmakingGameTypeToGame( sm_QueuedServerReservation.game_type() ) ) ) ) { UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Match_Will_Start_Waiting_Chat" ); } else { UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Match_Will_Start_Chat" ); } if ( !m_numGlobalGiftsGiven ) CheckForGiftsLeaderboardUpdate(); } #endif //bool bIsPlayingProgressive = CSGameRules() && CSGameRules()->IsPlayingGunGameProgressive(); extern ConVar mp_do_warmup_period; if ( ( UTIL_HumansInGame( true, true ) > 0 && ( GetWarmupPeriodEndTime() - 5 < gpGlobals->curtime) ) || !mp_do_warmup_period.GetBool() ) { extern ConVar mp_warmup_pausetimer; mp_warmup_pausetimer.SetValue( 0 ); // Timer is unpausable within 5 seconds of its end. if (GetWarmupPeriodEndTime() <= gpGlobals->curtime) { // when the warmup period ends, set the round to restart in 3 seconds if (!m_bCompleteReset && !m_bGameRestart) { GetGlobalTeam( TEAM_CT )->ResetTeamLeaders(); GetGlobalTeam( TEAM_TERRORIST )->ResetTeamLeaders(); m_flRestartRoundTime = gpGlobals->curtime + g_flWarmupToFreezetimeDelay;/*4.0*/; m_bCompleteReset = true; m_bGameRestart = true; FreezePlayers(); CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource ); if ( pResource ) pResource->ForcePlayersPickColors(); { CReliableBroadcastRecipientFilter filter; UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, "#SFUI_Notice_Warmup_Has_Ended" ); m_fWarmupNextChatNoticeTime = gpGlobals->curtime + 10; } { CReliableBroadcastRecipientFilter filter; CCSUsrMsg_WarmupHasEnded msg; SendUserMessage( filter, CS_UM_WarmupHasEnded, msg ); } } // when the round resets, turn off the warmup period if ( m_flRestartRoundTime <= gpGlobals->curtime ) { m_bWarmupPeriod = false; } } } } // Check for the end of the round. if ( IsFreezePeriod() ) { CheckFreezePeriodExpired(); } else { CheckRoundTimeExpired(); } CheckLevelInitialized(); if ( !m_bRoundTimeWarningTriggered && GetRoundRemainingTime() < ROUND_END_WARNING_TIME && !IsPlayingTraining( ) ) { m_bRoundTimeWarningTriggered = true; IGameEvent * event = gameeventmanager->CreateEvent( "round_time_warning" ); if ( event ) { gameeventmanager->FireEvent( event ); } } if ( IsPlayingCoopMission() && m_flRestartRoundTime > 0.0f && ( ( m_flRestartRoundTime - 0.5 ) <= gpGlobals->curtime ) && !IsWarmupPeriod() && !m_bHasTriggeredCoopSpawnReset ) { // we have to reset the spawns, BEFORE the PreRestartRound because that's where the spawns are initially set up // and we have to let anought time for map logic to set them all up CGameCoopMissionManager *pManager = GetCoopMissionManager(); // we really dont have one if ( !pManager ) { DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't keep track of completed waves without it!\n" ); } else { pManager->SetSpawnsReset(); } m_bHasTriggeredCoopSpawnReset = true; } if (m_flRestartRoundTime > 0.0f && ((m_flRestartRoundTime - 0.3) <= gpGlobals->curtime) && !m_bHasTriggeredRoundStartMusic) { // Perform round-related processing at the point when there is less than 1 second of "free play" to go before the round officially ends // At this point the round winner has been determined and displayed to the players PreRestartRound(); } if ( m_flRestartRoundTime > 0.0f && m_flRestartRoundTime <= gpGlobals->curtime ) { if ( IsWarmupPeriod() && m_match.GetPhase() != GAMEPHASE_MATCH_ENDED && GetWarmupPeriodEndTime() <= gpGlobals->curtime && UTIL_HumansInGame( false, true ) && m_flGameStartTime != 0 ) { m_bCompleteReset = true; m_flRestartRoundTime = gpGlobals->curtime + 1; mp_restartgame.SetValue( 5 ); m_bWarmupPeriod = false; } else { bool botSpeaking = false; for ( int i=1; i <= gpGlobals->maxClients; ++i ) { CBasePlayer *player = UTIL_PlayerByIndex( i ); if (player == NULL) continue; if (!player->IsBot()) continue; CCSBot *bot = dynamic_cast< CCSBot * >(player); if ( !bot ) continue; if ( bot->IsUsingVoice() ) { if ( gpGlobals->curtime > m_flRestartRoundTime + 10.0f ) { Msg( "Ignoring speaking bot %s at round end\n", bot->GetPlayerName() ); } else { botSpeaking = true; break; } } } // restart only if no bots are speaking, and if no people are watching replay. // But still restart even when people are watching replay, if they've been delaying restart for over 10 seconds; we don't want a bug where someone can indefinitely delay a round by watching replays over and over float flMaxRoundDelayDueToReplay = spec_replay_round_delay.GetFloat(); if ( !botSpeaking && ( gpGlobals->curtime > m_flRestartRoundTime + flMaxRoundDelayDueToReplay || !engine->AnyClientsInHltvReplayMode() ) ) { m_bHasTriggeredRoundStartMusic = false; m_bHasTriggeredCoopSpawnReset = false; // Don't call RoundEnd() before the first round of a match if (m_match.GetRoundsPlayed() > 0) { if (IsWarmupPeriod() && (GetWarmupPeriodEndTime() <= gpGlobals->curtime) && UTIL_HumansInGame(false, true)) { m_bCompleteReset = true; m_flRestartRoundTime = gpGlobals->curtime + 1; mp_restartgame.SetValue(5); m_bWarmupPeriod = false; return; } else { // Perform round-related processing at the official end of round RoundEnd(); } } if (IsPlayingGunGameTRBomb() && m_bGameRestart) { m_bGameRestart = false; // Reset all the gun game demolition mode data ClearGunGameData(); // Destroy primary and secondary weapons, as well as grenades for (int i = 1; i <= gpGlobals->maxClients; i++) { CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex(i); if (pPlayer) { pPlayer->DestroyWeapons(false); pPlayer->GiveDefaultItems(); } } } if ( IsPlayingCoopMission() ) { // Destroy primary and secondary weapons, as well as grenades for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( pPlayer ) { pPlayer->DestroyWeapons( true ); pPlayer->GiveDefaultItems(); pPlayer->ResetAccount(); pPlayer->AddAccount( mp_startmoney.GetInt(), false, false, NULL ); } } } IGameEvent * leaderboardEvent = gameeventmanager->CreateEvent("round_end_upload_stats"); if (leaderboardEvent) { gameeventmanager->FireEvent(leaderboardEvent); } // Save the round data information one more time when the round officially ends (overwriting the previously saved file) if ( ( ( m_iRoundWinStatus == WINNER_TER ) || ( m_iRoundWinStatus == WINNER_CT ) ) && Helper_mp_backup_round_IsEnabled() ) { SaveRoundDataInformation( mp_backup_round_file_last.GetString() ); } else { mp_backup_round_file_last.SetValue( "" ); } // Perform round-related processing at the point when the next round is beginning RestartRound(); } } } if ( gpGlobals->curtime > m_tmNextPeriodicThink ) { CheckRestartRound(); CheckRespawnWaves(); m_tmNextPeriodicThink = gpGlobals->curtime + 1.0; } if ( IsPlayingGunGameProgressive() ) { // Perform any queued team moves for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( clientIndex ); if ( pPlayer ) { if ( ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) && ( pPlayer->GetTeamNumber() != TEAM_UNASSIGNED ) && ( pPlayer->GetPendingTeamNumber() != pPlayer->GetTeamNumber() ) ) { pPlayer->HandleCommand_JoinTeam( pPlayer->GetPendingTeamNumber() ) ; } } } } if ( IsPlayingCoopGuardian() ) { if ( m_bBombPlanted ) { //AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB ); //TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); } else if ( m_flCoopRespawnAndHealTime > -1 && m_flCoopRespawnAndHealTime <= gpGlobals->curtime ) { // HACK int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT; //int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST; // give health to all of the players on the other team for ( int j = 1; j <= MAX_PLAYERS; j++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) ); if ( pPlayer && pPlayer->GetTeamNumber() == nTeam ) { if ( pPlayer->IsAlive() ) { pPlayer->GiveHealthAndArmorForGuardianMode( true ); } else { // respawn any dead CTs as well if ( pPlayer->IsAlive() == false ) { pPlayer->State_Transition( STATE_GUNGAME_RESPAWN ); } } } } m_flCoopRespawnAndHealTime = -1; } } // if ( IsPlayingCoopMission() ) // { // if ( m_flCoopRespawnAndHealTime > -1 && m_flCoopRespawnAndHealTime <= gpGlobals->curtime ) // { // int nTeam = TEAM_CT; // // give health to all of the players on the other team // for ( int j = 1; j <= MAX_PLAYERS; j++ ) // { // CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) ); // if ( pPlayer && pPlayer->GetTeamNumber() == nTeam ) // { // // respawn any dead CTs as well // if ( pPlayer->IsAlive() == false ) // { // // pPlayer->State_Transition( STATE_GUNGAME_RESPAWN ); // } // } // } // // m_flCoopRespawnAndHealTime = -1; // } // } if (IsPlayingGunGameDeathmatch()) { if ( m_flDMBonusStartTime != -1 && gpGlobals->curtime >= m_flDMBonusStartTime ) { if ( m_bDMBonusActive && gpGlobals->curtime > (m_flDMBonusStartTime + m_flDMBonusTimeLength) ) { // bonus time ended..... m_bDMBonusActive = false; m_unDMBonusWeaponLoadoutSlot = PickRandomWeaponForDMBonus(); // pick the new one if we have enough time in the round if ( GetRoundRemainingTime() > (mp_dm_time_between_bonus_max.GetFloat() + mp_dm_bonus_length_max.GetFloat()) ) { m_flDMBonusStartTime = gpGlobals->curtime + random->RandomFloat( mp_dm_time_between_bonus_min.GetFloat(), mp_dm_time_between_bonus_max.GetFloat() ); m_flDMBonusTimeLength = random->RandomFloat( mp_dm_bonus_length_min.GetFloat(), mp_dm_bonus_length_max.GetFloat() ); } else if ( GetRoundRemainingTime() > (mp_dm_time_between_bonus_min.GetFloat() + mp_dm_bonus_length_min.GetFloat()) ) { m_flDMBonusTimeLength = mp_dm_bonus_length_min.GetFloat(); m_flDMBonusStartTime = gpGlobals->curtime + (GetRoundRemainingTime() - m_flDMBonusTimeLength); } else { // we're not going to hit a bonus, just disable it m_flDMBonusStartTime = -1; } } // BONUS TIME!!!! else if ( !m_bDMBonusActive && m_flDMBonusStartTime > 0 ) { m_bDMBonusActive = true; // reset bonus dm suicide limiter. for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = ToCSPlayer(UTIL_PlayerByIndex( i )); if( pPlayer ) pPlayer->m_bHasUsedDMBonusRespawn = false; } // CSWeaponID wepID = WEAPON_NONE; // // for ( int i = 0; i < GetItemSchema()->GetItemDefinitionCount(); i++ ) // { // CCStrike15ItemDefinition *pItemDef = ( CCStrike15ItemDefinition * )GetItemSchema()->GetItemDefinitionByMapIndex( i ); // // if ( pItemDef->GetDefaultLoadoutSlot() == m_unDMBonusWeaponLoadoutSlot ) // { // wepID = WeaponIdFromString( pItemDef->GetDefinitionName() ); // break; // } // } // // const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo(wepID); // const char *szWeaponName = pWeaponInfo ? pWeaponInfo->szPrintName : ""; // // char szSec[32]; // Q_snprintf(szSec, sizeof(szSec), "%d", (int)m_flDMBonusTimeLength); // // CReliableBroadcastRecipientFilter filter; // UTIL_ClientPrintFilter(filter, HUD_PRINTTALK, "#SFUI_Notice_DM_BonusWeaponText", szSec, szWeaponName); IGameEvent *bonusWeaponEvent = gameeventmanager->CreateEvent( "dm_bonus_weapon_start" ); if( bonusWeaponEvent ) { bonusWeaponEvent->SetInt("time", (int)m_flDMBonusTimeLength); // bonusWeaponEvent->SetInt("wepID", wepID); bonusWeaponEvent->SetInt("Pos", m_unDMBonusWeaponLoadoutSlot ); gameeventmanager->FireEvent(bonusWeaponEvent); } } } } m_flLastThinkTime = gpGlobals->curtime; } // The bots do their processing after physics simulation etc so their visibility checks don't recompute // bone positions multiple times a frame. void CCSGameRules::EndGameFrame( void ) { TheBots->StartFrame(); BaseClass::EndGameFrame(); } void ServerThinkReplayUploader() { } bool CCSGameRules::CheckGameOver() { if ( g_fGameOver ) // someone else quit the game already { // [Forrest] Calling ChangeLevel multiple times was causing IncrementMapCycleIndex // to skip over maps in the list. Avoid this using a technique from CTeamplayRoundBasedRules::Think. // // In queue matchmaking mode we let users vote for rematch // if ( IsQueuedMatchmaking() ) { extern ConVar mp_match_restart_delay; switch ( m_eQueuedMatchmakingRematchState ) { case k_EQueuedMatchmakingRematchState_MatchInProgress: // give the clients several seconds for the scoreboard to be displayed if ( gpGlobals->curtime < m_flIntermissionStartTime + MAX( mp_match_restart_delay.GetInt(), ( m_flCMMItemDropRevealStartTime - m_flIntermissionStartTime + (GetCMMItemDropRevealDuration() + 4.0f) ) ) ) return true; // else fallthrough case k_EQueuedMatchmakingRematchState_VoteStarting: { #if VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED char const *szCannotRematchExplanation = "#SFUI_vote_failed_rematch_explain"; static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL ); if ( m_pQueuedMatchmakingReportedRoundStats ) { uint32 numHumansPresent = 0; for ( int k = 0; k < m_pQueuedMatchmakingReportedRoundStats->pings().size(); ++ k ) { if ( m_pQueuedMatchmakingReportedRoundStats->pings(k) > 0 ) ++ numHumansPresent; } if ( numHumansPresent == m_numQueuedMatchmakingAccounts ) szCannotRematchExplanation = NULL; } szCannotRematchExplanation = "#SFUI_vote_failed_rematch_mmsvr"; if ( !s_pchTournamentServer && !szCannotRematchExplanation ) { m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteStarting; engine->ServerCommand( "callvote rematch;\n" ); } else { m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed; if ( !s_pchTournamentServer ) { // Send chat message to let players know why match cannot proceed CRecipientFilter filter; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer && ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR || pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST ) ) { filter.AddRecipient( pPlayer ); } } filter.MakeReliable(); // CTs have surrendered UTIL_ClientPrintFilter( filter, HUD_PRINTTALK, szCannotRematchExplanation ); } } #else // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed; #endif // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED } return true; case k_EQueuedMatchmakingRematchState_VoteToRematchInProgress: // waiting for the vote outcome return true; case k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender: case k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender: case k_EQueuedMatchmakingRematchState_VoteToRematch_Aborted: m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed; // fall through: case k_EQueuedMatchmakingRematchState_VoteToRematchFailed: // This condition will then cause all people to disconnect if ( m_flIntermissionStartTime + MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ) < gpGlobals->curtime + mp_competitive_endofmatch_extra_time.GetFloat() ) m_flIntermissionStartTime = ( gpGlobals->curtime + mp_competitive_endofmatch_extra_time.GetFloat() ) - MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ); m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done; { CReliableBroadcastRecipientFilter filter; CCSUsrMsg_ServerRankRevealAll msg; static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL ); if ( s_pchTournamentServer ) msg.set_seconds_till_shutdown( MAX( 0, mp_competitive_endofmatch_extra_time.GetInt() ) ); SendUserMessage( filter, CS_UM_ServerRankRevealAll, msg ); } break; case k_EQueuedMatchmakingRematchState_VoteToRematchSucceeded: #if VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematch_Done; #else // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematchFailed; #endif // VALVE_COMPETITIVE_REMATCH_FEATURE_ENABLED break; // let the normal logic proceed and change level case k_EQueuedMatchmakingRematchState_VoteToRematch_Done: break; // let the normal logic proceed and change level case k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done: if ( IsQueuedMatchmaking() && ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done ) && m_flIntermissionStartTime && ( m_flIntermissionStartTime + MIN( mp_competitive_endofmatch_extra_time.GetFloat(), GetIntermissionDuration() ) < gpGlobals->curtime ) ) { // Tell all clients to return to the lobby CReliableBroadcastRecipientFilter filter; CCSUsrMsg_DisconnectToLobby msg; SendUserMessage( filter, CS_UM_DisconnectToLobby, msg ); } break; // let the normal logic proceed and change level } } // if we're in a "season" map group, let players vote to pick the next map at the end of the match if ( IsEndMatchVotingForNextMap() ) { extern ConVar mp_match_restart_delay; switch ( m_eEndMatchMapVoteState ) { case k_EEndMatchMapVoteState_MatchInProgress: // give the clients several seconds for the scoreboard to be displayed if ( gpGlobals->curtime < m_flIntermissionStartTime + 5.0f ) return true; // else fallthrough case k_EEndMatchMapVoteState_VoteInProgress: { // waiting for the vote outcome CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource ); if ( pResource && pResource->EndMatchNextMapAllVoted() ) { m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_AllPlayersVoted; } if ( (m_flIntermissionStartTime + GetIntermissionDuration()) < gpGlobals->curtime ) { m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteTimeEnded; } return true; } case k_EEndMatchMapVoteState_VoteTimeEnded: case k_EEndMatchMapVoteState_AllPlayersVoted: { // first find out if we have a tie in the vote // this is done in three different places // TODO: make a function out of this typedef int NumberOfVotes_t; typedef int MapInCollection_t; CUtlMap< MapInCollection_t, NumberOfVotes_t > mapMaps2Votes; mapMaps2Votes.SetLessFunc( DefLessFunc( int ) ); // ask all players what they voted for and store it // someone voted for a map in the mapgroup, see if we've recorded a vote for this map yet for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); #if 0 // #ifdef _DEBUG // cause everybody even bots that didn't vote to random vote in debug if ( pPlayer ) { int nVoteNum = pPlayer->GetEndMatchNextMapVote(); if ( nVoteNum < 0 ) nVoteNum = RandomInt( 0, MAX_ENDMATCH_VOTE_PANELS - 1 ); #else if ( pPlayer && !pPlayer->IsBot() && ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) ) { // players store the keybind list index that displays on the client int nVoteNum = pPlayer->GetEndMatchNextMapVote(); #endif if ( (nVoteNum < 0) || (nVoteNum >= MAX_ENDMATCH_VOTE_PANELS) || (m_nEndMatchMapGroupVoteOptions[nVoteNum] < 0) ) { if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: player#%d (%s) voted %d which is ignored\n", pPlayer->entindex(), pPlayer->GetPlayerName(), nVoteNum ); } continue; } // convert the player vote to the map index int nDesiredMapIndex = m_nEndMatchMapGroupVoteOptions[nVoteNum]; if ( nDesiredMapIndex < 0 ) continue; if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: player#%d (%s) voted %d (map=%d)\n", pPlayer->entindex(), pPlayer->GetPlayerName(), nVoteNum, nDesiredMapIndex ); } // store the number of votes sorted by the map index // someone voted for a map in the mapgroup, see if we've recorded a vote for this map yet CUtlMap< MapInCollection_t, NumberOfVotes_t >::IndexType_t iMapEntry = mapMaps2Votes.Find( nDesiredMapIndex ); if ( iMapEntry == mapMaps2Votes.InvalidIndex() ) mapMaps2Votes.Insert( nDesiredMapIndex, 1 ); // we don't have a vote for this map, so insert one else mapMaps2Votes.Element( iMapEntry ) ++; // someone voted for this map already so increment it by one } } // now takes the votes and do this so we can sort them my the total number of votes for each map in ascending order CUtlMap< NumberOfVotes_t, MapInCollection_t > mapVotesInOrder; mapVotesInOrder.SetLessFunc( DefLessFunc( int ) ); FOR_EACH_MAP( mapMaps2Votes, itMap2Vote ) { mapVotesInOrder.Insert( mapMaps2Votes.Element( itMap2Vote ), mapMaps2Votes.Key( itMap2Vote ) ); if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: Total %d votes for map %d\n", mapMaps2Votes.Element( itMap2Vote ), mapMaps2Votes.Key( itMap2Vote ) ); } } // clear the tie votes in our global list m_nEndMatchTiedVotes.RemoveAll(); for ( CUtlMap< NumberOfVotes_t, MapInCollection_t >::IndexType_t idxVotesInOrder = mapVotesInOrder.LastInorder(); idxVotesInOrder != mapVotesInOrder.InvalidIndex(); idxVotesInOrder = mapVotesInOrder.PrevInorder( idxVotesInOrder ) ) { // compare the first to the last in the list and add any to it that matches because we have a tie if ( mapVotesInOrder.Key( idxVotesInOrder ) == mapVotesInOrder.Key( mapVotesInOrder.LastInorder() ) ) { m_nEndMatchTiedVotes.AddToTail( mapVotesInOrder.Element( idxVotesInOrder ) ); if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: Considering best map %d with %d votes\n", mapVotesInOrder.Element( idxVotesInOrder ), mapVotesInOrder.Key( idxVotesInOrder ) ); } } else break; } m_nEndMatchMapVoteWinner = -1; // if we don't have any ties at all, it means no one voted, so add all of the maps to the "tie" list if ( !m_nEndMatchTiedVotes.Count() ) { for ( int iVoteOption = 0; iVoteOption < MAX_ENDMATCH_VOTE_PANELS; ++ iVoteOption ) { if ( m_nEndMatchMapGroupVoteOptions[iVoteOption] >= 0 ) { m_nEndMatchTiedVotes.AddToTail( m_nEndMatchMapGroupVoteOptions[iVoteOption] ); if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: No votes considering map %d\n", m_nEndMatchMapGroupVoteOptions[iVoteOption] ); } } } } // if there is only one in the list, it means at least one player votes, but one of the maps is a clear winner if ( m_nEndMatchTiedVotes.Count() == 1 ) { m_nEndMatchMapVoteWinner = m_nEndMatchTiedVotes.Head(); if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: Absolute winner map %d\n", m_nEndMatchMapVoteWinner ); } } if ( m_nEndMatchMapVoteWinner == -1 ) { // if we have a tie, goto SelectingWinner and tell the client to do something m_flIntermissionStartTime = MAX( m_flIntermissionStartTime, ( (gpGlobals->curtime + 4.0f) - (GetIntermissionDuration()) ) ); m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_SelectingWinner; IGameEvent * event = gameeventmanager->CreateEvent( "endmatch_mapvote_selecting_map" ); if( event ) { // we want to send all of the "ties" to the client. We send ten because technically we could have a 10 person server with each player voting for different maps event->SetInt( "count", m_nEndMatchTiedVotes.Count() ); char sz[16]; for ( int i = 0 ; i < m_nEndMatchTiedVotes.Count() ; ++i) { V_sprintf_safe( sz, "slot%d", i+1 ); // since we store the index in the map group, we need to translate that to the index in the voting panel for the client before we send it down for ( int j = 0 ; j < MAX_ENDMATCH_VOTE_PANELS ; ++j) { if ( m_nEndMatchTiedVotes[i] == m_nEndMatchMapGroupVoteOptions[j] ) { event->SetInt( sz, j ); break; } } } gameeventmanager->FireEvent( event ); } } else { // else just pick the winner and move on m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_SettingNextLevel; } } return true; case k_EEndMatchMapVoteState_SelectingWinner: { if ( m_flIntermissionStartTime + GetIntermissionDuration() < gpGlobals->curtime ) m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_SettingNextLevel; } return true; case k_EEndMatchMapVoteState_SettingNextLevel: { m_flIntermissionStartTime = MAX( m_flIntermissionStartTime, ( (gpGlobals->curtime + 5.0f) - GetIntermissionDuration() ) ); m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteAllDone; if ( g_pGameTypes ) { const char* mapGroupName = gpGlobals->mapGroupName.ToCStr(); const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName ); if ( mapsInGroup ) { int nNumMaps = mapsInGroup->Count(); // we don't have a clear winner if ( m_nEndMatchMapVoteWinner == -1 ) { // if we have a tie for first place, randomly pick between the ones in the lead m_nEndMatchMapVoteWinner = m_nEndMatchTiedVotes[ RandomInt( 0, (m_nEndMatchTiedVotes.Count()-1) ) ]; if ( mp_verbose_changelevel_spew.GetInt() >= 2 ) { Msg( "NEXTLEVELVOTE: Randomly picked winner map=%d from %d tied for nextlevel\n", m_nEndMatchMapVoteWinner, m_nEndMatchTiedVotes.Count() ); } } if ( m_nEndMatchMapVoteWinner < 0 || m_nEndMatchMapVoteWinner >= nNumMaps ) return true; const char* internalMapName = (*mapsInGroup)[m_nEndMatchMapVoteWinner]; if ( internalMapName && V_strcmp( "undefined", internalMapName ) != 0 && V_strlen( internalMapName ) > 0 ) { engine->ServerCommand( CFmtStr( "nextlevel %s;", internalMapName ) ); extern ConVar nextmap_print_enabled; if ( nextmap_print_enabled.GetBool() ) { UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_nextmap", V_GetFileName( internalMapName ) ); } } } } } return true; case k_EEndMatchMapVoteState_VoteAllDone: { } break; } } // check to see if we should change levels now if ( m_flIntermissionStartTime && ( m_flIntermissionStartTime + GetIntermissionDuration() < gpGlobals->curtime ) ) { IGameEvent * leaderboardEvent = gameeventmanager->CreateEvent( "round_end_upload_stats" ); if( leaderboardEvent ) { gameeventmanager->FireEvent( leaderboardEvent ); } if ( !IsPlayingTraining() ) { // Perform round-related processing at the official end of round RoundEnd(); } // get the next map name char szNextMap[MAX_PATH]; if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) ) { if ( mp_verbose_changelevel_spew.GetBool() ) { Msg( "CHANGELEVEL: ConVar '%s' is set, next map will be '%s'\n", nextlevel.GetName(), nextlevel.GetString() ); } Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) ); } else { GetNextLevelName( szNextMap, sizeof(szNextMap) ); } // intermission is over // check to see if we should restart or change maps // mp_match_end_restart trumps everything and just restarts the current map regardless of next map // mp_match_end_changelevel will force a changelevel even if next map matches current map // otherwise if the next map is the same one we are on, don't reload it, just restart the map instead // if the next map is different then changelevel bool bNextLevelDiffers = !!Q_strcmp( szNextMap, STRING( gpGlobals->mapname ) ); bool bWantsChangeLevel = !mp_match_end_restart.GetBool() && ( mp_match_end_changelevel.GetBool() || bNextLevelDiffers ); if ( bWantsChangeLevel == false && mp_verbose_changelevel_spew.GetBool() ) { Msg( "CHANGELEVEL: Not changing level, %s is false, %s is %s %s\n", mp_match_end_restart.GetName(), mp_match_end_changelevel.GetName(), mp_match_end_changelevel.GetBool() ? "true" : "false", !bNextLevelDiffers ? "and next is the same" : "" ); } #if defined GAME_DLL if ( engine->IsDedicatedServer() && DedicatedServerWorkshop().CurrentLevelNeedsUpdate() ) { bWantsChangeLevel = true; // if current level is out of date, we need to change level no matter the settings. } #endif if ( IsQueuedMatchmaking() && ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematchFailed_Done ) ) { // Tell all clients to return to the lobby CReliableBroadcastRecipientFilter filter; CCSUsrMsg_DisconnectToLobby msg; SendUserMessage( filter, CS_UM_DisconnectToLobby, msg ); } else if ( bWantsChangeLevel ) { ChangeLevel(); } else { //Sometimes we don't want the new match the allow players to pick teams, such as in the case of a vote to scramble or swap teams. if ( m_bPickNewTeamsOnReset ) { // Clear the players to unassigned teams. for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( clientIndex ); if(pPlayer) { pPlayer->ChangeTeam( TEAM_UNASSIGNED ); } } } // Restart the round with a complete reset that will clear all the match status such as team scores. m_bCompleteReset = true; // clear the next level nextlevel.SetValue( "" ); RestartRound(); g_fGameOver = false; // Send an event that clients can key off of to update their UI state. IGameEvent *restartEvent = gameeventmanager->CreateEvent( "cs_match_end_restart" ); if( m_bPickNewTeamsOnReset && restartEvent ) { gameeventmanager->FireEvent( restartEvent ); } m_bPickNewTeamsOnReset = true; #if defined ( GAME_DLL ) // If we're taking this path, tell the server ugc manager to check for updates now and force a real change level should we need one. if ( engine->IsDedicatedServer() ) { DedicatedServerWorkshop().CheckIfCurrentLevelNeedsUpdate(); } #endif } // Don't run this code again m_flIntermissionStartTime = 0.f; } return true; } return false; } bool CCSGameRules::CheckFragLimit() { if ( fraglimit.GetInt() <= 0 ) return false; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer && pPlayer->FragCount() >= fraglimit.GetInt() ) { const char *teamName = "UNKNOWN"; if ( pPlayer->GetTeam() ) { teamName = pPlayer->GetTeam()->GetName(); } UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"Intermission_Kill_Limit\"\n", pPlayer->GetPlayerName(), pPlayer->GetUserID(), pPlayer->GetNetworkIDString(), teamName ); GoToIntermission(); return true; } } return false; } void CCSGameRules::CheckFreezePeriodExpired() { float startTime = m_fRoundStartTime; if ( !IsFinite( startTime ) ) { Warning( "Infinite round start time!\n" ); m_fRoundStartTime.GetForModify() = gpGlobals->curtime; } float flStartTime = m_fRoundStartTime; if( IsFinite( startTime ) && ( gpGlobals->curtime < flStartTime ) ) { if ( CSGameRules() && CSGameRules()->IsMatchWaitingForResume() ) { m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime; } // TIMEOUTS if ( m_bTerroristTimeOutActive ) { m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime; m_flTerroristTimeOutRemaining -= ( gpGlobals->curtime - m_flLastThinkTime ); if ( m_flTerroristTimeOutRemaining <= 0 ) { EndTerroristTimeOut(); } } else if ( m_bCTTimeOutActive ) { m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime; m_flCTTimeOutRemaining -= ( gpGlobals->curtime - m_flLastThinkTime ); if ( m_flCTTimeOutRemaining <= 0 ) { EndCTTimeOut(); } } #ifndef CLIENT_DLL else { int nTimeToStart = (int) ((flStartTime - gpGlobals->curtime) + 1.0); if( nTimeToStart <= 3 && m_nLastFreezeEndBeep != nTimeToStart ) { m_nLastFreezeEndBeep = nTimeToStart; IGameEvent *pRoundStartBeepEvent = gameeventmanager->CreateEvent( "cs_round_start_beep" ); if( pRoundStartBeepEvent ) { gameeventmanager->FireEvent( pRoundStartBeepEvent ); } } } #endif return; // not time yet to start round } #ifndef CLIENT_DLL IGameEvent *pRoundStartBeepEvent = gameeventmanager->CreateEvent( "cs_round_final_beep" ); if( pRoundStartBeepEvent ) { gameeventmanager->FireEvent( pRoundStartBeepEvent ); } // Mark all alive players as receiving money, they could join/reconnect at freezetime for ( int iAlivePlayer = 1; iAlivePlayer <= MAX_PLAYERS; ++ iAlivePlayer ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iAlivePlayer ) ); if ( pPlayer ) // mark all players to have all cash available pPlayer->m_iAccountMoneyEarnedForNextRound = 0; if ( pPlayer && pPlayer->IsConnected() && !pPlayer->IsBot() && pPlayer->IsAlive() && !pPlayer->IsControllingBot() ) { switch ( pPlayer->GetTeamNumber() ) { case TEAM_TERRORIST: case TEAM_CT: pPlayer->MarkAsNotReceivingMoneyNextRound( true ); // allow money next round break; } } } #endif // Log this information UTIL_LogPrintf("World triggered \"Round_Start\"\n"); char CT_sentence[40]; char T_sentence[40]; switch ( random->RandomInt( 0, 3 ) ) { case 0: Q_strncpy(CT_sentence,"radio.moveout", sizeof( CT_sentence ) ); Q_strncpy(T_sentence ,"radio.moveout", sizeof( T_sentence ) ); break; case 1: Q_strncpy(CT_sentence, "radio.letsgo", sizeof( CT_sentence ) ); Q_strncpy(T_sentence , "radio.letsgo", sizeof( T_sentence ) ); break; case 2: Q_strncpy(CT_sentence , "radio.locknload", sizeof( CT_sentence ) ); Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) ); break; default: Q_strncpy(CT_sentence , "radio.go", sizeof( CT_sentence ) ); Q_strncpy(T_sentence , "radio.go", sizeof( T_sentence ) ); break; } // More specific radio commands for the new scenarios : Prison & Assasination if (m_bMapHasEscapeZone == TRUE) { Q_strncpy(CT_sentence , "radio.elim", sizeof( CT_sentence ) ); Q_strncpy(T_sentence , "radio.getout", sizeof( T_sentence ) ); } // Freeze period expired: kill the flag m_bFreezePeriod = false; IGameEvent * event = gameeventmanager->CreateEvent( "round_freeze_end" ); if ( event ) { gameeventmanager->FireEvent( event ); } if ( m_match.GetRoundsPlayed() == 0 && !IsWarmupPeriod() ) { IGameEvent * event = gameeventmanager->CreateEvent( "round_announce_match_start" ); if ( event ) gameeventmanager->FireEvent( event ); } if ( !IsPlayingGunGameTRBomb() || ( IsPlayingGunGameTRBomb() && m_match.GetPhase() != GAMEPHASE_MATCH_ENDED ) ) { // Update the timers for all clients and play a sound bool bCTPlayed = false; bool bTPlayed = false; // Don't play start round VO for terrorists in coop if ( IsPlayingCoopMission() ) bTPlayed = true; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = CCSPlayer::Instance( i ); if ( pPlayer && !FNullEnt( pPlayer->edict() ) ) { if ( pPlayer->State_Get() == STATE_ACTIVE ) { if ( (pPlayer->GetTeamNumber() == TEAM_CT) && !bCTPlayed ) { pPlayer->Radio( CT_sentence ); bCTPlayed = true; } else if ( (pPlayer->GetTeamNumber() == TEAM_TERRORIST) && !bTPlayed ) { pPlayer->Radio( T_sentence ); bTPlayed = true; } pPlayer->UpdateFreezetimeEndEquipmentValue(); } //pPlayer->SyncRoundTimer(); } } } if ( mp_use_respawn_waves.GetInt() == 2 && IsPlayingCoopGuardian() ) { int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT; //int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST; // if ( nTeam == TEAM_CT ) // m_bCTCantBuy = true; // else // m_bTCantBuy = true; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->GetTeamNumber() == nTeam && !IsWarmupPeriod() ) { CRecipientFilter filter; filter.AddRecipient( pPlayer ); const char *szTeam = (nTeam == TEAM_CT) ? "CT" : "T"; char szNotice[512]; Q_snprintf( szNotice, sizeof( szNotice ), "#SFUI_Notice_NewWaveBegun_%s0", szTeam ); char szWave[32]; Q_snprintf( szWave, sizeof( szWave ), "%d", ( int )m_nGuardianModeWaveNumber ); UTIL_ClientPrintFilter( filter, HUD_PRINTCENTER, szNotice, szWave ); } } } } void CCSGameRules::CheckRoundTimeExpired() { if ( mp_ignore_round_win_conditions.GetBool() || IsWarmupPeriod() ) return; if ( GetRoundRemainingTime() > 0 || m_iRoundWinStatus != WINNER_NONE ) return; //We haven't completed other objectives, so go for this!. if( !m_bFirstConnected ) return; // New code to get rid of round draws!! if ( IsPlayingGunGameDeathmatch() ) { // TODO: make this a shared function so playercount runs the same code CCSPlayer *pWinner = NULL; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { if ( pWinner == NULL ) pWinner = pPlayer; if ( pWinner != pPlayer ) { if ( pWinner->GetScore() > pPlayer->GetScore() ) continue; else if ( pWinner->GetScore() < pPlayer->GetScore() ) pWinner = pPlayer; else pWinner = (pWinner->entindex() > pPlayer->entindex()) ? pWinner : pPlayer; } } } if ( pWinner ) { if ( pWinner->GetTeamNumber() == TEAM_CT ) TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win ); else TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); } else { TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw ); } } else if ( IsPlayingCoopMission() ) { // When playing coop mission Terrorists win if timer runs out bool bCTsFailedToDeploy = m_coopPlayersInDeploymentZone; // However if CTs even failed to deploy in time kick them from the server m_match.AddTerroristWins( 1 ); if ( bCTsFailedToDeploy ) { if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) ) pTeam->MarkSurrendered(); CReliableBroadcastRecipientFilter filter; UTIL_ClientPrintFilter(filter, HUD_PRINTCENTER, "#SFUIHUD_InfoPanel_Coop_DeployMissionBust" ); UTIL_ClientPrintAll( HUD_PRINTTALK, "#SFUIHUD_InfoPanel_Coop_DeployMissionBust" ); } TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); if ( bCTsFailedToDeploy ) { // Mission is a bust m_match.SetPhase( GAMEPHASE_MATCH_ENDED ); m_phaseChangeAnnouncementTime = gpGlobals->curtime + 1.5; GoToIntermission(); } } else if ( mp_default_team_winner_no_objective.GetInt() != -1 ) { int nTeam = mp_default_team_winner_no_objective.GetInt(); if ( nTeam == TEAM_CT ) { m_match.AddCTWins( 1 ); if ( IsPlayingCoopGuardian() && !IsHostageRescueMap() ) { if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) ) pTeam->MarkSurrendered(); } else { MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound( TEAM_TERRORIST ); AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB ); } TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win ); } else if ( nTeam == TEAM_TERRORIST ) { m_match.AddTerroristWins( 1 ); if ( IsPlayingCoopGuardian() && IsHostageRescueMap() ) { if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) ) pTeam->MarkSurrendered(); } else { MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound( TEAM_CT ); AddTeamAccount( TEAM_TERRORIST, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE ); } TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); } else { TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw ); } } else if ( m_bMapHasBombTarget ) { //If the bomb is planted, don't let the round timer end the round. //keep going until the bomb explodes or is defused if( !m_bBombPlanted ) { m_match.AddCTWins( 1 ); MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_TERRORIST); AddTeamAccount( TEAM_CT, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB ); TerminateRound( mp_round_restart_delay.GetFloat(), Target_Saved ); } } else if ( m_bMapHasRescueZone ) { MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_CT); m_match.AddTerroristWins( 1 ); AddTeamAccount( TEAM_TERRORIST, TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE ); TerminateRound( mp_round_restart_delay.GetFloat(), Hostages_Not_Rescued ); } else if ( m_bMapHasEscapeZone ) { m_match.AddCTWins( 1 ); TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Not_Escaped ); } //If there is no scenario-specific winner when the time runs out, just declare a draw else { TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw ); } } bool CCSGameRules::ShouldGunGameSpawnBomb( void ) { return ( m_bGunGameRespawnWithBomb && ( m_fGunGameBombRespawnTimer <= gpGlobals->curtime ) ); } void CCSGameRules::SetGunGameSpawnBomb( bool allow ) { m_bGunGameRespawnWithBomb = allow; m_fGunGameBombRespawnTimer = gpGlobals->curtime + mp_ggtr_bomb_respawn_delay.GetFloat(); } void CCSGameRules::RewardMatchEndDrops( bool bAbortedMatch ) { } void CCSGameRules::GoToIntermission( bool bAbortedMatch ) { Msg( "Going to intermission...\n" ); bool bAnnounceNextMap = true; bool bDoGenericRewardMatchEndDrops = true; // Do old style match end drops (community and official non-competitive) if ( bDoGenericRewardMatchEndDrops ) { RewardMatchEndDrops( bAbortedMatch ); } else { ClearItemsDroppedDuringMatch(); } m_bIsDroppingItems = true; // Always wait in case items drop // generate the map list that players will be voting on CreateEndMatchMapGroupVoteOptions(); m_match.SetPhase( GAMEPHASE_MATCH_ENDED ); m_bHasMatchStarted = false; IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" ); if ( winEvent ) { gameeventmanager->FireEvent( winEvent ); } BaseClass::GoToIntermission(); // // Tell everyone what the next map is // bool bNextMapAlreadySet = false; char szNextMap[ MAX_PATH ] = { 0 }; if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) ) { bNextMapAlreadySet = true; Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) ); } else { GetNextLevelName( szNextMap, sizeof( szNextMap ) ); } // if we're running map group, let players vote to pick the next map at the end of the match if ( IsEndMatchVotingForNextMap() && !bNextMapAlreadySet && GetCMMItemDropRevealEndTime() < gpGlobals->curtime ) { bAnnounceNextMap = !CheckSetVoteTime(); } else if ( !m_bIsDroppingItems ) { m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteAllDone; } // In queued matchmaking or during a "season" map group, don't message the next map if ( IsQueuedMatchmaking() || bAnnounceNextMap == false ) { szNextMap[ 0 ] = 0; } //Clear various states from all players and freeze them in place for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->Unblind(); pPlayer->AddFlag( FL_FROZEN ); pPlayer->SetPendingTeamNum( TEAM_UNASSIGNED ); UpdateMatchStats( pPlayer, m_match.GetWinningTeam() ); extern ConVar nextmap_print_enabled; if ( szNextMap[ 0 ] && nextmap_print_enabled.GetBool() ) { if ( IsGameConsole() || engine->IsDedicatedServerForXbox() || engine->IsDedicatedServerForPS3() ) { // we know all the map names on console so we can safely assume we have a token for all maps const int nMaxLength = MAX_MAP_NAME + 10 + 1; char szToken[ nMaxLength ]; CreateFriendlyMapNameToken( szNextMap, szToken, nMaxLength ); ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szToken ); } else { ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", V_GetFileName( szNextMap ) ); } } } } // freeze players while in intermission m_bFreezePeriod = true; // When not queued matchmaking that is driven by rematch endmatch vote machine reveal all ranks now if ( !IsQueuedMatchmaking() ) { CReliableBroadcastRecipientFilter filter; CCSUsrMsg_ServerRankRevealAll msg; SendUserMessage( filter, CS_UM_ServerRankRevealAll, msg ); } } bool CCSGameRules::CheckSetVoteTime() { int numMaps = 0; if ( g_pGameTypes ) { const char* mapGroupName = gpGlobals->mapGroupName.ToCStr(); const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName ); if ( mapsInGroup ) numMaps = mapsInGroup->Count(); } if ( numMaps > 1 ) { m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteInProgress; m_nEndMatchTiedVotes.RemoveAll(); // artificially set the m_flIntermissionStartTime if ( m_flIntermissionStartTime && ( gpGlobals->curtime + mp_endmatch_votenextleveltime.GetFloat() > m_flIntermissionStartTime + GetIntermissionDuration() ) ) { m_flIntermissionStartTime = gpGlobals->curtime + mp_endmatch_votenextleveltime.GetFloat() - GetIntermissionDuration(); } return true; } m_eEndMatchMapVoteState = k_EEndMatchMapVoteState_VoteAllDone; return false; } void CCSGameRules::GoToMatchRestartIntermission() { //We use our own intermission steps here to bypass the match win panel (AKA scoreboard) BaseClass::GoToIntermission(); for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->Unblind(); // set all players to FL_FROZEN pPlayer->AddFlag( FL_FROZEN ); } } // freeze players while in intermission m_bFreezePeriod = true; } bool CCSGameRules::GameModeSupportsHealthBuffer( void ) { return false; } void CCSGameRules::UpdateMatchStats( CCSPlayer* pPlayer, int winnerIndex ) { if( pPlayer->GetTeamNumber() == winnerIndex) { CCS_GameStats.IncrementStat( pPlayer, CSSTAT_MATCHES_WON, 1, false, true ); if( IsPlayingGunGame() ) { if( ( IsPlayingGunGameProgressive() && pPlayer->MadeFinalGunGameProgressiveKill() ) || ( IsPlayingGunGameTRBomb() ) ) { CCS_GameStats.IncrementStat(pPlayer,CSSTAT_GUN_GAME_MATCHES_WON,1); } if( IsPlayingGunGameProgressive() ) { CCS_GameStats.IncrementStat(pPlayer,CSSTAT_GUN_GAME_PROGRESSIVE_MATCHES_WON,1); // check for a rampage: See if we made it thru gun game progressive without dying once if( pPlayer->WasKilledThisRound() == false && pPlayer->MadeFinalGunGameProgressiveKill() ) { pPlayer->AwardAchievement(CSGunGameProgressiveRampage); } // see if we won a progressive match without reloading if( !pPlayer->HasReloaded() ) { pPlayer->AwardAchievement(CSGunGameConservationist); } } else if( IsPlayingGunGameTRBomb()) { CCS_GameStats.IncrementStat(pPlayer,CSSTAT_GUN_GAME_TRBOMB_MATCHES_WON,1); } } int mapIndex = GetCSLevelIndex(gpGlobals->mapname.ToCStr()); if ( mapIndex != -1 ) { CSStatType_t matchStatId = MapName_StatId_Table[mapIndex].matchesWonId; // look up our map information and increment the matches won stat. Rounds and other stats are done elsewhere if(matchStatId != CSSTAT_UNDEFINED) CCS_GameStats.IncrementStat( pPlayer,matchStatId, 1, false, true ); } } else if( winnerIndex == WINNER_DRAW ) { CCS_GameStats.IncrementStat( pPlayer, CSSTAT_MATCHES_DRAW, 1, false, true ); } CCS_GameStats.IncrementStat( pPlayer, CSSTAT_MATCHES_PLAYED, 1, false, true ); if( IsPlayingGunGame() ) { CCS_GameStats.IncrementStat( pPlayer, CSSTAT_GUN_GAME_MATCHES_PLAYED, 1, false, true ); } // CSN-8618 and others - Make sure these get sent before stats are reset. // End rounds stats are sent BEFORE this call, and the match ends before the next send happens // (resetting what we're recording above). Send these now so they don't get lost. CCS_GameStats.SendStatsToPlayer( pPlayer, CSSTAT_PRIORITY_ENDROUND ); } // --------------------------------------------------------------------------------------------------- // // Contribution score helper functions // --------------------------------------------------------------------------------------------------- // inline float FalloffWeight( float fDistance, float fRangeInner, float fRangeOuter ) { if ( fDistance > fRangeInner ) return (fRangeOuter - fDistance) / (fRangeOuter - fRangeInner); else return 1.0f; } // splits point amongst all players in zone void CCSGameRules::SplitScoreAmongPlayersInZone( int iPoints, int iTeam, CCSPlayer* pExcludePlayer, uint iPlace ) { float fWeights[MAX_PLAYERS+1]; memset( fWeights, 0, sizeof( fWeights ) ); float fWeightSum = 0.0f; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; if ( pPlayer == pExcludePlayer ) continue; if ( pPlayer->GetTeamNumber() != iTeam ) continue; if ( pPlayer->GetLastKnownArea() && pPlayer->GetLastKnownArea()->GetPlace() == iPlace ) { fWeightSum += 1.0f; fWeights[i] = 1.0f; } } if ( fWeightSum == 0.0f ) return; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { if ( fWeights[i] > 0.0f ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); ASSERT( pPlayer ); int score = RoundFloatToInt(iPoints * fWeights[i] / fWeightSum ); pPlayer->AddScore( score ); pPlayer->AddRoundProximityScore( score ); } } } void CCSGameRules::SplitScoreAmongPlayersInRange( int iPoints, int iTeam, CCSPlayer* pExcludePlayer, const Vector& center, float fRangeInner, float fRangeOuter ) { float fMaxRangeSquared = fRangeOuter * fRangeOuter; float fWeights[MAX_PLAYERS+1]; memset( fWeights, 0, sizeof( fWeights ) ); float fWeightSum = 0.0f; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); if ( !pPlayer ) continue; if ( pPlayer == pExcludePlayer ) continue; if ( pPlayer->GetTeamNumber() != iTeam ) continue; if ( center.DistToSqr( pPlayer->GetAbsOrigin() ) <= fMaxRangeSquared ) { float fWeight = FalloffWeight( center.DistTo( pPlayer->GetAbsOrigin()), fRangeInner, fRangeOuter ); fWeightSum += fWeight; fWeights[i] = fWeight; } } if ( fWeightSum == 0.0f ) return; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { if ( fWeights[i] > 0.0f ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); int score = RoundFloatToInt( iPoints * fWeights[i] / fWeightSum ); pPlayer->AddScore( score ); pPlayer->AddRoundProximityScore( score ); } } } void CCSGameRules::ScorePlayerKill( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { if ( IsPlayingGunGameDeathmatch() && pPlayer->GetActiveCSWeapon() ) { CEconItemView* pItem = pPlayer->GetActiveCSWeapon()->GetEconItemView(); CSWeaponID wepID = ( pItem && pItem->IsValid() ) ? (CSWeaponID)(pItem->GetItemIndex()) : pPlayer->GetActiveCSWeapon()->GetCSWeaponID(); int iWepSlot = ( pItem && pItem->GetItemDefinition() ) ? pItem->GetItemDefinition()->GetDefaultLoadoutSlot() : LOADOUT_POSITION_INVALID; int nScore = GetWeaponScoreForDeathmatch( iWepSlot ); if ( pPlayer->AddDeathmatchKillScore( nScore, wepID, iWepSlot ) <= 0 ) { pPlayer->AddContributionScore( contributionscore_kill.GetInt() ); } } else { pPlayer->AddContributionScore( contributionscore_kill.GetInt() ); pPlayer->AddScore( RoundFloatToInt( score_kill_enemy_bonus.GetFloat() ) ); } } } void CCSGameRules::ScorePlayerAssist( CCSPlayer* pPlayer, CCSPlayer* pCSVictim ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { if ( IsPlayingGunGameDeathmatch() && pPlayer->GetActiveCSWeapon() ) { CEconItemView* pItem = pPlayer->GetActiveCSWeapon()->GetEconItemView(); CSWeaponID wepID = ( pItem && pItem->IsValid() ) ? (CSWeaponID)(pItem->GetItemIndex()) : pPlayer->GetActiveCSWeapon()->GetCSWeaponID(); //int nScore = GetWeaponScoreForDeathmatch( wepID ); // we don't store what weapon the player did the assist damage with and we can't guarantee the player has a weapon // when the assist is awarded, so we just give them and average of half the points awarded int nScore = 6; pPlayer->AddDeathmatchKillScore( nScore, wepID, pItem->GetItemDefinition()->GetDefaultLoadoutSlot(), true, pCSVictim->GetPlayerName() ); pPlayer->IncrementAssistsCount( 1 ); } else if ( IPointsForKill( pPlayer, pCSVictim ) > 0 ) // this ensures that only assists on enemies are recorded, but "assists" for teammate kills are not { pPlayer->AddContributionScore( contributionscore_assist.GetInt() ); pPlayer->IncrementAssistsCount( 1 ); } } } void CCSGameRules::ScorePlayerObjectiveKill( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( contributionscore_objective_kill.GetInt() ); } } void CCSGameRules::ScorePlayerTeamKill( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( contributionscore_team_kill.GetInt( ) ); } } void CCSGameRules::ScorePlayerSuicide( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( contributionscore_suicide.GetInt( ) ); if ( !m_bLoadingRoundBackupData ) pPlayer->ProcessSuicideAsKillReward(); } } void CCSGameRules::ScorePlayerDamage( CCSPlayer* pPlayer, float fDamage ) { pPlayer->AddScore( RoundFloatToInt( fDamage * score_damage.GetFloat() ) ); Place iPlace = pPlayer->GetLastKnownArea() ? pPlayer->GetLastKnownArea()->GetPlace() : UNDEFINED_PLACE; if ( iPlace != UNDEFINED_PLACE ) SplitScoreAmongPlayersInZone( RoundFloatToInt( fDamage * score_team_damage_bonus.GetFloat() ), pPlayer->GetTeamNumber(), pPlayer, iPlace ); // award bonus for being near planted bomb { const float fRadiusInner = score_planted_bomb_proximity_damage_radius_inner.GetFloat(); const float fRadiusOuter = score_planted_bomb_proximity_damage_radius_outer.GetFloat(); const float fRadiusOuterSquared = fRadiusOuter * fRadiusOuter; FOR_EACH_VEC( g_PlantedC4s, iBomb ) { CPlantedC4 *pC4 = g_PlantedC4s[iBomb]; if ( pC4 && pC4->IsBombActive() ) { Vector bombPos = pC4->GetAbsOrigin(); Vector playerToBomb = pPlayer->GetAbsOrigin() - bombPos; if ( playerToBomb.LengthSqr() < fRadiusOuterSquared ) { float fWeight = FalloffWeight( playerToBomb.Length(), fRadiusInner, fRadiusOuter ); int score = RoundFloatToInt( fWeight * score_planted_bomb_proximity_damage_bonus.GetFloat() ); pPlayer->AddScore( score ); pPlayer->AddRoundProximityScore( score ); break; } } } } // bonus for being near hostage const float fRadiusInner = score_hostage_proximity_damage_radius_inner.GetFloat(); const float fRadiusOuter = score_hostage_proximity_damage_radius_outer.GetFloat(); float fClosestSquared = FLT_MAX; FOR_EACH_VEC( g_Hostages, iHostage ) { CHostage* pHostage = g_Hostages[iHostage]; if ( pHostage->m_iHealth > 0 && !pHostage->IsRescued() ) { float fDistanceSquared = pHostage->GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ); if ( fDistanceSquared < fClosestSquared ) { fClosestSquared = fDistanceSquared; } } } if ( fClosestSquared < fRadiusOuter * fRadiusOuter ) { float fWeight = FalloffWeight( sqrtf( fClosestSquared ), fRadiusInner, fRadiusOuter ); int score = RoundFloatToInt( fDamage * fWeight * score_hostage_proximity_damage_bonus.GetFloat() ); pPlayer->AddScore( score ); pPlayer->AddRoundProximityScore( score ); } // damage near dropped bomb bonus { const float fRadiusInner = score_dropped_bomb_proximity_damage_bonus_radius_inner.GetFloat(); const float fRadiusOuter = score_dropped_bomb_proximity_damage_bonus_radius_outer.GetFloat(); CBaseEntity* pC4 = gEntList.FindEntityByClassnameNearest( "weapon_c4", pPlayer->GetAbsOrigin(), fRadiusOuter ); if ( pC4 && pC4->GetOwnerEntity() == NULL ) { float fWeight = FalloffWeight( pC4->GetAbsOrigin().DistTo( pPlayer->GetAbsOrigin()), fRadiusInner, fRadiusOuter ); int score = RoundFloatToInt( fDamage * fWeight * score_dropped_bomb_proximity_damage_bonus.GetFloat() ); pPlayer->AddScore( score ); pPlayer->AddRoundProximityScore( score ); } } // damage near dropped defuser { const float fRadiusInner = score_dropped_defuser_proximity_damage_radius_inner.GetFloat(); const float fRadiusOuter = score_dropped_defuser_proximity_damage_radius_outer.GetFloat(); CBaseEntity* pDefuser = gEntList.FindEntityByClassnameNearest( "item_defuser", pPlayer->GetAbsOrigin(), fRadiusOuter ); if ( pDefuser && pDefuser->GetOwnerEntity() == NULL ) { float fWeight = FalloffWeight( pDefuser->GetAbsOrigin().DistTo( pPlayer->GetAbsOrigin() ), fRadiusInner, fRadiusOuter ); int score = RoundFloatToInt( fDamage * fWeight * score_dropped_defuser_proximity_damage_bonus.GetFloat() ); pPlayer->AddScore( score ); pPlayer->AddRoundProximityScore( score ); } } } void CCSGameRules::ScoreBombPlant( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( contributionscore_bomb_planted.GetInt() ); } CPlantedC4 *pC4 = g_PlantedC4s[0]; ASSERT( pC4 != NULL ); if ( pC4 ) { SplitScoreAmongPlayersInRange( score_bomb_plant_bonus.GetInt(), TEAM_TERRORIST, NULL, pC4->GetAbsOrigin(), score_bomb_plant_radius_inner.GetFloat(), score_bomb_plant_radius_outer.GetFloat() ); } } void CCSGameRules::ScoreBombExploded( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( contributionscore_bomb_exploded.GetInt() ); } } void CCSGameRules::ScoreBombDefuse( CCSPlayer* pPlayer, bool bMajorEvent ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( bMajorEvent ? contributionscore_bomb_defuse_major.GetInt() : contributionscore_bomb_defuse_minor.GetInt() ); } m_bBombDefused = true; CPlantedC4 *pC4 = g_PlantedC4s[0]; ASSERT( pC4 != NULL ); if ( pC4 && bMajorEvent ) { SplitScoreAmongPlayersInRange( score_bomb_defuse_bonus.GetInt(), TEAM_CT, NULL, pC4->GetAbsOrigin(), score_bomb_defuse_radius_inner.GetFloat(), score_bomb_defuse_radius_outer.GetFloat() ); } } void CCSGameRules::ScoreHostageRescue( CCSPlayer* pPlayer, CHostage* pHostage, bool bMajorEvent ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( bMajorEvent ? contributionscore_hostage_rescue_major.GetInt() : contributionscore_hostage_rescue_minor.GetInt() ); } ASSERT( pHostage != NULL ); if ( pHostage && bMajorEvent ) { SplitScoreAmongPlayersInRange( score_hostage_rescue_bonus.GetInt(), TEAM_CT, NULL, pHostage->GetAbsOrigin(), score_hostage_rescue_radius_inner.GetFloat(), score_hostage_rescue_radius_outer.GetFloat() ); } } void CCSGameRules::ScoreHostageKilled( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddContributionScore( contributionscore_hostage_kill.GetInt() ); } } void CCSGameRules::ScoreHostageDamage( CCSPlayer* pPlayer, float fDamage ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddScore( -RoundFloatToInt( fDamage * score_hostage_damage_penalty.GetInt() ) ); } } void CCSGameRules::ScoreFriendlyFire( CCSPlayer* pPlayer, float fDamage ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddScore( -RoundFloatToInt( fDamage * score_ff_damage.GetFloat() ) ); } } void CCSGameRules::ScoreBlindEnemy( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddScore( score_blind_enemy_bonus.GetInt() ); if ( ShouldRecordMatchStats() ) { // match stats for blinding enemy pPlayer->m_iMatchStats_EnemiesFlashed.GetForModify( GetRoundsPlayed( ) ) ++; // record in MatchStats' // Keep track in QMM data if ( pPlayer->GetHumanPlayerAccountID() ) { if ( CCSGameRules::CQMMPlayerData_t *pQMM = QueuedMatchmakingPlayersDataFind( pPlayer->GetHumanPlayerAccountID() ) ) { pQMM->m_iMatchStats_EnemiesFlashed[ GetRoundsPlayed( ) ] = pPlayer->m_iMatchStats_EnemiesFlashed.Get( GetRoundsPlayed( ) ); } } } } } void CCSGameRules::ScoreBlindFriendly( CCSPlayer* pPlayer ) { ASSERT( pPlayer != NULL ); if ( pPlayer ) { pPlayer->AddScore( -score_blind_friendly_penalty.GetInt() ); } } // --------------------------------------------------------------------------------------------------- // // End Contribution score functions // --------------------------------------------------------------------------------------------------- // static void PrintToConsole( CBasePlayer *player, const char *text ) { if ( player ) { ClientPrint( player, HUD_PRINTCONSOLE, text ); } else { Msg( "%s", text ); } } void CCSGameRules::DumpTimers( void ) const { extern ConVar bot_join_delay; CBasePlayer *player = UTIL_GetCommandClient(); CFmtStr str; PrintToConsole( player, str.sprintf( "Timers and related info at %f:\n", gpGlobals->curtime ) ); PrintToConsole( player, str.sprintf( "m_bCompleteReset: %d\n", m_bCompleteReset ) ); PrintToConsole( player, str.sprintf( "m_iTotalRoundsPlayed: %d\n", m_iTotalRoundsPlayed ) ); PrintToConsole( player, str.sprintf( "m_iRoundTime: %d\n", m_iRoundTime.Get() ) ); PrintToConsole( player, str.sprintf( "m_iRoundWinStatus: %d\n", m_iRoundWinStatus.Get() ) ); PrintToConsole( player, str.sprintf( "first connected: %d\n", m_bFirstConnected ) ); PrintToConsole( player, str.sprintf( "intermission start time: %f\n", m_flIntermissionStartTime ) ); PrintToConsole( player, str.sprintf( "intermission duration: %f\n", GetIntermissionDuration() ) ); PrintToConsole( player, str.sprintf( "freeze period: %d\n", m_bFreezePeriod.Get() ) ); PrintToConsole( player, str.sprintf( "round restart time: %f\n", m_flRestartRoundTime.Get() ) ); PrintToConsole( player, str.sprintf( "game start time: %f\n", m_flGameStartTime.Get() ) ); PrintToConsole( player, str.sprintf( "m_fMatchStartTime: %f\n", m_fMatchStartTime.Get() ) ); PrintToConsole( player, str.sprintf( "m_fRoundStartTime: %f\n", m_fRoundStartTime.Get() ) ); PrintToConsole( player, str.sprintf( "freeze time: %d\n", m_iFreezeTime ) ); PrintToConsole( player, str.sprintf( "next think: %f\n", m_tmNextPeriodicThink ) ); PrintToConsole( player, str.sprintf( "fraglimit: %d\n", fraglimit.GetInt() ) ); PrintToConsole( player, str.sprintf( "mp_maxrounds: %d\n", mp_maxrounds.GetInt() ) ); PrintToConsole( player, str.sprintf( "mp_winlimit: %d\n", mp_winlimit.GetInt() ) ); PrintToConsole( player, str.sprintf( "bot_quota: %d\n", cv_bot_quota.GetInt() ) ); PrintToConsole( player, str.sprintf( "bot_quota_mode: %s\n", cv_bot_quota_mode.GetString() ) ); PrintToConsole( player, str.sprintf( "bot_join_after_player: %d\n", cv_bot_join_after_player.GetInt() ) ); PrintToConsole( player, str.sprintf( "bot_join_delay: %d\n", bot_join_delay.GetInt() ) ); PrintToConsole( player, str.sprintf( "nextlevel: %s\n", nextlevel.GetString() ) ); int humansInGame = UTIL_HumansInGame( true ); int botsInGame = UTIL_BotsInGame(); PrintToConsole( player, str.sprintf( "%d humans and %d bots in game\n", humansInGame, botsInGame ) ); PrintToConsole( player, str.sprintf( "num CTs (spawnable): %d (%d)\n", m_iNumCT, m_iNumSpawnableCT ) ); PrintToConsole( player, str.sprintf( "num Ts (spawnable): %d (%d)\n", m_iNumTerrorist, m_iNumSpawnableTerrorist ) ); if ( g_fGameOver ) { PrintToConsole( player, str.sprintf( "Game is over!\n" ) ); } PrintToConsole( player, str.sprintf( "\n" ) ); } CON_COMMAND( mp_dump_timers, "Prints round timers to the console for debugging" ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; if ( CSGameRules() ) { CSGameRules()->DumpTimers(); } } // living players on the given team need to be marked as not receiving any money // next round. void CCSGameRules::MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int team) { int playerNum; for (playerNum = 1; playerNum <= gpGlobals->maxClients; ++playerNum) { CCSPlayer *player = (CCSPlayer *)UTIL_PlayerByIndex(playerNum); if (player == NULL) { continue; } if ((player->GetTeamNumber() == team) && (player->IsAlive())) { // Exception here: only here and not inside "MarkAsNotReceivingMoneyNextRound" // to not affect team-wide money management, round backups, etc. // When a player is alive and controlling a bot then the monetary punishment should go // to the bot being controlled if ( player->IsControllingBot() ) { if ( CCSPlayer* controlledBotPlayer = player->GetControlledBot() ) player = controlledBotPlayer; } player->MarkAsNotReceivingMoneyNextRound(); } } } void CCSGameRules::CheckLevelInitialized( void ) { if ( !m_bLevelInitialized ) { // Count the number of spawn points for each team // This determines the maximum number of players allowed on each m_iSpawnPointCount_Terrorist = 0; m_iSpawnPointCount_CT = 0; m_iMaxNumTerrorists = 0; m_iMaxNumCTs = 0; m_flCoopRespawnAndHealTime = -1; const char * szMapName = STRING( gpGlobals->mapname ); int nNumSlots = 2; uint32 dwRichPresenceContext = 0xFFFF; if ( szMapName ) { g_pGameTypes->GetMapInfo( szMapName, dwRichPresenceContext ); } int iGameType = g_pGameTypes->GetCurrentGameType(); int iGameMode = g_pGameTypes->GetCurrentGameMode(); nNumSlots = g_pGameTypes->GetMaxPlayersForTypeAndMode( iGameType, iGameMode ); // for the training map, our max is 1 CT and 0 T's, diving 1 in half doesn't work for us in this case, so make sure our min is 1 m_iMaxNumTerrorists = MAX( nNumSlots / 2, 1 ); m_iMaxNumCTs = MAX( nNumSlots / 2, 1 ); m_iSpectatorSlotCount = mp_spectators_max.GetInt(); m_nGuardianModeWaveNumber = 1; m_nGuardianModeSpecialKillsRemaining = mp_guardian_special_kills_needed.GetInt(); // create the spawn point lists here GenerateSpawnPointListsFirstTime(); // Is this a logo map? if ( gEntList.FindEntityByClassname( NULL, "info_player_logo" ) ) m_bLogoMap = true; m_bLevelInitialized = true; } } void CCSGameRules::DoCoopSpawnAndNavInit( void ) { // TODO: here is where we should pick the spawn points we will use for coop and delete the rest if ( m_vecMainCTSpawnPos == Vector( 0, 0, 0 ) ) { CBaseEntity* ent = NULL; // we need to find at least one CT spawn point to figure out where the "start" of the map is while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent ); if ( pSpawnPoint ) { m_vecMainCTSpawnPos = pSpawnPoint->GetAbsOrigin(); break; } } } } if ( TheNavMesh && !TheNavMesh->IsLoaded() && !TheNavMesh->IsAnalyzed() && !TheNavMesh->IsGenerating() ) { // If there isn't a Navigation Mesh in memory, create one // we do this here because we need to generate the nav each time we create a new map // and generated it when a bot join is too late ConVarRef nav_generate_fast_for_tilegen( "nav_generate_fast_for_tilegen" ); if ( nav_generate_fast_for_tilegen.IsValid() ) nav_generate_fast_for_tilegen.SetValue( 1 ); TheNavMesh->BeginGeneration(); } else { ConVarRef bot_quota( "bot_quota" ); bot_quota.SetValue( 20 ); } } void CCSGameRules::AddSpawnPointToMasterList( SpawnPoint* pSpawnPoint ) { //if classname == T if ( FClassnameIs( pSpawnPoint, "info_player_terrorist" ) || FClassnameIs( pSpawnPoint, "info_enemy_terrorist_spawn" ) || FClassnameIs( pSpawnPoint, "info_armsrace_terrorist" ) ) { // check to make sure it isn't already in the list if ( m_TerroristSpawnPointsMasterList.Find( pSpawnPoint ) != m_TerroristSpawnPointsMasterList.InvalidIndex() ) { AssertMsg( false, "AddSpawnPointToMasterList tried to add a spawn point to the list, but it already exists in the list!" ); return; } m_TerroristSpawnPointsMasterList.AddToTail( pSpawnPoint ); } else if ( FClassnameIs( pSpawnPoint, "info_player_counterterrorist" ) || FClassnameIs( pSpawnPoint, "info_armsrace_counterterrorist" ) ) { if ( m_CTSpawnPointsMasterList.Find( pSpawnPoint ) != m_CTSpawnPointsMasterList.InvalidIndex() ) { AssertMsg( false, "AddSpawnPointToMasterList tried to add a spawn point to the list, but it already exists in the list!" ); return; } m_CTSpawnPointsMasterList.AddToTail( pSpawnPoint ); } else if ( FClassnameIs( pSpawnPoint, "info_deathmatch_spawn" ) ) { // No team specific spawns in this mode } else { // doesn't match any classes we are aware of! AssertMsg( false, "AddSpawnPointToMasterList is looking to adda class to the master spawn list, but it doesn't recognize the class type!" ); } RefreshCurrentSpawnPointLists(); } void CCSGameRules::GenerateSpawnPointListsFirstTime( void ) { //CUtlVector< SpawnPoint* > m_CTSpawnPointsMasterList; // The master list of CT spawn points (contains all points whether enabled or disabled) //CUtlVector< SpawnPoint* > m_TerroristSpawnPointsMasterList; // The master list of Terrorist spawn points (contains all points whether enabled or disabled) // Clear out existing spawn point lists m_TerroristSpawnPointsMasterList.RemoveAll(); m_CTSpawnPointsMasterList.RemoveAll(); const char* szTSpawnEntName = "info_player_terrorist"; CBaseEntity* ent = NULL; if ( IsPlayingCoopMission() ) szTSpawnEntName = "info_enemy_terrorist_spawn"; while ( ( ent = gEntList.FindEntityByClassname( ent, szTSpawnEntName ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent ); if ( pSpawnPoint ) { // Store off the terrorist spawn point m_TerroristSpawnPointsMasterList.AddToTail( pSpawnPoint ); } } else { Warning("Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n", ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[1],ent->GetAbsOrigin()[2] ); } } while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent ); if ( pSpawnPoint ) { // Store off the CT spawn point m_CTSpawnPointsMasterList.AddToTail( pSpawnPoint ); } } else { Warning("Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n", ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[1],ent->GetAbsOrigin()[2] ); } } ent = NULL; // if we're playing armsrace, add the armsrace spawns to the list as well if ( CSGameRules()->IsPlayingGunGameProgressive() ) { while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_terrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent ); if ( pSpawnPoint ) { pSpawnPoint->m_nType = SpawnPoint::ArmsRace; // Store off the terrorist spawn point m_TerroristSpawnPointsMasterList.AddToTail( pSpawnPoint ); } } else { Warning( "Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n", ent->GetAbsOrigin()[0], ent->GetAbsOrigin()[1], ent->GetAbsOrigin()[2] ); } } while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { SpawnPoint* pSpawnPoint = assert_cast< SpawnPoint* >( ent ); if ( pSpawnPoint ) { pSpawnPoint->m_nType = SpawnPoint::ArmsRace; // Store off the CT spawn point m_CTSpawnPointsMasterList.AddToTail( pSpawnPoint ); } } else { Warning( "Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n", ent->GetAbsOrigin()[0], ent->GetAbsOrigin()[1], ent->GetAbsOrigin()[2] ); } } } // we want the arms race spawns to shuffle with the regular spawns if ( CSGameRules()->IsPlayingGunGameProgressive() ) ShuffleMasterSpawnPointLists(); // sort them to ensure the priority ones are up front SortMasterSpawnPointLists(); RefreshCurrentSpawnPointLists(); } void CCSGameRules::RefreshCurrentSpawnPointLists( void ) { // Clear out existing spawn point lists m_TerroristSpawnPoints.RemoveAll(); m_CTSpawnPoints.RemoveAll(); m_iSpawnPointCount_Terrorist = 0; m_iSpawnPointCount_CT = 0; FOR_EACH_VEC( m_TerroristSpawnPointsMasterList, i ) { SpawnPoint* pSpawnPoint = m_TerroristSpawnPointsMasterList[i]; if ( pSpawnPoint && pSpawnPoint->IsEnabled() ) { m_iSpawnPointCount_Terrorist++; // Store off the terrorist spawn point m_TerroristSpawnPoints.AddToTail( pSpawnPoint ); } } FOR_EACH_VEC( m_CTSpawnPointsMasterList, i ) { SpawnPoint* pSpawnPoint = m_CTSpawnPointsMasterList[i]; if ( pSpawnPoint && pSpawnPoint->IsEnabled() ) { m_iSpawnPointCount_CT++; // Store off the CT spawn point m_CTSpawnPoints.AddToTail( pSpawnPoint ); } } if ( IsPlayingCoopMission() ) { // reset so that T spawns always try to start from 0 m_iNextTerroristSpawnPoint = 0; // make sure that there are always enough bots ConVarRef bot_quota( "bot_quota" ); int nQuota = UTIL_HumansInGame( true, true ) + MIN( m_iMaxNumTerrorists, m_iSpawnPointCount_Terrorist ); bot_quota.SetValue( nQuota ); //DevMsg( ">> Setting bot_quota to %d\n", nQuota ); } else { // Shuffle the spawn points ShuffleSpawnPointLists(); // Sort the list now that the spawn points have been shuffled SortSpawnPointLists(); } } void CCSGameRules::SortSpawnPointLists( void ) { if ( CSGameRules()->IsPlayingGunGameProgressive() ) { // Sort the spawn point lists m_TerroristSpawnPoints.Sort( ArmsRaceSpawnPointSortFunction ); m_CTSpawnPoints.Sort( ArmsRaceSpawnPointSortFunction ); } else { // Sort the spawn point lists m_TerroristSpawnPoints.Sort( SpawnPointSortFunction ); m_CTSpawnPoints.Sort( SpawnPointSortFunction ); } } void CCSGameRules::ShuffleSpawnPointLists( void ) { // Shuffle terrorist spawn points VectorShuffle( m_TerroristSpawnPoints ); // Shuffle CT spawn points VectorShuffle( m_CTSpawnPoints ); } void CCSGameRules::ShuffleMasterSpawnPointLists( void ) { // Shuffle terrorist spawn points VectorShuffle( m_TerroristSpawnPointsMasterList ); // Shuffle CT spawn points VectorShuffle( m_CTSpawnPointsMasterList ); } void CCSGameRules::SortMasterSpawnPointLists( void ) { if ( CSGameRules()->IsPlayingGunGameProgressive() ) { // Sort the spawn point lists m_TerroristSpawnPointsMasterList.Sort( ArmsRaceSpawnPointSortFunction ); m_CTSpawnPointsMasterList.Sort( ArmsRaceSpawnPointSortFunction ); int nSpots = 0; // disable all spawns over 10 because we already shoved teh arms race ones up front and // if there are enough, disable the ones that aren't flagged as arms race FOR_EACH_VEC( m_TerroristSpawnPointsMasterList, i ) { SpawnPoint* pSpawnPoint = m_TerroristSpawnPointsMasterList[i]; if ( pSpawnPoint && pSpawnPoint->IsEnabled() ) { if ( IsSpawnPointValid( pSpawnPoint, NULL ) == false ) { pSpawnPoint->m_bEnabled = false; continue; } nSpots++; if ( nSpots > 12 ) pSpawnPoint->m_bEnabled = false; } } nSpots = 0; // disable for the CTs as well FOR_EACH_VEC( m_CTSpawnPointsMasterList, i ) { SpawnPoint* pSpawnPoint = m_CTSpawnPointsMasterList[i]; if ( pSpawnPoint && pSpawnPoint->IsEnabled() ) { if ( IsSpawnPointValid( pSpawnPoint, NULL ) == false ) { pSpawnPoint->m_bEnabled = false; continue; } nSpots++; if ( nSpots > 12 ) pSpawnPoint->m_bEnabled = false; } } } else { if ( !IsPlayingCoopMission() ) m_TerroristSpawnPointsMasterList.Sort( SpawnPointSortFunction ); m_CTSpawnPointsMasterList.Sort( SpawnPointSortFunction ); } } void CCSGameRules::ShufflePlayerList( CUtlVector< CCSPlayer* > &playersList ) { // Shuffle players VectorShuffle( playersList ); // shuffle and sort the T spawn points here too if ( IsPlayingCoopMission() ) { ShuffleSpawnPointLists(); SortSpawnPointLists(); } } CBaseEntity*CCSGameRules::GetNextSpawnpoint( int teamNumber ) { CBaseEntity* pRetVal = NULL; if ( teamNumber == TEAM_CT ) { if ( m_iNextCTSpawnPoint >= m_CTSpawnPoints.Count() ) { m_iNextCTSpawnPoint = 0; } if ( m_iNextCTSpawnPoint < m_CTSpawnPoints.Count() ) { pRetVal = m_CTSpawnPoints[ m_iNextCTSpawnPoint ]; m_iNextCTSpawnPoint++; } } else if ( teamNumber == TEAM_TERRORIST ) { if ( m_iNextTerroristSpawnPoint >= m_TerroristSpawnPoints.Count() ) { m_iNextTerroristSpawnPoint = 0; } if ( m_iNextTerroristSpawnPoint < m_TerroristSpawnPoints.Count() ) { pRetVal = m_TerroristSpawnPoints[ m_iNextTerroristSpawnPoint ]; m_iNextTerroristSpawnPoint++; } } return pRetVal; } void CCSGameRules::ShowSpawnPoints( int duration ) { CBaseEntity* ent = NULL; while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) == false ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration ); } else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration ); } } while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) == false ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration ); } else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration ); } } while ( ( ent = gEntList.FindEntityByClassname( ent, "info_deathmatch_spawn" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration ); } else if ( IsSpawnPointHiddenFromOtherPlayers( ent, NULL ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration ); } } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration ); } } if ( CSGameRules()->IsPlayingGunGameProgressive() ) { while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_terrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) == false ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration ); } else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration ); } } while ( ( ent = gEntList.FindEntityByClassname( ent, "info_armsrace_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) == false ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, duration ); } else if ( !( assert_cast< SpawnPoint* >( ent )->IsEnabled() ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 255, 200, duration ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, duration ); } } } } void CCSGameRules::CheckRestartRound( void ) { // Restart the game if specified by the server int iRestartDelay = mp_restartgame.GetInt(); if ( iRestartDelay > 0 ) { if ( iRestartDelay > 60 ) iRestartDelay = 60; // log the restart UTIL_LogPrintf( "World triggered \"Restart_Round_(%i_%s)\"\n", iRestartDelay, iRestartDelay == 1 ? "second" : "seconds" ); UTIL_LogPrintf( "Team \"CT\" scored \"%i\" with \"%i\" players\n", m_match.GetCTScore(), m_iNumCT ); UTIL_LogPrintf( "Team \"TERRORIST\" scored \"%i\" with \"%i\" players\n", m_match.GetTerroristScore(), m_iNumTerrorist ); // let the players know char strRestartDelay[64]; Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay ); UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "#SFUI_Second" : "#SFUI_Seconds" ); UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#SFUI_Notice_Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "#SFUI_Second" : "#SFUI_Seconds" ); m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay; m_bCompleteReset = true; m_bGameRestart = true; m_bHasMatchStarted = false; mp_restartgame.SetValue( 0 ); } } void cc_ScrambleTeams( const CCommand& args ) { if ( UTIL_IsCommandIssuedByServerAdmin() ) { CCSGameRules *pRules = dynamic_cast( GameRules() ); if ( pRules ) { pRules->SetScrambleTeamsOnRestart( true ); mp_restartgame.SetValue( 1 ); } } } static ConCommand mp_scrambleteams( "mp_scrambleteams", cc_ScrambleTeams, "Scramble the teams and restart the game" ); void cc_SwapTeams( const CCommand& args ) { if ( UTIL_IsCommandIssuedByServerAdmin() ) { CCSGameRules *pRules = dynamic_cast( GameRules() ); if ( pRules ) { pRules->SetSwapTeamsOnRestart( true ); mp_restartgame.SetValue( 1 ); } } } static ConCommand mp_swapteams( "mp_swapteams", cc_SwapTeams, "Swap the teams and restart the game" ); // sort function for the list of players that we're going to use to scramble the teams int ScramblePlayersSort( CCSPlayer* const *p1, CCSPlayer* const *p2 ) { CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource ); if ( pResource ) { // check the priority if ( p1 && p2 && (*p1) && (*p2) && (*p1)->GetScore() > (*p2)->GetScore() ) { return 1; } } return -1; } //////// PAUSE void cc_PauseMatch( const CCommand& args ) { if ( UTIL_IsCommandIssuedByServerAdmin() ) { CCSGameRules *pRules = dynamic_cast( GameRules() ); if ( pRules ) { if ( !pRules->IsMatchWaitingForResume() ) { UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" ); } pRules->SetMatchWaitingForResume( true ); } } } static ConCommand mp_pause_match( "mp_pause_match", cc_PauseMatch, "Pause the match in the next freeze time" ); //////// RESUME void cc_ResumeMatch( const CCommand& args ) { if ( UTIL_IsCommandIssuedByServerAdmin() ) { CCSGameRules *pRules = dynamic_cast( GameRules() ); if ( pRules && pRules->IsMatchWaitingForResume() ) { pRules->SetMatchWaitingForResume( false ); if ( !pRules->IsMatchWaitingForResume() ) { UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Resume" ); } else { UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" ); } } } } static ConCommand mp_unpause_match( "mp_unpause_match", cc_ResumeMatch, "Resume the match" ); /////////// class SetHumanTeamFunctor { public: SetHumanTeamFunctor( int targetTeam ) { m_targetTeam = targetTeam; m_sourceTeam = ( m_targetTeam == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT; m_traitors.MakeReliable(); m_loyalists.MakeReliable(); m_loyalists.AddAllPlayers(); } bool operator()( CBasePlayer *basePlayer ) { CCSPlayer *player = ToCSPlayer( basePlayer ); if ( !player ) return true; if ( player->IsBot() ) return true; if ( player->GetTeamNumber() != m_sourceTeam ) return true; if ( player->State_Get() == STATE_PICKINGCLASS ) return true; if ( CSGameRules()->TeamFull( m_targetTeam ) ) return false; if ( CSGameRules()->TeamStacked( m_targetTeam, m_sourceTeam ) ) return false; player->SwitchTeam( m_targetTeam ); m_traitors.AddRecipient( player ); m_loyalists.RemoveRecipient( player ); return true; } void SendNotice( void ) { if ( m_traitors.GetRecipientCount() > 0 ) { UTIL_ClientPrintFilter( m_traitors, HUD_PRINTCENTER, "#SFUI_Notice_Player_Balanced" ); UTIL_ClientPrintFilter( m_loyalists, HUD_PRINTCENTER, "#SFUI_Notice_Teams_Balanced" ); } } private: int m_targetTeam; int m_sourceTeam; CRecipientFilter m_traitors; CRecipientFilter m_loyalists; }; void CCSGameRules::MoveHumansToHumanTeam( void ) { int targetTeam = GetHumanTeam(); if ( targetTeam != TEAM_TERRORIST && targetTeam != TEAM_CT ) return; SetHumanTeamFunctor setTeam( targetTeam ); ForEachPlayer( setTeam ); setTeam.SendNotice(); } void CCSGameRules::AddDroppedWeaponToList( CWeaponCSBase *pWeapon ) { static ConVarRef weapon_max_before_cleanup( "weapon_max_before_cleanup" ); // NOTE: dont check is removeable here because weapons just thrown aren't removeable, just clean up when we actually do the removals if ( pWeapon /*&& pWeapon->IsRemoveable()*/ && weapon_max_before_cleanup.GetInt() > 0 ) { for ( int i = 0; i < m_weaponsDroppedInWorld.Count(); i++ ) { if ( pWeapon == m_weaponsDroppedInWorld[i] ) return; } m_weaponsDroppedInWorld.AddToTail( pWeapon ); } while ( weapon_max_before_cleanup.GetInt() > 0 && m_weaponsDroppedInWorld.Count() > weapon_max_before_cleanup.GetInt() ) { //CUtlVector< float> m_weaponsDist; // get the oldest 5 (or max) weapons int nMaxIndex = Min( 5, m_weaponsDroppedInWorld.Count() ); // for ( int i = 0; i < nMaxIndex; i++ ) // { // m_weaponsDist.AddToTail( 0 ); // } int index = 0; for ( int i = 0; i < nMaxIndex; i++ ) { CWeaponCSBase *pWepTemp = m_weaponsDroppedInWorld[i].Get(); if ( pWepTemp && pWepTemp->IsRemoveable() ) { // find the first weapon to be far enough away for ( int j = 1; j <= MAX_PLAYERS; j++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( j ); if ( !pPlayer ) continue; float flDist = ( pWepTemp->GetAbsOrigin() - pPlayer->GetAbsOrigin() ).Length(); if ( flDist > 1200 ) { index = i; break; } } if ( index > 0 ) break; } } CWeaponCSBase *pWep = m_weaponsDroppedInWorld[index].Get(); if ( pWep && pWep->IsRemoveable() ) { UTIL_Remove( pWep ); } m_weaponsDroppedInWorld.Remove( index ); } } void CCSGameRules::RemoveDroppedWeaponFromList( CWeaponCSBase *pWeapon ) { if ( !pWeapon ) return; for ( int i = 0; i < m_weaponsDroppedInWorld.Count(); i++ ) { if ( pWeapon == m_weaponsDroppedInWorld[i] ) { m_weaponsDroppedInWorld.Remove( i ); return; } } } void CCSGameRules::ProcessAutoBalance( void ) { // No autobalancing for people who abandon games in queued matchmaking if ( IsQueuedMatchmaking() ) return; /*************** AUTO-BALANCE CODE *************/ if ( mp_autoteambalance.GetInt() != 0 && (m_iUnBalancedRounds >= 1) ) { if ( GetHumanTeam() == TEAM_UNASSIGNED ) { BalanceTeams(); } } if ( ((m_iNumSpawnableCT - m_iNumSpawnableTerrorist) >= 2) || ((m_iNumSpawnableTerrorist - m_iNumSpawnableCT) >= 2) ) { m_iUnBalancedRounds++; } else { m_iUnBalancedRounds = 0; } // Warn the players of an impending auto-balance next round... if ( mp_autoteambalance.GetInt() != 0 && (m_iUnBalancedRounds == 1) ) { if ( GetHumanTeam() == TEAM_UNASSIGNED ) { // Queue up impending auto-balance display text m_fAutobalanceDisplayTime = gpGlobals->curtime + AUTOBALANCE_TEXT_DELAY; m_AutobalanceStatus = AutobalanceStatus::NEXT_ROUND; } } } void CCSGameRules::BalanceTeams( void ) { // No autobalancing for people who abandon games in queued matchmaking if ( IsQueuedMatchmaking() ) return; int iTeamToSwap = TEAM_UNASSIGNED; int iNumToSwap; if (m_iNumCT > m_iNumTerrorist) { iTeamToSwap = TEAM_CT; iNumToSwap = (m_iNumCT - m_iNumTerrorist)/2; } else if (m_iNumTerrorist > m_iNumCT) { iTeamToSwap = TEAM_TERRORIST; iNumToSwap = (m_iNumTerrorist - m_iNumCT)/2; } else { return; // Teams are even.. Get out of here. } if (iNumToSwap > 3) // Don't swap more than 3 players at a time.. This is a naive method of avoiding infinite loops. iNumToSwap = 3; int iTragetTeam = TEAM_UNASSIGNED; if ( iTeamToSwap == TEAM_CT ) { iTragetTeam = TEAM_TERRORIST; } else if ( iTeamToSwap == TEAM_TERRORIST ) { iTragetTeam = TEAM_CT; } else { // no valid team to swap return; } m_AutoBalanceTraitors.MakeReliable(); m_AutoBalanceLoyalists.MakeReliable(); m_AutoBalanceLoyalists.AddAllPlayers(); for (int i = 0; i < iNumToSwap; i++) { // last person to join the server int iHighestUserID = -1; CCSPlayer *pPlayerToSwap = NULL; // check if target team is full, exit if so if ( TeamFull(iTragetTeam) ) break; // search for player with highest UserID = most recently joined to switch over for ( int j = 1; j <= gpGlobals->maxClients; j++ ) { CCSPlayer *pPlayer = (CCSPlayer *)UTIL_PlayerByIndex( j ); if ( !pPlayer ) continue; CCSBot *bot = dynamic_cast< CCSBot * >(pPlayer); if ( bot ) continue; // don't swap bots - the bot system will handle that if ( pPlayer && ( pPlayer->GetTeamNumber() == iTeamToSwap ) && ( engine->GetPlayerUserId( pPlayer->edict() ) > iHighestUserID ) && ( pPlayer->State_Get() != STATE_PICKINGCLASS ) ) { iHighestUserID = engine->GetPlayerUserId( pPlayer->edict() ); pPlayerToSwap = pPlayer; } } if ( pPlayerToSwap != NULL ) { m_AutoBalanceTraitors.AddRecipient( pPlayerToSwap ); m_AutoBalanceLoyalists.RemoveRecipient( pPlayerToSwap ); pPlayerToSwap->SwitchTeam( iTragetTeam ); } } if ( m_AutoBalanceTraitors.GetRecipientCount() > 0 ) { // Queue up traitor and loyalist display text m_fAutobalanceDisplayTime = gpGlobals->curtime + AUTOBALANCE_TEXT_DELAY; m_AutobalanceStatus = AutobalanceStatus::THIS_ROUND; } } void CCSGameRules::HandleScrambleTeams( void ) { CCSPlayer *pCSPlayer = NULL; CUtlVector pListPlayers; // add all the players (that are on CT or Terrorist) to our temp list for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ ) { pCSPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pCSPlayer && ( pCSPlayer->GetTeamNumber() == TEAM_TERRORIST || pCSPlayer->GetTeamNumber() == TEAM_CT ) ) { pListPlayers.AddToHead( pCSPlayer ); } } // sort the list pListPlayers.Sort( ScramblePlayersSort ); int team = TEAM_INVALID; bool assignToOpposingTeam = false; for ( int i = 0 ; i < pListPlayers.Count() ; i++ ) { pCSPlayer = pListPlayers[i]; if ( pCSPlayer ) { //First assignment goes to random team //Second assignment goes to the opposite //Keep alternating until out of players. if ( !assignToOpposingTeam ) { team = ( rand() % 2 ) ? TEAM_TERRORIST : TEAM_CT; } else { team = ( team == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST; } pCSPlayer->SwitchTeam( team ); assignToOpposingTeam = !assignToOpposingTeam; } } } void CCSGameRules::OnTeamsSwappedAtRoundReset() { // // This function is called both when the halftime ends and teams swap // and when cc_SwapTeams was called on the server and m_bCompleteReset is being performed // if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender || m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender ) { if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender ) m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender; else if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender ) m_eQueuedMatchmakingRematchState = k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender; } // // Flip the timeouts as well // bool bTemp; bTemp = m_bTerroristTimeOutActive; m_bTerroristTimeOutActive = m_bCTTimeOutActive; m_bCTTimeOutActive = bTemp; float flTemp; flTemp = m_flTerroristTimeOutRemaining; m_flTerroristTimeOutRemaining = m_flCTTimeOutRemaining; m_flCTTimeOutRemaining = flTemp; int nTemp = m_nTerroristTimeOuts; m_nTerroristTimeOuts = m_nCTTimeOuts; m_nCTTimeOuts = nTemp; g_voteControllerT->EndVoteImmediately(); g_voteControllerCT->EndVoteImmediately(); } void CCSGameRules::HandleSwapTeams( void ) { CCSPlayer *pCSPlayer = NULL; CUtlVector pListPlayers; CUtlVector pListCoaches; // add all the players (that are on CT or Terrorist) to our temp list for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ ) { pCSPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pCSPlayer && ( pCSPlayer->GetTeamNumber() == TEAM_TERRORIST || pCSPlayer->GetTeamNumber() == TEAM_CT ) ) { pListPlayers.AddToHead( pCSPlayer ); } else if ( pCSPlayer && pCSPlayer->IsCoach() ) { pListCoaches.AddToHead( pCSPlayer ); } } for ( int i = 0 ; i < pListPlayers.Count() ; i++ ) { pCSPlayer = pListPlayers[i]; if ( pCSPlayer ) { int currentTeam = pCSPlayer->GetTeamNumber(); int newTeam = ( currentTeam == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST; pCSPlayer->SwitchTeam( newTeam ); } } //coaches last for ( int i = 0 ; i < pListCoaches.Count() ; i++ ) { pCSPlayer = pListCoaches[i]; if ( pCSPlayer ) { int currentTeam = pCSPlayer->GetAssociatedTeamNumber(); int newTeam = ( currentTeam == TEAM_TERRORIST ) ? TEAM_CT : TEAM_TERRORIST; pCSPlayer->m_iCoachingTeam = newTeam; } } // // Flip the convars for custom team names and flags as well // CUtlString sTemp; sTemp = mp_teamname_1.GetString(); mp_teamname_1.SetValue( mp_teamname_2.GetString() ); mp_teamname_2.SetValue( sTemp.Get() ); sTemp = mp_teamflag_1.GetString(); mp_teamflag_1.SetValue( mp_teamflag_2.GetString() ); mp_teamflag_2.SetValue( sTemp.Get() ); sTemp = mp_teamlogo_1.GetString(); mp_teamlogo_1.SetValue( mp_teamlogo_2.GetString() ); mp_teamlogo_2.SetValue( sTemp.Get() ); sTemp = mp_teammatchstat_1.GetString(); mp_teammatchstat_1.SetValue( mp_teammatchstat_2.GetString() ); mp_teammatchstat_2.SetValue( sTemp.Get() ); sTemp = mp_teamscore_1.GetString(); mp_teamscore_1.SetValue( mp_teamscore_2.GetString() ); mp_teamscore_2.SetValue( sTemp.Get() ); if ( ( mp_teamprediction_pct.GetInt() >= 1 ) && ( mp_teamprediction_pct.GetInt() <= 99 ) ) mp_teamprediction_pct.SetValue( 100 - mp_teamprediction_pct.GetInt() ); OnTeamsSwappedAtRoundReset(); } // the following two functions cap the number of players on a team to five instead of basing it on the number of spawn points int CCSGameRules::MaxNumPlayersOnTerrTeam() { if ( IsPlayingCoopMission() ) return m_iSpawnPointCount_Terrorist; bool bRandomTSpawn = mp_randomspawn.GetInt() == 1 || mp_randomspawn.GetInt() == TEAM_TERRORIST; return MIN(m_iMaxNumTerrorists, bRandomTSpawn ? MAX_PLAYERS : m_iSpawnPointCount_Terrorist); } int CCSGameRules::MaxNumPlayersOnCTTeam() { bool bRandomCTSpawn = mp_randomspawn.GetInt() == 1 || mp_randomspawn.GetInt() == TEAM_CT; return MIN(m_iMaxNumCTs, bRandomCTSpawn ? MAX_PLAYERS : m_iSpawnPointCount_CT); } bool CCSGameRules::TeamFull( int team_id ) { CheckLevelInitialized(); switch ( team_id ) { case TEAM_TERRORIST: return m_iNumTerrorist >= MaxNumPlayersOnTerrTeam(); case TEAM_CT: return m_iNumCT >= MaxNumPlayersOnCTTeam(); case TEAM_SPECTATOR: return GetGlobalTeam( TEAM_SPECTATOR )->GetNumPlayers() >= m_iSpectatorSlotCount; } return false; } bool CCSGameRules::WillTeamHaveRoomForPlayer( CCSPlayer* pThePlayer, int newTeam ) { int teamSize = 0; for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( clientIndex ); if ( pPlayer && ( pPlayer != pThePlayer ) ) { if ( pPlayer->GetPendingTeamNumber() == newTeam ) teamSize++; } } bool result = false; switch( newTeam ) { case TEAM_TERRORIST: result = ( teamSize < MaxNumPlayersOnTerrTeam() ); break; case TEAM_CT: result = ( teamSize < MaxNumPlayersOnCTTeam() ); break; case TEAM_SPECTATOR: result = ( teamSize < m_iSpectatorSlotCount ); break; } return result; } int CCSGameRules::GetHumanTeam() { if ( FStrEq( "CT", mp_humanteam.GetString() ) ) { return TEAM_CT; } else if ( FStrEq( "T", mp_humanteam.GetString() ) ) { return TEAM_TERRORIST; } return TEAM_UNASSIGNED; } int CCSGameRules::SelectDefaultTeam( bool ignoreBots /*= false*/ ) { if ( ignoreBots && ( FStrEq( cv_bot_join_team.GetString(), "T" ) || FStrEq( cv_bot_join_team.GetString(), "CT" ) ) ) { ignoreBots = false; // don't ignore bots when they can't switch teams } if ( ignoreBots && !mp_autoteambalance.GetBool() ) { ignoreBots = false; // don't ignore bots when they can't switch teams } int team = TEAM_UNASSIGNED; int numTerrorists = m_iNumTerrorist; int numCTs = m_iNumCT; if ( ignoreBots ) { numTerrorists = UTIL_HumansOnTeam( TEAM_TERRORIST ); numCTs = UTIL_HumansOnTeam( TEAM_CT ); } // Choose the team that's lacking players if ( numTerrorists < numCTs ) { team = TEAM_TERRORIST; } else if ( numTerrorists > numCTs ) { team = TEAM_CT; } // Choose the team that's losing else if ( m_match.GetTerroristScore() < m_match.GetCTScore() ) { team = TEAM_TERRORIST; } else if ( m_match.GetCTScore() < m_match.GetTerroristScore() ) { team = TEAM_CT; } else { // Teams and scores are equal, pick a random team if ( random->RandomInt( 0, 1 ) == 0 ) { team = TEAM_CT; } else { team = TEAM_TERRORIST; } } if ( TeamFull( team ) ) { // Pick the opposite team if ( team == TEAM_TERRORIST ) { team = TEAM_CT; } else { team = TEAM_TERRORIST; } // No choices left if ( TeamFull( team ) ) return TEAM_UNASSIGNED; } return team; } //checks to see if the desired team is stacked, returns true if it is bool CCSGameRules::TeamStacked( int newTeam_id, int curTeam_id ) { //players are allowed to change to their own team if(newTeam_id == curTeam_id) return false; // if mp_limitteams is 0, don't check if ( mp_limitteams.GetInt() == 0 ) return false; // Queued matchmaking code forces correct teams already, don't check stacked teams here if ( IsQueuedMatchmaking() ) return false; switch ( newTeam_id ) { case TEAM_TERRORIST: if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR) { if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt() - 1)) return true; else return false; } else { if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt())) return true; else return false; } break; case TEAM_CT: if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR) { if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt() - 1)) return true; else return false; } else { if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt())) return true; else return false; } break; } return false; } //========================================================= //========================================================= bool CCSGameRules::FPlayerCanRespawn( CBasePlayer *pBasePlayer ) { CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer ); if ( !pPlayer ) Error( "FPlayerCanRespawn: pPlayer=0" ); int nTeamNum = pPlayer->GetTeamNumber(); // Player cannot respawn twice in a round if ( !pPlayer->IsAbleToInstantRespawn() && !IsWarmupPeriod() ) { if ( pPlayer->m_iNumSpawns > 0 && m_bFirstConnected ) return false; } // If they're dead after the map has ended, and it's about to start the next round, // wait for the round restart to respawn them. if ( ( m_flRestartRoundTime - gpGlobals->curtime ) > MAX_TIME_TO_WAIT_BEFORE_ENTERING ) return false; // Only valid team members can spawn if ( nTeamNum != TEAM_CT && nTeamNum != TEAM_TERRORIST ) return false; // Only players with a valid class can spawn if ( pPlayer->GetClass() == CS_CLASS_NONE ) return false; //if ( !IsPlayingGunGameProgressive() && !IsPlayingGunGameDeathmatch() && !IsWarmupPeriod() ) if ( !pPlayer->IsAbleToInstantRespawn() && !IsWarmupPeriod() ) { // Player cannot respawn until next round if more than 20 seconds in // Tabulate the number of players on each team. m_iNumCT = GetGlobalTeam( TEAM_CT )->GetNumPlayers(); m_iNumTerrorist = GetGlobalTeam( TEAM_TERRORIST )->GetNumPlayers(); if ( m_iNumTerrorist > 0 && m_iNumCT > 0 ) { // gurjeets - Between rounds m_fRoundStartTime gets set to cur_time + freeze_time ie // sometime in the future. Thus, when we get here, players are still within the // round restart time // First time into a game, however, m_fRoundStartTime is 0. On this code path, // the code that sets this properly only kicks in *after* players are spawned // This results in the msg "player_spawned" not getting sent. // Hence, added the check for m_fRoundStartTime being 0 if ( (m_fRoundStartTime != 0) && (gpGlobals->curtime > (m_fRoundStartTime + mp_join_grace_time.GetFloat() )) ) { //If this player just connected and fadetoblack is on, then maybe //the server admin doesn't want him peeking around. color32_s clr = {0,0,0,255}; if ( mp_forcecamera.GetInt() == OBS_ALLOW_NONE ) { UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT ); } return false; } } } // Player cannot respawn while in the Choose Appearance menu //if ( pPlayer->m_iMenu == Menu_ChooseAppearance ) // return false; return true; } void CCSGameRules::LoadRoundDataInformation( char const *szFilename ) { #if BACKUPSUPPORTZEROZERO // // Custom handling for loading 0:0 backup // if ( !V_strcmp( szFilename, g_szRoundBackupZeroZeroFileName ) ) { // Ensure that the warmup starts and stays paused extern ConVar mp_warmup_pausetimer; mp_warmup_pausetimer.SetValue( 1 ); StartWarmup(); return; } #endif KeyValues *kvSaveFile = new KeyValues( "" ); KeyValues::AutoDelete autodelete_kvSaveFile( kvSaveFile ); autodelete_kvSaveFile->UsesEscapeSequences( true ); if ( !kvSaveFile->LoadFromFile( filesystem, szFilename ) ) { Warning( "Failed to load file: %s\n", szFilename ); return; } UTIL_ClientPrintAll( HUD_PRINTTALK, CFmtStr( "Restoring match backup %s, score %d:%d after round %d\n", kvSaveFile->GetString( "timestamp" ), kvSaveFile->GetInt( "FirstHalfScore/team1" ) + kvSaveFile->GetInt( "SecondHalfScore/team1" ) + kvSaveFile->GetInt( "OvertimeScore/team1" ), kvSaveFile->GetInt( "FirstHalfScore/team2" ) + kvSaveFile->GetInt( "SecondHalfScore/team2" ) + kvSaveFile->GetInt( "OvertimeScore/team2" ), kvSaveFile->GetInt( "round" ) ) ); int numRoundsPlayed = kvSaveFile->GetInt( "round" ); int numOvertimePlaying = kvSaveFile->GetInt( "OvertimeScore/OvertimeID" ); bool bResetPlayerAccounts = ( ( numRoundsPlayed < mp_maxrounds.GetInt() ) || !mp_overtime_maxrounds.GetInt() ) ? ( numRoundsPlayed == ( mp_maxrounds.GetInt() / 2 ) ) : !( ( numRoundsPlayed - mp_maxrounds.GetInt() ) % ( mp_overtime_maxrounds.GetInt() / 2 ) ); // Send all CT and T players to spectators for now int nSavedSpectatorSlotsCount = m_iSpectatorSlotCount; m_iSpectatorSlotCount = INT_MAX; m_bForceTeamChangeSilent = true; // silence all the messages about players changing teams m_bLoadingRoundBackupData = true; // make sure the game rules don't do any additional round restarts for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( pPlayer && pPlayer->IsConnected() && !pPlayer->IsBot() && ( ( pPlayer->GetTeamNumber() == TEAM_CT ) || ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) || ( pPlayer->GetPendingTeamNumber() == TEAM_CT ) || ( pPlayer->GetPendingTeamNumber() == TEAM_TERRORIST ) ) ) { pPlayer->HandleCommand_JoinTeam( TEAM_SPECTATOR ); pPlayer->SetPendingTeamNum( TEAM_UNASSIGNED ); } } // Wipe cached accounts information and team assignments if ( !IsQueuedMatchmaking() ) { m_mapQueuedMatchmakingPlayersData.PurgeAndDeleteElements(); } // // Reset match and streak data // { m_iTotalRoundsPlayed = 0; // Reset score info m_match.Reset(); m_iNumConsecutiveTerroristLoses = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/NumConsecutiveTerroristLoses" ); m_iNumConsecutiveCTLoses = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/NumConsecutiveCTLoses" ); m_iAccountTerrorist = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/ExtraAccountTerrorist" ); m_iAccountCT = bResetPlayerAccounts ? 0 : kvSaveFile->GetInt( "History/ExtraAccountCT" ); m_iLoserBonus = bResetPlayerAccounts ? TeamCashAwardValue( TeamCashAward::LOSER_BONUS ) : kvSaveFile->GetInt( "History/LoserBonus" ); // Reset hostage spawn indices m_arrSelectedHostageSpawnIndices.RemoveAll(); if ( char const *szHostageSpawnIndices = kvSaveFile->GetString( "History/HostageSpawnIndices", NULL ) ) { CUtlVector< char* > tagStrings; V_SplitString( szHostageSpawnIndices, ",", tagStrings ); m_arrSelectedHostageSpawnIndices.EnsureCapacity( tagStrings.Count() ); FOR_EACH_VEC( tagStrings, iTagString ) { m_arrSelectedHostageSpawnIndices.AddToTail( Q_atoi( tagStrings[iTagString] ) ); } tagStrings.PurgeAndDeleteElements(); } } // // Process save data, disregarding team assignment // we'll figure out which team players are supposed to be on later // m_match.AddCTWins( kvSaveFile->GetInt( "FirstHalfScore/team1" ) ); m_match.AddTerroristWins( kvSaveFile->GetInt( "FirstHalfScore/team2" ) ); if ( numRoundsPlayed >= ( mp_maxrounds.GetInt() / 2 ) ) { m_match.SetPhase( GAMEPHASE_PLAYING_SECOND_HALF ); m_match.AddCTWins( kvSaveFile->GetInt( "SecondHalfScore/team1" ) ); m_match.AddTerroristWins( kvSaveFile->GetInt( "SecondHalfScore/team2" ) ); } if ( ( numRoundsPlayed >= mp_maxrounds.GetInt() ) || numOvertimePlaying ) { int nOvertimeBasedOnRoundsPlayed = mp_overtime_maxrounds.GetInt() ? ( numRoundsPlayed - mp_maxrounds.GetInt() ) / mp_overtime_maxrounds.GetInt() : 0; numOvertimePlaying = MAX( numOvertimePlaying, nOvertimeBasedOnRoundsPlayed + 1 ); m_match.GoToOvertime( numOvertimePlaying ); if ( mp_overtime_maxrounds.GetInt() && ( ( ( numRoundsPlayed - mp_maxrounds.GetInt() ) % mp_overtime_maxrounds.GetInt() ) < ( mp_overtime_maxrounds.GetInt() / 2 ) ) ) m_match.SetPhase( GAMEPHASE_PLAYING_FIRST_HALF ); m_match.AddCTWins( kvSaveFile->GetInt( "OvertimeScore/team1" ) ); m_match.AddTerroristWins( kvSaveFile->GetInt( "OvertimeScore/team2" ) ); } // Switch team scores if they are supposed to be switched now bool bAreTeamsPlayingSwitchedSides = AreTeamsPlayingSwitchedSides(); if ( bAreTeamsPlayingSwitchedSides ) { m_match.SwapTeamScores(); } for ( int r = 0; r < m_iMatchStats_RoundResults.Count(); r++ ) { char szKey[30]; V_sprintf_safe( szKey, "RoundResults/round%d", r + 1 ); m_iMatchStats_RoundResults.GetForModify( r ) = kvSaveFile->GetInt( szKey ); } for ( int r = 0; r < m_iMatchStats_PlayersAlive_T.Count(); r++ ) { char szKey[30]; V_sprintf_safe( szKey, "PlayersAliveT/round%d", r + 1 ); m_iMatchStats_PlayersAlive_T.GetForModify( r ) = kvSaveFile->GetInt( szKey ); } for ( int r = 0; r < m_iMatchStats_PlayersAlive_CT.Count(); r++ ) { char szKey[30]; V_sprintf_safe( szKey, "PlayersAliveCT/round%d", r + 1 ); m_iMatchStats_PlayersAlive_CT.GetForModify( r ) = kvSaveFile->GetInt( szKey ); } // Now assign the players to correct teams with correct stats for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( pPlayer && pPlayer->IsConnected() && !pPlayer->IsBot() ) { CSteamID steamIdHumanPlayer; if ( pPlayer->GetSteamID( &steamIdHumanPlayer ) ) { int iTeamOrder = 0; KeyValues *kvPlayerData = NULL; for ( ; iTeamOrder < 2; ++ iTeamOrder ) { kvPlayerData = kvSaveFile->FindKey( CFmtStr( "PlayersOnTeam%d/%u", iTeamOrder + 1, steamIdHumanPlayer.GetAccountID() ), false ); if ( kvPlayerData ) break; } if ( !kvPlayerData ) { Warning( "Server checkpoint has no information for player %u: %s\n", steamIdHumanPlayer.GetAccountID(), pPlayer->GetPlayerName() ); } else { int iRulesTeam = bAreTeamsPlayingSwitchedSides ? ( (iTeamOrder == 0)?TEAM_TERRORIST:TEAM_CT ) : ( (iTeamOrder == 0)?TEAM_CT:TEAM_TERRORIST ); pPlayer->HandleCommand_JoinTeam( iRulesTeam ); // join the correct team pPlayer->HandleCommand_JoinClass(); // force join class now pPlayer->MarkAsNotReceivingMoneyNextRound(); // after loading backup we shouldn't give out extra end round money pPlayer->ResetFragCount(); pPlayer->SetEnemyKillTrackInfo( kvPlayerData->GetInt( "enemyKs" ), kvPlayerData->GetInt( "enemyHSs" ), kvPlayerData->GetInt( "enemy3Ks" ), kvPlayerData->GetInt( "enemy4Ks" ), kvPlayerData->GetInt( "enemy5Ks" ), kvPlayerData->GetInt( "enemyKAg" ) ); pPlayer->SetEnemyFirstKills( kvPlayerData->GetInt( "firstKs" ), kvPlayerData->GetInt( "clutchKs" ) ); pPlayer->SetEnemyWeaponKills( kvPlayerData->GetInt( "kills_weapon_pistol" ), kvPlayerData->GetInt( "kills_weapon_sniper" ) ); pPlayer->IncrementFragCount( kvPlayerData->GetInt( "kills" ), -1 ); // -1 headshots is a special flag indicating that we are faking the call here, but it will still record kill info in QMM pPlayer->ResetAssistsCount(); pPlayer->IncrementAssistsCount( kvPlayerData->GetInt( "assists" ) ); pPlayer->ResetDeathCount(); pPlayer->IncrementDeathCount( kvPlayerData->GetInt( "deaths" ) ); pPlayer->SetNumMVPs( kvPlayerData->GetInt( "mvps" ) ); pPlayer->ClearContributionScore(); pPlayer->AddContributionScore( kvPlayerData->GetInt( "score" ) ); pPlayer->ResetNumRoundKills(); int iCashAccount = kvPlayerData->GetInt( "cash" ); if ( bResetPlayerAccounts ) { iCashAccount = GetOvertimePlaying() ? mp_overtime_startmoney.GetInt() : mp_startmoney.GetInt(); } pPlayer->AddAccount( iCashAccount - pPlayer->GetAccountBalance(), true, false, NULL ); pPlayer->RemoveAllItems( true ); if ( !bResetPlayerAccounts ) { for ( KeyValues *kvItem = kvPlayerData->FindKey( "Items" )->GetFirstValue(); kvItem; kvItem = kvItem->GetNextValue() ) { pPlayer->GiveNamedItem( kvItem->GetString() ); } pPlayer->SetArmorValue( kvPlayerData->GetInt( "armor" ) ); pPlayer->m_bHasHelmet = kvPlayerData->GetBool( "helmet" ); if ( kvPlayerData->GetBool( "defusekit" ) && ( pPlayer->GetTeamNumber() == TEAM_CT ) ) pPlayer->GiveDefuser(); } for ( int r = 0; r < MAX_MATCH_STATS_ROUNDS; r++ ) { char szKey[64] = {}; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Kills/round%d", r + 1 ); pPlayer->m_iMatchStats_Kills.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Damage/round%d", r + 1 ); pPlayer->m_iMatchStats_Damage.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/EquipmentValue/round%d", r + 1 ); pPlayer->m_iMatchStats_EquipmentValue.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/MoneySaved/round%d", r + 1 ); pPlayer->m_iMatchStats_MoneySaved.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/KillReward/round%d", r + 1 ); pPlayer->m_iMatchStats_KillReward.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/LiveTime/round%d", r + 1 ); pPlayer->m_iMatchStats_LiveTime.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Deaths/round%d", r + 1 ); pPlayer->m_iMatchStats_Deaths.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Assists/round%d", r + 1 ); pPlayer->m_iMatchStats_Assists.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/HeadShotKills/round%d", r + 1 ); pPlayer->m_iMatchStats_HeadShotKills.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/Objective/round%d", r + 1 ); pPlayer->m_iMatchStats_Objective.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/CashEarned/round%d", r + 1 ); pPlayer->m_iMatchStats_CashEarned.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/UtilityDamage/round%d", r + 1 ); pPlayer->m_iMatchStats_UtilityDamage.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; V_snprintf( szKey, ARRAYSIZE( szKey ), "MatchStats/EnemiesFlashed/round%d", r + 1 ); pPlayer->m_iMatchStats_EnemiesFlashed.GetForModify( r ) = ( r < numRoundsPlayed ) ? kvPlayerData->GetInt( szKey ) : 0; } } } } } // Restore spectators m_iSpectatorSlotCount = nSavedSpectatorSlotsCount; m_bForceTeamChangeSilent = false; // Tell that we have loaded the checkpoint Warning( "Loaded server checkpoint %s, starting match with score %d:%d after round %d\n", kvSaveFile->GetString( "timestamp" ), kvSaveFile->GetInt( "FirstHalfScore/team1" ) + kvSaveFile->GetInt( "SecondHalfScore/team1" ), kvSaveFile->GetInt( "FirstHalfScore/team2" ) + kvSaveFile->GetInt( "SecondHalfScore/team2" ), kvSaveFile->GetInt( "round" ) ); // Make sure we restart without warmup if warmup was active m_bWarmupPeriod = false; m_bCompleteReset = false; m_fWarmupPeriodStart = -1; m_flRestartRoundTime = 0; // Notify clients of potential phase change if ( IGameEvent * event = gameeventmanager->CreateEvent( "announce_phase_end" ) ) { gameeventmanager->FireEvent( event ); } m_phaseChangeAnnouncementTime = 0.0f; if ( mp_backup_restore_load_autopause.GetBool() ) { // pause the match SetMatchWaitingForResume( true ); } // Restart round EndRound(); } static void Helper_CleanupStringForSaveRoundDataInformation( char *pch ) { int nLength = Q_strlen( pch ); char *pchStart = pch; for ( ; *pch; ++pch ) { if ( ( ( pch[ 0 ] >= 'a' ) && ( pch[ 0 ] <= 'z' ) ) || ( ( pch[ 0 ] >= 'A' ) && ( pch[ 0 ] <= 'Z' ) ) || ( ( pch[ 0 ] >= '0' ) && ( pch[ 0 ] <= '9' ) ) ) ; else { Q_memmove( pch, pch + 1, nLength - ( pch - pchStart ) ); --pch; } } } void CCSGameRules::SaveRoundDataInformation( char const *szFilenameOverride ) { /** Removed for partner depot **/ } CCSGameRules::CQMMPlayerData_t * CCSGameRules::QueuedMatchmakingPlayersDataFindOrCreate( CCSPlayer *pPlayer ) { CSteamID steamIdHumanPlayer; if ( !pPlayer->GetSteamID( &steamIdHumanPlayer ) ) return NULL; if ( !steamIdHumanPlayer.IsValid() || !steamIdHumanPlayer.BIndividualAccount() ) return NULL; if ( steamIdHumanPlayer.GetAccountID() != pPlayer->GetHumanPlayerAccountID() ) return NULL; CCSGameRules::CQMMPlayerData_t *pQMM = QueuedMatchmakingPlayersDataFind( steamIdHumanPlayer.GetAccountID() ); if ( pQMM ) // Already exists return pQMM; // There are some cases where we cannot create the player data // or are supposed to have all entries previously created upon GC request if ( IsQueuedMatchmaking() ) return NULL; // Prepare for creating the entry bool bControllingBot = pPlayer->IsControllingBot(); bool bTeamsArePlayingSwitchedSides = AreTeamsPlayingSwitchedSides(); int iTeamOrder = 0; // see "CCSGameRules::SaveRoundDataInformation" processing switch ( pPlayer->GetTeamNumber() ) { case TEAM_CT: case TEAM_TERRORIST: iTeamOrder = ( ( pPlayer->GetTeamNumber() == TEAM_CT ) == bTeamsArePlayingSwitchedSides ) ? 1 : 0; break; default: return NULL; } // // Otherwise it is valid to create a new entry // CQMMPlayerData_t &qmmPlayerData = *new CQMMPlayerData_t; qmmPlayerData.m_uiPlayerAccountId = steamIdHumanPlayer.GetAccountID(); qmmPlayerData.m_iDraftIndex = iTeamOrder * 5; Q_strncpy( qmmPlayerData.m_chPlayerName, pPlayer->GetPlayerName(), sizeof( qmmPlayerData.m_chPlayerName ) ); qmmPlayerData.m_numKills = bControllingBot ? pPlayer->GetBotPreControlData().m_iFrags : pPlayer->FragCount(); qmmPlayerData.m_numAssists = bControllingBot ? pPlayer->GetBotPreControlData().m_iAssists : pPlayer->AssistsCount(); qmmPlayerData.m_numDeaths = bControllingBot ? pPlayer->GetBotPreControlData().m_iDeaths : pPlayer->DeathCount(); qmmPlayerData.m_numScorePoints = pPlayer->GetScore(); qmmPlayerData.m_numMVPs = pPlayer->GetNumMVPs(); qmmPlayerData.m_cash = bControllingBot ? pPlayer->GetBotPreControlData().m_iAccount : pPlayer->GetAccountBalance(); qmmPlayerData.m_bReceiveNoMoneyNextRound = !pPlayer->DoesPlayerGetRoundStartMoney(); int nKills, iEnemyKillHeadshots, iEnemy3Ks, iEnemy4Ks, iEnemy5Ks, iEnemyKillsAgg; pPlayer->GetEnemyKillTrackInfo( nKills, iEnemyKillHeadshots, iEnemy3Ks, iEnemy4Ks, iEnemy5Ks, iEnemyKillsAgg ); qmmPlayerData.m_numEnemyKills = nKills; qmmPlayerData.m_numEnemyKillHeadshots = iEnemyKillHeadshots; qmmPlayerData.m_numEnemy3Ks = iEnemy3Ks; qmmPlayerData.m_numEnemy4Ks = iEnemy4Ks; qmmPlayerData.m_numEnemy5Ks = iEnemy5Ks; qmmPlayerData.m_numEnemyKillsAgg = iEnemyKillsAgg; qmmPlayerData.m_numRoundsWon = pPlayer->m_iRoundsWon; for ( int i = 0; i < MAX_MATCH_STATS_ROUNDS; i++ ) { qmmPlayerData.m_iMatchStats_Kills[ i ] = pPlayer->m_iMatchStats_Kills.Get( i ); qmmPlayerData.m_iMatchStats_Damage[ i ] = pPlayer->m_iMatchStats_Damage.Get( i ); qmmPlayerData.m_iMatchStats_MoneySaved[ i ] = pPlayer->m_iMatchStats_MoneySaved.Get( i ); qmmPlayerData.m_iMatchStats_EquipmentValue[ i ] = pPlayer->m_iMatchStats_EquipmentValue.Get( i ); qmmPlayerData.m_iMatchStats_KillReward[ i ] = pPlayer->m_iMatchStats_KillReward.Get( i ); qmmPlayerData.m_iMatchStats_LiveTime[ i ] = pPlayer->m_iMatchStats_LiveTime.Get( i ); qmmPlayerData.m_iMatchStats_Deaths[ i ] = pPlayer->m_iMatchStats_Deaths.Get( i ); qmmPlayerData.m_iMatchStats_Assists[ i ] = pPlayer->m_iMatchStats_Assists.Get( i ); qmmPlayerData.m_iMatchStats_HeadShotKills[ i ] = pPlayer->m_iMatchStats_HeadShotKills.Get( i ); qmmPlayerData.m_iMatchStats_Objective[ i ] = pPlayer->m_iMatchStats_Objective.Get( i ); qmmPlayerData.m_iMatchStats_CashEarned[ i ] = pPlayer->m_iMatchStats_CashEarned.Get( i ); qmmPlayerData.m_iMatchStats_UtilityDamage[ i ] = pPlayer->m_iMatchStats_UtilityDamage.Get( i ); qmmPlayerData.m_iMatchStats_EnemiesFlashed[ i ] = pPlayer->m_iMatchStats_EnemiesFlashed.Get( i ); } m_mapQueuedMatchmakingPlayersData.InsertOrReplace( qmmPlayerData.m_uiPlayerAccountId, &qmmPlayerData ); return &qmmPlayerData; } void CCSGameRules::IncrementAndTerminateRound( float tmDelay, int reason ) { bool roundIsAlreadyOver = ( CSGameRules()->m_iRoundWinStatus != WINNER_NONE ); if ( roundIsAlreadyOver ) return; if ( reason == Round_Draw ) { m_match.IncrementRound( 1 ); } else if ( reason == Terrorists_Win ) { m_match.AddTerroristWins( 1 ); } else if ( reason == CTs_Win ) { m_match.AddCTWins( 1 ); if ( IsPlayingCoopMission() ) { if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) ) pTeam->MarkSurrendered(); } } else { Assert( !"IncrementAndTerminateRound not implemented for the reason passed" ); return; } TerminateRound( tmDelay, reason ); } static inline int GetNumPlayers( CTeam *pTeam ) { return pTeam ? pTeam->GetNumPlayers() : 0; } void CCSGameRules::TerminateRound(float tmDelay, int iReason ) { if ( m_iRoundWinStatus != WINNER_NONE ) return; variant_t emptyVariant; int iWinnerTeam = WINNER_NONE; const char *text = "UNKNOWN"; // extend the time a bit if we have items to display float flItemDelay = tmDelay; // if ( m_ItemsPtrDroppedDuringMatch.Count() > 0 && !IsPlayingAnyCompetitiveStrictRuleset() ) // flItemDelay += MIN(5, m_ItemsPtrDroppedDuringMatch.Count())*0.75; // UTIL_ClientPrintAll( HUD_PRINTCENTER, sentence ); if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_T_Surrender ) { iReason = Terrorists_Surrender; if ( CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ) ) pTeam->MarkSurrendered(); } else if ( m_eQueuedMatchmakingRematchState == k_EQueuedMatchmakingRematchState_VoteToRematch_CT_Surrender ) { iReason = CTs_Surrender; if ( CTeam *pTeam = GetGlobalTeam( TEAM_CT ) ) pTeam->MarkSurrendered(); } switch ( iReason ) { // Terror wins: case Target_Bombed: text = "#SFUI_Notice_Target_Bombed"; iWinnerTeam = WINNER_TER; break; case Terrorists_Escaped: text = "#SFUI_Notice_Terrorists_Escaped"; iWinnerTeam = WINNER_TER; break; case Terrorists_Win: text = "#SFUI_Notice_Terrorists_Win"; iWinnerTeam = WINNER_TER; break; case Hostages_Not_Rescued: text = "#SFUI_Notice_Hostages_Not_Rescued"; iWinnerTeam = WINNER_TER; break; case Terrorists_Planted: text = "#SFUI_Notice_Terrorists_Planted"; iWinnerTeam = WINNER_TER; break; case CTs_ReachedHostage: text = "#SFUI_Notice_CTs_ReachedHostage"; iWinnerTeam = WINNER_CT; break; case CTs_PreventEscape: text = "#SFUI_Notice_CTs_PreventEscape"; iWinnerTeam = WINNER_CT; break; case Escaping_Terrorists_Neutralized: text = "#SFUI_Notice_Escaping_Terrorists_Neutralized"; iWinnerTeam = WINNER_CT; break; case Bomb_Defused: text = "#SFUI_Notice_Bomb_Defused"; iWinnerTeam = WINNER_CT; break; case CTs_Win: text = "#SFUI_Notice_CTs_Win"; iWinnerTeam = WINNER_CT; break; case All_Hostages_Rescued: text = "#SFUI_Notice_All_Hostages_Rescued"; iWinnerTeam = WINNER_CT; break; case Target_Saved: text = "#SFUI_Notice_Target_Saved"; iWinnerTeam = WINNER_CT; break; case Terrorists_Not_Escaped: text = "#SFUI_Notice_Terrorists_Not_Escaped"; iWinnerTeam = WINNER_CT; break; // no winners: case Game_Commencing: text = "#SFUI_Notice_Game_Commencing"; iWinnerTeam = WINNER_DRAW; break; case Round_Draw: text = "#SFUI_Notice_Round_Draw"; iWinnerTeam = WINNER_DRAW; break; case Terrorists_Surrender: text = "#SFUI_Notice_Terrorists_Surrender"; iWinnerTeam = WINNER_CT; break; case CTs_Surrender: text = "#SFUI_Notice_CTs_Surrender"; iWinnerTeam = WINNER_TER; break; default: DevMsg("TerminateRound: unknown round end ID %i\n", iReason ); break; } m_iRoundWinStatus = iWinnerTeam; m_eRoundWinReason = iReason; m_flRestartRoundTime = gpGlobals->curtime + flItemDelay; if ( ( m_iRoundWinStatus == WINNER_TER ) || ( m_iRoundWinStatus == WINNER_CT ) ) { // If the round is ending and there's a pending hook for MVP calculation // and player scoring then run these calculations here before the round // officially ends and the game goes on to save round stats or goes to // intermission and submits match outcome if ( m_pfnCalculateEndOfRoundMVPHook ) m_pfnCalculateEndOfRoundMVPHook->CalculateEndOfRoundMVP(); else // run default MVP rules without any special hooks CalculateEndOfRoundMVP(); } if ( ( ( m_iRoundWinStatus == WINNER_TER ) || ( m_iRoundWinStatus == WINNER_CT ) ) && Helper_mp_backup_round_IsEnabled() ) { SaveRoundDataInformation(); } if ( iWinnerTeam == WINNER_CT ) { for( int i=0;iAcceptInput( "CTsWin", NULL, NULL, emptyVariant, 0 ); } else if ( iWinnerTeam == WINNER_TER ) { for( int i=0;iAcceptInput( "TerroristsWin", NULL, NULL, emptyVariant, 0 ); } else { Assert( iWinnerTeam == WINNER_NONE || iWinnerTeam == WINNER_DRAW ); } for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); if (pPlayer) { // have all players do any end of round bookkeeping pPlayer->HandleEndOfRound(); } } // [tj] Check for any non-player-specific achievements. ProcessEndOfRoundAchievements(iWinnerTeam, iReason); if( iReason != Game_Commencing ) { // [pfreese] Setup and send win panel event (primarily funfact data) FunFact funfact; funfact.szLocalizationToken = ""; funfact.iPlayer = 0; funfact.iData1 = 0; funfact.iData2 = 0; funfact.iData3 = 0; m_pFunFactManager->GetRoundEndFunFact( iWinnerTeam, (e_RoundEndReason)iReason, funfact); //Send all the info needed for the win panel IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" ); if ( winEvent ) { // determine what categories to send if ( GetRoundRemainingTime() <= 0 ) { // timer expired, defenders win // show total time that was defended winEvent->SetBool( "show_timer_defend", true ); winEvent->SetInt( "timer_time", m_iRoundTime ); } else { // attackers win // show time it took for them to win winEvent->SetBool( "show_timer_attack", true ); int iTimeElapsed = GetRoundElapsedTime(); winEvent->SetInt( "timer_time", iTimeElapsed ); } winEvent->SetInt( "final_event", iReason ); // Set the fun fact data in the event winEvent->SetString( "funfact_token", funfact.szLocalizationToken); winEvent->SetInt( "funfact_player", funfact.iPlayer ); winEvent->SetInt( "funfact_data1", funfact.iData1 ); winEvent->SetInt( "funfact_data2", funfact.iData2 ); winEvent->SetInt( "funfact_data3", funfact.iData3 ); gameeventmanager->FireEvent( winEvent ); } } // [tj] Inform players that the round is over for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if(pPlayer) { pPlayer->OnRoundEnd(iWinnerTeam, iReason); if ( ( pPlayer->GetTeamNumber() == iWinnerTeam ) && ( IsPlayingClassic() ) ) { ++ pPlayer->m_iRoundsWon; // Keep track in QMM data if ( pPlayer->GetHumanPlayerAccountID() ) { if ( CCSGameRules::CQMMPlayerData_t *pQMM = CSGameRules()->QueuedMatchmakingPlayersDataFind( pPlayer->GetHumanPlayerAccountID() ) ) { pQMM->m_numRoundsWon = pPlayer->m_iRoundsWon; } } } if ( IsPlayingCoopGuardian() && (iReason == CTs_ReachedHostage || iReason == Terrorists_Planted) && (pPlayer->GetTeamNumber() == TEAM_CT || pPlayer->GetTeamNumber() == TEAM_TERRORIST) ) { color32_s clr = {0,0,0,255}; UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT ); } if ( IsPlayingCoopMission() ) { color32_s clr = { 0, 0, 0, 255 }; UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT ); } } } if ( IsPlayingCoopGuardian() && ( iReason == CTs_ReachedHostage || iReason == Terrorists_Planted ) ) FreezePlayers(); if ( IsPlayingCoopMission() ) { CGameCoopMissionManager *pManager = GetCoopMissionManager(); // we really dont have one if ( !pManager ) DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't fire round ended outputs without it!\n" ); else if ( iReason == Terrorists_Win ) { if ( GetRoundRemainingTime() <= 0 ) pManager->SetRoundLostTime(); else pManager->SetRoundLostKilled(); } } IGameEvent * event = gameeventmanager->CreateEvent( "round_end" ); if ( event ) { event->SetInt( "winner", iWinnerTeam ); event->SetInt( "reason", iReason ); event->SetString( "message", text ); event->SetInt( "priority", 6 ); // round_end event->SetInt( "player_count", GetNumPlayers( GetGlobalTeam( TEAM_CT ) ) + GetNumPlayers( GetGlobalTeam( TEAM_TERRORIST ) ) ); if ( ( iWinnerTeam == TEAM_CT ) && ( iReason == Bomb_Defused ) ) { // Check if there are Terrorists alive bool bTerroristsAlive = false; float flSecondsTillDetonationRemaining = 0.0f; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer* pCheckPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); if ( !pCheckPlayer ) continue; if ( ( pCheckPlayer->GetTeamNumber() == TEAM_TERRORIST ) && pCheckPlayer->IsAlive() ) { bTerroristsAlive = true; break; } if ( pCheckPlayer->GetTeamNumber() == TEAM_CT ) { float flTimeTillDetonation = pCheckPlayer->GetDefusedBombWithThisTimeRemaining(); if ( flTimeTillDetonation > flSecondsTillDetonationRemaining ) flSecondsTillDetonationRemaining = flTimeTillDetonation; } } if ( !bTerroristsAlive && ( flSecondsTillDetonationRemaining > 0 ) ) { // Can only play legacy radio if no terrorists are alive (so all players are focused on the defuse) if ( flSecondsTillDetonationRemaining < 1.0f ) { // Always play the coolest brag for a close defuse event->SetInt( "legacy", 3 ); } else if ( flSecondsTillDetonationRemaining < 3.0f ) { if ( RandomFloat() < 0.75f ) // 75% chance for a medium tier brag event->SetInt( "legacy", 2 ); } else if ( flSecondsTillDetonationRemaining < 7.0f ) { if ( RandomFloat() < 0.5f ) // 50% chance for a low tier brag event->SetInt( "legacy", 1 ); } } } gameeventmanager->FireEvent( event ); } if ( ( iReason == CTs_Surrender || iReason == Terrorists_Surrender ) && !IsQueuedMatchmaking() ) { m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); GoToIntermission(); } if ( ( GetMapRemainingTime() == 0.0f ) && !IsQueuedMatchmaking() ) { UTIL_LogPrintf("World triggered \"Intermission_Time_Limit\"\n"); m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); GoToIntermission(); } // Only update bot difficulty if playing in an Online-game (In offline mode, bot difficulty is set by user) if ( !IsPlayingOffline() ) { // Determine the difficulty level that the bots should be at to compete ModifyRealtimeBotDifficulty(); } if ( ( static_cast< e_RoundEndReason > ( iReason ) != Game_Commencing ) && !m_bLoadingRoundBackupData ) { // Perform round-related processing at the point when a round winner has been determined RoundWin(); } // This is a fantastic opportunity to submit round results to GC if ( iReason != Game_Commencing ) { ReportRoundEndStatsToGC(); if ( Helper_ShouldBroadcastCoopScoreLeaderboardData() ) { CReliableBroadcastRecipientFilter filter; CCSUsrMsg_ScoreLeaderboardData msg; Helper_FillScoreLeaderboardData( *msg.mutable_data() ); SendUserMessage( filter, CS_UM_ScoreLeaderboardData, msg ); } if ( IsPlayingCoopGuardian() && ( iWinnerTeam == ( IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT ) ) ) { // If human team wins then end the match right now! m_match.SetPhase( GAMEPHASE_MATCH_ENDED ); m_phaseChangeAnnouncementTime = gpGlobals->curtime + mp_win_panel_display_time.GetInt(); GoToIntermission(); } else if ( IsPlayingCoopMission() && iWinnerTeam == TEAM_CT ) { if ( IsPlayingCoopMission() ) { CGameCoopMissionManager *pManager = GetCoopMissionManager(); // we really dont have one if ( !pManager ) DevMsg( "Coop mission map is missing a game_coopmission_manager entity. You can't fire MissionCompleted outputs without it!\n" ); else pManager->SetMissionCompleted(); } // If human team wins then end the match right now! m_match.SetPhase( GAMEPHASE_MATCH_ENDED ); m_phaseChangeAnnouncementTime = gpGlobals->curtime + 1.5; GoToIntermission(); } } if ( iReason == Game_Commencing ) { m_bWarmupPeriod = true; } } void CCSGameRules::CreateEndMatchMapGroupVoteOptions( void ) { CUtlVector< int > arrVoteCandidates; if ( g_pGameTypes ) { const char* mapGroupName = gpGlobals->mapGroupName.ToCStr(); const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName ); if ( mapsInGroup ) { int nCount = mapsInGroup->Count(); int nCurrentMapIndex = -1; for ( int iMap = 0; iMap < nCount; ++ iMap ) { arrVoteCandidates.AddToTail( iMap ); if ( !V_stricmp( mapsInGroup->Element( iMap ), STRING( gpGlobals->mapname ) ) ) nCurrentMapIndex = iMap; } // Remove current map from pool if mp_endmatch_votenextmap_keepcurrent is set to 0. if ( !mp_endmatch_votenextmap_keepcurrent.GetBool() && nCurrentMapIndex >= 0 ) arrVoteCandidates.Remove( nCurrentMapIndex ); while ( arrVoteCandidates.Count() > 10 ) { int nRemoveIndex = RandomInt( 0, arrVoteCandidates.Count() - 1 ); if ( mp_endmatch_votenextmap_keepcurrent.GetBool() && ( arrVoteCandidates[nRemoveIndex] == nCurrentMapIndex ) ) { nRemoveIndex ++; nRemoveIndex %= arrVoteCandidates.Count(); } arrVoteCandidates.Remove( nRemoveIndex ); } } } for ( int iVoteOption = 0; iVoteOption < MAX_ENDMATCH_VOTE_PANELS; ++ iVoteOption ) m_nEndMatchMapGroupVoteOptions.Set( iVoteOption, arrVoteCandidates.IsValidIndex( iVoteOption ) ? arrVoteCandidates[iVoteOption] : -1 ); } void CCSGameRules::ReportRoundEndStatsToGC( CMsgGCCStrike15_v2_MatchmakingServerRoundStats **ppAllocateStats ) { /** Removed for partner depot **/ } // Helper to determine if all players on a team are playing for the same clan bool CCSGameRules::IsClanTeam( CTeam *pTeam ) { uint32 iTeamClan = 0; bool bTeamInitialized = false; for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CBasePlayer *pPlayer = pTeam->GetPlayer( iPlayer ); if ( !pPlayer ) return false; if ( pPlayer->IsBot() ) continue; const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" ); uint32 iPlayerClan = atoi( pClanID ); // Initialize the team clan if ( !bTeamInitialized ) { iTeamClan = iPlayerClan; bTeamInitialized = true; } if ( iPlayerClan != iTeamClan || iPlayerClan == 0 ) return false; } return iTeamClan != 0; } void CCSGameRules::ModifyRealtimeBotDifficulty( CCSPlayer* pOnlyBotToProcess /* = NULL */ ) { if ( !sv_auto_adjust_bot_difficulty.GetBool() ) return; float fAvgPlayerCS = CalculateAveragePlayerContributionScore(); float fAvgBotCS; if ( !sv_compute_per_bot_difficulty.GetBool() ) { fAvgBotCS = CalculateAverageBotContributionScore(); } else { fAvgBotCS = 0.0f; } float fLowWindowExtent = fAvgPlayerCS + bot_autodifficulty_threshold_low.GetFloat(); float fHighWindowExtent = fAvgPlayerCS + bot_autodifficulty_threshold_high.GetFloat(); // Ensure the high/low window extents are accurate if ( fLowWindowExtent > fHighWindowExtent ) { float swapval = fLowWindowExtent; fLowWindowExtent = fHighWindowExtent; fHighWindowExtent = swapval; } int nextBotDifficulty = -1; if ( sv_compute_per_bot_difficulty.GetBool() ) { // Compare incoming target contribution score to levels of difficulty, and choose a level for each bot for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( !pPlayer ) { continue; } if ( pOnlyBotToProcess && pPlayer != pOnlyBotToProcess ) { continue; } if ( pPlayer->IsBot() ) { CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer ); if ( pBot ) { const BotProfile* pProfile = pBot->GetProfile(); if ( pProfile && TheBotProfiles ) { float cScore = ( float )pPlayer->GetContributionScore(); nextBotDifficulty = -1; DevMsg( "+++++Bot %s has CS = %f. Low = %f, High = %f\n", pPlayer->GetPlayerName(), cScore, fLowWindowExtent, fHighWindowExtent ); if ( cScore < fLowWindowExtent ) { // Bot should have higher difficulty DevMsg( "+++++Bot %s with CS = %f, should increase its difficulty\n", pPlayer->GetPlayerName(), cScore ); nextBotDifficulty = pProfile->GetMaxDifficulty() + 1; // Ensure new difficulty level is valid if ( nextBotDifficulty >= NUM_DIFFICULTY_LEVELS ) { nextBotDifficulty = NUM_DIFFICULTY_LEVELS - 1; } } else if ( cScore > fHighWindowExtent ) { // Bot should have lower difficulty DevMsg( "+++++Bot %s with CS = %f, should decrease its difficulty\n", pPlayer->GetPlayerName(), cScore ); nextBotDifficulty = pProfile->GetMaxDifficulty() - 1; // Ensure new difficulty level is valid if ( nextBotDifficulty < BOT_EASY ) { nextBotDifficulty = BOT_EASY; } } // In queue matchmaking mode bots are always obeying bot manager difficulty if ( IsQueuedMatchmaking() ) nextBotDifficulty = CCSBotManager::GetDifficultyLevel(); if ( nextBotDifficulty >= BOT_EASY ) { // Change the bot's new profile based on desired difficulty const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )nextBotDifficulty, pBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true ); if ( NULL == pNewProfileData ) { Warning( "-----No profile found to match search criteria. Not updating this bot's difficulty."); } else { // Change the parameters of the bot's profile to match the new difficulty settings DevMsg( "+++++Bot %s with Max Difficulty %d will become Bot %s with Max Difficulty %d\n", pProfile->GetName(), pProfile->GetMaxDifficulty(), pNewProfileData->GetName(), pNewProfileData->GetMaxDifficulty() ); pBot->Initialize( pNewProfileData, pBot->GetTeamNumber() ); } } } } } } } else { // Handle the alternate case for all bots changing difficulty as a group (all bots will have identical difficulty) // DevMsg( "+++++Average Bot Contribution Score = %f\n", fAvgBotCS ); if ( fAvgBotCS < fLowWindowExtent ) { // Bots should have higher difficulty nextBotDifficulty = -1; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( pOnlyBotToProcess && pPlayer != pOnlyBotToProcess ) { continue; } if ( pPlayer && pPlayer->IsBot() ) { CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer ); if ( pBot ) { const BotProfile* pProfile = pBot->GetProfile(); if ( pProfile && TheBotProfiles && nextBotDifficulty == -1 ) { nextBotDifficulty = pProfile->GetMaxDifficulty() + 1; if ( nextBotDifficulty > BOT_EXPERT ) { nextBotDifficulty = BOT_EXPERT; } } // In queue matchmaking mode bots are always obeying bot manager difficulty if ( IsQueuedMatchmaking() ) nextBotDifficulty = CCSBotManager::GetDifficultyLevel(); if ( nextBotDifficulty > -1 ) { // Have a valid difficulty level, so apply it to each bot const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )nextBotDifficulty, pBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true ); if ( NULL == pNewProfileData ) { Warning( "-----No profile found to match search criteria. Not updating this bot's difficulty."); } else { // Change the parameters of the bot's profile to match the new difficulty settings DevMsg( "+++++Bot %s with Max Difficulty %d will become Bot %s with Max Difficulty %d\n", pProfile->GetName(), pProfile->GetMaxDifficulty(), pNewProfileData->GetName(), pNewProfileData->GetMaxDifficulty() ); pBot->Initialize( pNewProfileData, pBot->GetTeamNumber() ); } } } } } } else if ( fAvgBotCS > fHighWindowExtent ) { // Bots should have lower difficulty nextBotDifficulty = -1; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( pOnlyBotToProcess && pPlayer != pOnlyBotToProcess ) { continue; } if ( pPlayer && pPlayer->IsBot() ) { CCSBot* pBot = dynamic_cast< CCSBot* >( pPlayer ); if ( pBot ) { const BotProfile* pProfile = pBot->GetProfile(); if ( pProfile && TheBotProfiles && nextBotDifficulty == -1 ) { nextBotDifficulty = pProfile->GetMaxDifficulty() - 1; // Ensure new difficulty level is valid if ( nextBotDifficulty < BOT_EASY ) { nextBotDifficulty = BOT_EASY; } } // In queue matchmaking mode bots are always obeying bot manager difficulty if ( IsQueuedMatchmaking() ) nextBotDifficulty = CCSBotManager::GetDifficultyLevel(); if ( nextBotDifficulty > -1 && ( pProfile->GetMaxDifficulty() != nextBotDifficulty ) ) { // Have a valid difficulty level, so apply it to each bot const BotProfile* pNewProfileData = TheBotProfiles->GetRandomProfile( ( BotDifficultyType )nextBotDifficulty, pBot->GetTeamNumber(), WEAPONTYPE_UNKNOWN, true ); if ( NULL == pNewProfileData ) { Warning( "-----No profile found to match search criteria. Not updating this bot's difficulty."); } else { // Change the parameters of the bot's profile to match the new difficulty settings DevMsg( "+++++Bot %s with Max Difficulty %d will become Bot %s with Max Difficulty %d\n", pProfile->GetName(), pProfile->GetMaxDifficulty( ), pNewProfileData->GetName(), pNewProfileData->GetMaxDifficulty() ); pBot->Initialize( pNewProfileData, pBot->GetTeamNumber() ); } } } } } } } if ( !IsPlayingOffline() ) { fAvgBotCS = CalculateAverageBotContributionScore(); //DevMsg( "Average Bot Difficulty = %f\n", fAvgBotCS ); // Store max bot difficulty in the convar representing the player's input device sv_bot_difficulty_kbm.SetValue( fAvgBotCS ); } } float CCSGameRules::CalculateAveragePlayerContributionScore( void ) { // Loop through all players and get average human contribution score int cscoreTotal = 0; int numHumanPlayers = 0; float avgHumanContributionScore = 0.0f; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( !pPlayer ) { continue; } if ( !pPlayer->IsBot() ) { cscoreTotal += pPlayer->GetContributionScore(); numHumanPlayers++; } } if ( numHumanPlayers > 0 ) { avgHumanContributionScore = ( float )cscoreTotal / ( float )numHumanPlayers; } return avgHumanContributionScore; } float CCSGameRules::CalculateAverageBotContributionScore( void ) { // Loop through all players and get average bot contribution score int cscoreTotal = 0; int numBots = 0; float avgBotContributionScore = 0.0f; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ( CCSPlayer* )UTIL_PlayerByIndex( i ); if ( !pPlayer ) { continue; } if ( pPlayer->IsBot() ) { cscoreTotal += pPlayer->GetContributionScore(); numBots++; } } if ( numBots > 0 ) { avgBotContributionScore = ( float )cscoreTotal / ( float )numBots; } return avgBotContributionScore; } // [tj] This is where we check non-player-specific that occur at the end of the round void CCSGameRules::ProcessEndOfRoundAchievements(int iWinnerTeam, int iReason) { if (iWinnerTeam == WINNER_CT || iWinnerTeam == WINNER_TER) { int losingTeamId = (iWinnerTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT; CTeam* losingTeam = GetGlobalTeam(losingTeamId); //Check for players we should ignore when checking team size. int ignoreCount = 0; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); if (pPlayer) { if( IsPlayingGunGameProgressive() ) UpdateMatchStats(pPlayer,iWinnerTeam); int teamNum = pPlayer->GetTeamNumber(); if ( teamNum == losingTeamId ) { if (pPlayer->WasNotKilledNaturally()) { ignoreCount++; } } } } // [tj] Check extermination with no losses achievement if ((iReason == CTs_Win && m_bNoCTsKilled || iReason == Terrorists_Win && m_bNoTerroristsKilled) && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement) { CTeam *pTeam = GetGlobalTeam( iWinnerTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; pPlayer->AwardAchievement(CSLosslessExtermination); } } // [tj] Check flawless victory achievement - currently requiring extermination if ((iReason == CTs_Win && m_bNoCTsDamaged || iReason == Terrorists_Win && m_bNoTerroristsDamaged) && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement && !CSGameRules()->IsPlayingGunGameProgressive() ) { CTeam *pTeam = GetGlobalTeam( iWinnerTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; pPlayer->AwardAchievement(CSFlawlessVictory); } } // [tj] Check bloodless victory achievement if ((iWinnerTeam == TEAM_TERRORIST && m_bNoCTsKilled || iWinnerTeam == TEAM_CT && m_bNoTerroristsKilled) && losingTeam && losingTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement) { CTeam *pTeam = GetGlobalTeam( iWinnerTeam ); for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) ); Assert( pPlayer ); if ( !pPlayer ) continue; pPlayer->AwardAchievement(CSBloodlessVictory); } } } } //[tj] Counts the number of players in each category in the struct (dead, alive, etc...) void CCSGameRules::GetPlayerCounts(TeamPlayerCounts teamCounts[TEAM_MAXCOUNT]) { memset(teamCounts, 0, sizeof(TeamPlayerCounts) * TEAM_MAXCOUNT); for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); if (pPlayer) { int iTeam = pPlayer->GetTeamNumber(); if (iTeam >= 0 && iTeam < TEAM_MAXCOUNT) { ++teamCounts[iTeam].totalPlayers; if (pPlayer->IsAlive()) { ++teamCounts[iTeam].totalAlivePlayers; } else { ++teamCounts[iTeam].totalDeadPlayers; //If the player has joined a team bit isn't in the game yet if (pPlayer->State_Get() == STATE_PICKINGCLASS) { ++teamCounts[iTeam].unenteredPlayers; } else if (pPlayer->WasNotKilledNaturally()) { ++teamCounts[iTeam].suicidedPlayers; } else { ++teamCounts[iTeam].killedPlayers; } } } } } } void CCSGameRules::CheckMapConditions() { // Check to see if this map has a bomb target in it if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) ) { // this is a bit hacky, but it makes it so the bomb stuff only shows up on mission 3 of the coop mission if ( IsPlayingCoopMission() && mp_anyone_can_pickup_c4.GetBool() == false ) { m_bMapHasBombTarget = false; m_bMapHasBombZone = false; } else { m_bMapHasBombTarget = true; m_bMapHasBombZone = true; } } else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) ) { m_bMapHasBombTarget = true; m_bMapHasBombZone = false; } else { m_bMapHasBombTarget = false; m_bMapHasBombZone = false; } // See if the map has func_buyzone entities // Used by CBasePlayer::HandleSignals() to support maps without these entities if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) ) { m_bMapHasBuyZone = true; } else { m_bMapHasBuyZone = false; } // Check to see if this map has hostage rescue zones if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) ) { m_bMapHasRescueZone = true; } else { m_bMapHasRescueZone = false; } // GOOSEMAN : See if this map has func_escapezone entities if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) ) { m_bMapHasEscapeZone = true; } else { m_bMapHasEscapeZone = false; } } void CCSGameRules::SwapAllPlayers() { // MOTODO we have to make sure that enought spaning points exits Assert ( 0 ); for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { /* CCSPlayer *pPlayer = CCSPlayer::Instance( i ); if ( pPlayer && !FNullEnt( pPlayer->edict() ) ) pPlayer->SwitchTeam(); */ } m_match.SwapTeamScores(); } // reset player scores, team scores, and player controls, restart round; useful for running a demo void CCSGameRules::ResetForTradeshow( void ) { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = CCSPlayer::Instance( i ); if ( pPlayer ) { pPlayer->Reset( true ); } } m_match.Reset(); IGameEvent * event = gameeventmanager->CreateEvent( "reset_player_controls" ); if ( event ) { gameeventmanager->FireEvent( event ); } ClearGunGameData(); EndRound(); } bool CS_FindInList( const char **pStrings, const char *pToFind ) { return FindInList( pStrings, pToFind ); } void CCSGameRules::CleanUpMap() { if (IsLogoMap()) return; // Recreate all the map entities from the map data (preserving their indices), // then remove everything else except the players. // first go through the current players and see if they are parented to something // if so, unparent them before we might remove their parent and cause a crash for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = CCSPlayer::Instance( i ); if ( pPlayer && !FNullEnt( pPlayer->edict() ) ) { if ( pPlayer->GetParent() ) pPlayer->SetParent( NULL ); } } // Get rid of all entities except players. CBaseEntity *pCur = gEntList.FirstEnt(); while ( pCur ) { CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pCur ); CBombTarget *pBombTarg = dynamic_cast< CBombTarget* >( pCur ); CHostageRescueZone *pRescueZone = dynamic_cast< CHostageRescueZone* >( pCur ); // Weapons with owners don't want to be removed.. if ( pWeapon ) { // [dwenger] Handle round restart processing for the weapon. pWeapon->OnRoundRestart(); if ( pWeapon->ShouldRemoveOnRoundRestart() ) { UTIL_Remove( pCur ); } } // bomb targets have a re-init function to call else if ( pBombTarg ) { pBombTarg->ReInitOnRoundStart(); } else if ( pRescueZone ) { pRescueZone->ReInitOnRoundStart(); } // remove entities that has to be restored on roundrestart (breakables etc) else if ( !CS_FindInList( s_PreserveEnts, pCur->GetClassname() ) ) { if( !Commentary_IsCommentaryEntity( pCur ) ) //leave commentary alone { UTIL_Remove( pCur ); } } pCur = gEntList.NextEnt( pCur ); } // Really remove the entities so we can have access to their slots below. gEntList.CleanupDeleteList(); // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that // could kill respawning CTs g_EventQueue.Clear(); // Now reload the map entities. class CCSMapEntityFilter : public IMapEntityFilter { public: virtual bool ShouldCreateEntity( const char *pClassname ) { // Don't recreate the preserved entities. if ( !CS_FindInList( s_PreserveEnts, pClassname ) ) { return true; } else { // Increment our iterator since it's not going to call CreateNextEntity for this ent. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() ) m_iIterator = g_MapEntityRefs.Next( m_iIterator ); return false; } } virtual CBaseEntity* CreateNextEntity( const char *pClassname ) { if ( m_iIterator == g_MapEntityRefs.InvalidIndex() ) { // This shouldn't be possible. When we loaded the map, it should have used // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list // with the same list of entities we're referring to here. Assert( false ); return NULL; } else { CMapEntityRef &ref = g_MapEntityRefs[m_iIterator]; m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity. if ( ref.m_iEdict == -1 || INDEXENT( ref.m_iEdict ) ) { // Doh! The entity was delete and its slot was reused. // Just use any old edict slot. This case sucks because we lose the baseline. return CreateEntityByName( pClassname ); } else { // Cool, the slot where this entity was is free again (most likely, the entity was // freed above). Now create an entity with this specific index. return CreateEntityByName( pClassname, ref.m_iEdict ); } } } public: int m_iIterator; // Iterator into g_MapEntityRefs. }; CCSMapEntityFilter filter; filter.m_iIterator = g_MapEntityRefs.Head(); // DO NOT CALL SPAWN ON info_node ENTITIES! MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true ); // this kind of sucks. on ps3, we need to delete all physics props that are not set to debris to fix a crash, // but we don't know if the prop has the debris flag until after it's been removed and then recreated again. // that's why we have to loop through all of the entities again here. if ( IsPS3() || engine->IsDedicatedServerForPS3() ) { int nNumPhysPropsDeleted = 0; CBaseEntity *pPhysCur = gEntList.FirstEnt(); while ( pPhysCur ) { CPhysicsProp *pPhysProp = dynamic_cast< CPhysicsProp* >( pPhysCur ); if ( pPhysProp && !pPhysProp->HasSpawnFlags( SF_PHYSPROP_DEBRIS ) ) { UTIL_Remove( pPhysCur ); nNumPhysPropsDeleted++; } pPhysCur = gEntList.NextEnt( pPhysCur ); } if ( nNumPhysPropsDeleted > 0 ) Msg( "DELETED %d prop_physics or prop_physics_multiplayer that were not set to debris!!\n", nNumPhysPropsDeleted ); } } CCSPlayer *CCSGameRules::IsThereABomber() { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = CCSPlayer::Instance( i ); if ( pPlayer && !FNullEnt( pPlayer->edict() ) ) { if ( pPlayer->GetTeamNumber() == TEAM_CT ) continue; if ( pPlayer->HasC4() ) return pPlayer; //There you are. } } //Didn't find a bomber. return NULL; } void CCSGameRules::EndRound() { // fake a round end CSGameRules()->TerminateRound( 0.0f, Round_Draw ); } CBaseEntity *CCSGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer ) { // we don't want to get a spawn if we are already in the process of spawning with one because // it increments the spawn number without actually spawning and messes up the spawn priority CCSPlayer* pCSPlayer = ToCSPlayer( pPlayer ); if ( !pCSPlayer || pCSPlayer->IsPlayerSpawning() ) return NULL; pCSPlayer->SetPlayerSpawning( true ); // get valid spawn point CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint(); if ( pPlayer->IsBot() && pPlayer->GetTeamNumber() == TEAM_TERRORIST ) { // // disable the spawn after someone spawn here in coop SpawnPointCoopEnemy *pEnemySpawnSpot = dynamic_cast< SpawnPointCoopEnemy* >( pSpawnSpot ); if ( pEnemySpawnSpot ) { if ( IsPlayingCoopMission() ) { CCSBot* pBot = static_cast< CCSBot* >( pPlayer ); if ( pBot ) { pBot->SetLastCoopSpawnPoint( pEnemySpawnSpot ); } } } } // drop down to ground Vector GroundPos = DropToGround( pPlayer, pSpawnSpot->GetAbsOrigin() + Vector( 0, 0, 16 ), VEC_HULL_MIN + Vector( -4, -4, 0 ), VEC_HULL_MAX + Vector( 4, 4, 0 ) ); // Move the player to the place it said. pPlayer->Teleport( &GroundPos, &pSpawnSpot->GetLocalAngles(), &vec3_origin ); pPlayer->m_Local.m_viewPunchAngle = vec3_angle; return pSpawnSpot; } // checks if the spot is clear of players bool CCSGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer ) { if ( !pSpot ) return false; if ( !pSpot->IsTriggered( pPlayer ) ) return false; Vector mins = GetViewVectors()->m_vHullMin; Vector maxs = GetViewVectors()->m_vHullMax; Vector vTestMins = pSpot->GetAbsOrigin() + mins; Vector vTestMaxs = pSpot->GetAbsOrigin() + maxs; // First test the starting origin. CTraceFilterSimple traceFilter( pPlayer, COLLISION_GROUP_PLAYER ); if ( !UTIL_IsSpaceEmpty( pPlayer, vTestMins, vTestMaxs, MASK_SOLID, &traceFilter ) ) return false; // Test against other players potentially occupying this spot for ( int k = 1; k <= gpGlobals->maxClients; ++ k ) { CBasePlayer *pOther = UTIL_PlayerByIndex( k ); if ( !pOther ) continue; if ( pOther == pPlayer ) continue; if ( ( pOther->GetTeamNumber() != TEAM_TERRORIST ) && ( pOther->GetTeamNumber() != TEAM_CT ) ) continue; if ( ( pOther->GetAbsOrigin().AsVector2D() - pSpot->GetAbsOrigin().AsVector2D() ).IsZero() ) return false; } return true; } bool CCSGameRules::IsSpawnPointHiddenFromOtherPlayers( CBaseEntity *pSpot, CBasePlayer *pPlayer, int nHideFromTeam ) { Vector vecSpot = pSpot->GetAbsOrigin() + Vector( 0, 0, 32 ); if ( nHideFromTeam > 0 ) { if ( nHideFromTeam == TEAM_CT && UTIL_IsVisibleToTeam( vecSpot, TEAM_CT ) ) return false; else if ( nHideFromTeam == TEAM_TERRORIST && UTIL_IsVisibleToTeam( vecSpot, TEAM_TERRORIST ) ) return false; } else if ( nHideFromTeam == 0 && ( UTIL_IsVisibleToTeam( vecSpot, TEAM_CT ) ) || ( UTIL_IsVisibleToTeam( vecSpot, TEAM_TERRORIST ) ) ) return false; return true; } bool CCSGameRules::IsThereABomb() { bool bBombFound = false; /* are there any bombs, either laying around, or in someone's inventory? */ if( gEntList.FindEntityByClassname( NULL, WEAPON_C4_CLASSNAME ) != 0 ) { bBombFound = true; } /* what about planted bombs!? */ else if( gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME ) != 0 ) { bBombFound = true; } return bBombFound; } void CCSGameRules::HostageTouched() { if( gpGlobals->curtime > m_flNextHostageAnnouncement && m_iRoundWinStatus == WINNER_NONE ) { //BroadcastSound( "Event.HostageTouched" ); m_flNextHostageAnnouncement = gpGlobals->curtime + 60.0; } m_bHasHostageBeenTouched = true; if ( IsPlayingCoopGuardian() ) CTsReachedHostageRoundEndCheck(); } void CCSGameRules::CreateStandardEntities() { // Create the player resource g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "cs_player_manager", vec3_origin, vec3_angle ); // Create the entity that will send our data to the client. #ifdef DBGFLAG_ASSERT CBaseEntity *pEnt = #endif CBaseEntity::Create( "cs_gamerules", vec3_origin, vec3_angle ); Assert( pEnt ); g_voteControllerGlobal = static_cast< CVoteController *>( CBaseEntity::Create("vote_controller", vec3_origin, vec3_angle) ); g_voteControllerCT = static_cast< CVoteController *>( CBaseEntity::Create("vote_controller", vec3_origin, vec3_angle) ); g_voteControllerT = static_cast< CVoteController *>( CBaseEntity::Create("vote_controller", vec3_origin, vec3_angle) ); // Vote Issue classes are handled/cleaned-up by g_voteControllers NewTeamIssue< CKickIssue >(); NewGlobalIssue< CRestartGameIssue >(); NewGlobalIssue< CChangeLevelIssue >(); NewGlobalIssue< CNextLevelIssue >(); if ( IsPlayingAnyCompetitiveStrictRuleset() ) { NewTeamIssue< CStartTimeOutIssue >(); } static char const * s_pchTournamentServer = CommandLine()->ParmValue( "-tournament", ( char const * ) NULL ); if ( s_pchTournamentServer && IsQueuedMatchmaking() ) { NewGlobalIssue< CPauseMatchIssue >(); NewGlobalIssue< CUnpauseMatchIssue >(); NewGlobalIssue< CLoadBackupIssue >(); NewGlobalIssue< CReadyForMatchIssue >(); NewGlobalIssue< CNotReadyForMatchIssue >(); } if ( IsQueuedMatchmaking() ) { // new CQueuedMatchmakingRematch; // new CQueuedMatchmakingContinue; NewTeamIssue< CSurrender >(); } else { NewGlobalIssue< CScrambleTeams >(); NewGlobalIssue< CSwapTeams >(); // new CSurrender; } } #endif // CLIENT_DLL ConVar cash_team_terrorist_win_bomb( "cash_team_terrorist_win_bomb", "3500", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_elimination_hostage_map_t( "cash_team_elimination_hostage_map_t", "1000", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_elimination_hostage_map_ct( "cash_team_elimination_hostage_map_ct", "2000", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_elimination_bomb_map( "cash_team_elimination_bomb_map", "3250", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_survive_guardian_wave( "cash_team_survive_guardian_wave", "1000", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY ); ConVar cash_team_win_by_time_running_out_hostage( "cash_team_win_by_time_running_out_hostage", "3250", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_win_by_time_running_out_bomb( "cash_team_win_by_time_running_out_bomb", "3250", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_win_by_defusing_bomb( "cash_team_win_by_defusing_bomb", "3250", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_win_by_hostage_rescue( "cash_team_win_by_hostage_rescue", "3500", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_loser_bonus( "cash_team_loser_bonus", "1400", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_loser_bonus_consecutive_rounds( "cash_team_loser_bonus_consecutive_rounds", "500", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_rescued_hostage( "cash_team_rescued_hostage", "0", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_hostage_alive( "cash_team_hostage_alive", "0", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_planted_bomb_but_defused( "cash_team_planted_bomb_but_defused", "800", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_team_hostage_interaction( "cash_team_hostage_interaction", "500", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_killed_teammate( "cash_player_killed_teammate", "-300", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_killed_enemy_factor( "cash_player_killed_enemy_factor", "1", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_killed_enemy_default( "cash_player_killed_enemy_default", "300", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_bomb_planted( "cash_player_bomb_planted", "300", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_bomb_defused( "cash_player_bomb_defused", "300", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_rescued_hostage( "cash_player_rescued_hostage", "1000", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_interact_with_hostage( "cash_player_interact_with_hostage", "150", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_damage_hostage( "cash_player_damage_hostage", "-30", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_killed_hostage( "cash_player_killed_hostage", "-1000", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_respawn_amount( "cash_player_respawn_amount", "0", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); ConVar cash_player_get_killed( "cash_player_get_killed", "0", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_NOTIFY); int CCSGameRules::TeamCashAwardValue( TeamCashAward::Type reason) { switch( reason ) { case TeamCashAward::TERRORIST_WIN_BOMB: return cash_team_terrorist_win_bomb.GetInt(); case TeamCashAward::ELIMINATION_HOSTAGE_MAP_T: return cash_team_elimination_hostage_map_t.GetInt(); case TeamCashAward::ELIMINATION_HOSTAGE_MAP_CT: return cash_team_elimination_hostage_map_ct.GetInt(); case TeamCashAward::ELIMINATION_BOMB_MAP: return cash_team_elimination_bomb_map.GetInt(); case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE:return cash_team_win_by_time_running_out_hostage.GetInt(); case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB: return cash_team_win_by_time_running_out_bomb.GetInt(); case TeamCashAward::WIN_BY_DEFUSING_BOMB: return cash_team_win_by_defusing_bomb.GetInt(); case TeamCashAward::WIN_BY_HOSTAGE_RESCUE: return cash_team_win_by_hostage_rescue.GetInt(); case TeamCashAward::LOSER_BONUS: return cash_team_loser_bonus.GetInt(); case TeamCashAward::LOSER_BONUS_CONSECUTIVE_ROUNDS: return cash_team_loser_bonus_consecutive_rounds.GetInt(); case TeamCashAward::RESCUED_HOSTAGE: return cash_team_rescued_hostage.GetInt(); case TeamCashAward::HOSTAGE_ALIVE: return cash_team_hostage_alive.GetInt(); case TeamCashAward::PLANTED_BOMB_BUT_DEFUSED: return cash_team_planted_bomb_but_defused.GetInt(); case TeamCashAward::HOSTAGE_INTERACTION: return cash_team_hostage_interaction.GetInt(); case TeamCashAward::SURVIVE_GUARDIAN_WAVE: return cash_team_survive_guardian_wave.GetInt(); default: AssertMsg( false, "Unhandled TeamCashAwardReason" ); return 0; }; } int CCSGameRules::PlayerCashAwardValue( PlayerCashAward::Type reason) { switch( reason ) { case PlayerCashAward::NONE: return 0; case PlayerCashAward::KILL_TEAMMATE: return cash_player_killed_teammate.GetInt(); case PlayerCashAward::KILLED_ENEMY: return cash_player_killed_enemy_default.GetInt(); case PlayerCashAward::BOMB_PLANTED: return cash_player_bomb_planted.GetInt(); case PlayerCashAward::BOMB_DEFUSED: return cash_player_bomb_defused.GetInt(); case PlayerCashAward::RESCUED_HOSTAGE: return cash_player_rescued_hostage.GetInt(); case PlayerCashAward::INTERACT_WITH_HOSTAGE: return cash_player_interact_with_hostage.GetInt(); case PlayerCashAward::DAMAGE_HOSTAGE: return cash_player_damage_hostage.GetInt(); case PlayerCashAward::KILL_HOSTAGE: return cash_player_killed_hostage.GetInt(); case PlayerCashAward::RESPAWN: return cash_player_respawn_amount.GetInt(); case PlayerCashAward::GET_KILLED: return cash_player_get_killed.GetInt(); default: AssertMsg( false, "Unhandled PlayerCashAwardReason" ); return 0; }; } ////////////////////////////////////////////////////////////////////////// // Guardian mode getter functions for UI ////////////////////////////////////////////////////////////////////////// int CCSGameRules::GetGuardianRequiredKills() const { #ifdef CLIENT_DLL static ConVarRef mp_guardian_special_kills_needed( "mp_guardian_special_kills_needed" ); #endif return mp_guardian_special_kills_needed.GetInt(); } int CCSGameRules::GetGuardianKillsRemaining() const { return m_nGuardianModeSpecialKillsRemaining; } int CCSGameRules::GetGuardianSpecialWeapon() const { return m_nGuardianModeSpecialWeaponNeeded; } ////////////////////////////////////////////////////////////////////////// // Gun game getter functions ////////////////////////////////////////////////////////////////////////// // Determines the highest weapon index of all players in the arms race match void CCSGameRules::CalculateMaxGunGameProgressiveWeaponIndex( void ) { m_iMaxGunGameProgressiveWeaponIndex = 0; if ( IsPlayingGunGameProgressive() ) { // Loop through all players and find the max progressive weapon index for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { if ( pPlayer->GetPlayerGunGameWeaponIndex() > m_iMaxGunGameProgressiveWeaponIndex ) { m_iMaxGunGameProgressiveWeaponIndex = pPlayer->GetPlayerGunGameWeaponIndex(); } } } } } int CCSGameRules::GetCurrentGunGameWeapon ( int nCurrentWeaponIndex, int nTeamID ) { if ( GetNumProgressiveGunGameWeapons( nTeamID ) == 0 ) { // Don't process if weapon array is empty return -1; } if ( nCurrentWeaponIndex < 0 || nCurrentWeaponIndex >= GetNumProgressiveGunGameWeapons( nTeamID ) ) { // Don't process if out of the array bounds return -1; } return GetProgressiveGunGameWeapon( nCurrentWeaponIndex, nTeamID ); } int CCSGameRules::GetNextGunGameWeapon( int nCurrentWeaponIndex, int nTeamID ) { if ( GetNumProgressiveGunGameWeapons( nTeamID ) == 0 ) { // Don't process if weapon array is empty return -1; } if ( nCurrentWeaponIndex < 0 || nCurrentWeaponIndex >= GetNumProgressiveGunGameWeapons( nTeamID ) - 1 ) { // Don't process if already at last weapon or out of the array bounds return -1; } return GetProgressiveGunGameWeapon( nCurrentWeaponIndex + 1, nTeamID ); } int CCSGameRules::GetPreviousGunGameWeapon( int nCurrentWeaponIndex, int nTeamID ) { if ( GetNumProgressiveGunGameWeapons( nTeamID ) == 0 ) { // Don't process if weapon array is empty return -1; } if ( nCurrentWeaponIndex <= 0 || nCurrentWeaponIndex >= GetNumProgressiveGunGameWeapons( nTeamID ) ) { // Don't process if already at first weapon or out of the array bounds return -1; } return GetProgressiveGunGameWeapon( nCurrentWeaponIndex - 1, nTeamID ); } bool CCSGameRules::IsFinalGunGameProgressiveWeapon( int nCurrentWeaponIndex, int nTeamID ) { // Determine if the current weapon is the last in the list of gun game weapons if ( nCurrentWeaponIndex == GetNumProgressiveGunGameWeapons( nTeamID ) - 1 ) { return true; } return false; } int CCSGameRules::GetGunGameNumKillsRequiredForWeapon( int nCurrentWeaponIndex, int nTeamID ) { if ( nCurrentWeaponIndex < 0 || nCurrentWeaponIndex > GetNumProgressiveGunGameWeapons( nTeamID ) - 1 ) { // Don't process if out of the array bounds return -1; } return GetProgressiveGunGameWeaponKillRequirement( nCurrentWeaponIndex, nTeamID ); } CBaseCombatWeapon *CCSGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon ) { CBaseCombatWeapon *bestWeapon = NULL; // search all the weapons looking for the closest next for ( int i = 0; i < MAX_WEAPONS; i++ ) { CBaseCombatWeapon *weapon = pPlayer->GetWeapon(i); if ( !weapon ) continue; if ( !weapon->CanBeSelected() || weapon == pCurrentWeapon ) continue; #ifndef CLIENT_DLL CCSPlayer *csPlayer = ToCSPlayer(pPlayer); CWeaponCSBase *csWeapon = static_cast< CWeaponCSBase * >(weapon); if ( csPlayer && csPlayer->IsBot() && !TheCSBots()->IsWeaponUseable( csWeapon ) ) continue; #endif // CLIENT_DLL if ( bestWeapon ) { if ( weapon->GetSlot() < bestWeapon->GetSlot() ) { int nAmmo = 0; if ( weapon->UsesClipsForAmmo1() ) { nAmmo = weapon->Clip1(); } else { if ( pPlayer ) { nAmmo = weapon->GetReserveAmmoCount( AMMO_POSITION_PRIMARY ); } else { // No owner, so return how much primary ammo I have along with me. nAmmo = weapon->GetPrimaryAmmoCount(); } } if ( nAmmo > 0 ) { bestWeapon = weapon; } } else if ( weapon->GetSlot() == bestWeapon->GetSlot() && weapon->GetPosition() < bestWeapon->GetPosition() ) { bestWeapon = weapon; } } else { bestWeapon = weapon; } } return bestWeapon; } float CCSGameRules::GetMapRemainingTime() { // if timelimit is disabled, return -1 if ( mp_timelimit.GetInt() <= 0 ) return -1; // timelimit is in minutes float flTimeLeft = ( m_flGameStartTime + mp_timelimit.GetInt() * 60 ) - gpGlobals->curtime; // never return a negative value if ( flTimeLeft < 0 ) flTimeLeft = 0; return flTimeLeft; } float CCSGameRules::GetMapElapsedTime( void ) { return gpGlobals->curtime; } float CCSGameRules::GetRoundRemainingTime() const { return (float) (m_fRoundStartTime + m_iRoundTime) - gpGlobals->curtime; } float CCSGameRules::GetRoundStartTime() { return m_fRoundStartTime; } float CCSGameRules::GetRoundElapsedTime() { if ( IsPlayingTraining() ) return 0.0f; float remainingTime = m_iRoundTime - GetRoundRemainingTime(); if ( remainingTime >= 0.0f ) return remainingTime; else return 0.0f; } bool CCSGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 ) { if ( collisionGroup0 > collisionGroup1 ) { // swap so that lowest is always first V_swap(collisionGroup0,collisionGroup1); } //Don't stand on COLLISION_GROUP_WEAPONs if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT && collisionGroup1 == COLLISION_GROUP_WEAPON ) { return false; } // TODO: make a CS-SPECIFIC COLLISION GROUP FOR PHYSICS PROPS THAT USE THIS COLLISION BEHAVIOR. if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) && collisionGroup1 == COLLISION_GROUP_PUSHAWAY ) { return false; } if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY ) { // let debris and multiplayer objects collide return true; } return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 ); } bool CCSGameRules::IsFreezePeriod() { return m_bFreezePeriod; } bool CCSGameRules::IsWarmupPeriod() const { if ( IsPlayingTraining() ) return false; if ( IsPlayingOffline() && !mp_do_warmup_offine.GetBool() ) return false; return m_bWarmupPeriod; } bool CCSGameRules::AllowTaunts( void ) { /** Removed for partner depot **/ return false; } #ifdef CLIENT_DLL bool CCSGameRules::AllowThirdPersonCamera() { return sv_allow_thirdperson.GetBool(); } bool CCSGameRules::IsGoodDownTime( void ) { if ( CSGameRules()->IsGameRestarting() ) return true; if ( CSGameRules()->IsIntermission() ) return true; if ( CSGameRules()->InRoundRestart() ) return true; if ( ( ( CSGameRules()->GetRoundRestartTime() - gpGlobals->curtime ) < 0.5f && ( CSGameRules()->GetRoundRestartTime() - gpGlobals->curtime ) > 0.0f ) ) return true; return false; } bool CCSGameRules::IsLoadoutAllowed( void ) { // main menu if ( !engine->IsConnected() ) return true; C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); if ( !pLocalPlayer ) return false; // non players if ( ( pLocalPlayer->IsSpectator() ) || ( pLocalPlayer->IsHLTV() ) || ( engine->IsPlayingDemo() ) ) { return true; } // game mode specific rules if ( IsPlayingAnyCompetitiveStrictRuleset() ) { return ( IsWarmupPeriod() ); } else if ( ( IsPlayingGunGameDeathmatch() || IsPlayingCooperativeGametype() ) ) { return true; } else if ( IsWarmupPeriod() || !pLocalPlayer->IsAlive() ) { return true; } return false; } #endif float CCSGameRules::GetWarmupPeriodEndTime() const { return m_fWarmupPeriodStart + mp_warmuptime.GetFloat(); } bool CCSGameRules::IsWarmupPeriodPaused() { return mp_warmup_pausetimer.GetBool(); } bool CCSGameRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer ) { #ifndef CLIENT_DLL if ( pPlayer ) { int iPlayerTeam = pPlayer->GetTeamNumber(); if ( ( iPlayerTeam == TEAM_TERRORIST ) || ( iPlayerTeam == TEAM_CT ) ) return false; } #else int iLocalPlayerTeam = GetLocalPlayerTeam(); if ( ( iLocalPlayerTeam == TEAM_TERRORIST ) || ( iLocalPlayerTeam == TEAM_CT ) ) return false; #endif return true; } #ifndef CLIENT_DLL void CCSGameRules::StartWarmup( void ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; m_bWarmupPeriod = true; m_bCompleteReset = true; m_fWarmupPeriodStart = gpGlobals->curtime; RestartRound(); } void CCSGameRules::EndWarmup( void ) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; if ( !m_bWarmupPeriod ) return; m_bWarmupPeriod = false; m_bCompleteReset = true; m_fWarmupPeriodStart = -1; CCSPlayerResource *pResource = dynamic_cast< CCSPlayerResource * >( g_pPlayerResource ); if ( pResource ) pResource->ForcePlayersPickColors(); RestartRound(); } void CCSGameRules::StartTerroristTimeOut( void ) { if ( m_bTerroristTimeOutActive || m_bCTTimeOutActive ) return; if ( m_nTerroristTimeOuts <= 0 ) return; m_bTerroristTimeOutActive = true; m_flTerroristTimeOutRemaining = mp_team_timeout_time.GetInt(); m_nTerroristTimeOuts--; m_bMatchWaitingForResume = true; UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" ); } void CCSGameRules::EndTerroristTimeOut( void ) { if ( !m_bTerroristTimeOutActive ) return; m_bTerroristTimeOutActive = false; m_bMatchWaitingForResume = false; } void CCSGameRules::StartCTTimeOut( void ) { if ( m_bCTTimeOutActive || m_bTerroristTimeOutActive ) return; if ( m_nCTTimeOuts <= 0 ) return; m_bCTTimeOutActive = true; m_flCTTimeOutRemaining = mp_team_timeout_time.GetInt(); m_nCTTimeOuts--; m_bMatchWaitingForResume = true; UTIL_ClientPrintAll( HUD_PRINTCENTER, "#SFUI_Notice_Match_Will_Pause" ); } void CCSGameRules::EndCTTimeOut( void ) { if ( !m_bCTTimeOutActive ) return; m_bCTTimeOutActive = false; m_bMatchWaitingForResume = false; } #endif AcquireResult::Type CCSGameRules::IsWeaponAllowed( const CCSWeaponInfo *pWeaponInfo, int nTeamNumber, CEconItemView *pItem ) { CSWeaponID weaponId = WEAPON_NONE; CSWeaponType weaponType = WEAPONTYPE_UNKNOWN; if ( pItem && pItem->IsValid() ) { weaponId = WeaponIdFromString( pItem->GetStaticData()->GetItemClass() ); if ( pWeaponInfo ) weaponType = pWeaponInfo->GetWeaponType( pItem ); } else if ( pWeaponInfo ) { weaponId = pWeaponInfo->m_weaponId; weaponType = pWeaponInfo->GetWeaponType( pItem ); } // prohibited items. currently only supports schema items // // if ( ( CSGameRules()->m_arrProhibitedItemIndices[ 0 ] != 0 ) && pItem && pItem->IsValid() ) { int nPosition = pItem->GetItemDefinition()->GetLoadoutSlot( nTeamNumber ); const CEconItemView* pBaseItem = CSInventoryManager()->GetBaseItemForTeam( nTeamNumber, nPosition ); if ( pBaseItem && pBaseItem->IsValid() ) { for ( int j = 0; j < MAX_PROHIBITED_ITEMS; j++ ) { // if the base item is prohibited then the slot is prohibited if ( CSGameRules()->m_arrProhibitedItemIndices[ j ] == pBaseItem->GetItemDefinition()->GetDefinitionIndex() ) { return AcquireResult::NotAllowedByProhibition; } // if the base item is not prohibited then the alternate item might still be prohibited if ( CSGameRules()->m_arrProhibitedItemIndices[ j ] == pItem->GetItemDefinition()->GetDefinitionIndex() ) { return AcquireResult::NotAllowedByProhibition; } } } } /////////////////////////////// switch ( weaponId ) { case ITEM_DEFUSER: case ITEM_CUTTERS: if ( nTeamNumber == TEAM_TERRORIST ) { return AcquireResult::NotAllowedByTeam; } if ( !IsBombDefuseMap() ) { return AcquireResult::NotAllowedByMap; } break; case WEAPON_TASER: // special case for limiting taser to classic casual; data drive this if it becomes more complex if ( !mp_weapons_allow_zeus.GetBool() ) { return AcquireResult::NotAllowedForPurchase; } break; case WEAPON_C4: if ( nTeamNumber != TEAM_TERRORIST && !IsPlayingTraining() && mp_anyone_can_pickup_c4.GetBool() == false ) { return AcquireResult::NotAllowedByTeam; } break; } return AcquireResult::Allowed; } bool CCSGameRules::IsBombDefuseMap() const { return m_bMapHasBombTarget; } bool CCSGameRules::IsHostageRescueMap() const { return m_bMapHasRescueZone; } bool CCSGameRules::MapHasBuyZone() const { return m_bMapHasBuyZone; } bool CCSGameRules::CanSpendMoneyInMap() { if ( GetMaxMoney() <= 0 ) return false; if ( PlayerCashAwardsEnabled() == false && TeamCashAwardsEnabled() == false ) return false; if ( IsPlayingCoopMission() ) return false; if ( IsPlayingCoopGuardian() ) { int nTeam = CSGameRules()->IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT; ConVarRef sv_buy_status_override( "sv_buy_status_override" ); int iBuyStatus = sv_buy_status_override.GetInt(); if ( iBuyStatus > 0 && (( nTeam == TEAM_CT && iBuyStatus != 1 ) || ( nTeam == TEAM_TERRORIST && iBuyStatus != 2 )) ) return false; } return true; } bool CCSGameRules::IsLogoMap() const { return m_bLogoMap; } float CCSGameRules::GetBuyTimeLength() { if ( IsWarmupPeriod() ) { if ( IsWarmupPeriodPaused( ) ) return GetWarmupPeriodEndTime( )-m_fWarmupPeriodStart; if ( mp_buytime.GetFloat() < GetWarmupPeriodEndTime() ) return GetWarmupPeriodEndTime(); } return mp_buytime.GetFloat(); } bool CCSGameRules::IsBuyTimeElapsed() { if ( IsWarmupPeriod() && IsWarmupPeriodPaused() ) return false; return ( GetRoundElapsedTime() > GetBuyTimeLength() ); } bool CCSGameRules::IsMatchWaitingForResume() { if ( m_bMatchWaitingForResume ) return true; if ( sv_matchpause_auto_5v5.GetBool() && !IsWarmupPeriod() ) { #ifdef CLIENT_DLL C_Team #else CTeam #endif *arrTeams[2] = { GetGlobalTeam( TEAM_CT ), GetGlobalTeam( TEAM_TERRORIST ) }; for ( int iTeam = 0; iTeam < Q_ARRAYSIZE( arrTeams ); ++ iTeam ) { if ( !arrTeams[iTeam] ) continue; int numPlayers = arrTeams[iTeam]->GetNumPlayers(); if ( numPlayers < 5 ) { SetMatchWaitingForResume( true ) ; return true; } int numHumans = 0; for ( int iPlayer = 0; iPlayer < numPlayers; ++ iPlayer ) { CBasePlayer *pPlayer = arrTeams[iTeam]->GetPlayer( iPlayer ); if ( !pPlayer ) continue; #ifndef CLIENT_DLL if ( !pPlayer->IsConnected() ) continue; #endif if ( pPlayer->IsBot() ) continue; ++ numHumans; } if ( numHumans < 5 ) { SetMatchWaitingForResume( true ); return true; } } } return false; } int CCSGameRules::DefaultFOV() { return 90; } const CViewVectors* CCSGameRules::GetViewVectors() const { return &g_CSViewVectors; } #if defined( GAME_DLL) //========================================================= //========================================================= bool CCSGameRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker ) { CCSPlayer *pCSAttacker = ToCSPlayer( pAttacker ); if ( pCSAttacker && PlayerRelationship( pPlayer, pCSAttacker ) == GR_TEAMMATE && !pCSAttacker->IsOtherEnemy( pPlayer->entindex() ) ) { // my teammate hit me. if ( ( mp_friendlyfire.GetInt() == 0 ) && ( pCSAttacker != pPlayer ) ) { // friendly fire is off, and this hit came from someone other than myself, then don't get hurt return false; } } return BaseClass::FPlayerCanTakeDamage( pPlayer, pCSAttacker ); } //========================================================= //========================================================= int CCSGameRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled ) { CCSPlayer *pCSAttacker = ToCSPlayer( pAttacker ); if ( !pKilled ) return 0; if ( !pCSAttacker ) return 1; if ( pCSAttacker != pKilled && PlayerRelationship( pCSAttacker, pKilled ) == GR_TEAMMATE && !pCSAttacker->IsOtherEnemy( pKilled->entindex() ) ) return -1; return 1; } /* Helper function which handles both voice and chat. The only difference is which convar to use to determine whether enemies can be heard (sv_alltalk or sv_allchat). */ bool CanPlayerHear( CBasePlayer* pListener, CBasePlayer *pSpeaker, bool bTeamOnly, bool bHearEnemies ) { Assert(pListener != NULL && pSpeaker != NULL); if ( pListener == NULL || pSpeaker == NULL ) return false; // sv_full_alltalk lets everyone can talk to everyone else, except comms specifically flagged as team-only if ( !bTeamOnly && sv_full_alltalk.GetBool() ) return true; // if either speaker or listener are coaching then for intents and purposes treat them as teammates. int iListenerTeam = pListener->GetAssociatedTeamNumber(); int iSpeakerTeam = pSpeaker->GetAssociatedTeamNumber(); // use the observed target's team when sv_spec_hear is mode 2 if ( iListenerTeam == TEAM_SPECTATOR && sv_spec_hear.GetInt() == SpecHear::SpectatedTeam && ( pListener->GetObserverMode() == OBS_MODE_IN_EYE || pListener->GetObserverMode() == OBS_MODE_CHASE ) ) { CBaseEntity *pTarget = pListener->GetObserverTarget(); if ( pTarget && pTarget->IsPlayer() ) { iListenerTeam = pTarget->GetTeamNumber(); } } if ( iListenerTeam == TEAM_SPECTATOR ) { if ( sv_spec_hear.GetInt() == SpecHear::Nobody ) return false; // spectators are selected to not hear other spectators if ( sv_spec_hear.GetInt() == SpecHear::Self ) return ( pListener == pSpeaker ); // spectators are selected to not hear other spectators // spectators can always hear other spectators if ( iSpeakerTeam == TEAM_SPECTATOR ) return true; return !bTeamOnly && sv_spec_hear.GetInt() == SpecHear::AllPlayers; } // no one else can hear spectators if ( ( iSpeakerTeam != TEAM_TERRORIST ) && ( iSpeakerTeam != TEAM_CT ) ) return false; // are enemy teams prevented from hearing each other by sv_alltalk/sv_allchat? if ( (bTeamOnly || !bHearEnemies) && iSpeakerTeam != iListenerTeam ) return false; // living players can only hear dead players if sv_deadtalk is enabled if ( pListener->IsAlive() && !pSpeaker->IsAlive() ) { return sv_deadtalk.GetBool(); } return true; } bool CCSGameRules::CanPlayerHearTalker( CBasePlayer* pListener, CBasePlayer *pSpeaker, bool bTeamOnly ) { bool bHearEnemy = false; if ( sv_talk_enemy_living.GetBool() && sv_talk_enemy_dead.GetBool() ) { bHearEnemy = true; } else if ( !pListener->IsAlive() && !pSpeaker->IsAlive() ) { bHearEnemy = sv_talk_enemy_dead.GetBool(); } else if ( pListener->IsAlive() && pSpeaker->IsAlive() ) { bHearEnemy = sv_talk_enemy_living.GetBool(); } return CanPlayerHear( pListener, pSpeaker, bTeamOnly, bHearEnemy ); } extern ConVar sv_allchat; bool CCSGameRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker, bool bTeamOnly ) { return CanPlayerHear( pListener, pSpeaker, bTeamOnly, sv_allchat.GetBool() ); } int CCSGameRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget ) { // half life multiplay has a simple concept of Player Relationships. // you are either on another player's team, or you are not. if ( !pPlayer || !pTarget || !pTarget->IsPlayer() ) return GR_NOTTEAMMATE; // don't do string compares, just compare the team number // if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) ) // { // return GR_TEAMMATE; // } CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer ); CCSPlayer *pCSTarget = ToCSPlayer( pTarget ); if ( pCSPlayer->GetAssociatedTeamNumber() != TEAM_INVALID && pCSTarget->GetAssociatedTeamNumber() != TEAM_INVALID && pCSPlayer->GetAssociatedTeamNumber() == pCSTarget->GetAssociatedTeamNumber() ) { return GR_TEAMMATE; } return GR_NOTTEAMMATE; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCSGameRules::SetTeamRespawnWaveTime( int iTeam, float flValue ) { if ( flValue < 0 ) { flValue = 0; } // initialized to -1 so we can try to determine if this is the first spawn time we have received for this team // if ( m_flOriginalTeamRespawnWaveTime[iTeam] < 0 ) // { // m_flOriginalTeamRespawnWaveTime[iTeam] = flValue; // } m_TeamRespawnWaveTimes.Set( iTeam, flValue ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CCSGameRules::CheckRespawnWaves( void ) { for ( int team = LAST_SHARED_TEAM+1; team < GetNumberOfTeams(); team++ ) { if ( m_flNextRespawnWave[team] > gpGlobals->curtime ) continue; if ( mp_use_respawn_waves.GetInt() == 2 ) { if ( IsWarmupPeriod() == false && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) ) { // we sometimes don't want to increment the wav number if ( m_bDontIncrementCoopWave == false ) m_nGuardianModeWaveNumber++; m_bDontIncrementCoopWave = false; if ( IsPlayingCoopGuardian() ) { int nTeam = IsHostageRescueMap() ? TEAM_TERRORIST : TEAM_CT; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->GetTeamNumber() == nTeam ) { //CBroadcastRecipientFilter filter; CRecipientFilter filter; filter.AddRecipient( pPlayer ); pPlayer->EmitSound( filter, pPlayer->entindex(), "UI.DeathMatchBonusAlertEnd" ); char szWave[32]; Q_snprintf( szWave, sizeof( szWave ), "%d", ( int )m_nGuardianModeWaveNumber ); const char *szTeam = (nTeam == TEAM_CT) ? "CT" : "T"; int nMsg = RandomInt( 1, 8 ); char szNotice[512]; Q_snprintf( szNotice, sizeof( szNotice ), "#SFUI_Notice_NewWaveBegun_%s%d", szTeam, ( int )nMsg ); // send the message UTIL_ClientPrintFilter(filter, HUD_PRINTCENTER, szNotice, szWave ); } } } } // if this mode is set, we don't want to set the respawn time to anythign other than infinity, pretty much m_flNextRespawnWave.Set( team, gpGlobals->curtime + 99999 ); } else m_flNextRespawnWave.Set( team, gpGlobals->curtime + GetRespawnWaveMaxLength( team ) ); // respawn all players who are able to respawn here for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer && pPlayer->PlayerClass() != 0 && pPlayer->GetTeamNumber() > TEAM_SPECTATOR && pPlayer->GetTeamNumber() == team && !pPlayer->IsAlive() ) { bool bMatchRespawnGamePhase = false; switch ( m_match.GetPhase() ) { case GAMEPHASE_WARMUP_ROUND: case GAMEPHASE_PLAYING_STANDARD: case GAMEPHASE_PLAYING_FIRST_HALF: case GAMEPHASE_PLAYING_SECOND_HALF: bMatchRespawnGamePhase = true; break; } if ( ( IsPlayingCoopGuardian() || IsPlayingCoopMission() ) && pPlayer->IsBot() && pPlayer->IsAbleToInstantRespawn() ) { GuardianUpdateBotAccountAndWeapons( pPlayer ); // set the bot's name CCSBot *bot = dynamic_cast< CCSBot * >( pPlayer ); if ( bot ) { char botName[MAX_PLAYER_NAME_LENGTH]; UTIL_ConstructBotNetName( botName, MAX_PLAYER_NAME_LENGTH, bot->GetProfile() ); engine->SetFakeClientConVarValue( bot->edict(), "name", botName ); } m_nGuardianGrenadesToGiveBots = RandomInt( 2, MIN( 5, m_nGuardianModeWaveNumber ) ); // respawn pPlayer->State_Transition( STATE_GUNGAME_RESPAWN ); } else if ( bMatchRespawnGamePhase && pPlayer->IsAbleToInstantRespawn() && pPlayer->GetObserverMode() > OBS_MODE_FREEZECAM ) { // respawn pPlayer->State_Transition( STATE_GUNGAME_RESPAWN ); } } } } } void CCSGameRules::GuardianUpdateBotAccountAndWeapons( CCSPlayer *pBot ) { if ( pBot->IsBot() && mp_use_respawn_waves.GetInt() == 2 && IsWarmupPeriod() == false && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) ) { pBot->RemoveAllItems( true ); pBot->ResetAccount(); pBot->AddAccount( ( mp_guardian_bot_money_per_wave.GetInt() * m_nGuardianModeWaveNumber ), false, false ); } } extern const char* Helper_PickBotGrenade(); void CCSGameRules::GiveGuardianBotGrenades( CCSPlayer *pBot ) { if ( m_nGuardianGrenadesToGiveBots > 0 && pBot->IsBot() && mp_use_respawn_waves.GetInt() == 2 && IsWarmupPeriod() == false && (IsPlayingCoopGuardian() || IsPlayingCoopMission()) ) { const char* szGrenade = Helper_PickBotGrenade(); if ( szGrenade && pBot->GiveNamedItem( CFmtStr( "weapon_%s", szGrenade ).Access() ) != NULL ) m_nGuardianGrenadesToGiveBots--; } } //----------------------------------------------------------------------------- // Purpose: Is the player past the required delays for spawning //----------------------------------------------------------------------------- bool CCSGameRules::HasPassedMinRespawnTime( CBasePlayer *pPlayer ) { float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer ); return ( gpGlobals->curtime > flMinSpawnTime ); } #endif //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CCSGameRules::GetRespawnTimeScalar( int iTeam ) { if ( mp_use_respawn_waves.GetInt() == 2 ) return 1; // For long respawn times, scale the time as the number of players drops int iOptimalPlayers = 8; // 16 players total, 8 per team int iNumPlayers = GetGlobalTeam(iTeam)->GetNumPlayers(); float flScale = RemapValClamped( iNumPlayers, 1, iOptimalPlayers, 0.25, 1.0 ); return flScale; } bool CCSGameRules::IsEndMatchVotingForNextMapEnabled() { if ( mp_endmatch_votenextmap.GetBool() ) { //int numMaps = 0; if ( g_pGameTypes ) { #ifndef CLIENT_DLL const char* mapGroupName = gpGlobals->mapGroupName.ToCStr(); #else const char* mapGroupName = engine->GetMapGroupName(); #endif const CUtlStringList* mapsInGroup = g_pGameTypes->GetMapGroupMapList( mapGroupName ); if ( mapsInGroup ) return (mapsInGroup->Count() > 1); } } return false; } bool CCSGameRules::IsEndMatchVotingForNextMap() { if ( !IsEndMatchVotingForNextMapEnabled() || GetGamePhase() != GAMEPHASE_MATCH_ENDED ) return false; if ( GetCMMItemDropRevealEndTime() > gpGlobals->curtime ) return false; if ( m_bIsDroppingItems ) return false; // // TODO: add a check to make sure items aren't dropping // if ( IsEndMatchVotingForNextMapEnabled() && ( GetGamePhase() == GAMEPHASE_MATCH_ENDED ) && // ((gpGlobals->curtime+m_timeUntilNextPhaseStarts) >= gpGlobals->curtime+mp_win_panel_display_time.GetFloat()) && gpGlobals->curtime+GetCMMItemDropRevealEndTime() < gpGlobals->curtime ) // { // return true; // } return true; } //----------------------------------------------------------------------------- // Purpose: How long are the respawn waves for this team currently? //----------------------------------------------------------------------------- float CCSGameRules::GetRespawnWaveMaxLength( int iTeam, bool bScaleWithNumPlayers /* = true */ ) { //Let's just turn off respawn times while players are messing around waiting for the tournament to start if ( IsWarmupPeriod() || m_bFreezePeriod ) return 0.0f; if ( mp_use_respawn_waves.GetInt() == 0 ) return 0.0f; float flWaveTime = 0; if ( iTeam == TEAM_TERRORIST ) flWaveTime = mp_respawnwavetime_t.GetFloat(); else if ( iTeam == TEAM_CT ) flWaveTime = mp_respawnwavetime_ct.GetFloat(); //float flTime = ( ( m_TeamRespawnWaveTimes[iTeam] >= 0 ) ? m_TeamRespawnWaveTimes[iTeam] : flWaveTime ); float flTime = flWaveTime; // For long respawn times, scale the time as the number of players drops if ( bScaleWithNumPlayers && flTime > 5 ) { flTime = MAX( 5, flTime * GetRespawnTimeScalar(iTeam) ); } return flTime; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CCSGameRules::GetMinTimeWhenPlayerMaySpawn( CBasePlayer *pPlayer ) { // Min respawn time is the sum of // // a) the length of one full *unscaled* respawn wave for their team // and // b) death anim length + freeze panel length float fMinDelay = 2.0f + GetRespawnWaveMaxLength( pPlayer->GetTeamNumber(), false ); return pPlayer->GetDeathTime() + fMinDelay; } void CCSGameRules::SetNextTeamRespawnWaveDelay( int iTeam, float flDelay ) { float flNextRespawn = gpGlobals->curtime + flDelay; m_flNextRespawnWave.Set( iTeam, flNextRespawn ); } //----------------------------------------------------------------------------- // Purpose: don't let us spawn before our freezepanel time would have ended, even if we skip it //----------------------------------------------------------------------------- float CCSGameRules::GetNextRespawnWave( int iTeam, CBasePlayer *pPlayer ) { // if ( State_Get() == GR_STATE_STALEMATE ) // return 0; // If we are purely checking when the next respawn wave is for this team if ( pPlayer == NULL ) { return m_flNextRespawnWave[iTeam]; } // The soonest this player may spawn float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer ); // the next scheduled respawn wave time float flNextRespawnTime = m_flNextRespawnWave[iTeam]; // the length of one respawn wave. We'll check in increments of this float flRespawnWaveMaxLen = GetRespawnWaveMaxLength( iTeam ); if ( flRespawnWaveMaxLen <= 0 ) { return flNextRespawnTime; } // Keep adding the length of one respawn until we find a wave that // this player will be eligible to spawn in. while ( flNextRespawnTime < flMinSpawnTime ) { flNextRespawnTime += flRespawnWaveMaxLen; } return flNextRespawnTime; } //----------------------------------------------------------------------------- // Purpose: Init CS ammo definitions //----------------------------------------------------------------------------- // shared ammo definition // JAY: Trying to make a more physical bullet response #define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f) #define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains)) static CCSAmmoDef ammoDef; CCSAmmoDef* GetCSAmmoDef() { GetAmmoDef(); // to initialize the ammo info return &ammoDef; } CAmmoDef* GetAmmoDef() { static bool bInitted = false; if ( !bInitted ) { bInitted = true; ammoDef.AddAmmoType( BULLET_PLAYER_50AE, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_50AE_max", "ammo_50AE_impulse", 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_762MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_762mm_max", "ammo_762mm_impulse", 0, 10, 14 );//Sniper ammo has little force since it just goes through the target. ammoDef.AddAmmoType( BULLET_PLAYER_556MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_max", "ammo_556mm_impulse", 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_556MM_SMALL, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_small_max","ammo_556mm_impulse", 0, 10, 14 ); // 15 round clip ammoDef.AddAmmoType( BULLET_PLAYER_556MM_BOX, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_box_max","ammo_556mm_box_impulse", 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_338MAG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_338mag_max", "ammo_338mag_impulse", 0, 12, 16 ); ammoDef.AddAmmoType( BULLET_PLAYER_9MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_9mm_max", "ammo_9mm_impulse", 0, 5, 10 ); ammoDef.AddAmmoType( BULLET_PLAYER_BUCKSHOT, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_buckshot_max", "ammo_buckshot_impulse", 0, 3, 6 ); ammoDef.AddAmmoType( BULLET_PLAYER_45ACP, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_45acp_max", "ammo_45acp_impulse", 0, 6, 10 ); ammoDef.AddAmmoType( BULLET_PLAYER_357SIG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_max", "ammo_357sig_impulse", 0, 4, 8 ); ammoDef.AddAmmoType( BULLET_PLAYER_357SIG_SMALL,DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_small_max", "ammo_357sig_impulse", 0, 4, 8 ); ammoDef.AddAmmoType( BULLET_PLAYER_357SIG_MIN, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_min_max", "ammo_357sig_impulse", 0, 4, 8 ); ammoDef.AddAmmoType( BULLET_PLAYER_57MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_57mm_max", "ammo_57mm_impulse", 0, 4, 8 ); ammoDef.AddAmmoType( AMMO_TYPE_HEGRENADE, DMG_BLAST, TRACER_LINE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_FLASHBANG, 0, TRACER_LINE, 0, 0, "ammo_grenade_limit_flashbang", 0, 0, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_SMOKEGRENADE, 0, TRACER_LINE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_MOLOTOV, DMG_BURN, TRACER_NONE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_DECOY, 0, TRACER_NONE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_TASERCHARGE, DMG_SHOCK, TRACER_BEAM, 0, 0, 0, 0, 0, 0 ); ammoDef.AddAmmoType( BULLET_PLAYER_357SIG_P250, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_p250_max", "ammo_357sig_impulse", 0, 4, 8 ); ammoDef.AddAmmoType( AMMO_TYPE_HEALTHSHOT, 0, TRACER_LINE, 0, 0, "ammo_item_limit_healthshot", 0, 0, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_TAGRENADE, 0, TRACER_NONE, 0, 0, "ammo_grenade_limit_default", 0, 0, 0 ); //Adrian: I set all the prices to 0 just so the rest of the buy code works //This should be revisited. ammoDef.AddAmmoCost( BULLET_PLAYER_50AE, 0, 7 ); ammoDef.AddAmmoCost( BULLET_PLAYER_762MM, 0, 30 ); ammoDef.AddAmmoCost( BULLET_PLAYER_556MM, 0, 30 ); ammoDef.AddAmmoCost( BULLET_PLAYER_556MM_SMALL, 0, 15 ); ammoDef.AddAmmoCost( BULLET_PLAYER_556MM_BOX, 0, 30 ); ammoDef.AddAmmoCost( BULLET_PLAYER_338MAG, 0, 10 ); ammoDef.AddAmmoCost( BULLET_PLAYER_9MM, 0, 30 ); ammoDef.AddAmmoCost( BULLET_PLAYER_BUCKSHOT, 0, 8 ); ammoDef.AddAmmoCost( BULLET_PLAYER_45ACP, 0, 25 ); ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG, 0, 13 ); ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG_P250, 0, 13 ); ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG_SMALL, 0, 13 ); ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG_MIN, 0, 12 ); ammoDef.AddAmmoCost( BULLET_PLAYER_57MM, 0, 50 ); } return &ammoDef; } bool CCSGameRules::IsRoundOver() const { return m_iRoundWinStatus != WINNER_NONE; } void CCSGameRules::ClearItemsDroppedDuringMatch( void ) { #ifndef CLIENT_DLL m_bPlayerItemsHaveBeenDisplayed = false; #endif m_ItemsPtrDroppedDuringMatch.PurgeAndDeleteElements(); } void CCSGameRules::RecordPlayerItemDrop( const CEconItemPreviewDataBlock &iteminfo ) { if ( !iteminfo.accountid() ) return; for ( int i = 0; i < m_ItemsPtrDroppedDuringMatch.Count(); i++ ) { // if we've recorded this item already, don't record it again if ( m_ItemsPtrDroppedDuringMatch[i]->itemid() == iteminfo.itemid() && m_ItemsPtrDroppedDuringMatch[i]->accountid() == iteminfo.accountid() ) return; } // on the client, we add all local player items to the top of the list // server just adds them #if defined( CLIENT_DLL ) // don't put the local player's at the top anymore - matt wood // C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer(); // C_CS_PlayerResource *cs_PR = dynamic_cast( g_PR ); // if ( cs_PR && pLocalPlayer ) // { // XUID localXuid = cs_PR->GetXuid( pLocalPlayer->entindex() ); // if (localXuid == steamOwnerID.ConvertToUint64()) // { // m_ItemsPtrDroppedDuringMatch.AddToHead( event ); // return; // } // } if ( CDemoPlaybackParameters_t const *pParams = engine->GetDemoPlaybackParameters() ) { // When playing back Overwatch with anonymous player identities don't collect drops on client if ( pParams->m_bAnonymousPlayerIdentity ) return; } // check to see if this player is still connected bool bFoundPlayer = false; for ( int j = 1; j <= MAX_PLAYERS; j++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) ); if ( pPlayer ) { CSteamID steamID; pPlayer->GetSteamID( &steamID ); if ( steamID.GetAccountID() == iteminfo.accountid() ) { bFoundPlayer = true; break; } } } // check to see if this player is still connected if ( bFoundPlayer ) m_ItemsPtrDroppedDuringMatch.AddToTail( new CEconItemPreviewDataBlock( iteminfo ) ); #else m_ItemsPtrDroppedDuringMatch.AddToTail( new CEconItemPreviewDataBlock( iteminfo ) ); #endif } #ifndef CLIENT_DLL const char *CCSGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer ) { char *pszPrefix = NULL; if ( !pPlayer ) // dedicated server output { pszPrefix = ""; } else { // team only if ( bTeamOnly == TRUE ) { if ( pPlayer->GetTeamNumber() == TEAM_CT ) { if ( pPlayer->m_lifeState == LIFE_ALIVE ) { pszPrefix = "(Counter-Terrorist)"; } else { pszPrefix = "*DEAD*(Counter-Terrorist)"; } } else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) { if ( pPlayer->m_lifeState == LIFE_ALIVE ) { pszPrefix = "(Terrorist)"; } else { pszPrefix = "*DEAD*(Terrorist)"; } } else if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR ) { pszPrefix = "(Spectator)"; } } // everyone else { if ( pPlayer->m_lifeState == LIFE_ALIVE ) { pszPrefix = ""; } else { if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) { pszPrefix = "*DEAD*"; } else { pszPrefix = "*SPEC*"; } } } } return pszPrefix; } const char *CCSGameRules::GetChatLocation( bool bTeamOnly, CBasePlayer *pPlayer ) { if ( !pPlayer ) // dedicated server output { return NULL; } // only teammates see locations if ( !bTeamOnly ) return NULL; // only living players have locations if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST ) return NULL; if ( !pPlayer->IsAlive() ) return NULL; return pPlayer->GetLastKnownPlaceName(); } const char *CCSGameRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer ) { if ( !pPlayer ) // dedicated server output { return NULL; } const char *pszFormat = NULL; // team only if ( bTeamOnly == TRUE ) { if ( pPlayer->GetAssociatedTeamNumber() == TEAM_CT ) { if ( ( pPlayer->m_lifeState == LIFE_ALIVE ) ) { const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer ); if ( chatLocation && *chatLocation ) { pszFormat = "Cstrike_Chat_CT_Loc"; } else { pszFormat = "Cstrike_Chat_CT"; } } else { pszFormat = "Cstrike_Chat_CT_Dead"; } } else if ( pPlayer->GetAssociatedTeamNumber() == TEAM_TERRORIST ) { if ( ( pPlayer->m_lifeState == LIFE_ALIVE ) ) { const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer ); if ( chatLocation && *chatLocation ) { pszFormat = "Cstrike_Chat_T_Loc"; } else { pszFormat = "Cstrike_Chat_T"; } } else { pszFormat = "Cstrike_Chat_T_Dead"; } } else if ( pPlayer->IsSpectator() ) { pszFormat = "Cstrike_Chat_Spec"; } } // everyone else { if ( pPlayer->m_lifeState == LIFE_ALIVE ) { pszFormat = "Cstrike_Chat_All"; } else { if ( !pPlayer->IsSpectator() ) { pszFormat = "Cstrike_Chat_AllDead"; } else { pszFormat = "Cstrike_Chat_AllSpec"; } } } return pszFormat; } void CCSGameRules::ClientSettingsChanged( CBasePlayer *pPlayer ) { CCSPlayer *pCSPlayer = dynamic_cast( pPlayer ); if ( pCSPlayer ) { // NOTE[pmf]: Before testing if the name has changed, we first copy it into a buffer of MAX_PLAYER_NAME_LENGTH length, // trimming malformed utf8 (truncated) characters at the end (which we get from Steam); otherwise, when this // correction happens as a result of CBasePlayer::SetPlayerName(), we'll always see the name as different, // and continue to spam name change attempts. if ( CSGameRules()->IsPlayingCoopMission() && pCSPlayer->IsBot() ) { pCSPlayer->ChangeName( pCSPlayer->GetPlayerName() ); } else if ( CanClientCustomizeOwnIdentity() ) { char szNewName[MAX_PLAYER_NAME_LENGTH]; V_UTF8_strncpy( szNewName, engine->GetClientConVarValue( pCSPlayer->entindex(), "name" ), sizeof(szNewName) ); const char *pszOldName = pCSPlayer->GetPlayerName(); if ( pszOldName[0] != 0 && Q_strcmp( pszOldName, szNewName ) ) { pCSPlayer->ChangeName( szNewName ); } } pCSPlayer->m_bShowHints = true; if ( pCSPlayer->IsNetClient() ) { const char *pShowHints = engine->GetClientConVarValue( ENTINDEX( pCSPlayer->edict() ), "cl_autohelp" ); if ( pShowHints && atoi( pShowHints ) <= 0 ) { pCSPlayer->m_bShowHints = false; } } //pCSPlayer->UpdateAppearanceIndex(); pCSPlayer->InitTeammatePreferredColor(); } } bool CCSGameRules::CanClientCustomizeOwnIdentity() { return !CCSGameRules::sm_QueuedServerReservation.tournament_teams().size(); } bool CCSGameRules::FAllowNPCs( void ) { if ( IsPlayingTraining() ) return true; return true; } bool CCSGameRules::IsFriendlyFireOn( void ) const { return mp_friendlyfire.GetBool(); } bool CCSGameRules::IsLastRoundBeforeHalfTime( void ) { if ( HasHalfTime() ) { int numRoundsBeforeHalftime = -1; if ( m_match.GetPhase() == GAMEPHASE_PLAYING_FIRST_HALF ) numRoundsBeforeHalftime = GetOvertimePlaying() ? ( mp_maxrounds.GetInt() + ( 2 * GetOvertimePlaying() - 1 ) * ( mp_overtime_maxrounds.GetInt() / 2 ) ) : ( mp_maxrounds.GetInt() / 2 ); if ( ( numRoundsBeforeHalftime > 0 ) && ( m_match.GetRoundsPlayed() == (numRoundsBeforeHalftime-1) ) ) { return true; } } return false; } CON_COMMAND( map_showspawnpoints, "Shows player spawn points (red=invalid). Optionally pass in the duration." ) { int duration = 600; if ( args.ArgC() == 2 ) { duration = Q_atoi( args[1] ); } CSGameRules()->ShowSpawnPoints( duration ); } void DrawSphere( const Vector& pos, float radius, int r, int g, int b, float lifetime ) { Vector edge, lastEdge; NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, true, lifetime ); lastEdge = Vector( radius + pos.x, pos.y, pos.z ); float angle; for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) { edge.x = radius * BotCOS( angle ) + pos.x; edge.y = pos.y; edge.z = radius * BotSIN( angle ) + pos.z; NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime ); lastEdge = edge; } lastEdge = Vector( pos.x, radius + pos.y, pos.z ); for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) { edge.x = pos.x; edge.y = radius * BotCOS( angle ) + pos.y; edge.z = radius * BotSIN( angle ) + pos.z; NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime ); lastEdge = edge; } lastEdge = Vector( pos.x, radius + pos.y, pos.z ); for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) { edge.x = radius * BotCOS( angle ) + pos.x; edge.y = radius * BotSIN( angle ) + pos.y; edge.z = pos.z; NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime ); lastEdge = edge; } } CON_COMMAND_F( map_showbombradius, "Shows bomb radius from the center of each bomb site and planted bomb.", FCVAR_CHEAT ) { float flBombDamage = 500.0f; if ( g_pMapInfo ) flBombDamage = g_pMapInfo->m_flBombRadius; float flBombRadius = flBombDamage * 3.5f; Msg( "Bomb Damage is %.0f, Radius is %.0f\n", flBombDamage, flBombRadius ); CBaseEntity* ent = NULL; while ( ( ent = gEntList.FindEntityByClassname( ent, "func_bomb_target" ) ) != NULL ) { const Vector &pos = ent->WorldSpaceCenter(); DrawSphere( pos, flBombRadius, 255, 255, 0, 10 ); } ent = NULL; while ( ( ent = gEntList.FindEntityByClassname( ent, "planted_c4" ) ) != NULL ) { const Vector &pos = ent->WorldSpaceCenter(); DrawSphere( pos, flBombRadius, 255, 0, 0, 10 ); } } CON_COMMAND_F( map_setbombradius, "Sets the bomb radius for the map.", FCVAR_CHEAT ) { if ( args.ArgC() != 2 ) return; if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; if ( !g_pMapInfo ) CBaseEntity::Create( "info_map_parameters", vec3_origin, vec3_angle ); if ( !g_pMapInfo ) return; g_pMapInfo->m_flBombRadius = atof( args[1] ); map_showbombradius( args ); } //----------------------------------------------------------------------------- // Purpose: Called when a player joins the game after it's started yet can still spawn in //----------------------------------------------------------------------------- void CCSGameRules::SpawningLatePlayer( CCSPlayer* pLatePlayer ) { //Reset the round kills number of enemies for the opposite team for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if(pPlayer) { if(pPlayer->GetTeamNumber() == pLatePlayer->GetTeamNumber()) { continue; } pPlayer->m_NumEnemiesAtRoundStart++; } } } //----------------------------------------------------------------------------- // Purpose: Test for "pistol" round, defined as the default starting round // when players cannot purchase anything primary weapons //----------------------------------------------------------------------------- bool CCSGameRules::IsPistolRound() { if ( !IsPlayingClassic() ) return false; return ( ( m_iTotalRoundsPlayed == 0 ) || ( HasHalfTime() && ( m_iTotalRoundsPlayed > 0 ) && ( m_iTotalRoundsPlayed == ( mp_maxrounds.GetInt() / 2 ) ) ) ) && ( GetStartMoney() <= 800 ); } void CCSGameRules::PlayerTookDamage(CCSPlayer* player, const CTakeDamageInfo &damageInfo) { CBaseEntity *pInflictor = damageInfo.GetInflictor(); CBaseEntity *pAttacker = damageInfo.GetAttacker(); CCSPlayer *pCSScorer = (CCSPlayer *)(GetDeathScorer( pAttacker, pInflictor )); if ( player && pCSScorer ) { if (player->GetTeamNumber() == TEAM_CT) { m_bNoCTsDamaged = false; } if (player->GetTeamNumber() == TEAM_TERRORIST) { m_bNoTerroristsDamaged = false; } // set the first blood if this is the first and the victim is on a different team then the player if ( m_pFirstBlood == NULL && pCSScorer != player && pCSScorer->GetTeamNumber() != player->GetTeamNumber() ) { m_pFirstBlood = pCSScorer; m_firstBloodTime = gpGlobals->curtime - m_fRoundStartTime; } } } CCSMatch* CCSGameRules::GetMatch( void ) { return &m_match; } void CCSGameRules::SendPlayerItemDropsToClient() { CReliableBroadcastRecipientFilter broadcastFilter; CCSUsrMsg_SendPlayerItemDrops msg; // first randomize the list before we send it VectorShuffle( m_ItemsPtrDroppedDuringMatch ); msg.mutable_entity_updates()->Reserve( m_ItemsPtrDroppedDuringMatch.Count() ); for ( int i = 0; i < m_ItemsPtrDroppedDuringMatch.Count(); i++ ) { *msg.add_entity_updates() = *m_ItemsPtrDroppedDuringMatch[i]; } SendUserMessage( broadcastFilter, CS_UM_SendPlayerItemDrops, msg ); m_bPlayerItemsHaveBeenDisplayed = true; } void CCSGameRules::FreezePlayers( void ) { for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->AddFlag( FL_FROZEN ); } } } void CCSGameRules::ClearGunGameData( void ) { for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { // Reset all players' progressive weapon index values pPlayer->ResetTRBombModeData(); pPlayer->ClearGunGameProgressiveWeaponIndex(); pPlayer->ResetTRBombModeWeaponProgressFlag(); pPlayer->ResetTRBombModeKillPoints(); pPlayer->ClearTRModeHEGrenade(); pPlayer->ClearTRModeFlashbang(); pPlayer->ClearTRModeMolotov(); pPlayer->ClearTRModeIncendiary(); } } m_iMaxGunGameProgressiveWeaponIndex = 0; } void CCSGameRules::SwitchTeamsAtRoundReset( void ) { m_bForceTeamChangeSilent = true; m_bSwitchingTeamsAtRoundReset = true; for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->SetPendingTeamNum( pPlayer->GetTeamNumber() ) ; if ( pPlayer->GetAssociatedTeamNumber() == TEAM_CT || pPlayer->GetAssociatedTeamNumber() == TEAM_TERRORIST ) { pPlayer->SwitchTeamsAtRoundReset(); } } } // Swap custom teamnames } void CCSGameRules::AddTeamAccount( int team, TeamCashAward::Type reason ) { int amount = TeamCashAwardValue( reason ); AddTeamAccount( team, reason, amount ); } void CCSGameRules::AddTeamAccount( int team, TeamCashAward::Type reason, int amount, const char* szAwardText ) { if ( !TeamCashAwardsEnabled() ) { DevMsg( "WARNING: Trying to award %d to team %d, but mp_teamcashawards is 0!\n", amount, team ); return; } if ( amount == 0 ) return; if ( IsPlayingCoopGuardian() || IsPlayingCoopMission() ) { int nOtherTeam = IsHostageRescueMap() ? TEAM_CT : TEAM_TERRORIST; // in this mode, bots are on the other side and we handle their money ourselves if ( team == nOtherTeam ) return; } // float flDisplayedBonus = 1.0f; // // if ( CSGameRules() ) // { // // Modify with slow money multiplier rules // amount = CSGameRules()->ModifyMoneyRewardByMultiplier( true, amount, &flDisplayedBonus ); // } char customAwardText[256]; bool bUsingCustom = false; const char* awardReasonToken = NULL; switch ( reason ) { case TeamCashAward::TERRORIST_WIN_BOMB: awardReasonToken = "#Team_Cash_Award_T_Win_Bomb"; break; case TeamCashAward::ELIMINATION_HOSTAGE_MAP_T: case TeamCashAward::ELIMINATION_HOSTAGE_MAP_CT: awardReasonToken = "#Team_Cash_Award_Elim_Hostage"; break; case TeamCashAward::ELIMINATION_BOMB_MAP: awardReasonToken = "#Team_Cash_Award_Elim_Bomb"; break; case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_HOSTAGE: case TeamCashAward::WIN_BY_TIME_RUNNING_OUT_BOMB: awardReasonToken = "#Team_Cash_Award_Win_Time"; break; case TeamCashAward::WIN_BY_DEFUSING_BOMB: awardReasonToken = "#Team_Cash_Award_Win_Defuse_Bomb"; break; case TeamCashAward::WIN_BY_HOSTAGE_RESCUE: if ( mp_hostages_rescuetowin.GetInt() == 1 ) awardReasonToken = "#Team_Cash_Award_Win_Hostage_Rescue"; else awardReasonToken = "#Team_Cash_Award_Win_Hostages_Rescue"; break; case TeamCashAward::LOSER_BONUS: if ( amount > 0 ) awardReasonToken = "#Team_Cash_Award_Loser_Bonus"; else awardReasonToken = "#Team_Cash_Award_Loser_Bonus_Neg"; break; case TeamCashAward::RESCUED_HOSTAGE: awardReasonToken = "#Team_Cash_Award_Rescued_Hostage"; break; case TeamCashAward::HOSTAGE_INTERACTION: awardReasonToken = "#Team_Cash_Award_Hostage_Interaction"; break; case TeamCashAward::HOSTAGE_ALIVE: awardReasonToken = "#Team_Cash_Award_Hostage_Alive"; break; case TeamCashAward::PLANTED_BOMB_BUT_DEFUSED: awardReasonToken = "#Team_Cash_Award_Planted_Bomb_But_Defused"; break; case TeamCashAward::SURVIVE_GUARDIAN_WAVE: awardReasonToken = "#Team_Cash_Award_Survive_GuardianMode_Wave"; break; case TeamCashAward::CUSTOM_AWARD: { if ( szAwardText && szAwardText[0] != 0 ) { awardReasonToken = "#Team_Cash_Award_Custom"; Q_snprintf( customAwardText, sizeof( customAwardText ), "%s", szAwardText ); bUsingCustom = true; } else { awardReasonToken = "#Team_Cash_Award_Generic"; } break; } default: break; } bool bTeamHasClinchedVictory = false; if ( IsPlayingClassic() && !IsPlayingTraining() ) { int iNumWinsToClinch = ( mp_maxrounds.GetInt() / 2 ) + 1 + GetOvertimePlaying() * ( mp_overtime_maxrounds.GetInt() / 2 ); bTeamHasClinchedVictory = mp_match_can_clinch.GetBool() && ( m_match.GetCTScore() >= iNumWinsToClinch ) || ( m_match.GetTerroristScore() >= iNumWinsToClinch ); } char strAmount[64]; Q_snprintf( strAmount, sizeof( strAmount ), "%s$%d", amount >= 0 ? "+" : "-", abs( amount )); // Update all QMM records for the teams bool bTeamsArePlayingSwitchedSides = AreTeamsPlayingSwitchedSides(); FOR_EACH_MAP_FAST( m_mapQueuedMatchmakingPlayersData, idxQMMEntry ) { CQMMPlayerData_t &qmmData = * m_mapQueuedMatchmakingPlayersData.Element( idxQMMEntry ); int iRulesTeam = bTeamsArePlayingSwitchedSides ? ( (qmmData.m_iDraftIndex < 5)?TEAM_TERRORIST:TEAM_CT ) : ( (qmmData.m_iDraftIndex < 5)?TEAM_CT:TEAM_TERRORIST ); if ( ( iRulesTeam == team ) && !qmmData.m_bReceiveNoMoneyNextRound ) { // we set all records even though alive players will stomp them when receiving money in the loop right below qmmData.m_cash = clamp( (int)( qmmData.m_cash + amount ), 0, GetMaxMoney() ); } } for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if ( !pPlayer || pPlayer->GetTeamNumber() != team ) continue; // hand out team cash awards from previous round if ( pPlayer->DoesPlayerGetRoundStartMoney() ) { pPlayer->AddAccountFromTeam( amount, true, reason ); if ( dev_reportmoneychanges.GetBool() ) Msg( "%s %d (total: %d) AddAccountFromTeam: REASON: %s\n", pPlayer->GetPlayerName(), amount, pPlayer->GetAccountBalance(), awardReasonToken ); if ( bUsingCustom ) { ClientPrint( pPlayer, HUD_PRINTTALK, awardReasonToken, strAmount, customAwardText ); } else if ( !IsLastRoundBeforeHalfTime() && ( m_match.GetPhase() != GAMEPHASE_HALFTIME ) && ( m_match.GetRoundsPlayed() != mp_maxrounds.GetInt() + GetOvertimePlaying() * mp_overtime_maxrounds.GetInt() ) && !bTeamHasClinchedVictory ) { ClientPrint( pPlayer, HUD_PRINTTALK, awardReasonToken, strAmount ); } } else { if ( !IsLastRoundBeforeHalfTime() && ( m_match.GetPhase() != GAMEPHASE_HALFTIME ) && ( m_match.GetRoundsPlayed() != mp_maxrounds.GetInt() + GetOvertimePlaying() * mp_overtime_maxrounds.GetInt() ) && !bTeamHasClinchedVictory ) { // TODO: This code assumes on there only being 2 possible reasons for DoesPlayerGetRoundStartMoney returning false: Suicide or Running down the clock as T. // This code should not make that assumption and the awardReasonToken should probably be plumbed to express those properly. if ( pPlayer->GetHealth() > 0 ) { ClientPrint( pPlayer, HUD_PRINTTALK, "#Team_Cash_Award_No_Income", strAmount ); } else { ClientPrint( pPlayer, HUD_PRINTTALK, "#Team_Cash_Award_No_Income_Suicide", strAmount ); } } } } } #endif // !CLIENT_DLL bool CCSGameRules::IsCSGOBirthday( void ) { return sv_party_mode.GetBool(); } int CCSGameRules::GetStartMoney( void ) { return IsWarmupPeriod() ? GetMaxMoney() : ( GetOvertimePlaying() ? mp_overtime_startmoney.GetInt() : mp_startmoney.GetInt() ); } int CCSGameRules::GetMaxMoney( void ) { return mp_maxmoney.GetInt(); } int CCSGameRules::GetBetweenRoundMoney( void ) { return mp_afterroundmoney.GetInt(); } bool CCSGameRules::PlayerCashAwardsEnabled( void ) { return mp_playercashawards.GetBool(); } bool CCSGameRules::TeamCashAwardsEnabled( void ) { return mp_teamcashawards.GetBool(); } bool CCSGameRules::IsPlayingGunGameProgressive( void ) const { return ( IsPlayingGunGame() && g_pGameTypes->GetCurrentGameMode() == CS_GameMode::GunGame_Progressive ); } bool CCSGameRules::IsPlayingGunGameDeathmatch( void ) const { return ( IsPlayingGunGame() && g_pGameTypes->GetCurrentGameMode() == CS_GameMode::GunGame_Deathmatch ); } bool CCSGameRules::IsPlayingGunGameTRBomb( void ) const { return ( IsPlayingGunGame() && g_pGameTypes->GetCurrentGameMode() == CS_GameMode::GunGame_Bomb ); } bool CCSGameRules::IsPlayingClassicCasual( void ) const { return ( IsPlayingClassic() && g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Classic_Casual); } bool CCSGameRules::IsPlayingAnyCompetitiveStrictRuleset( void ) const { return ( IsPlayingClassic() && ( (g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Classic_Competitive) ) ); } bool CCSGameRules::IsPlayingCoopGuardian( void ) const { return ( IsPlayingCooperativeGametype() && g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Cooperative_Guardian ); } bool CCSGameRules::IsPlayingCoopMission( void ) const { return ( IsPlayingCooperativeGametype() && g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Cooperative_Mission ); } bool CCSGameRules::ShouldRecordMatchStats( void ) const { return ( IsPlayingCoopMission() || (IsPlayingClassic() && ( g_pGameTypes->GetCurrentGameMode() == CS_GameMode::Classic_Competitive )) && ( !IsWarmupPeriod() ) && ( m_nOvertimePlaying == 0 ) && ( GetTotalRoundsPlayed() < MAX_MATCH_STATS_ROUNDS ) ); } bool CCSGameRules::IsQueuedMatchmaking() const { #ifndef CLIENT_DLL static ConVarRef sv_mmqueue_reservation( "sv_mmqueue_reservation" ); return sv_mmqueue_reservation.GetString()[0] == 'Q'; #else return m_bIsQueuedMatchmaking.Get(); #endif } bool CCSGameRules::IsValveDS() const { #ifndef CLIENT_DLL return false; #else return m_bIsValveDS.Get(); #endif } bool CCSGameRules::IsQuestEligible() const { #ifndef CLIENT_DLL return false; #else return m_bIsQuestEligible.Get(); #endif } bool CCSGameRules::IsPlayingOffline( void ) const { #ifndef CLIENT_DLL if ( engine->IsDedicatedServer() ) return false; #endif extern ConVar game_online; return !game_online.GetBool(); } bool CCSGameRules::IsAwardsProgressAllowedForBotDifficulty( void ) const { return ( !IsPlayingOffline() || ( GetCustomBotDifficulty() >= CUSTOM_BOT_MIN_DIFFICULTY_FOR_AWARDS_PROGRESS ) ); } bool CCSGameRules::IsPlayingCustomGametype( void ) const { return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Custom ); } bool CCSGameRules::IsPlayingTraining( void ) const { return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Training ); } bool CCSGameRules::IsPlayingClassic( void ) const { return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Classic ); } bool CCSGameRules::IsPlayingGunGame( void ) const { return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_GunGame ); } bool CCSGameRules::IsPlayingCooperativeGametype( void ) const { return ( g_pGameTypes->GetCurrentGameType() == CS_GameType_Cooperative ); } int CCSGameRules::GetCustomBotDifficulty( void ) const { return ( g_pGameTypes->GetCustomBotDifficulty() ); } bool CCSGameRules::ForceSplitScreenPlayersOnToSameTeam( void ) { extern ConVar game_online; return game_online.GetBool(); } ConVar mp_solid_teammates("mp_solid_teammates", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether teammates are solid or not." ); ConVar mp_free_armor("mp_free_armor", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether armor and helmet are given automatically." ); ConVar mp_halftime("mp_halftime", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether the match switches sides in a halftime event."); ConVar mp_randomspawn("mp_randomspawn", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Determines whether players are to spawn. 0 = default; 1 = both teams; 2 = Terrorists; 3 = CTs." ); ConVar mp_randomspawn_los("mp_randomspawn_los", "1", FCVAR_REPLICATED | FCVAR_RELEASE, "If using mp_randomspawn, determines whether to test Line of Sight when spawning." ); ConVar mp_randomspawn_dist( "mp_randomspawn_dist", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "If using mp_randomspawn, determines whether to test distance when selecting this spot." ); // Returns true if teammates are solid obstacles in the current game mode bool CCSGameRules::IsTeammateSolid( void ) const { return mp_solid_teammates.GetBool(); } // Returns true if armor is given automatically. bool CCSGameRules::IsArmorFree( void ) const { return mp_free_armor.GetBool(); } // Returns true if the game is to be split into two halves. bool CCSGameRules::HasHalfTime( void ) const { return mp_halftime.GetBool(); } int CCSGameRules::GetWeaponScoreForDeathmatch( int nPos ) { int nScore = 1; CSWeaponID wepID = WEAPON_NONE; for ( int i = 0; i < GetItemSchema()->GetItemDefinitionCount(); i++ ) { CCStrike15ItemDefinition *pItemDef = ( CCStrike15ItemDefinition * )GetItemSchema()->GetItemDefinitionByMapIndex( i ); if ( pItemDef->GetDefaultLoadoutSlot() == nPos ) { wepID = WeaponIdFromString( pItemDef->GetItemClass() ); break; } } if ( wepID == WEAPON_NONE ) return 0; const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( wepID ); if ( pWeaponInfo ) { // float flScore1 = ((pWeaponInfo->m_flCycleTime / pWeaponInfo->m_iDamage)-0.001f) * 10; // float flScore2 = ((pWeaponInfo->m_iKillAward / MAX( 500, pWeaponInfo->m_iWeaponPrice )) + flScore1) * 100; // int nScore = MAX( 1, ceil(flScore2) ); // pPlayer->AddContributionScore( nScore ); if ( wepID == WEAPON_KNIFE ) { nScore = 20; } else { int nPrice = MIN( 4500, MAX(100, pWeaponInfo->GetWeaponPrice() - (pWeaponInfo->GetKillAward()/2)) ); float flScore1 = MAX( 0.05f, ((1 - ((float)nPrice/4500.0f)) * 10)/5); float flScore2 = flScore1 + MIN( 2.0f, (((pWeaponInfo->GetCycleTime() / pWeaponInfo->GetDamage() ) *10 ) + ( ( 2.0f-pWeaponInfo->GetArmorRatio() ) /2 ) ) / 4 ); float flFinal = ceil(flScore2-0.5f);//ceil((flScore2/6) * 10 ); nScore = MAX( 0, flFinal ) + 10; } } return nScore; } float CCSGameRules::GetRestartRoundTime( void ) const { return m_fRoundStartTime; } #if !defined( CLIENT_DLL ) CGameCoopMissionManager *CCSGameRules::GetCoopMissionManager( void ) { CGameCoopMissionManager *pManager = static_cast< CGameCoopMissionManager* >( m_coopMissionManager.Get() ); if ( !pManager ) { CBaseEntity *ent = NULL; do { ent = gEntList.FindEntityByClassname( ent, "game_coopmission_manager" ); if ( ent ) { CSGameRules()->SetCoopMissionManager( ent ); pManager = GetCoopMissionManager(); } } while ( ent ); } return pManager; } void CCSGameRules::CoopSetBotQuotaAndRefreshSpawns( int nMaxEnemiesToSpawn ) { RefreshCurrentSpawnPointLists(); m_iMaxNumTerrorists = nMaxEnemiesToSpawn; ConVarRef bot_quota( "bot_quota" ); int nQuota = UTIL_HumansInGame( true, true ) + MIN( m_iMaxNumTerrorists, m_iSpawnPointCount_Terrorist ); bot_quota.SetValue( nQuota ); ShuffleSpawnPointLists(); SortSpawnPointLists(); } void CCSGameRules::CoopMissionSpawnFirstEnemies( int nMaxEnemiesToSpawn ) { // set to 0 so we start in wave 1 m_nGuardianModeWaveNumber = 0; CoopMissionSpawnNextWave( nMaxEnemiesToSpawn ); } void CCSGameRules::CoopMissionSetNextRespawnIn( float flSeconds, bool bIncrementWaveNumber ) { m_bDontIncrementCoopWave = !bIncrementWaveNumber; // if T's are supposed to respawn float flNextRespawn = gpGlobals->curtime + flSeconds; m_flNextRespawnWave.Set( TEAM_TERRORIST, flNextRespawn ); } void CCSGameRules::CoopMissionSpawnNextWave( int nMaxEnemiesToSpawn ) { // the CT spawn points for respawn are enabled, respawn now - we can work on the appropriate timings later int nTeam = TEAM_CT; // give health to all of the players on the other team for ( int j = 1; j <= MAX_PLAYERS; j++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( j ) ); if ( pPlayer && pPlayer->GetTeamNumber() == nTeam ) { // respawn any dead CTs as well if ( pPlayer->IsAlive() == false ) { pPlayer->State_Transition( STATE_GUNGAME_RESPAWN ); } } } // make sure this is 0 so we start from the beginning m_iNextTerroristSpawnPoint = 0; // do a refresh here, but we don't refresh as frequently in coop mission // we would normally refresh every time a spot enabled or disabled and a few other places // just do it here once before we spawn new guys RefreshCurrentSpawnPointLists(); m_iMaxNumTerrorists = nMaxEnemiesToSpawn; ShuffleSpawnPointLists(); SortSpawnPointLists(); // make sure that there are always enough bots ConVarRef bot_quota( "bot_quota" ); int nQuota = UTIL_HumansInGame( true, true ) + MIN( m_iMaxNumTerrorists, m_iSpawnPointCount_Terrorist ); bot_quota.SetValue( nQuota ); //DevMsg( ">> Setting bot_quota to %d\n", nQuota ); // if T's are supposed to respawn CoopMissionSetNextRespawnIn( -1, true ); } void CCSGameRules::CoopMissionRespawnDeadPlayers( void ) { // do a refresh here, but we don't refresh as frequently in coop mission // we would normally refresh every time a spot enabled or disabled and a few other places // just do it here once before we spawn new guys if ( !IsWarmupPeriod() ) RefreshCurrentSpawnPointLists(); // the CT spawn points for respawn are enabled, respawn now - we can work on the appropriate timings later CTeam *pTeam = GetGlobalTeam( TEAM_CT ); CUtlVector < CCSPlayer* > vecPlayers; if ( pTeam->GetHumanMembers( &vecPlayers ) ) { FOR_EACH_VEC( vecPlayers, i ) { // respawn any dead CTs as well if ( vecPlayers[i]->IsAlive() == false ) { vecPlayers[i]->State_Transition( STATE_GUNGAME_RESPAWN ); } } } } void CCSGameRules::CoopCollectBonusCoin( void ) { m_coopBonusCoinsFound++; CRecipientFilter filter; filter.MakeReliable(); filter.AddRecipientsByTeam( GetGlobalTeam( TEAM_CT ) ); char szCoins[32]; Q_snprintf( szCoins, sizeof( szCoins ), "%d", ( int )m_coopBonusCoinsFound ); UTIL_ClientPrintFilter( filter, HUD_PRINTCENTER, "#SFUIHUD_InfoPanel_Coop_CollectCoin", szCoins ); } #endif #ifdef CLIENT_DLL CCSGameRules::CCSGameRules() { // Reset borrow music convar because player indices scramble after level transition. cl_borrow_music_from_player_index.SetValue( cl_borrow_music_from_player_index.GetDefault() ); InitializeGameTypeAndMode(); // Set the bestof maps m_numBestOfMaps = 0; // Set global gifts state m_numGlobalGiftsGiven = 0; m_numGlobalGifters = 0; m_numGlobalGiftsPeriodSeconds = 0; for ( int j = 0; j < MAX_GIFT_GIVERS_FEATURED_COUNT; ++ j ) { m_arrFeaturedGiftersAccounts.Set( j, 0 ); m_arrFeaturedGiftersGifts.Set( j, 0 ); } for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; ++ j ) { m_arrTournamentActiveCasterAccounts.Set( j, 0 ); } for ( int j = 0; j < MAX_MATCH_STATS_ROUNDS; ++j ) { m_iMatchStats_PlayersAlive_T.GetForModify( j ) = 0x3F; m_iMatchStats_PlayersAlive_CT.GetForModify( j ) = 0x3F; } ResetCasterConvars(); m_szTournamentEventName.GetForModify()[0] = 0; m_szTournamentEventStage.GetForModify()[0] = 0; m_szTournamentPredictionsTxt.GetForModify()[0] = 0; m_szMatchStatTxt.GetForModify()[ 0 ] = 0; m_nTournamentPredictionsPct = 0; HOOK_MESSAGE( SendPlayerItemDrops ); HOOK_MESSAGE( SendPlayerItemFound ); m_bMarkClientStopRecordAtRoundEnd = false; } CCSGameRules::~CCSGameRules() { ResetCasterConvars(); for ( int j = 0; j < MAX_TOURNAMENT_ACTIVE_CASTER_COUNT; ++ j ) { m_arrTournamentActiveCasterAccounts.Set( j, 0 ); } } // CLIENT bool __MsgFunc_SendPlayerItemDrops( const CCSUsrMsg_SendPlayerItemDrops &msg ) { //IViewPortPanel* panel = GetViewPortInterface()->FindPanelByName( PANEL_SCOREBOARD ); if ( CSGameRules() ) { CSGameRules()->ClearItemsDroppedDuringMatch(); for ( int i = 0; i < msg.entity_updates_size(); i ++ ) { const CEconItemPreviewDataBlock &update = msg.entity_updates(i); CSGameRules()->RecordPlayerItemDrop( update ); } } return true; } void CCSGameRules::MarkClientStopRecordAtRoundEnd( bool bStop ) { m_bMarkClientStopRecordAtRoundEnd = bStop; } void CCSGameRules::OpenBuyMenu( int nPlayerID ) { // skip buy menu during demo playback if ( engine->IsPlayingDemo() || engine->IsPlayingTimeDemo() ) return; GetViewPortInterface()->ShowPanel( PANEL_BUY, true ); IGameEvent *pEvent = gameeventmanager->CreateEvent( "buymenu_open" ); if ( pEvent ) { pEvent->SetInt("userid", nPlayerID ); gameeventmanager->FireEventClientSide( pEvent ); } } void CCSGameRules::CloseBuyMenu( int nPlayerID ) { // skip buy menu during demo playback if ( engine->IsPlayingDemo() || engine->IsPlayingTimeDemo() ) return; GetViewPortInterface()->ShowPanel( PANEL_BUY, false ); IGameEvent *pEvent = gameeventmanager->CreateEvent( "buymenu_close" ); if ( pEvent ) { pEvent->SetInt("userid", nPlayerID ); gameeventmanager->FireEventClientSide( pEvent ); } // update the inventory CCSPlayer *pPlayer = static_cast( UTIL_PlayerByIndex(engine->GetPlayerForUserID( nPlayerID )) ); C_WeaponCSBase *pWeapon = (pPlayer && pPlayer->IsLocalPlayer()) ? pPlayer->GetActiveCSWeapon() : NULL ; if ( pWeapon ) { CBaseHudWeaponSelection *pHudSelection = GetHudWeaponSelection(); if ( pHudSelection ) { pHudSelection->OnWeaponSwitch( pWeapon ); } } } const wchar_t* CCSGameRules::GetFriendlyMapName( const char* szShortName ) { // Look up the nice version of the name. char szToken[MAX_MAP_NAME+10+1]; // includes prefix size szToken[0] = '\0'; V_snprintf( szToken, ARRAYSIZE( szToken ), "#SFUI_Map_%s", szShortName ); wchar_t* szTranslated = g_pVGuiLocalize->Find( szToken ); if ( szTranslated ) return szTranslated; char szPath[MAX_PATH]; V_strcpy_safe( szPath, szShortName ); V_FixSlashes( szPath, '/' ); // internal path strings use forward slashes, make sure we compare like that. if ( V_strstr( szPath, "workshop/" ) ) { PublishedFileId_t ullId = GetMapIDFromMapPath( szPath ); const PublishedFileInfo_t *pInfo = WorkshopManager().GetPublishedFileInfoByID( ullId ); static wchar_t wszMapName[128]; if ( pInfo ) { g_pVGuiLocalize->ConvertANSIToUnicode(pInfo->m_rgchTitle, wszMapName, sizeof(wszMapName)); return wszMapName; } else { g_pVGuiLocalize->ConvertANSIToUnicode( V_GetFileName( szShortName ), wszMapName, sizeof(wszMapName)); return wszMapName; } } static wchar_t wszMapName[128]; g_pVGuiLocalize->ConvertANSIToUnicode(szShortName, wszMapName, sizeof(wszMapName)); return wszMapName; } bool CCSGameRules::GetFriendlyMapNameToken( const char* szShortName, char* szOutBuffer, int nBuffSize ) { // Create the nice version of the name. CreateFriendlyMapNameToken( szShortName, szOutBuffer, nBuffSize ); if ( g_pVGuiLocalize->Find( szOutBuffer ) ) return true; return false; } char const * CCSGameRules::GetTournamentEventName() const { const char *sz = m_szTournamentEventName.Get(); if ( sz && *sz && ( sz[0] != '#' ) ) { // Localize it: static char const * const arrEvents[] = { "" ,"The 2013 DreamHack SteelSeries CS:GO Championship" ,"The Valve DPR Championship" ,"The 2014 EMS One Katowice CS:GO Championship" ,"ESL One Cologne 2014 CS:GO Championship" ,"The 2014 DreamHack CS:GO Championship" ,"ESL One Katowice 2015 CS:GO Championship" ,"ESL One Cologne 2015 CS:GO Championship" ,"DreamHack Cluj-Napoca 2015 CS:GO Championship" ,"MLG Columbus 2016 CS:GO Championship" ,"ESL One Cologne 2016 CS:GO Championship" ,"ELEAGUE Atlanta 2017 CS:GO Championship" }; for ( int k = Q_ARRAYSIZE( arrEvents ) - 1; k > 0; -- k ) { if ( !V_strcmp( sz, arrEvents[k] ) ) { V_snprintf( const_cast< char * >( sz ), MAX_PATH, "#CSGO_Tournament_Event_Name_%d", k ); return sz; } } } return m_szTournamentEventName; } char const * CCSGameRules::GetTournamentEventStage() const { const char *sz = m_szTournamentEventStage.Get(); if ( sz && *sz && ( sz[ 0 ] != '#' ) ) { // Localize it: static char const * const arrEvents[] = { "" ,"Exhibition" ,"Group Stage | First Stage" ,"BYOC" ,"Valve Pre-Event Test" ,"Match 1 of 3 | Quarterfinal" ,"Match 2 of 3 | Quarterfinal" ,"Match 3 of 3 | Quarterfinal" ,"Match 1 of 3 | Semifinal" ,"Match 2 of 3 | Semifinal" ,"Match 3 of 3 | Semifinal" ,"Match 1 of 3 | Grand Final" ,"Match 2 of 3 | Grand Final" ,"Match 3 of 3 | Grand Final" ,"All-Star" ,"Group Stage | Winners Match" ,"Group Stage | Elimination Match" ,"Group Stage | Decider Match" ,"Qualification" ,"Match 2 of 3 | Qualification" ,"Match 3 of 3 | Qualification" ,"Group Stage | Decider Match 1 of 3" ,"Group Stage | Decider Match 2 of 3" ,"Group Stage | Decider Match 3 of 3" ,"Group Stage | Second Stage (Upper Pool)" ,"Group Stage | Second Stage (Lower Pool)" ,"Group Stage | Third Stage" }; for ( int k = Q_ARRAYSIZE( arrEvents ) - 1; k > 0; --k ) { if ( !V_strcmp( sz, arrEvents[ k ] ) ) { V_snprintf( const_cast< char * >( sz ), MAX_PATH, "#CSGO_Tournament_Event_Stage_Display_%d", k ); return sz; } } } return m_szTournamentEventStage; } // // all of this is here to pick the first caster in the active caster list by default // later when we have a "pick caster" dialog then this can go away // extern ConVar spec_autodirector_cameraman; extern ConVar spec_cameraman_ui; extern ConVar spec_cameraman_xray; void CCSGameRules::RecvProxy_TournamentActiveCasterAccounts( const CRecvProxyData *pData, void *pStruct, void *pOut ) { RecvProxy_Int32ToInt32( pData, pStruct, pOut ); // set convars here if ( spec_autodirector_cameraman.GetInt() == -1 && CSGameRules() && CSGameRules()->m_arrTournamentActiveCasterAccounts[ 0 ] != 0 ) { int casterID = ( int ) ( CSGameRules()->m_arrTournamentActiveCasterAccounts[ 0 ] ); spec_autodirector_cameraman.SetValue( casterID ); spec_cameraman_ui.SetValue( casterID ); spec_cameraman_xray.SetValue( casterID ); engine->SetVoiceCasterID( CSGameRules()->m_arrTournamentActiveCasterAccounts[ 0 ] ); } } void CCSGameRules::ResetCasterConvars() { spec_autodirector_cameraman.SetValue( -1 ); spec_cameraman_ui.SetValue( 0 ); spec_cameraman_xray.SetValue( 0 ); engine->SetVoiceCasterID( 0 ); } #endif CEconQuestDefinition* CCSGameRules::GetActiveAssassinationQuest( void ) const { if ( IsPlayingCooperativeGametype() ) return NULL; // Skip the map lookup for invlaid quest id if ( !m_iActiveAssassinationTargetMissionID ) return NULL; else return GetItemSchema()->GetQuestDefinition( m_iActiveAssassinationTargetMissionID ); } float CCSGameRules::GetCMMItemDropRevealDuration() { if ( m_flCMMItemDropRevealEndTime == 0 && m_ItemsPtrDroppedDuringMatch.Count() <= 0 ) return 2; if ( m_flCMMItemDropRevealEndTime == 0 && m_flCMMItemDropRevealStartTime != 0 ) { float flItemDropTime = 2.0f; for ( int i = 0; i < m_ItemsPtrDroppedDuringMatch.Count(); i++ ) { switch ( m_ItemsPtrDroppedDuringMatch[i]->rarity() ) { case 6: case 5: { flItemDropTime += sv_endmatch_item_drop_interval_ancient.GetFloat(); break; } case 4: { flItemDropTime += sv_endmatch_item_drop_interval_legendary.GetFloat(); break; } case 3: { flItemDropTime += sv_endmatch_item_drop_interval_mythical.GetFloat(); break; } case 2: { flItemDropTime += sv_endmatch_item_drop_interval_rare.GetFloat(); break; } default: { flItemDropTime += sv_endmatch_item_drop_interval.GetFloat(); break; } } } m_flCMMItemDropRevealEndTime = (m_flCMMItemDropRevealStartTime + flItemDropTime); } return MAX( 2, (m_flCMMItemDropRevealEndTime - m_flCMMItemDropRevealStartTime) ); } void CCSGameRules::CreateFriendlyMapNameToken( const char* szShortName, char* szOutBuffer, int nBuffSize ) { // must be able to fit the token below Assert( nBuffSize >= MAX_MAP_NAME+10+1 ); // Create the nice version of the name. // right now we blindly create the token because we can assume (on consoles) that we don't have any maps // that we don't know about. For PC, we'll need to fix this so all checks will happen on the client V_snprintf( szOutBuffer, nBuffSize, "#SFUI_Map_%s", szShortName ); } const char *CCSGameRules::GetDefaultTeamName( int nTeam ) { Assert( nTeam >= 0 && nTeam < ARRAYSIZE( sTeamNames ) ); return sTeamNames[nTeam]; } void CCSGameRules::AddGunGameWeapon( const char* pWeaponName, int nNumKillsToUpgrade, int nTeamID ) { if ( !pWeaponName ) { return; } Assert( nNumKillsToUpgrade > 0 ); if ( nNumKillsToUpgrade <= 0 ) { Warning( "CCSGameRules: Invalid number of kills-to-upgrade (%d) for weapon %s.\n", nNumKillsToUpgrade, pWeaponName ); nNumKillsToUpgrade = 1; } char weaponName[MAX_WEAPON_STRING]; weaponName[0] = '\0'; V_snprintf( weaponName, sizeof( weaponName ), "weapon_%s", pWeaponName ); int nWeaponID = 0; // Try to get the ID from the item name const CEconItemDefinition *pDef = GetItemSchema()->GetItemDefinitionByName( weaponName ); if ( pDef ) { nWeaponID = pDef->GetDefinitionIndex(); } if ( nWeaponID == 0 ) { // Fall back to the older weapon id system for things like knifegg nWeaponID = WeaponIdFromString( weaponName ); } if ( nWeaponID != WEAPON_NONE ) { if ( nTeamID == TEAM_CT ) { m_GGProgressiveWeaponOrderCT.Set( m_iNumGunGameProgressiveWeaponsCT, nWeaponID ); m_GGProgressiveWeaponKillUpgradeOrderCT.Set( m_iNumGunGameProgressiveWeaponsCT, nNumKillsToUpgrade ); m_iNumGunGameProgressiveWeaponsCT++; } else if ( nTeamID == TEAM_TERRORIST ) { m_GGProgressiveWeaponOrderT.Set( m_iNumGunGameProgressiveWeaponsT, nWeaponID ); m_GGProgressiveWeaponKillUpgradeOrderT.Set( m_iNumGunGameProgressiveWeaponsT, nNumKillsToUpgrade ); m_iNumGunGameProgressiveWeaponsT++; } #if defined ( GAME_DLL ) // PERF: Force creation of instance baselines so we don't take the hit upon creation. // We're doing this because we get a huge network spike upon player death which causes hitches on PS3. // Pre-creating baselines offloads a bit of that spike. UTIL_EnsureInstanceBaseline( weaponName ); #endif } else { Warning( "CCSGameRules::AddGunGameWeapon: encountered an unknown weapon \"%s\".\n", pWeaponName ); } } //----------------------------------------------------------------------------- // Purpose: Set up the convars and data associated with the current game type and mode //----------------------------------------------------------------------------- void CCSGameRules::InitializeGameTypeAndMode( void ) { #if !defined( CLIENT_DLL ) bool isMultiplayer = !IsPlayingOffline(); const char* szMapNameFull = STRING(gpGlobals->mapname); ///////////// load kv values from map sidecar file ( map.kv ) //////// char filename[ MAX_PATH ]; V_StripExtension( V_UnqualifiedFileName( STRING( gpGlobals->mapname ) ), filename, MAX_PATH ); uint32 dwRichPresenceContext = 0xFFFF; if ( !g_pGameTypes->GetMapInfo( filename, dwRichPresenceContext ) ) { char kvFilename[ MAX_PATH ]; bool bLoadSideCar = true; V_snprintf( kvFilename, sizeof( kvFilename ), "maps/%s.kv", filename ); if ( !g_pFullFileSystem->FileExists( kvFilename ) ) bLoadSideCar = false; // Load the Map sidecar entry if ( bLoadSideCar ) { KeyValues *pkvMap = new KeyValues( "Map" ); KeyValues::AutoDelete autodelete_kvMap( pkvMap ); if ( pkvMap->LoadFromFile( g_pFullFileSystem, kvFilename ) ) { g_pGameTypes->LoadMapEntry( pkvMap ); } else { Warning( "Failed to load %s\n", kvFilename ); } } } ///// /////////////////// g_pGameTypes->CheckShouldSetDefaultGameModeAndType( szMapNameFull ); // Set the mode convars if ( g_pGameTypes->ApplyConvarsForCurrentMode( isMultiplayer ) ) { // Add the gun game weapons. if ( IsPlayingGunGameProgressive() && mp_ggprogressive_use_random_weapons.GetBool() ) { // this is where we build the list of GG progressive weapons // collect all of the weapons here CUtlVector< GGWeaponAliasName > pSMGs; for ( int i=GGLIST_SMGS_START; i<(GGLIST_SMGS_LAST+1); i++ ) pSMGs.AddToTail(ggWeaponAliasNameList[i]); CUtlVector< GGWeaponAliasName > pShotguns; for ( int i=GGLIST_SGS_START; i<(GGLIST_SGS_LAST+1); i++ ) pShotguns.AddToTail(ggWeaponAliasNameList[i]); CUtlVector< GGWeaponAliasName > pRifles; for ( int i=GGLIST_RIFLES_START; i<(GGLIST_RIFLES_LAST+1); i++ ) pRifles.AddToTail(ggWeaponAliasNameList[i]); CUtlVector< GGWeaponAliasName > pSnipers; for ( int i=GGLIST_SNIPERS_START; i<(GGLIST_SNIPERS_LAST+1); i++ ) pSnipers.AddToTail(ggWeaponAliasNameList[i]); CUtlVector< GGWeaponAliasName > pMGs; for ( int i=GGLIST_MGS_START; i<(GGLIST_MGS_LAST+1); i++ ) pMGs.AddToTail(ggWeaponAliasNameList[i]); CUtlVector< GGWeaponAliasName > pPistols; for ( int i=GGLIST_PISTOLS_START; i<(GGLIST_PISTOLS_LAST+1); i++ ) pPistols.AddToTail(ggWeaponAliasNameList[i]); int nNumSMGs = 3; int nNumRifles = 4; int nNumShotguns = 2; int nNumSnipers = 2; int nNumMGs = 1; int nNumPistols = 4; // this should total 16 weapons int nKillsNeeded = MAX( 1, mp_ggprogressive_random_weapon_kills_needed.GetInt() ); // now pick a random one from the list we created above for each category CUtlVector< GGWeaponAliasName > pWeaponProgression; for ( int i=0; i *pWeaponProgressionCT = g_pGameTypes->GetWeaponProgressionForCurrentModeCT(); if ( pWeaponProgressionCT ) { FOR_EACH_VEC( *pWeaponProgressionCT, iWeaponProgression ) { const IGameTypes::WeaponProgression &wp = (*pWeaponProgressionCT)[iWeaponProgression]; AddGunGameWeapon( wp.m_Name, wp.m_Kills, TEAM_CT ); } } const CUtlVector< IGameTypes::WeaponProgression > *pWeaponProgressionT = g_pGameTypes->GetWeaponProgressionForCurrentModeT(); if ( pWeaponProgressionT ) { FOR_EACH_VEC( * pWeaponProgressionT, iWeaponProgression ) { const IGameTypes::WeaponProgression &wp = (* pWeaponProgressionT )[iWeaponProgression]; AddGunGameWeapon( wp.m_Name, wp.m_Kills, TEAM_TERRORIST ); } } } if ( CSGameRules()->HasHalfTime() ) { m_match.SetPhase( GAMEPHASE_PLAYING_FIRST_HALF ); } else { m_match.SetPhase( GAMEPHASE_PLAYING_STANDARD ); } // Set the map convars ConVarRef host_map( "host_map" ); g_pGameTypes->ApplyConvarsForMap( host_map.GetString(), engine->IsDedicatedServer() || isMultiplayer ); if ( CSGameRules()->IsPlayingCooperativeGametype() ) { uint32 unQuestID = MatchmakingGameTypeToMapGroup(CCSGameRules::sm_QueuedServerReservation.game_type() ); const CEconQuestDefinition *pQuest = GetItemSchema()->GetQuestDefinition( unQuestID ); if ( pQuest && !StringIsEmpty ( pQuest->GetQuestConVars() ) ) { m_iActiveAssassinationTargetMissionID = unQuestID; engine->ServerCommand( CFmtStr( "%s\n", pQuest->GetQuestConVars() ) ); engine->ServerExecute(); } } } #endif #if ( CSTRIKE_DEMO_PRESSBUILD || CSTRIKE_E3_BUILD ) ConVarRef bot_quota( "bot_quota" ); bot_quota.SetValue( 8 ); ConVarRef bot_difficulty( "bot_difficulty" ); bot_difficulty.SetValue( 0 ); #endif // this allows the weapon recoil tables to incorporate changes made to convars before the game launches g_WeaponDatabase.RefreshAllWeapons(); } int CCSGameRules::GetNumProgressiveGunGameWeapons( int nTeamID ) const { if ( nTeamID == TEAM_CT ) { return m_iNumGunGameProgressiveWeaponsCT; } else { return m_iNumGunGameProgressiveWeaponsT; } } int CCSGameRules::GetGunGameTRBonusGrenade( CCSPlayer *pPlayer ) { if ( !pPlayer ) return 0; int nNumTRKillPoints = pPlayer->GetNumGunGameTRKillPoints(); if ( nNumTRKillPoints >= mp_ggtr_bomb_pts_for_molotov.GetInt() ) { if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) return WEAPON_MOLOTOV; else return WEAPON_INCGRENADE; } else if ( nNumTRKillPoints >= mp_ggtr_bomb_pts_for_flash.GetInt() ) { return WEAPON_FLASHBANG; } else if ( nNumTRKillPoints >= mp_ggtr_bomb_pts_for_he.GetInt() ) { return WEAPON_HEGRENADE; } return 0; } bool CCSGameRules::IsIntermission( void ) const { #ifndef CLIENT_DLL return m_flIntermissionStartTime + GetIntermissionDuration() > gpGlobals->curtime; #endif return false; } int CCSGameRules::GetMaxSpectatorSlots( void ) const { return m_iSpectatorSlotCount; } int CCSGameRules::GetMaxPlayers() { if ( sv_competitive_official_5v5.GetInt() ) return 10; #ifdef CLIENT_DLL if ( engine->IsPlayingDemo() || !engine->IsConnected() ) { return 0; } #endif return MIN( g_pGameTypes->GetCurrentServerNumSlots(), 24 ); } void CCSGameRules::CoopResetRoundStartTime( void ) { m_fRoundStartTime.GetForModify() = gpGlobals->curtime; #ifdef GAME_DLL m_coopPlayersInDeploymentZone = false; if ( IsPlayingCoopMission() ) { // Reset certain player stats when this happens FOR_EACH_MAP( m_mapQueuedMatchmakingPlayersData, idxQueuedPlayer ) { CQMMPlayerData_t &qmm = *m_mapQueuedMatchmakingPlayersData.Element( idxQueuedPlayer ); qmm.m_numHealthPointsRemovedTotal = 0; qmm.m_numHealthPointsDealtTotal = 0; qmm.m_numShotsFiredTotal = 0; qmm.m_numShotsOnTargetTotal = 0; } // now the actual round is starting, adjust the duration here m_iRoundTime.GetForModify() = int( mp_roundtime.GetFloat() * 60 ); } // we also want to refresh the difficulty of the bots here CTeam *pTeam = GetGlobalTeam( TEAM_TERRORIST ); CUtlVector < CCSBot* > vecBotsOnTeam; if ( pTeam->GetBotMembers( &vecBotsOnTeam ) ) { FOR_EACH_VEC( vecBotsOnTeam, i ) { vecBotsOnTeam[i]->CoopInitialize(); } } #endif } void CCSGameRules::CoopGiveC4sToCTs( int nC4sToGive ) { #ifdef GAME_DLL CTeam *pTeam = GetGlobalTeam( TEAM_CT ); CUtlVector < CCSPlayer* > vecPlayers; int nC4s = nC4sToGive; if ( pTeam->GetHumanMembers( &vecPlayers ) ) { FOR_EACH_VEC( vecPlayers, i ) { if ( vecPlayers[i]->Weapon_OwnsThisType( WEAPON_C4_CLASSNAME ) ) nC4s--; } // don't give more C4s than players if ( nC4s == 0 ) return; FOR_EACH_VEC( vecPlayers, i ) { if ( vecPlayers[i]->IsAlive() && !vecPlayers[i]->Weapon_OwnsThisType( WEAPON_C4_CLASSNAME ) ) vecPlayers[i]->GiveNamedItem( WEAPON_C4_CLASSNAME ); } } #endif } int CCSGameRules::GetNumWinsToClinch() const { int iNumWinsToClinch = (mp_maxrounds.GetInt() > 0 && mp_match_can_clinch.GetBool()) ? ( mp_maxrounds.GetInt() / 2 ) + 1 + GetOvertimePlaying() * ( mp_overtime_maxrounds.GetInt() / 2 ) : -1; return iNumWinsToClinch; } bool CCSGameRules::IsLastRoundOfMatch() const { bool bLastRound = mp_maxrounds.GetInt() > 0 ? ( GetTotalRoundsPlayed() == ( mp_maxrounds.GetInt()-1 + GetOvertimePlaying()*mp_overtime_maxrounds.GetInt() ) ) : false; return bLastRound; } bool CCSGameRules::IsMatchPoint() const { int iNumWinsToClinch = GetNumWinsToClinch(); bool bMatchPoint = false; #ifdef CLIENT_DLL if ( GetGamePhase() != GAMEPHASE_PLAYING_FIRST_HALF ) { C_Team *pTerrorists = GetGlobalTeam( TEAM_TERRORIST ); C_Team *pCTs = GetGlobalTeam( TEAM_CT ); bMatchPoint = ( pCTs && ( pCTs->Get_Score() == iNumWinsToClinch-1 ) ) || ( pTerrorists && ( pTerrorists->Get_Score() == iNumWinsToClinch-1 ) ); } #else if ( m_match.GetPhase() != GAMEPHASE_PLAYING_FIRST_HALF ) { bMatchPoint = ( m_match.GetCTScore() == iNumWinsToClinch-1 || m_match.GetTerroristScore() == iNumWinsToClinch-1); } #endif return bMatchPoint; } // AreTeamsPlayingSwitchedSides() -- will return true when match is in second half, or in the half of overtime period where teams are switched. // Overtime logic is as follows: TeamA plays CTs as first half of regulation, then Ts as second half of regulation, // then if tied in regulation continues to play Ts as first half of 1st overtime, then switches to CTs for second half of 1st overtime, // then if still tied after 1st OT they continue to play CTs as first half of 2nd overtime, then switch to Ts for second half of 2nd overtime, // then if still tied after 2nd OT they continue to play Ts as first half of 3rd overtime, then switch to CTs for second half of 3rd overtime, // and so on until the match determines a winner. // So AreTeamsPlayingSwitchedSides will return true when TeamA is playing T-side and will return false when TeamA plays CT-side as they started match on CT // in scenario outlined above. bool CCSGameRules::AreTeamsPlayingSwitchedSides() const { if ( !GetOvertimePlaying() ) { switch ( GetGamePhase() ) { case GAMEPHASE_PLAYING_SECOND_HALF: return true; case GAMEPHASE_MATCH_ENDED: return HasHalfTime() && ( GetTotalRoundsPlayed() > ( mp_maxrounds.GetInt() / 2 ) ); default: return false; } } else { switch ( GetGamePhase() ) { case GAMEPHASE_PLAYING_SECOND_HALF: // Playing 2nd half of 2nd half of every even OT, e.g. second OT, will result in switched teams return ( GetOvertimePlaying() % 2 ) ? false : true; case GAMEPHASE_MATCH_ENDED: { bool bEndedInSecondHalfOfOvertime = HasHalfTime() && ( GetTotalRoundsPlayed() > mp_maxrounds.GetInt() + ( 2*GetOvertimePlaying() - 1 ) * ( mp_overtime_maxrounds.GetInt() / 2 ) ); if ( GetOvertimePlaying() % 2 ) bEndedInSecondHalfOfOvertime = !bEndedInSecondHalfOfOvertime; return bEndedInSecondHalfOfOvertime; } case GAMEPHASE_HALFTIME: { // halftime can also be at the end of regulation or at the end of both OT halves, in this case the overtime number has // already been incremented into the next overtime bool bSecondHalfOfOvertime = HasHalfTime() && ( GetTotalRoundsPlayed() <= ( mp_maxrounds.GetInt() + ( GetOvertimePlaying() - 1 )*mp_overtime_maxrounds.GetInt() ) ); int nOvertimeInWhichHalftimeIsActuallyReached = GetOvertimePlaying(); if ( bSecondHalfOfOvertime ) -- nOvertimeInWhichHalftimeIsActuallyReached; // this is the case when we already advanced the OT index and wait in intermission if ( nOvertimeInWhichHalftimeIsActuallyReached % 2 ) bSecondHalfOfOvertime = !bSecondHalfOfOvertime; return bSecondHalfOfOvertime; } break; default: // Playing 1st half, opposite of GAMEPHASE_PLAYING_SECOND_HALF state coded above return ( GetOvertimePlaying() % 2 ) ? true : false; } } } // music selection #ifdef CLIENT_DLL const char *musicTypeStrings[] = { "NONE", "Music.StartRound_GG", "Music.StartRound", "Music.StartAction", "Music.DeathCam", "Music.BombPlanted", "Music.BombTenSecCount", "Music.TenSecCount", "Music.WonRound", "Music.LostRound", "Music.GotHostage", "Music.MVPAnthem", "Music.Selection", "Musix.HalfTime", }; void PlayMusicSelection( IRecipientFilter& filter, CsMusicType_t nMusicType , int nPlayerEntIndex /* = 0*/ , float flPreElapsedTime /*= 0.0*/ ) { ////////////////////////////////////////////////////////////////////////////////////////// // test for between rounds and block incoming events until in round // static bool bBetweenRound = false; if( nMusicType == CSMUSIC_LOSTROUND || nMusicType == CSMUSIC_WONROUND || nMusicType == CSMUSIC_MVP || nMusicType == CSMUSIC_HALFTIME ) { bBetweenRound = true; } else if( nMusicType == CSMUSIC_START || nMusicType == CSMUSIC_ACTION || nMusicType == CSMUSIC_STARTGG ) { bBetweenRound = false; } if( bBetweenRound && ( nMusicType == CSMUSIC_BOMB || nMusicType == CSMUSIC_BOMBTEN || nMusicType == CSMUSIC_ROUNDTEN )) { return; } const char *pEntry = musicTypeStrings[ nMusicType ]; //////////////////////////////////////////////////////////////////////////////////////////// // this is to be used in the case that no music pack is equipped, mimicing original csgo behavior // music selection switches on team select screen and halftime static int nUpdatedMusic = snd_music_selection.GetInt(); // hack cause halftime music is getting called twice for some reason if( nMusicType == CSMUSIC_SELECTION || nMusicType == CSMUSIC_HALFTIME ) { nUpdatedMusic = (nUpdatedMusic == 2 ) ? 1 : 2; } if( nMusicType == CSMUSIC_SELECTION || nMusicType == CSMUSIC_START ) { snd_music_selection.SetValue( nUpdatedMusic ); } char pMusicExtensionBuf[64]; V_sprintf_safe( pMusicExtensionBuf, "%s_%02i", "valve_csgo", snd_music_selection.GetInt() ); const char *pMusicExtension = pMusicExtensionBuf; ///////////////////////////////////////////////////////////////////////// // no halftime music in overwatch if ( CDemoPlaybackParameters_t const *pParameters = engine->GetDemoPlaybackParameters() ) { if(pParameters->m_bAnonymousPlayerIdentity && nMusicType == CSMUSIC_HALFTIME ) return; // or whatever is appropriate to not play the halftime music } ///////////////////////////////////////////////////////////////////////// // is there delay from switching spectated player? // ********* not currently used ****** // engine->SOSSetOpvarFloat( "spectateMusicDelay", flPreElapsedTime ); // if( nMusicType == CSMUSIC_START ) // { // engine->SOSSetOpvarFloat("csgo_roundstart_time", gpGlobals->curtime ); // } /////////////////////////////////////////////////////////////////////////////// // music source priority: // passed in arg (if has music pack)> borrowed music > own music int nPlayerIndex = 0; // use a specified playerindex if passed in ( MVP ) if( ( nPlayerEntIndex != 0 ) && g_PR ) { if ( g_PR->IsConnected( nPlayerEntIndex ) ) { if ( C_CS_PlayerResource *cs_PR = dynamic_cast< C_CS_PlayerResource * >( g_PR ) ) { uint32 unMusicID = cs_PR->GetMusicID( nPlayerEntIndex ); if( unMusicID > 1 ) nPlayerIndex = nPlayerEntIndex; } } } // if the passed in player didn't give us a music id, check for a borrowed kit. if ( !nPlayerIndex ) { int nBorrowPlIndex = cl_borrow_music_from_player_index.GetInt(); if ( nBorrowPlIndex ) { if ( g_PR->IsConnected( nBorrowPlIndex ) && !g_PR->IsFakePlayer( nBorrowPlIndex ) ) { nPlayerIndex = nBorrowPlIndex; } else { cl_borrow_music_from_player_index.SetValue( cl_borrow_music_from_player_index.GetDefault() ); } } } // otherwise use our own if ( !nPlayerIndex ) nPlayerIndex = GetLocalPlayerIndex(); { uint32 unMusicID = 0; if ( C_CS_PlayerResource *cs_PR = dynamic_cast< C_CS_PlayerResource * >( g_PR ) ) { unMusicID = cs_PR->GetMusicID( nPlayerIndex ); } if( unMusicID > 1 ) { const CEconMusicDefinition *pMusicDef = GetItemSchema()->GetMusicDefinition(unMusicID); if(pMusicDef) pMusicExtension = pMusicDef->GetName(); } } if( pEntry ) { char musicSelection[128]; int nExtLen = V_strlen( pMusicExtension ); int nStrLen = V_strlen( pEntry ); V_snprintf( musicSelection, nExtLen + nStrLen+2, "%s.%s", pEntry, pMusicExtension ); C_BaseEntity::EmitSound( filter, -1, musicSelection ); } } void PlayCustomDeathCamSelection( IRecipientFilter& filter, int nEntIndex, const char *pSoundEntry, int nDeathCamIndex ) { char musicSelection[128]; V_snprintf( musicSelection, 128, "%s_%03i", pSoundEntry, nDeathCamIndex ); C_BaseEntity::EmitSound( filter, nEntIndex, musicSelection ); } #endif float CCSGameRules::CheckTotalSmokedLength( float flSmokeRadiusSq, Vector vecGrenadePos, Vector from, Vector to ) { Vector sightDir = to - from; float sightLength = sightDir.NormalizeInPlace(); // the detonation position is the actual position of the smoke grenade, but the smoke volume center is actually some number of units above that Vector vecSmokeCenterOffset = Vector( 0, 0, 60 ); const Vector &smokeOrigin = vecGrenadePos + vecSmokeCenterOffset; float flSmokeRadius = sqrt(flSmokeRadiusSq); // if the start point or the end point is inside the radius of the smoke, then the line goes through the smoke if ( (smokeOrigin - from).IsLengthLessThan( flSmokeRadius*0.95f ) || (smokeOrigin - to).IsLengthLessThan( flSmokeRadius ) ) return -1; Vector toGrenade = smokeOrigin - from; float alongDist = DotProduct( toGrenade, sightDir ); // compute closest point to grenade along line of sight ray Vector close; // constrain closest point to line segment if (alongDist < 0.0f) close = from; else if (alongDist >= sightLength) close = to; else close = from + sightDir * alongDist; // if closest point is within smoke radius, the line overlaps the smoke cloud Vector toClose = close - smokeOrigin; float lengthSq = toClose.LengthSqr(); //float smokeRadius = (float)sqrt( flSmokeRadiusSq ); //NDebugOverlay::Sphere( smokeOrigin, smokeRadius, 0, 255, 0, true, 2.0f); if (lengthSq < flSmokeRadiusSq) { // some portion of the ray intersects the cloud // 'from' and 'to' lie outside of the cloud - the line of sight completely crosses it // determine the length of the chord that crosses the cloud float smokedLength = 2.0f * (float)sqrt( flSmokeRadiusSq - lengthSq ); return smokedLength; } return 0; } #if defined( CLIENT_DLL ) //----------------------------------------------------------------------------- // Enforce certain values on the specified convar. //----------------------------------------------------------------------------- void EnforceCompetitiveCVar( const char *szCvarName, float fMinValue, float fMaxValue = FLT_MAX, int iArgs = 0, ... ) { // Doing this check first because OK values might be outside the min/max range ConVarRef competitiveConvar(szCvarName); float fValue = competitiveConvar.GetFloat(); va_list vl; va_start(vl, iArgs); for( int i=0; i< iArgs; ++i ) { if( (int)fValue == va_arg(vl,int) ) return; } va_end(vl); if ( fValue < fMinValue || fValue > fMaxValue ) { float fNewValue = MAX( MIN( fValue, fMaxValue ), fMinValue ); competitiveConvar.SetValue( fNewValue ); DevMsg( "Convar %s was out of range and forced to %.2f. Valid values are between %.2f and %.2f. To remove the restriction set sv_competitive_minspec 0 on the server.\n", szCvarName, fNewValue, fMinValue, fMaxValue ); } else { competitiveConvar.SetValue( fValue ); } } //----------------------------------------------------------------------------- // An interface used by ENABLE_COMPETITIVE_CONVAR macro that lets the classes // defined in the macro to be stored and acted on. //----------------------------------------------------------------------------- class ICompetitiveConvar { public: virtual void BackupConvar() = 0; virtual void EnforceRestrictions() = 0; virtual void RestoreOriginalValue() = 0; virtual void InstallChangeCallback() = 0; }; //----------------------------------------------------------------------------- // A manager for all enforced competitive convars. //----------------------------------------------------------------------------- class CCompetitiveCvarManager : public CAutoGameSystem { public: typedef CUtlVector CompetitiveConvarList_t; static void AddConvarToList( ICompetitiveConvar* pCVar ) { GetConvarList()->AddToTail( pCVar ); } static void BackupAllConvars() { FOR_EACH_VEC( *GetConvarList(), i ) { (*GetConvarList())[i]->BackupConvar(); } } static void EnforceRestrictionsOnAllConvars() { FOR_EACH_VEC( *GetConvarList(), i ) { (*GetConvarList())[i]->EnforceRestrictions(); } } static void RestoreAllOriginalValues() { FOR_EACH_VEC( *GetConvarList(), i ) { (*GetConvarList())[i]->RestoreOriginalValue(); } } static CompetitiveConvarList_t* GetConvarList() { if( !s_pCompetitiveConvars ) { s_pCompetitiveConvars = new CompetitiveConvarList_t(); } return s_pCompetitiveConvars; } static KeyValues* GetConVarBackupKV() { if( !s_pConVarBackups ) { s_pConVarBackups = new KeyValues("ConVarBackups"); } return s_pConVarBackups; } virtual bool Init() { FOR_EACH_VEC( *GetConvarList(), i ) { (*GetConvarList())[i]->InstallChangeCallback(); } return true; } virtual void Shutdown() { FOR_EACH_VEC( *GetConvarList(), i ) { delete (*GetConvarList())[i]; } delete s_pCompetitiveConvars; s_pCompetitiveConvars = 0; s_pConVarBackups->deleteThis(); s_pConVarBackups = 0; } CCompetitiveCvarManager() { s_pCompetitiveConvars = 0; s_pConVarBackups = 0; } private: static CompetitiveConvarList_t* s_pCompetitiveConvars; static KeyValues* s_pConVarBackups; }; static CCompetitiveCvarManager *s_pCompetitiveCvarManager = new CCompetitiveCvarManager(); CCompetitiveCvarManager::CompetitiveConvarList_t* CCompetitiveCvarManager::s_pCompetitiveConvars = 0; KeyValues* CCompetitiveCvarManager::s_pConVarBackups = 0; //----------------------------------------------------------------------------- // Macro to define restrictions on convars with "sv_competitive_minspec 1" // Usage: ENABLE_COMPETITIVE_CONVAR( convarName, minValue, maxValue, optionalValues, opVal1, opVal2, ... //----------------------------------------------------------------------------- #define ENABLE_COMPETITIVE_CONVAR( convarName, ... ) \ class CCompetitiveMinspecConvar##convarName : public ICompetitiveConvar { \ public: \ CCompetitiveMinspecConvar##convarName(){ CCompetitiveCvarManager::AddConvarToList(this);} \ static void on_changed_##convarName( IConVar *var, const char *pOldValue, float flOldValue ){ \ if( sv_competitive_minspec.GetBool() ) { \ EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); }\ else {\ CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } } \ virtual void BackupConvar() { CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } \ virtual void EnforceRestrictions() { EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); } \ virtual void RestoreOriginalValue() { ConVarRef(#convarName).SetValue(CCompetitiveCvarManager::GetConVarBackupKV()->GetFloat( #convarName ) ); } \ virtual void InstallChangeCallback() { static_cast(ConVarRef( #convarName ).GetLinkedConVar())->InstallChangeCallback( CCompetitiveMinspecConvar##convarName::on_changed_##convarName); } \ }; \ static CCompetitiveMinspecConvar##convarName *s_pCompetitiveConvar##convarName = new CCompetitiveMinspecConvar##convarName(); //----------------------------------------------------------------------------- // Callback function for sv_competitive_minspec convar value change. //----------------------------------------------------------------------------- void sv_competitive_minspec_changed_f( IConVar *var, const char *pOldValue, float flOldValue ) { ConVar *pCvar = static_cast(var); if( pCvar->GetBool() == true && flOldValue == 0.0f ) { // Backup the values of each cvar and enforce new ones CCompetitiveCvarManager::BackupAllConvars(); CCompetitiveCvarManager::EnforceRestrictionsOnAllConvars(); } else if( pCvar->GetBool() == false && flOldValue != 0.0f ) { // If sv_competitive_minspec is disabled, restore old client values CCompetitiveCvarManager::RestoreAllOriginalValues(); } } #endif static ConVar sv_competitive_minspec( "sv_competitive_minspec", "1", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "Enable to force certain client convars to minimum/maximum values to help prevent competitive advantages." #ifdef CLIENT_DLL ,sv_competitive_minspec_changed_f #endif ); #ifdef CLIENT_DLL #if defined( _GAMECONSOLE ) // ENABLE_COMPETITIVE_CONVAR( convar, range minimum, range maximum, number of additional distinct valid values, distinct valid values... ); ENABLE_COMPETITIVE_CONVAR( fps_max, 29, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works #else ENABLE_COMPETITIVE_CONVAR( fps_max, 59, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works #endif ENABLE_COMPETITIVE_CONVAR( cl_interp_ratio, 1, 2 ); // force cl_interp_ratio from 1 to 2 ENABLE_COMPETITIVE_CONVAR( cl_interp, 0, 0.031 ); // force cl_interp from 0.0152 to 0.031 ENABLE_COMPETITIVE_CONVAR( cl_updaterate, 10, 150 ); // force cl_updaterate from 10 to 150 ENABLE_COMPETITIVE_CONVAR( cl_cmdrate, 10, 150 ); // force cl_cmdrate from 10 to 150 ENABLE_COMPETITIVE_CONVAR( rate, 20480, 786432 ); // force rate above min rate and below max rate ENABLE_COMPETITIVE_CONVAR( viewmodel_fov, 54, 68 ); // force viewmodel fov to be between 54 and 68 ENABLE_COMPETITIVE_CONVAR( viewmodel_offset_x, -2, 2.5 ); // restrict viewmodel positioning ENABLE_COMPETITIVE_CONVAR( viewmodel_offset_y, -2, 2 ); ENABLE_COMPETITIVE_CONVAR( viewmodel_offset_z, -2, 2 ); ENABLE_COMPETITIVE_CONVAR( cl_bobcycle, 0.98, 0.98 ); // tournament standard // replaced with sv_max_allowed_net_graph convar //ENABLE_COMPETITIVE_CONVAR( net_graph, 0, 1 ) // tournament standard #endif #ifdef GAME_DLL CCSPlayer* FindPlayerFromAccountID( uint32 account_id ) { for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pBasePlayer = UTIL_PlayerByIndex( i ); if ( !pBasePlayer ) continue; CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( pBasePlayer ); if ( !pPlayer || pPlayer->IsBot() || !pPlayer->IsConnected() ) continue; CSteamID steamID; pPlayer->GetSteamID( &steamID ); if ( steamID.GetAccountID() == account_id ) return pPlayer; } return NULL; } #endif #ifdef GAME_DLL class ClientJob_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty : public GCSDK::CGCClientJob { public: ClientJob_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { } virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg msg( pNetPacket ); DevMsg( "Notification about user penalty: %u/%u (%u sec)\n", msg.Body().account_id(), msg.Body().reason(), msg.Body().seconds() ); if ( !engine->IsDedicatedServer() || !msg.Body().account_id() ) return true; if ( !CCSGameRules::sm_mapGcBanInformation.Count() ) SetDefLessFunc( CCSGameRules::sm_mapGcBanInformation ); { CCSGameRules::CGcBanInformation_t baninfo = { msg.Body().reason(), Plat_FloatTime() + msg.Body().seconds() }; CCSGameRules::sm_mapGcBanInformation.InsertOrReplace( msg.Body().account_id(), baninfo ); } return true; } }; GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty, k_EMsgGCCStrike15_v2_ServerNotificationForUserPenalty ); class ClientJob_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification : public GCSDK::CGCClientJob { public: ClientJob_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { } virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg msg( pNetPacket ); if ( !msg.Body().has_iteminfo() ) return true; DevMsg( "Notification about user drop: %u %llu (%u-%u-%u)\n", msg.Body().iteminfo().accountid(), msg.Body().iteminfo().itemid(), msg.Body().iteminfo().defindex(), msg.Body().iteminfo().paintindex(), msg.Body().iteminfo().rarity() ); if ( msg.Body().iteminfo().accountid() && msg.Body().iteminfo().itemid() && CSGameRules() ) { CSGameRules()->RecordPlayerItemDrop( msg.Body().iteminfo() ); } return true; } }; GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification, k_EMsgGCCStrike15_v2_MatchEndRewardDropsNotification ); static CMsgGCCStrike15_v2_GiftsLeaderboardResponse g_dataGiftsLeaderboard; static double g_dblGiftsLeaderboardReceived = 0; void CCSGameRules::CheckForGiftsLeaderboardUpdate() { } class ClientJob_EMsgGCCStrike15_v2_GiftsLeaderboardResponse : public GCSDK::CGCClientJob { public: ClientJob_EMsgGCCStrike15_v2_GiftsLeaderboardResponse( GCSDK::CGCClient *pGCClient ) : GCSDK::CGCClientJob( pGCClient ) { } virtual bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket ) { GCSDK::CProtoBufMsg msg( pNetPacket ); if ( !msg.Body().has_servertime() ) return true; // Set our cached structure g_dataGiftsLeaderboard = msg.Body(); g_dblGiftsLeaderboardReceived = Plat_FloatTime(); if ( CCSGameRules *pCSGR = CSGameRules() ) { // Copy gifts pCSGR->m_numGlobalGiftsGiven = g_dataGiftsLeaderboard.total_gifts_given(); pCSGR->m_numGlobalGifters = g_dataGiftsLeaderboard.total_givers(); pCSGR->m_numGlobalGiftsPeriodSeconds = g_dataGiftsLeaderboard.time_period_seconds(); for ( int j = 0; j < MAX_GIFT_GIVERS_FEATURED_COUNT; ++ j ) { pCSGR->m_arrFeaturedGiftersAccounts.Set( j, ( j < g_dataGiftsLeaderboard.entries().size() ) ? g_dataGiftsLeaderboard.entries( j ).accountid() : 0 ); pCSGR->m_arrFeaturedGiftersGifts.Set( j, ( j < g_dataGiftsLeaderboard.entries().size() ) ? g_dataGiftsLeaderboard.entries( j ).gifts() : 0 ); } } return true; } }; GC_REG_CLIENT_JOB( ClientJob_EMsgGCCStrike15_v2_GiftsLeaderboardResponse, k_EMsgGCCStrike15_v2_GiftsLeaderboardResponse ); #endif // GAME_DLL #ifndef CLIENT_DLL bool CCSGameRules::OnReplayPrompt( CBasePlayer *pVictim, CBasePlayer *pScorer ) { if ( m_iRoundWinStatus != WINNER_NONE ) { // victim killed after the end of round: do not replay return false; } return CTeamplayRules::OnReplayPrompt( pVictim, pScorer ); // delegate to the base class } #endif