|
|
//========= 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 <deque>
#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<int, int > 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<ConVar*>(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; ii<MAX_AMMO_TYPES; ++ii ) { GetCSAmmoDef()->m_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<CTeam*>(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<CCSPlayer*>( 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<CCSPlayer *>( pPlayer ); if ( pCSPlayer ) pCSPlayer->SetRadarHidden( bHide ); } }
void CCSGameRules::TrainingSetMiniScoreHidden( bool bHide ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(1);
if ( pPlayer ) { CCSPlayer *pCSPlayer = static_cast<CCSPlayer *>( 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<CGamePlayerEquip*>( 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<int>(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<CPlantedC4*>( 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<attrib_value_t>( 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<CWeaponCSBase *>( 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<CWeaponCSBase *>( 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;i<iNumClasses;i++) { for (j=0;j<iNumClasses;j++) { // By default all relationships are neutral of priority zero
CBaseCombatCharacter::SetDefaultRelationship( (Class_T)i, (Class_T)j, D_NU, 0 ); } } }
//------------------------------------------------------------------------------
// Purpose : Return classify text for classify type
//------------------------------------------------------------------------------
const char *CCSGameRules::AIClassText(int classType) { switch (classType) { case CLASS_NONE: return "CLASS_NONE"; case CLASS_PLAYER: return "CLASS_PLAYER"; default: return "MISSING CLASS in ClassifyText()"; } }
//-----------------------------------------------------------------------------
// Purpose: When gaining new technologies in TF, prevent auto switching if we
// receive a weapon during the switch
// Input : *pPlayer -
// *pWeapon -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CCSGameRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon ) { bool bIsBeingGivenItem = false; CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer ); if ( pCSPlayer && pCSPlayer->IsBeingGivenItem() ) 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; i<iNumHostages; i++ ) { hostage = g_Hostages[i];
if ( hostage->m_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<CCSPlayer*> 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<CCSPlayer*> 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<CCSGameRules*>( 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<CCSGameRules*>( 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<CCSGameRules*>( 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<CCSGameRules*>( 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<CCSPlayer *> 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<CCSPlayer *> pListPlayers; CUtlVector<CCSPlayer *> 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;i<g_Hostages.Count();i++ ) g_Hostages[i]->AcceptInput( "CTsWin", NULL, NULL, emptyVariant, 0 ); }
else if ( iWinnerTeam == WINNER_TER ) { for( int i=0;i<g_Hostages.Count();i++ ) g_Hostages[i]->AcceptInput( "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<C_CS_PlayerResource *>( 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<CCSPlayer*>( 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<CCSPlayer*>( 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<nNumSMGs; i++ ) { int nPick = RandomInt( 0, pSMGs.Count()-1 ); pWeaponProgression.AddToTail(pSMGs[nPick]); pSMGs.FastRemove( nPick ); } for ( int i=0; i<nNumRifles; i++ ) { int nPick = RandomInt( 0, pRifles.Count()-1 ); pWeaponProgression.AddToTail(pRifles[nPick]); pRifles.FastRemove( nPick ); } for ( int i=0; i<nNumShotguns; i++ ) { int nPick = RandomInt( 0, pShotguns.Count()-1 ); pWeaponProgression.AddToTail(pShotguns[nPick]); pShotguns.FastRemove( nPick ); } for ( int i=0; i<nNumSnipers; i++ ) { int nPick = RandomInt( 0, pSnipers.Count()-1 ); pWeaponProgression.AddToTail(pSnipers[nPick]); pSnipers.FastRemove( nPick ); } for ( int i=0; i<nNumMGs; i++ ) { int nPick = RandomInt( 0, pMGs.Count()-1 ); pWeaponProgression.AddToTail(pMGs[nPick]); pMGs.FastRemove( nPick ); } for ( int i=0; i<nNumPistols; i++ ) { int nPick = RandomInt( 0, pPistols.Count()-1 ); pWeaponProgression.AddToTail(pPistols[nPick]); pPistols.FastRemove( nPick ); }
// go through the list we build and add them to the final list that will get used in the game
FOR_EACH_VEC( pWeaponProgression, iWeaponProgression ) { const GGWeaponAliasName &wp = (pWeaponProgression)[iWeaponProgression]; AddGunGameWeapon( wp.aliasName, nKillsNeeded, TEAM_CT ); AddGunGameWeapon( wp.aliasName, nKillsNeeded, TEAM_TERRORIST ); }
// add the knife manually
AddGunGameWeapon( "knifegg", 1, TEAM_CT ); AddGunGameWeapon( "knifegg", 1, TEAM_TERRORIST ); } // Add the gun game weapons.
else if ( IsPlayingGunGameTRBomb() || IsPlayingGunGameProgressive() ) { const CUtlVector< IGameTypes::WeaponProgression > *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<ICompetitiveConvar*> 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<ConVar*>(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<ConVar*>(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<CMsgGCCStrike15_v2_ServerNotificationForUserPenalty> 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<CMsgGCCStrike15_v2_MatchEndRewardDropsNotification> 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<CMsgGCCStrike15_v2_GiftsLeaderboardResponse> 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
|