|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The TF Game rules
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "cs_gamerules.h"
#include "cs_ammodef.h"
#include "weapon_csbase.h"
#include "cs_shareddefs.h"
#include "KeyValues.h"
#include "cs_achievement_constants.h"
#include "fmtstr.h"
#ifdef CLIENT_DLL
#include "networkstringtable_clientdll.h"
#include "utlvector.h"
#else
#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 "player_resource.h"
#include "cs_player_resource.h"
#if defined( REPLAY_ENABLED )
#include "replay/ireplaysystem.h"
#include "replay/iserverreplaycontext.h"
#include "replay/ireplaysessionrecorder.h"
#endif // REPLAY_ENABLED
#endif
#include "cs_blackmarket.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
extern IUploadGameStats *gamestatsuploader;
#if defined( REPLAY_ENABLED )
extern IReplaySystem *g_pReplay; #endif // REPLAY_ENABLED
#endif
/**
* 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, 62 ), // hull max
Vector(-16, -16, 0 ), // duck hull min
Vector( 16, 16, 45 ), // duck hull max
Vector( 0, 0, 47 ), // 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
LINK_ENTITY_TO_CLASS(info_player_terrorist, CPointEntity); LINK_ENTITY_TO_CLASS(info_player_counterterrorist,CPointEntity); LINK_ENTITY_TO_CLASS(info_player_logo,CPointEntity); #endif
REGISTER_GAMERULES_CLASS( CCSGameRules );
BEGIN_NETWORK_TABLE_NOBASE( CCSGameRules, DT_CSGameRules ) #ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bFreezePeriod ) ), RecvPropInt( RECVINFO( m_iRoundTime ) ), RecvPropFloat( RECVINFO( m_fRoundStartTime ) ), RecvPropFloat( RECVINFO( m_flGameStartTime ) ), RecvPropInt( RECVINFO( m_iHostagesRemaining ) ), RecvPropBool( RECVINFO( m_bMapHasBombTarget ) ), RecvPropBool( RECVINFO( m_bMapHasRescueZone ) ), RecvPropBool( RECVINFO( m_bLogoMap ) ), RecvPropBool( RECVINFO( m_bBlackMarket ) ) #else
SendPropBool( SENDINFO( m_bFreezePeriod ) ), SendPropInt( SENDINFO( m_iRoundTime ), 16 ), SendPropFloat( SENDINFO( m_fRoundStartTime ), 32, SPROP_NOSCALE ), SendPropFloat( SENDINFO( m_flGameStartTime ), 32, SPROP_NOSCALE ), SendPropInt( SENDINFO( m_iHostagesRemaining ), 4 ), SendPropBool( SENDINFO( m_bMapHasBombTarget ) ), SendPropBool( SENDINFO( m_bMapHasRescueZone ) ), SendPropBool( SENDINFO( m_bLogoMap ) ), SendPropBool( SENDINFO( m_bBlackMarket ) ) #endif
END_NETWORK_TABLE()
LINK_ENTITY_TO_CLASS( cs_gamerules, CCSGameRulesProxy ); IMPLEMENT_NETWORKCLASS_ALIASED( CSGameRulesProxy, DT_CSGameRulesProxy )
#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
ConVar ammo_50AE_max( "ammo_50AE_max", "35", FCVAR_REPLICATED ); ConVar ammo_762mm_max( "ammo_762mm_max", "90", FCVAR_REPLICATED ); ConVar ammo_556mm_max( "ammo_556mm_max", "90", FCVAR_REPLICATED ); ConVar ammo_556mm_box_max( "ammo_556mm_box_max", "200", FCVAR_REPLICATED ); ConVar ammo_338mag_max( "ammo_338mag_max", "30", FCVAR_REPLICATED ); ConVar ammo_9mm_max( "ammo_9mm_max", "120", FCVAR_REPLICATED ); ConVar ammo_buckshot_max( "ammo_buckshot_max", "32", FCVAR_REPLICATED ); ConVar ammo_45acp_max( "ammo_45acp_max", "100", FCVAR_REPLICATED ); ConVar ammo_357sig_max( "ammo_357sig_max", "52", FCVAR_REPLICATED ); ConVar ammo_57mm_max( "ammo_57mm_max", "100", FCVAR_REPLICATED ); ConVar ammo_hegrenade_max( "ammo_hegrenade_max", "1", FCVAR_REPLICATED ); ConVar ammo_flashbang_max( "ammo_flashbang_max", "2", FCVAR_REPLICATED ); ConVar ammo_smokegrenade_max( "ammo_smokegrenade_max", "1", FCVAR_REPLICATED );
//ConVar mp_dynamicpricing( "mp_dynamicpricing", "0", FCVAR_REPLICATED, "Enables or Disables the dynamic weapon prices" );
extern ConVar sv_stopspeed;
ConVar mp_buytime( "mp_buytime", "1.5", FCVAR_REPLICATED, "How many minutes after round start players can buy items for.", true, 0.25, false, 0 );
ConVar mp_playerid( "mp_playerid", "0", FCVAR_REPLICATED, "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.5", FCVAR_REPLICATED, "Number of seconds to delay showing information in the status bar", true, 0, true, 1 );
ConVar mp_playerid_hold( "mp_playerid_hold", "0.25", FCVAR_REPLICATED, "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", "5.0", FCVAR_REPLICATED, "Number of seconds to delay before restarting a round after a win", true, 0.0f, true, 10.0f );
ConVar sv_allowminmodels( "sv_allowminmodels", "1", FCVAR_REPLICATED | FCVAR_NOTIFY, "Allow or disallow the use of cl_minmodels on this server." );
#ifdef CLIENT_DLL
ConVar cl_autowepswitch( "cl_autowepswitch", "1", FCVAR_ARCHIVE | FCVAR_USERINFO, "Automatically switch to picked up weapons (if more powerful)" );
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 1100 // 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", "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_map_parameters", "keyframe_rope", "move_rope", "info_ladder", "player", "point_viewcontrol", "scene_manager", "shadow_control", "sky_camera", "soundent", "trigger_soundscape", "viewmodel", "predicted_viewmodel", "worldspawn", "point_devshot_camera", "", // END Marker
};
// --------------------------------------------------------------------------------------------------- //
// Voice helper
// --------------------------------------------------------------------------------------------------- //
class CVoiceGameMgrHelper : public IVoiceGameMgrHelper { public: virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity ) { // Dead players can only be heard by other dead team mates
if ( pTalker->IsAlive() == false ) { if ( pListener->IsAlive() == false ) return ( pListener->InSameTeam( pTalker ) );
return false; }
return ( pListener->InSameTeam( pTalker ) ); } }; CVoiceGameMgrHelper g_VoiceGameMgrHelper; IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
// --------------------------------------------------------------------------------------------------- //
// Globals.
// --------------------------------------------------------------------------------------------------- //
// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
const char *sTeamNames[] = { "Unassigned", "Spectator", "TERRORIST", "CT" };
extern ConVar mp_maxrounds;
ConVar mp_startmoney( "mp_startmoney", "800", FCVAR_REPLICATED | FCVAR_NOTIFY, "amount of money each player gets when they reset", true, 800, true, 16000 );
ConVar mp_roundtime( "mp_roundtime", "2.5", FCVAR_REPLICATED | FCVAR_NOTIFY, "How many minutes each round takes.", true, 1, // min value
true, 9 // max value
);
ConVar mp_freezetime( "mp_freezetime", "6", FCVAR_REPLICATED | FCVAR_NOTIFY, "how many seconds to keep players frozen when the round starts", true, 0, // min value
true, 60 // max value
);
ConVar mp_c4timer( "mp_c4timer", "45", FCVAR_REPLICATED | FCVAR_NOTIFY, "how long from when the C4 is armed until it blows", true, 10, // min value
true, 90 // max value
);
ConVar mp_limitteams( "mp_limitteams", "2", FCVAR_REPLICATED | FCVAR_NOTIFY, "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, "Will a TK'er be punished in the next round? {0=no, 1=yes}" );
ConVar mp_autokick( "mp_autokick", "1", FCVAR_REPLICATED, "Kick idle/team-killing players" );
ConVar mp_spawnprotectiontime( "mp_spawnprotectiontime", "5", FCVAR_REPLICATED, "Kick players who team-kill within this many seconds of a round restart." );
ConVar mp_humanteam( "mp_humanteam", "any", FCVAR_REPLICATED, "Restricts human players to a single team {any, CT, T}" );
ConVar mp_ignore_round_win_conditions( "mp_ignore_round_win_conditions", "0", FCVAR_REPLICATED, "Ignore conditions which would end the current round");
ConCommand EndRound( "endround", &CCSGameRules::EndRound, "End the current round.", FCVAR_CHEAT );
// --------------------------------------------------------------------------------------------------- //
// 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 ) { // 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
//
Vector mins, maxs; pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs ); mins -= pMainEnt->GetAbsOrigin(); maxs -= pMainEnt->GetAbsOrigin();
// 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 ) ) { 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 ) ) { // 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_SOLID, pMainEnt, COLLISION_GROUP_NONE, &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; }
int UTIL_HumansInGame( bool ignoreSpectators ) { 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;
iCount++; } }
return iCount; }
// --------------------------------------------------------------------------------------------------- //
// CCSGameRules implementation.
// --------------------------------------------------------------------------------------------------- //
CCSGameRules::CCSGameRules() { m_iRoundTime = 0; m_iRoundWinStatus = WINNER_NONE; m_iFreezeTime = 0;
m_fRoundStartTime = 0; m_bAllowWeaponSwitch = true; m_bFreezePeriod = true; m_iNumTerrorist = m_iNumCT = 0; // number of players per team
m_flRestartRoundTime = 0.1f; // restart first round as soon as possible
m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0; m_bFirstConnected = false; m_bCompleteReset = false; m_iAccountTerrorist = m_iAccountCT = 0; m_iNumCTWins = 0; m_iNumTerroristWins = 0; m_iNumConsecutiveCTLoses = 0; m_iNumConsecutiveTerroristLoses = 0; m_bTargetBombed = false; m_bBombDefused = false; m_iTotalRoundsPlayed = -1; m_iUnBalancedRounds = 0; m_flGameStartTime = 0; m_iHostagesRemaining = 0; m_bLevelInitialized = false; m_bLogoMap = false; m_tmNextPeriodicThink = 0;
m_bMapHasBombTarget = false; m_bMapHasRescueZone = false;
m_iSpawnPointCount_Terrorist = 0; m_iSpawnPointCount_CT = 0;
m_bTCantBuy = false; m_bCTCantBuy = false; m_bMapHasBuyZone = false;
m_iLoserBonus = 0;
m_iHostagesRescued = 0; m_iHostagesTouched = 0; m_flNextHostageAnnouncement = 0.0f;
//=============================================================================
// HPE_BEGIN
// [dwenger] Reset rescue-related achievement values
//=============================================================================
// [tj] reset flawless and lossless round related flags
m_bNoTerroristsKilled = true; m_bNoCTsKilled = true; m_bNoTerroristsDamaged = true; m_bNoCTsDamaged = true; m_pFirstKill = NULL; m_firstKillTime = 0;
// [menglish] Reset fun fact values
m_pFirstBlood = NULL; m_firstBloodTime = 0;
m_bCanDonateWeapons = true;
// [dwenger] Reset rescue-related achievement values
m_pLastRescuer = NULL; m_iNumRescuers = 0;
m_hostageWasInjured = false; m_hostageWasKilled = false;
m_pFunFactManager = new CCSFunFactMgr(); m_pFunFactManager->Init();
//=============================================================================
// HPE_END
//=============================================================================
m_iHaveEscaped = 0; m_bMapHasEscapeZone = false; m_iNumEscapers = 0; m_iNumEscapeRounds = 0;
m_iMapHasVIPSafetyZone = 0; m_pVIP = NULL; m_iConsecutiveVIP = 0;
m_bMapHasBombZone = false; m_bBombDropped = false; m_bBombPlanted = false; m_pLastBombGuy = NULL;
m_bAllowWeaponSwitch = true;
m_flNextHostageAnnouncement = gpGlobals->curtime; // asap.
ReadMultiplayCvars();
m_pPrices = NULL; m_bBlackMarket = false; m_bDontUploadStats = false;
// 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 ); }
if ( filesystem->FileExists( UTIL_VarArgs( "maps/cfg/%s.cfg", STRING(gpGlobals->mapname) ) ) ) { // Execute a map specific cfg file - as in Day of Defeat
// Map names cannot contain quotes or control characters so this is safe but silly that we have to do it.
engine->ServerCommand( UTIL_VarArgs( "exec \"%s.cfg\" */maps\n", STRING(gpGlobals->mapname) ) ); engine->ServerExecute(); }
#ifndef CLIENT_DLL
// stats
if ( g_flGameStatsUpdateTime == 0.0f ) { memset( g_iWeaponPurchases, 0, sizeof( g_iWeaponPurchases) ); memset( g_iTerroristVictories, 0, sizeof( g_iTerroristVictories) ); memset( g_iCounterTVictories, 0, sizeof( g_iTerroristVictories) ); g_flGameStatsUpdateTime = CS_GAME_STATS_UPDATE; //Next update is between 22 and 24 hours.
} #endif
}
void CCSGameRules::AddPricesToTable( weeklyprice_t prices ) { int iIndex = m_StringTableBlackMarket->FindStringIndex( "blackmarket_prices" );
if ( iIndex == INVALID_STRING_INDEX ) { m_StringTableBlackMarket->AddString( CBaseEntity::IsServer(), "blackmarket_prices", sizeof( weeklyprice_t), &prices ); } else { m_StringTableBlackMarket->SetStringUserData( iIndex, sizeof( weeklyprice_t), &prices ); }
SetBlackMarketPrices( false ); }
//-----------------------------------------------------------------------------
// 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(); if( m_pFunFactManager ) { delete m_pFunFactManager; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCSGameRules::UpdateClientData( CBasePlayer *player ) { }
//-----------------------------------------------------------------------------
// 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" ) ) { if ( pPlayer->m_iNextTimeCheck < gpGlobals->curtime ) { char szNextMap[32];
if ( nextlevel.GetString() && *nextlevel.GetString() ) { Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) ); } else { GetNextLevelName( szNextMap, sizeof( szNextMap ) ); }
ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szNextMap);
pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1; } return true; } else if( 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::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues ) { CCSPlayer *pPlayer = dynamic_cast< CCSPlayer * >( CBaseEntity::Instance( pEntity ) ); if ( pPlayer ) { char const *pszCommand = pKeyValues->GetName(); if ( pszCommand && pszCommand[0] ) { if ( FStrEq( pszCommand, "ClanTagChanged" ) ) { pPlayer->SetClanTag( pKeyValues->GetString( "tag", "" ) );
const char *teamName = "UNKNOWN"; if ( pPlayer->GetTeam() ) { teamName = pPlayer->GetTeam()->GetName(); } UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"clantag\" (value \"%s\")\n", pPlayer->GetPlayerName(), pPlayer->GetUserID(), pPlayer->GetNetworkIDString(), teamName, pKeyValues->GetString( "tag", "unknown" ) ); } } }
BaseClass::ClientCommandKeyValues( pEntity, pKeyValues ); }
//-----------------------------------------------------------------------------
// 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;
pPlayer->EquipSuit(); bool addDefault = true;
CBaseEntity *pWeaponEntity = NULL; while ( ( pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL ) { if ( addDefault ) { // remove all our weapons and armor before touching the first game_player_equip
pPlayer->RemoveAllItems( true ); } pWeaponEntity->Touch( pPlayer ); addDefault = false; }
if ( addDefault || pPlayer->m_bIsVIP ) pPlayer->GiveDefaultItems(); }
void CCSGameRules::BroadcastSound( const char *sound, int team ) { CBroadcastRecipientFilter filter; filter.MakeReliable();
if( team != -1 ) { filter.RemoveAllRecipients(); filter.AddRecipientsByTeam( GetGlobalTeam(team) ); }
UserMessageBegin ( filter, "SendAudio" ); WRITE_STRING( sound ); MessageEnd(); }
//-----------------------------------------------------------------------------
// 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;
//=============================================================================
// HPE_BEGIN:
//=============================================================================
// [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 = pInflictor && V_strcmp(pInflictor->GetClassname(), "hegrenade_projectile") == 0; bool isBomb = pInflictor && V_strcmp(pInflictor->GetClassname(), "planted_c4") == 0; //=============================================================================
// HPE_END
//=============================================================================
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) ? 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() ) { //=============================================================================
// HPE_BEGIN:
// [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(); } //=============================================================================
// HPE_END
//=============================================================================
if ( pEntity->m_takedamage != DAMAGE_NO ) { // 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 don't travel into or out of water
if ( !bIgnoreWorld ) { if (bInWater && pEntity->GetWaterLevel() == 0) continue; if (!bInWater && pEntity->GetWaterLevel() == 3) 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.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 ); //=============================================================================
// HPE_BEGIN:
// [sbodenbender] Increment grenade damage stat
//=============================================================================
if (pCSExplosionVictim && pCSExplosionAttacker && isGrenade) { CCS_GameStats.IncrementStat(pCSExplosionAttacker, CSSTAT_GRENADE_DAMAGE, static_cast<int>(adjustedInfo.GetDamage())); } //=============================================================================
// HPE_END
//=============================================================================
} } } //=============================================================================
// HPE_BEGIN:
// [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++; } } //=============================================================================
// HPE_END
//=============================================================================
}
//=============================================================================
// HPE_BEGIN:
// [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); }
//=============================================================================
// HPE_END
//=============================================================================
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pVictim -
// *pKiller -
// *pInflictor -
//-----------------------------------------------------------------------------
void CCSGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info ) { // Work out what killed the player, and 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;
// Find the killer & the scorer
CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor ); CCSPlayer *pCSVictim = (CCSPlayer*)(pVictim);
bool bHeadshot = false;
if ( pScorer ) // Is the killer a client?
{ killer_ID = pScorer->GetUserID(); 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() ) { killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); //GetDeathNoticeName();
} } 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 ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 ) { killer_weapon_name += 7; } else if ( strncmp( killer_weapon_name, "NPC_", 8 ) == 0 ) { killer_weapon_name += 8; } else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 ) { killer_weapon_name += 5; } else if( strncmp( killer_weapon_name, "hegrenade", 9 ) == 0 ) //"hegrenade_projectile"
{ killer_weapon_name = "hegrenade"; } else if( strncmp( killer_weapon_name, "flashbang", 9 ) == 0 ) //"flashbang_projectile"
{ killer_weapon_name = "flashbang"; }
IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
if ( event ) { event->SetInt("userid", pVictim->GetUserID() ); event->SetInt("attacker", killer_ID ); event->SetString("weapon", killer_weapon_name ); event->SetInt("headshot", bHeadshot ? 1 : 0 ); event->SetInt("priority", bHeadshot ? 8 : 7 ); // HLTV event priority, not transmitted
if ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION ) { event->SetInt( "dominated", 1 ); } else if ( pCSVictim->GetDeathFlags() & CS_DEATH_REVENGE ) { event->SetInt( "revenge", 1 ); } gameeventmanager->FireEvent( event ); } }
//=========================================================
//=========================================================
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;
CCS_GameStats.PlayerKilled( pVictim, info ); //=============================================================================
// HPE_BEGIN:
// [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 ); //=============================================================================
// HPE_END
//=============================================================================
// 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 ); } else { BaseClass::PlayerKilled( pVictim, info ); }
// check for team-killing, and give monetary rewards/penalties
// Find the killer & the scorer
if ( !pScorer ) return;
if ( IPointsForKill( pScorer, pVictim ) < 0 ) { // team-killer!
pCSScorer->AddAccount( -3300 ); ++pCSScorer->m_iTeamKills; pCSScorer->m_bJustKilledTeammate = true;
ClientPrint( pCSScorer, HUD_PRINTCENTER, "#Killed_Teammate" ); if ( mp_autokick.GetBool() ) { char strTeamKills[64]; Q_snprintf( strTeamKills, sizeof( strTeamKills ), "%d", pCSScorer->m_iTeamKills ); ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Game_teammate_kills", strTeamKills ); // this includes a " of 3" in it
if ( pCSScorer->m_iTeamKills >= 3 ) { ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Banned_For_Killing_Teammates" ); engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pCSScorer->GetUserID() ) ); } else if ( mp_spawnprotectiontime.GetInt() > 0 && GetRoundElapsedTime() < mp_spawnprotectiontime.GetInt() ) { ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Banned_For_Killing_Teammates" ); engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pCSScorer->GetUserID() ) ); } }
if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) ) { pCSScorer->m_iDisplayHistoryBits |= DHF_FRIEND_KILLED; pCSScorer->HintMessage( "#Hint_careful_around_teammates", false ); } } else { //=============================================================================
// HPE_BEGIN:
// [tj] Added a check to make sure we don't get money for suicides.
//=============================================================================
if (pCSScorer != pCSVictim) { //=============================================================================
// HPE_END
//=============================================================================
if ( pCSVictim->IsVIP() ) { pCSScorer->HintMessage( "#Hint_reward_for_killing_vip", true ); pCSScorer->AddAccount( 2500 ); } else { pCSScorer->AddAccount( 300 ); } }
if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_ENEMY_KILLED) ) { pCSScorer->m_iDisplayHistoryBits |= DHF_ENEMY_KILLED; pCSScorer->HintMessage( "#Hint_win_round_by_killing_enemy", false ); } } }
void CCSGameRules::InitDefaultAIRelationships() { // Allocate memory for default relationships
CBaseCombatCharacter::AllocateDefaultRelationships();
// --------------------------------------------------------------
// First initialize table so we can report missing relationships
// --------------------------------------------------------------
int i, j; for (i=0;i<NUM_AI_CLASSES;i++) { for (j=0;j<NUM_AI_CLASSES;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( engine->IndexOfEdict( 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(); } INetworkStringTable *g_StringTableBlackMarket = NULL;
void CCSGameRules::CreateCustomNetworkStringTables( void ) { m_StringTableBlackMarket = g_StringTableBlackMarket;
if ( 0 )//mp_dynamicpricing.GetBool() )
{ m_bBlackMarket = BlackMarket_DownloadPrices();
if ( m_bBlackMarket == false ) { Msg( "ERROR: mp_dynamicpricing set to 1 but couldn't download the price list!\n" ); } } else { m_bBlackMarket = false; SetBlackMarketPrices( true ); } }
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 * 1.25;
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 ); // HLTV event priority, not transmitted
gameeventmanager->FireEvent( event ); } }
return fallDamage; }
void CCSGameRules::ClientDisconnected( edict_t *pClient ) { BaseClass::ClientDisconnected( pClient );
//=============================================================================
// HPE_BEGIN:
// [tj] Clear domination data when a player disconnects
//=============================================================================
CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) ); if ( pPlayer ) { pPlayer->RemoveNemesisRelationships(); } //=============================================================================
// HPE_END
//=============================================================================
CheckWinConditions(); }
// Called when game rules are destroyed by CWorld
void CCSGameRules::LevelShutdown() { int iLevelIndex = GetCSLevelIndex( STRING( gpGlobals->mapname ) );
if ( iLevelIndex != -1 ) { g_iTerroristVictories[iLevelIndex] += m_iNumTerroristWins; g_iCounterTVictories[iLevelIndex] += m_iNumCTWins; }
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() ) { return false; }
// If a winner has already been determined.. then get the heck out of here
if (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 );
/***************************** OTHER PLAYER's CHECK *********************************************************/ bool bNeededPlayers = false; if ( NeededPlayersCheck( bNeededPlayers ) ) return false;
/****************************** ASSASINATION/VIP SCENARIO CHECK *******************************************************/ if ( VIPRoundEndCheck( bNeededPlayers ) ) return true;
/****************************** 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;
// scenario not won - still in progress
return false; }
bool CCSGameRules::NeededPlayersCheck( bool &bNeededPlayers ) { // 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, "#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( 3.0f, 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;
// 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; } } } }
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++; }
m_iHostagesRemaining = iNumLeftToRescue;
if ( (iNumLeftToRescue == 0) && (iNumHostages > 0) ) { if ( m_iHostagesRescued >= (iNumHostages * 0.5) ) { m_iAccountCT += 2500;
if ( !bNeededPlayers ) { m_iNumCTWins ++; // Update the clients team score
UpdateTeamScores(); } 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 ); }
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::VIPRoundEndCheck( bool bNeededPlayers ) { if (m_iMapHasVIPSafetyZone != 1) return false;
if (m_pVIP == NULL) return false;
if (m_pVIP->m_bEscaped == true) { m_iAccountCT += 3500;
if ( !bNeededPlayers ) { m_iNumCTWins ++; // Update the clients team score
UpdateTeamScores(); }
//MIKETODO: get this working when working on VIP scenarios
/*
MessageBegin( MSG_SPEC, SVC_DIRECTOR ); WRITE_BYTE ( 9 ); // command length in bytes
WRITE_BYTE ( DRC_CMD_EVENT ); // VIP rescued
WRITE_SHORT( ENTINDEX(m_pVIP->edict()) ); // index number of primary entity
WRITE_SHORT( 0 ); // index number of secondary entity
WRITE_LONG( 15 | DRC_FLAG_FINAL); // eventflags (priority and flags)
MessageEnd(); */
// tell the bots the VIP got out
IGameEvent * event = gameeventmanager->CreateEvent( "vip_escaped" ); if ( event ) { event->SetInt( "userid", m_pVIP->GetUserID() ); event->SetInt( "priority", 9 ); gameeventmanager->FireEvent( event ); }
//=============================================================================
// HPE_BEGIN:
// [menglish] If the VIP has escaped award him an MVP
//=============================================================================
m_pVIP->IncrementNumMVPs( CSMVP_UNDEFINED ); //=============================================================================
// HPE_END
//=============================================================================
TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Escaped ); return true; } else if ( m_pVIP->m_lifeState == LIFE_DEAD ) // The VIP is dead
{ m_iAccountTerrorist += 3250;
if ( !bNeededPlayers ) { m_iNumTerroristWins ++; // Update the clients team score
UpdateTeamScores(); }
// tell the bots the VIP was killed
IGameEvent * event = gameeventmanager->CreateEvent( "vip_killed" ); if ( event ) { event->SetInt( "userid", m_pVIP->GetUserID() ); event->SetInt( "priority", 9 ); gameeventmanager->FireEvent( event ); }
TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Assassinated ); return true; }
return false; }
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 ) ) { m_iAccountTerrorist += 3500;
if ( !bNeededPlayers ) { m_iNumTerroristWins ++; // Update the clients team score
UpdateTeamScores(); }
TerminateRound( mp_round_restart_delay.GetFloat(), Target_Bombed ); return true; } else if ( ( m_bBombDefused == true ) && ( m_bMapHasBombTarget == true ) ) { m_iAccountCT += 3250;
m_iAccountTerrorist += 800; // give the T's a little bonus for planting the bomb even though it was defused.
if ( !bNeededPlayers ) { m_iNumCTWins++; // Update the clients team score
UpdateTeamScores(); }
TerminateRound( mp_round_restart_delay.GetFloat(), Bomb_Defused ); return true; }
return false; }
bool CCSGameRules::TeamExterminationCheck( int NumAliveTerrorist, int NumAliveCT, int NumDeadTerrorist, int NumDeadCT, bool bNeededPlayers ) { if ( ( m_iNumCT > 0 && m_iNumSpawnableCT > 0 ) && ( m_iNumTerrorist > 0 && m_iNumSpawnableTerrorist > 0 ) ) { if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && 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 ( m_bMapHasBombTarget ) m_iAccountCT += 3250; else m_iAccountCT += 3000;
if ( !bNeededPlayers ) { m_iNumCTWins++; // Update the clients team score
UpdateTeamScores(); }
TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win ); return true; } } // Terrorists WON
if ( NumAliveCT == 0 && NumDeadCT != 0 && m_iNumSpawnableTerrorist > 0 ) { if ( m_bMapHasBombTarget ) m_iAccountTerrorist += 3250; else m_iAccountTerrorist += 3000;
if ( !bNeededPlayers ) { m_iNumTerroristWins++; // Update the clients team score
UpdateTeamScores(); }
TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win ); return true; } } else if ( NumAliveCT == 0 && NumAliveTerrorist == 0 ) { TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw ); return true; }
return false; }
void CCSGameRules::PickNextVIP() { // MIKETODO: work on this when getting VIP maps running.
/*
if (IsVIPQueueEmpty() != true) { // Remove the current VIP from his VIP status and make him a regular CT.
if (m_pVIP != NULL) ResetCurrentVIP();
for (int i = 0; i <= 4; i++) { if (VIPQueue[i] != NULL) { m_pVIP = VIPQueue[i]; m_pVIP->MakeVIP();
VIPQueue[i] = NULL; // remove this player from the VIP queue
StackVIPQueue(); // and re-organize the queue
m_iConsecutiveVIP = 0; return; } } } else if (m_iConsecutiveVIP >= 3) // If it's been the same VIP for 3 rounds already.. then randomly pick a new one
{ m_iLastPick++;
if (m_iLastPick > m_iNumCT) m_iLastPick = 1;
int iCount = 1;
CBaseEntity* pPlayer = NULL; CBasePlayer* player = NULL; CBasePlayer* pLastPlayer = NULL;
pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" ); while ( (pPlayer != NULL) && (!FNullEnt(pPlayer->edict())) ) { if ( !(pPlayer->pev->flags & FL_DORMANT) ) { player = GetClassPtr((CBasePlayer *)pPlayer->pev); if ( (player->m_iTeam == CT) && (iCount == m_iLastPick) ) { if ( (player == m_pVIP) && (pLastPlayer != NULL) ) player = pLastPlayer;
// Remove the current VIP from his VIP status and make him a regular CT.
if (m_pVIP != NULL) ResetCurrentVIP();
player->MakeVIP(); m_iConsecutiveVIP = 0;
return; } else if ( player->m_iTeam == CT ) iCount++;
if ( player->m_iTeam != SPECTATOR ) pLastPlayer = player; } pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" ); } } else if (m_pVIP == NULL) // There is no VIP and there is no one waiting to be the VIP.. therefore just pick the first CT player we can find.
{ CBaseEntity* pPlayer = NULL; CBasePlayer* player = NULL;
pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" ); while ( (pPlayer != NULL) && (!FNullEnt(pPlayer->edict())) ) { if ( pPlayer->pev->flags != FL_DORMANT ) { player = GetClassPtr((CBasePlayer *)pPlayer->pev); if ( player->m_iTeam == CT ) { player->MakeVIP(); m_iConsecutiveVIP = 0; return; } } pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" ); } } */ }
void CCSGameRules::ReadMultiplayCvars() { m_iRoundTime = (int)(mp_roundtime.GetFloat() * 60); m_iFreezeTime = mp_freezetime.GetInt(); }
void CCSGameRules::RestartRound() { #if defined( REPLAY_ENABLED )
if ( g_pReplay ) { // Write replay and stop recording if appropriate
if ( g_pReplay->IsRecording() ) { g_pReplay->SV_EndRecordingSession(); } int nActivePlayerCount = m_iNumTerrorist + m_iNumCT; if ( nActivePlayerCount && g_pReplay->SV_ShouldBeginRecording( false ) ) { // Tell the replay manager that it should begin recording the new round as soon as possible
g_pReplay->SV_GetContext()->GetSessionRecorder()->StartRecording(); } } #endif
//=============================================================================
// HPE_BEGIN:
// [tj] Notify players that the round is about to be reset
//=============================================================================
for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( clientIndex ); if(pPlayer) { pPlayer->OnPreResetRound(); } }
//=============================================================================
// HPE_END
//=============================================================================
if ( !IsFinite( gpGlobals->curtime ) ) { Warning( "NaN curtime in RestartRound\n" ); gpGlobals->curtime = 0.0f; }
int i;
m_iTotalRoundsPlayed++; //ClearBodyQue();
// Hardlock the player accelaration to 5.0
//CVAR_SET_FLOAT( "sv_accelerate", 5.0 );
//CVAR_SET_FLOAT( "sv_friction", 4.0 );
//CVAR_SET_FLOAT( "sv_stopspeed", 75 );
sv_stopspeed.SetValue( 75.0f );
// Tabulate the number of players on each team.
int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); m_bBombDropped = false; m_bBombPlanted = false; if ( GetHumanTeam() != TEAM_UNASSIGNED ) { MoveHumansToHumanTeam(); }
/*************** 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 ) { UTIL_ClientPrintAll( HUD_PRINTCENTER,"#Auto_Team_Balance_Next_Round"); } }
/*************** AUTO-BALANCE CODE *************/
if ( m_bCompleteReset ) { // bounds check
if ( mp_timelimit.GetInt() < 0 ) { mp_timelimit.SetValue( 0 ); } m_flGameStartTime = gpGlobals->curtime; if ( !IsFinite( m_flGameStartTime.Get() ) ) { Warning( "Trying to set a NaN game start time\n" ); m_flGameStartTime.GetForModify() = 0.0f; }
// Reset total # of rounds played
m_iTotalRoundsPlayed = 0;
// Reset score info
m_iNumTerroristWins = 0; m_iNumCTWins = 0; m_iNumConsecutiveTerroristLoses = 0; m_iNumConsecutiveCTLoses = 0;
// Reset team scores
UpdateTeamScores();
// Reset the player stats
for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = CCSPlayer::Instance( i );
if ( pPlayer && !FNullEnt( pPlayer->edict() ) ) pPlayer->Reset(); } }
m_bFreezePeriod = true;
ReadMultiplayCvars();
// Check to see if there's a mapping info paramater entity
if ( g_pMapInfo ) { switch ( g_pMapInfo->m_iBuyingStatus ) { 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" ) ) { 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;
// Check to see if this map has VIP safety zones
if ( gEntList.FindEntityByClassname( NULL, "func_vip_safetyzone" ) ) { PickNextVIP(); m_iConsecutiveVIP++; m_iMapHasVIPSafetyZone = 1; } else m_iMapHasVIPSafetyZone = 2;
// 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 += 150; } 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 > 1) m_iLoserBonus = 1500;//this is the default losing bonus
m_iNumConsecutiveTerroristLoses = 0;//starting fresh
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 > 1) m_iLoserBonus = 1500;//this is the default losing bonus
m_iNumConsecutiveCTLoses = 0;//starting fresh
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 hasen't maxed out.
if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000)) m_iLoserBonus += 500;//help out the team in the losing streak
else if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000)) m_iLoserBonus += 500;//help out the team in the losing streak
// assign the wining and losing bonuses
if (m_iRoundWinStatus == WINNER_TER) // terrorists won
{ m_iAccountTerrorist += iRescuedHostageBonus; m_iAccountCT += m_iLoserBonus; } else if (m_iRoundWinStatus == WINNER_CT) // CT Won
{ m_iAccountCT += iRescuedHostageBonus; if (m_bMapHasEscapeZone == false) // only give them the bonus if this isn't an escape map
m_iAccountTerrorist += m_iLoserBonus; }
//Update CT account based on number of hostages rescued
m_iAccountCT += m_iHostagesRescued * 750;
// 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; } //Adrian - No cash for anyone at first rounds! ( well, only the default. )
if ( m_bCompleteReset ) { m_iAccountTerrorist = m_iAccountCT = 0; //No extra cash!.
//We are starting fresh. So it's like no one has ever won or lost.
m_iNumTerroristWins = 0; m_iNumCTWins = 0; m_iNumConsecutiveTerroristLoses = 0; m_iNumConsecutiveCTLoses = 0; m_iLoserBonus = 1400; }
for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
if ( !pPlayer ) continue;
pPlayer->m_iNumSpawns = 0; pPlayer->m_bTeamChanged = false; if ( pPlayer->GetTeamNumber() == TEAM_CT ) { if (pPlayer->DoesPlayerGetRoundStartMoney()) { pPlayer->AddAccount( m_iAccountCT ); } } else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) { m_iNumEscapers++; // Add another potential escaper to the mix!
if (pPlayer->DoesPlayerGetRoundStartMoney()) { pPlayer->AddAccount( m_iAccountTerrorist ); } }
// 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 ); } } //=============================================================================
// HPE_BEGIN:
// [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; //=============================================================================
// HPE_END
//=============================================================================
// know respawn all players
for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
if ( !pPlayer ) continue;
if ( pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS ) { //=============================================================================
// HPE_BEGIN:
// [tj] Increment CT count and check CT uniforms.
//=============================================================================
numberOfCts++; if (ctUniform == -1) { ctUniform = pPlayer->PlayerClass(); } else if (pPlayer->PlayerClass() != ctUniform) { allCtsWearingSameUniform = false; } //=============================================================================
// HPE_END
//=============================================================================
pPlayer->RoundRespawn(); }
if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->PlayerClass() >= FIRST_T_CLASS && pPlayer->PlayerClass() <= LAST_T_CLASS ) { //=============================================================================
// HPE_BEGIN:
// [tj] Increment terrorist count and check terrorist uniforms
//=============================================================================
numberOfTerrorists++; if (terroristUniform == -1) { terroristUniform = pPlayer->PlayerClass(); } else if (pPlayer->PlayerClass() != terroristUniform) { allTerroristsWearingSameUniform = false; } //=============================================================================
// HPE_END
//=============================================================================
pPlayer->RoundRespawn(); } else { pPlayer->ObserverRoundRespawn(); }
if ( pPlayer->m_iAccount > pPlayer->m_iShouldHaveCash ) { m_bDontUploadStats = true; } }
//=============================================================================
// HPE_BEGIN:
//=============================================================================
// [tj] Award same uniform achievement for qualifying teams
for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
if ( !pPlayer ) continue;
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); } }
// [menglish] reset per-round achievement variables for each player
for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); if( pPlayer ) { pPlayer->ResetRoundBasedAchievementVariables(); } }
// [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(); }
//=============================================================================
// HPE_END
//=============================================================================
// Respawn entities (glass, doors, etc..)
CleanUpMap();
// now run a tkpunish check, after the map has been cleaned up
for ( i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
if ( !pPlayer ) continue;
if ( pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS ) { pPlayer->CheckTKPunishment(); } if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->PlayerClass() >= FIRST_T_CLASS && pPlayer->PlayerClass() <= LAST_T_CLASS ) { pPlayer->CheckTKPunishment(); } }
// Give C4 to the terrorists
if (m_bMapHasBombTarget == true ) GiveC4();
// Reset game variables
m_flIntermissionEndTime = 0; m_flRestartRoundTime = 0.0; m_iAccountTerrorist = m_iAccountCT = 0; m_iHostagesRescued = 0; m_iHostagesTouched = 0;
//=============================================================================
// HPE_BEGIN
// [dwenger] Reset rescue-related achievement values
//=============================================================================
// [tj] reset flawless and lossless round related flags
m_bNoTerroristsKilled = true; m_bNoCTsKilled = true; m_bNoTerroristsDamaged = true; m_bNoCTsDamaged = true; m_pFirstKill = NULL; m_pFirstBlood = NULL;
m_bCanDonateWeapons = true;
// [dwenger] Reset rescue-related achievement values
m_iHostagesRemaining = 0; m_pLastRescuer = NULL;
m_hostageWasInjured = false; m_hostageWasKilled = false;
//=============================================================================
// HPE_END
//=============================================================================
m_iNumRescuers = 0; m_iRoundWinStatus = WINNER_NONE; m_bTargetBombed = m_bBombDefused = false; m_bCompleteReset = false; m_flNextHostageAnnouncement = gpGlobals->curtime;
m_iHostagesRemaining = g_Hostages.Count();
// fire global game event
IGameEvent * event = gameeventmanager->CreateEvent( "round_start" ); if ( event ) { event->SetInt("timelimit", m_iRoundTime ); event->SetInt("fraglimit", 0 ); event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
if ( m_bMapHasRescueZone ) { event->SetString("objective","HOSTAGE RESCUE"); } else if ( m_bMapHasEscapeZone ) { event->SetString("objective","PRISON ESCAPE"); } else if ( m_iMapHasVIPSafetyZone == 1 ) { event->SetString("objective","VIP RESCUE"); } else if ( m_bMapHasBombTarget || m_bMapHasBombZone ) { event->SetString("objective","BOMB TARGET"); } else { event->SetString("objective","DEATHMATCH"); }
gameeventmanager->FireEvent( event ); } UploadGameStats();
//=============================================================================
// HPE_BEGIN:
// [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 );
//=============================================================================
// HPE_END
//=============================================================================
}
void CCSGameRules::GiveC4() { enum { ALL_TERRORISTS = 0, HUMAN_TERRORISTS, }; int iTerrorists[2][ABSOLUTE_PLAYER_LIMIT]; int numAliveTs[2] = { 0, 0 }; int lastBombGuyIndex[2] = { -1, -1 };
//Create an array of the indeces of bomb carrier candidates
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
if( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_TERRORIST && numAliveTs[ALL_TERRORISTS] < ABSOLUTE_PLAYER_LIMIT ) { if ( pPlayer == m_pLastBombGuy ) { lastBombGuyIndex[ALL_TERRORISTS] = numAliveTs[ALL_TERRORISTS]; lastBombGuyIndex[HUMAN_TERRORISTS] = numAliveTs[HUMAN_TERRORISTS]; }
iTerrorists[ALL_TERRORISTS][numAliveTs[ALL_TERRORISTS]] = i; numAliveTs[ALL_TERRORISTS]++; if ( !pPlayer->IsBot() ) { iTerrorists[HUMAN_TERRORISTS][numAliveTs[HUMAN_TERRORISTS]] = i; numAliveTs[HUMAN_TERRORISTS]++; } } }
int which = cv_bot_defer_to_human.GetBool(); if ( numAliveTs[HUMAN_TERRORISTS] == 0 ) { which = ALL_TERRORISTS; }
//pick one of the candidates randomly
if( numAliveTs[which] > 0 ) { int index = random->RandomInt(0,numAliveTs[which]-1); if ( lastBombGuyIndex[which] >= 0 ) { // give the C4 sequentially
index = (lastBombGuyIndex[which] + 1) % numAliveTs[which]; } CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iTerrorists[which][index] ) );
Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->IsAlive() );
pPlayer->GiveNamedItem( WEAPON_C4_CLASSNAME ); m_pLastBombGuy = pPlayer;
//pPlayer->SetBombIcon();
//pPlayer->pev->body = 1;
pPlayer->m_iDisplayHistoryBits |= DHF_BOMB_RETRIEVED; pPlayer->HintMessage( "#Hint_you_have_the_bomb", false, true );
// Log this information
//UTIL_LogPrintf("\"%s<%i><%s><TERRORIST>\" triggered \"Spawned_With_The_Bomb\"\n",
// STRING( pPlayer->GetPlayerName() ),
// GETPLAYERUSERID( pPlayer->edict() ),
// GETPLAYERAUTHID( pPlayer->edict() ) );
}
m_bBombDropped = false; }
void CCSGameRules::Think() { CGameRules::Think();
for ( int i = 0; i < GetNumberOfTeams(); i++ ) { GetGlobalTeam( i )->Think(); }
///// Check game rules /////
if ( CheckGameOver() ) { return; }
// have we hit the max rounds?
if ( CheckMaxRounds() ) { return; }
// did somebaody hit the fraglimit ?
if ( CheckFragLimit() ) { return; }
if ( CheckWinLimit() ) { return; }
// Check for the end of the round.
if ( IsFreezePeriod() ) { CheckFreezePeriodExpired(); } else { CheckRoundTimeExpired(); }
CheckLevelInitialized(); if ( m_flRestartRoundTime > 0.0f && m_flRestartRoundTime <= gpGlobals->curtime ) { 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; } } }
if ( !botSpeaking ) { RestartRound(); } } if ( gpGlobals->curtime > m_tmNextPeriodicThink ) { CheckRestartRound(); m_tmNextPeriodicThink = gpGlobals->curtime + 1.0; } }
// 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(); }
bool CCSGameRules::CheckGameOver() { if ( g_fGameOver ) // someone else quit the game already
{ //=============================================================================
// HPE_BEGIN:
// [Forrest] Calling ChangeLevel multiple times was causing IncrementMapCycleIndex
// to skip over maps in the list. Avoid this using a technique from CTeamplayRoundBasedRules::Think.
//=============================================================================
// check to see if we should change levels now
if ( m_flIntermissionEndTime && ( m_flIntermissionEndTime < gpGlobals->curtime ) ) { ChangeLevel(); // intermission is over
// Don't run this code again
m_flIntermissionEndTime = 0.f; } //=============================================================================
// HPE_END
//=============================================================================
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; }
bool CCSGameRules::CheckMaxRounds() { if ( mp_maxrounds.GetInt() != 0 ) { if ( m_iTotalRoundsPlayed >= mp_maxrounds.GetInt() ) { UTIL_LogPrintf("World triggered \"Intermission_Round_Limit\"\n"); GoToIntermission(); return true; } } return false; }
bool CCSGameRules::CheckWinLimit() { // has one team won the specified number of rounds?
if ( mp_winlimit.GetInt() != 0 ) { if ( m_iNumCTWins >= mp_winlimit.GetInt() ) { UTIL_LogPrintf("Team \"CT\" triggered \"Intermission_Win_Limit\"\n"); GoToIntermission(); return true; } if ( m_iNumTerroristWins >= mp_winlimit.GetInt() ) { UTIL_LogPrintf("Team \"TERRORIST\" triggered \"Intermission_Win_Limit\"\n"); 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; }
if ( IsFinite( startTime ) && gpGlobals->curtime < startTime ) { return; // not time yet to start round
}
// 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 ) ); } else if (m_iMapHasVIPSafetyZone == 1) { Q_strncpy(CT_sentence , "radio.vip", sizeof( CT_sentence ) ); Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) ); }
// Freeze period expired: kill the flag
m_bFreezePeriod = false;
IGameEvent * event = gameeventmanager->CreateEvent( "round_freeze_end" ); if ( event ) { gameeventmanager->FireEvent( event ); }
// Update the timers for all clients and play a sound
bool bCTPlayed = false; bool bTPlayed = false;
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->SyncRoundTimer();
} } }
void CCSGameRules::CheckRoundTimeExpired() { if ( mp_ignore_round_win_conditions.GetBool() ) 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 ( 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_iAccountCT += 3250; m_iNumCTWins++; TerminateRound( mp_round_restart_delay.GetFloat(), Target_Saved ); UpdateTeamScores(); MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_TERRORIST); } } else if ( m_bMapHasRescueZone ) { m_iAccountTerrorist += 3250; m_iNumTerroristWins++; TerminateRound( mp_round_restart_delay.GetFloat(), Hostages_Not_Rescued ); UpdateTeamScores(); MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_CT); } else if ( m_bMapHasEscapeZone ) { m_iNumCTWins++; TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Not_Escaped ); UpdateTeamScores(); } else if ( m_iMapHasVIPSafetyZone == 1 ) { m_iAccountTerrorist += 3250; m_iNumTerroristWins++;
TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Not_Escaped ); UpdateTeamScores(); }
#if defined( REPLAY_ENABLED )
if ( g_pReplay ) { // Write replay and stop recording if appropriate
g_pReplay->SV_EndRecordingSession(); } #endif
}
void CCSGameRules::GoToIntermission( void ) { Msg( "Going to intermission...\n" );
IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
if( winEvent ) { for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ ) { CTeam *team = GetGlobalTeam( teamIndex ); if ( team ) { float kills = CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_KILLS]; float deaths = CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_DEATHS]; // choose dialog variables to set depending on team
switch ( teamIndex ) { case TEAM_TERRORIST: winEvent->SetInt( "t_score", team->GetScore() ); if(deaths == 0) { winEvent->SetFloat( "t_kd", kills ); } else { winEvent->SetFloat( "t_kd", kills / deaths ); } winEvent->SetInt( "t_objectives_done", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_OBJECTIVES_COMPLETED] ); winEvent->SetInt( "t_money_earned", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_MONEY_EARNED] ); break; case TEAM_CT: winEvent->SetInt( "ct_score", team->GetScore() ); if(deaths == 0) { winEvent->SetFloat( "ct_kd", kills ); } else { winEvent->SetFloat( "ct_kd", kills / deaths ); } winEvent->SetInt( "ct_objectives_done", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_OBJECTIVES_COMPLETED] ); winEvent->SetInt( "ct_money_earned", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_MONEY_EARNED] ); break; default: Assert( false ); break; } } }
gameeventmanager->FireEvent( winEvent ); }
BaseClass::GoToIntermission();
// set all players to FL_FROZEN
for ( int i = 1; i <= MAX_PLAYERS; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
if ( pPlayer ) { pPlayer->AddFlag( FL_FROZEN ); } }
// freeze players while in intermission
m_bFreezePeriod = true; }
int PlayerScoreInfoSort( const playerscore_t *p1, const playerscore_t *p2 ) { // check frags
if ( p1->iScore > p2->iScore ) return -1; if ( p2->iScore > p1->iScore ) return 1;
// check index
if ( p1->iPlayerIndex < p2->iPlayerIndex ) return -1;
return 1; }
#if defined (_DEBUG)
void TestRoundWinpanel( void ) { IGameEvent *event = gameeventmanager->CreateEvent( "round_end" ); event->SetInt( "winner", TEAM_TERRORIST );
if ( event ) { gameeventmanager->FireEvent( event ); }
IGameEvent *event2 = gameeventmanager->CreateEvent( "player_death" ); if ( event2 ) { CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(1) ); // pCappingPlayers is a null terminated list of player indeces
event2->SetInt("userid", pPlayer->GetUserID() ); event2->SetInt("attacker", pPlayer->GetUserID() ); event2->SetString("weapon", "Bare Hands" ); event2->SetInt("headshot", 1 ); event2->SetInt( "revenge", 1 );
gameeventmanager->FireEvent( event2 ); }
IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" );
if ( winEvent ) { if ( 1 ) { if ( 0 /*team == m_iTimerWinTeam */) { // timer expired, defenders win
// show total time that was defended
winEvent->SetBool( "show_timer_defend", true ); winEvent->SetInt( "timer_time", 0 /*m_pRoundTimer->GetTimerMaxLength() */); } else { // attackers win
// show time it took for them to win
winEvent->SetBool( "show_timer_attack", true );
int iTimeElapsed = 90; //m_pRoundTimer->GetTimerMaxLength() - (int)m_pRoundTimer->GetTimeRemaining();
winEvent->SetInt( "timer_time", iTimeElapsed ); } } else { winEvent->SetBool( "show_timer_attack", false ); winEvent->SetBool( "show_timer_defend", false ); }
int iLastEvent = Terrorists_Win;
winEvent->SetInt( "final_event", iLastEvent );
// Set the fun fact data in the event
winEvent->SetString( "funfact_token", "#funfact_first_blood" ); winEvent->SetInt( "funfact_player", 1 ); winEvent->SetInt( "funfact_data1", 20 ); winEvent->SetInt( "funfact_data2", 31 ); winEvent->SetInt( "funfact_data3", 45 );
gameeventmanager->FireEvent( winEvent ); } } ConCommand test_round_winpanel( "test_round_winpanel", TestRoundWinpanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
void TestMatchWinpanel( void ) { IGameEvent *event = gameeventmanager->CreateEvent( "round_end" ); event->SetInt( "winner", TEAM_TERRORIST );
if ( event ) { gameeventmanager->FireEvent( event ); }
IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
if ( winEvent ) { winEvent->SetInt( "t_score", 4 ); winEvent->SetInt( "ct_score", 1 );
winEvent->SetFloat( "t_kd", 1.8f ); winEvent->SetFloat( "ct_kd", 0.4f );
winEvent->SetInt( "t_objectives_done", 5 ); winEvent->SetInt( "ct_objectives_done", 2 );
winEvent->SetInt( "t_money_earned", 30000 ); winEvent->SetInt( "ct_money_earned", 19999 );
gameeventmanager->FireEvent( winEvent ); } } ConCommand test_match_winpanel( "test_match_winpanel", TestMatchWinpanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
void TestFreezePanel( void ) { IGameEvent *event = gameeventmanager->CreateEvent( "freezecam_started" ); if ( event ) { gameeventmanager->FireEvent( event ); }
IGameEvent *winEvent = gameeventmanager->CreateEvent( "show_freezepanel" );
if ( winEvent ) { winEvent->SetInt( "killer", 1 ); gameeventmanager->FireEvent( winEvent ); } } ConCommand test_freezepanel( "test_freezepanel", TestFreezePanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT ); #endif // _DEBUG
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 ) );
PrintToConsole( player, str.sprintf( "first connected: %d\n", m_bFirstConnected ) ); PrintToConsole( player, str.sprintf( "intermission end time: %f\n", m_flIntermissionEndTime ) ); PrintToConsole( player, str.sprintf( "freeze period: %d\n", m_bFreezePeriod.Get() ) ); PrintToConsole( player, str.sprintf( "round restart time: %f\n", m_flRestartRoundTime ) ); PrintToConsole( player, str.sprintf( "game start time: %f\n", m_flGameStartTime.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())) { 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
CBaseEntity* ent = NULL; m_iSpawnPointCount_Terrorist = 0; m_iSpawnPointCount_CT = 0;
while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { m_iSpawnPointCount_Terrorist++; } else { Warning("Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n", ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] ); } }
while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { m_iSpawnPointCount_CT++; } else { Warning("Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n", ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] ); } }
// Is this a logo map?
if ( gEntList.FindEntityByClassname( NULL, "info_player_logo" ) ) m_bLogoMap = true;
m_bLevelInitialized = true; } }
void CCSGameRules::ShowSpawnPoints( void ) { CBaseEntity* ent = NULL; while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, 600 ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, 600); } }
while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL ) { if ( IsSpawnPointValid( ent, NULL ) ) { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, 600 ); } else { NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, 600 ); } } }
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_iNumCTWins, m_iNumCT ); UTIL_LogPrintf( "Team \"TERRORIST\" scored \"%i\" with \"%i\" players\n", m_iNumTerroristWins, m_iNumTerrorist );
// let the players know
char strRestartDelay[64]; Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay ); UTIL_ClientPrintAll( HUD_PRINTCENTER, "#Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" ); UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay; m_bCompleteReset = true; mp_restartgame.SetValue( 0 ); } }
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, "#Player_Balanced" ); UTIL_ClientPrintFilter( m_loyalists, HUD_PRINTCENTER, "#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::BalanceTeams( void ) { int iTeamToSwap = TEAM_UNASSIGNED; int iNumToSwap;
if (m_iMapHasVIPSafetyZone == 1) // The ratio for teams is different for Assasination maps
{ int iDesiredNumCT, iDesiredNumTerrorist; if ( (m_iNumCT + m_iNumTerrorist)%2 != 0) // uneven number of players
iDesiredNumCT = (int)((m_iNumCT + m_iNumTerrorist) * 0.55) + 1; else iDesiredNumCT = (int)((m_iNumCT + m_iNumTerrorist)/2); iDesiredNumTerrorist = (m_iNumCT + m_iNumTerrorist) - iDesiredNumCT;
if ( m_iNumCT < iDesiredNumCT ) { iTeamToSwap = TEAM_TERRORIST; iNumToSwap = iDesiredNumCT - m_iNumCT; } else if ( m_iNumTerrorist < iDesiredNumTerrorist ) { iTeamToSwap = TEAM_CT; iNumToSwap = iDesiredNumTerrorist - m_iNumTerrorist; } else return; } else { 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; }
CRecipientFilter traitors; CRecipientFilter loyalists;
traitors.MakeReliable(); loyalists.MakeReliable(); loyalists.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 && ( m_pVIP != pPlayer ) && ( pPlayer->GetTeamNumber() == iTeamToSwap ) && ( engine->GetPlayerUserId( pPlayer->edict() ) > iHighestUserID ) && ( pPlayer->State_Get() != STATE_PICKINGCLASS ) ) { iHighestUserID = engine->GetPlayerUserId( pPlayer->edict() ); pPlayerToSwap = pPlayer; } }
if ( pPlayerToSwap != NULL ) { traitors.AddRecipient( pPlayerToSwap ); loyalists.RemoveRecipient( pPlayerToSwap ); pPlayerToSwap->SwitchTeam( iTragetTeam ); } }
if ( traitors.GetRecipientCount() > 0 ) { UTIL_ClientPrintFilter( traitors, HUD_PRINTCENTER, "#Player_Balanced" ); UTIL_ClientPrintFilter( loyalists, HUD_PRINTCENTER, "#Teams_Balanced" ); } }
bool CCSGameRules::TeamFull( int team_id ) { CheckLevelInitialized();
switch ( team_id ) { case TEAM_TERRORIST: return m_iNumTerrorist >= m_iSpawnPointCount_Terrorist;
case TEAM_CT: return m_iNumCT >= m_iSpawnPointCount_CT; }
return false; } 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_iNumTerroristWins < m_iNumCTWins ) { team = TEAM_TERRORIST; } else if ( m_iNumCTWins < m_iNumTerroristWins ) { 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;
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" );
// Player cannot respawn twice in a round
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 ( gpGlobals->curtime < m_flRestartRoundTime ) return false;
// Only valid team members can spawn
if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST ) return false;
// Only players with a valid class can spawn
if ( pPlayer->GetClass() == CS_CLASS_NONE ) return false;
// 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 ) { if ( gpGlobals->curtime > (m_fRoundStartTime + 20) ) { //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_fadetoblack.GetBool() ) { 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::TerminateRound(float tmDelay, int iReason ) { variant_t emptyVariant; int iWinnerTeam = WINNER_NONE; const char *text = "UNKNOWN"; // UTIL_ClientPrintAll( HUD_PRINTCENTER, sentence );
switch ( iReason ) { // Terror wins:
case Target_Bombed: text = "#Target_Bombed"; iWinnerTeam = WINNER_TER; break;
case VIP_Assassinated: text = "#VIP_Assassinated"; iWinnerTeam = WINNER_TER; break;
case Terrorists_Escaped: text = "#Terrorists_Escaped"; iWinnerTeam = WINNER_TER; break;
case Terrorists_Win: text = "#Terrorists_Win"; iWinnerTeam = WINNER_TER; break;
case Hostages_Not_Rescued: text = "#Hostages_Not_Rescued"; iWinnerTeam = WINNER_TER; break;
case VIP_Not_Escaped: text = "#VIP_Not_Escaped"; iWinnerTeam = WINNER_TER; break; // CT wins:
case VIP_Escaped: text = "#VIP_Escaped"; iWinnerTeam = WINNER_CT; break;
case CTs_PreventEscape: text = "#CTs_PreventEscape"; iWinnerTeam = WINNER_CT; break;
case Escaping_Terrorists_Neutralized: text = "#Escaping_Terrorists_Neutralized"; iWinnerTeam = WINNER_CT; break;
case Bomb_Defused: text = "#Bomb_Defused"; iWinnerTeam = WINNER_CT; break;
case CTs_Win: text = "#CTs_Win"; iWinnerTeam = WINNER_CT; break;
case All_Hostages_Rescued: text = "#All_Hostages_Rescued"; iWinnerTeam = WINNER_CT; break;
case Target_Saved: text = "#Target_Saved"; iWinnerTeam = WINNER_CT; break;
case Terrorists_Not_Escaped: text = "#Terrorists_Not_Escaped"; iWinnerTeam = WINNER_CT; break; // no winners:
case Game_Commencing: text = "#Game_Commencing"; iWinnerTeam = WINNER_DRAW; break;
case Round_Draw: text = "#Round_Draw"; iWinnerTeam = WINNER_DRAW; break;
default: DevMsg("TerminateRound: unknown round end ID %i\n", iReason ); break; }
m_iRoundWinStatus = iWinnerTeam; m_flRestartRoundTime = gpGlobals->curtime + tmDelay;
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 ); }
//=============================================================================
// HPE_BEGIN:
//=============================================================================
// [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, 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 = m_iRoundTime - GetRoundRemainingTime(); 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); } } //=============================================================================
// HPE_END
//=============================================================================
IGameEvent * event = gameeventmanager->CreateEvent( "round_end" ); if ( event ) { event->SetInt( "winner", iWinnerTeam ); event->SetInt( "reason", iReason ); event->SetString( "message", text ); event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
gameeventmanager->FireEvent( event ); }
if ( GetMapRemainingTime() == 0.0f ) { UTIL_LogPrintf("World triggered \"Intermission_Time_Limit\"\n"); GoToIntermission(); } }
//=============================================================================
// HPE_BEGIN:
//=============================================================================
// Helper to determine if all players on a team are playing for the same clan
static bool IsClanTeam( CTeam *pTeam ) { uint32 iTeamClan = 0; for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ ) { CBasePlayer *pPlayer = pTeam->GetPlayer( iPlayer ); if ( !pPlayer ) return false;
const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" ); uint32 iPlayerClan = atoi( pClanID ); if ( iPlayer == 0 ) { // Initialize the team clan
iTeamClan = iPlayerClan; } else { if ( iPlayerClan != iTeamClan || iPlayerClan == 0 ) return false; } } return iTeamClan != 0; }
// [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) { 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) { 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 == Terrorists_Win && 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); } }
// Check the clan match achievement
CTeam *pWinningTeam = GetGlobalTeam( iWinnerTeam ); if ( pWinningTeam && pWinningTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement && IsClanTeam( pWinningTeam ) && IsClanTeam( losingTeam ) ) { for ( int iPlayer=0; iPlayer < pWinningTeam->GetNumPlayers(); iPlayer++ ) { CCSPlayer *pPlayer = ToCSPlayer( pWinningTeam->GetPlayer( iPlayer ) ); if ( !pPlayer ) continue;
pPlayer->AwardAchievement( CSWinClanMatch ); } } } }
//[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; } } } } } } //=============================================================================
// HPE_END
//=============================================================================
void CCSGameRules::UpdateTeamScores() { CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST ); CTeam *pCTs = GetGlobalTeam( TEAM_CT );
Assert( pTerrorists && pCTs );
if( pTerrorists ) pTerrorists->SetScore( m_iNumTerroristWins );
if( pCTs ) pCTs->SetScore( m_iNumCTWins ); }
void CCSGameRules::CheckMapConditions() { // Check to see if this map has a bomb target in it
if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) ) { 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; }
// Check to see if this map has VIP safety zones
if ( gEntList.FindEntityByClassname( NULL, "func_vip_safetyzone" ) ) { m_iMapHasVIPSafetyZone = 1; } else { m_iMapHasVIPSafetyZone = 2; } }
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(); */ }
// Swap Team victories
int iTemp;
iTemp = m_iNumCTWins; m_iNumCTWins = m_iNumTerroristWins; m_iNumTerroristWins = iTemp; // Update the clients team score
UpdateTeamScores(); }
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.
// Get rid of all entities except players.
CBaseEntity *pCur = gEntList.FirstEnt(); while ( pCur ) { CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pCur ); // Weapons with owners don't want to be removed..
if ( pWeapon ) { //=============================================================================
// HPE_BEGIN:
// [dwenger] Handle round restart processing for the weapon.
//=============================================================================
pWeapon->OnRoundRestart();
//=============================================================================
// HPE_END
//=============================================================================
if ( pWeapon->ShouldRemoveOnRoundRestart() ) { UTIL_Remove( pCur ); } } // remove entities that has to be restored on roundrestart (breakables etc)
else if ( !CS_FindInList( s_PreserveEnts, pCur->GetClassname() ) ) { 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 || engine->PEntityOfEntIndex( 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 ); }
bool 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 true; //There you are.
} }
//Didn't find a bomber.
return false; }
void CCSGameRules::EndRound() { // fake a round end
CSGameRules()->TerminateRound( 0.0f, Round_Draw ); }
CBaseEntity *CCSGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer ) { // gat valid spwan point
CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
// drop down to ground
Vector GroundPos = DropToGround( pPlayer, pSpawnSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX );
// Move the player to the place it said.
pPlayer->Teleport( &pSpawnSpot->GetAbsOrigin(), &pSpawnSpot->GetLocalAngles(), &vec3_origin ); pPlayer->m_Local.m_vecPunchAngle = vec3_angle; return pSpawnSpot; } // checks if the spot is clear of players
bool CCSGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer ) { 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.
return UTIL_IsSpaceEmpty( pPlayer, vTestMins, vTestMaxs ); }
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; } }
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 ); }
#define MY_USHRT_MAX 0xffff
#define MY_UCHAR_MAX 0xff
bool DataHasChanged( void ) { for ( int i = 0; i < CS_NUM_LEVELS; i++ ) { if ( g_iTerroristVictories[i] != 0 || g_iCounterTVictories[i] != 0 ) return true; }
for ( int i = 0; i < WEAPON_MAX; i++ ) { if ( g_iWeaponPurchases[i] != 0 ) return true; }
return false; }
void CCSGameRules::UploadGameStats( void ) { g_flGameStatsUpdateTime -= gpGlobals->curtime;
if ( g_flGameStatsUpdateTime > 0 ) return;
if ( IsBlackMarket() == false ) return;
if ( m_bDontUploadStats == true ) return;
if ( DataHasChanged() == true ) { cs_gamestats_t stats; memset( &stats, 0, sizeof(stats) );
// Header
stats.header.iVersion = CS_STATS_BLOB_VERSION; Q_strncpy( stats.header.szGameName, "cstrike", sizeof(stats.header.szGameName) ); Q_strncpy( stats.header.szMapName, STRING( gpGlobals->mapname ), sizeof( stats.header.szMapName ) );
ConVar *hostip = cvar->FindVar( "hostip" ); if ( hostip ) { int ip = hostip->GetInt(); stats.header.ipAddr[0] = ip >> 24; stats.header.ipAddr[1] = ( ip >> 16 ) & MY_UCHAR_MAX; stats.header.ipAddr[2] = ( ip >> 8 ) & MY_UCHAR_MAX; stats.header.ipAddr[3] = ( ip ) & MY_UCHAR_MAX; }
ConVar *hostport = cvar->FindVar( "hostip" ); if ( hostport ) { stats.header.port = hostport->GetInt(); }
stats.header.serverid = 0;
stats.iMinutesPlayed = clamp( (short)( gpGlobals->curtime / 60 ), 0, MY_USHRT_MAX );
memcpy( stats.iTerroristVictories, g_iTerroristVictories, sizeof( g_iTerroristVictories) ); memcpy( stats.iCounterTVictories, g_iCounterTVictories, sizeof( g_iCounterTVictories) ); memcpy( stats.iBlackMarketPurchases, g_iWeaponPurchases, sizeof( g_iWeaponPurchases) );
stats.iAutoBuyPurchases = g_iAutoBuyPurchases; stats.iReBuyPurchases = g_iReBuyPurchases;
stats.iAutoBuyM4A1Purchases = g_iAutoBuyM4A1Purchases; stats.iAutoBuyAK47Purchases = g_iAutoBuyAK47Purchases; stats.iAutoBuyFamasPurchases = g_iAutoBuyFamasPurchases; stats.iAutoBuyGalilPurchases = g_iAutoBuyGalilPurchases; stats.iAutoBuyVestHelmPurchases = g_iAutoBuyVestHelmPurchases; stats.iAutoBuyVestPurchases = g_iAutoBuyVestPurchases;
const void *pvBlobData = ( const void * )( &stats ); unsigned int uBlobSize = sizeof( stats );
if ( gamestatsuploader ) { gamestatsuploader->UploadGameStats( STRING( gpGlobals->mapname ), CS_STATS_BLOB_VERSION, uBlobSize, pvBlobData ); }
memset( g_iWeaponPurchases, 0, sizeof( g_iWeaponPurchases) ); memset( g_iTerroristVictories, 0, sizeof( g_iTerroristVictories) ); memset( g_iCounterTVictories, 0, sizeof( g_iTerroristVictories) );
g_iAutoBuyPurchases = 0; g_iReBuyPurchases = 0;
g_iAutoBuyM4A1Purchases = 0; g_iAutoBuyAK47Purchases = 0; g_iAutoBuyFamasPurchases = 0; g_iAutoBuyGalilPurchases = 0; g_iAutoBuyVestHelmPurchases = 0; g_iAutoBuyVestPurchases = 0; }
g_flGameStatsUpdateTime = CS_GAME_STATS_UPDATE; //Next update is between 22 and 24 hours.
} #endif // CLIENT_DLL
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() ) { bestWeapon = weapon; } else if ( weapon->GetSlot() == bestWeapon->GetSlot() && weapon->GetPosition() < bestWeapon->GetPosition() ) { bestWeapon = weapon; } } else { bestWeapon = weapon; } }
return bestWeapon; }
float CCSGameRules::GetMapRemainingTime() { #ifdef GAME_DLL
if ( nextlevel.GetString() && *nextlevel.GetString() ) { return 0; } #endif
// 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() { return (float) (m_fRoundStartTime + m_iRoundTime) - gpGlobals->curtime; }
float CCSGameRules::GetRoundStartTime() { return m_fRoundStartTime; }
float CCSGameRules::GetRoundElapsedTime() { return gpGlobals->curtime - m_fRoundStartTime; }
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::IsVIPMap() const { //MIKETODO: VIP mode
return false; }
bool CCSGameRules::IsBombDefuseMap() const { return m_bMapHasBombTarget; }
bool CCSGameRules::IsHostageRescueMap() const { return m_bMapHasRescueZone; }
bool CCSGameRules::IsLogoMap() const { return m_bLogoMap; }
float CCSGameRules::GetBuyTimeLength() const { return ( mp_buytime.GetFloat() * 60 ); }
bool CCSGameRules::IsBuyTimeElapsed() { return ( GetRoundElapsedTime() > GetBuyTimeLength() ); }
int CCSGameRules::DefaultFOV() { return 90; }
const CViewVectors* CCSGameRules::GetViewVectors() const { return &g_CSViewVectors; }
//-----------------------------------------------------------------------------
// 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))
// exaggerate all of the forces, but use real numbers to keep them consistent
#define BULLET_IMPULSE_EXAGGERATION 1
// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
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", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_762MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_762mm_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_556MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_556MM_BOX, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_box_max",2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 ); ammoDef.AddAmmoType( BULLET_PLAYER_338MAG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_338mag_max", 2800 * BULLET_IMPULSE_EXAGGERATION, 0, 12, 16 ); ammoDef.AddAmmoType( BULLET_PLAYER_9MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_9mm_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 5, 10 ); ammoDef.AddAmmoType( BULLET_PLAYER_BUCKSHOT, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_buckshot_max", 600 * BULLET_IMPULSE_EXAGGERATION, 0, 3, 6 ); ammoDef.AddAmmoType( BULLET_PLAYER_45ACP, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_45acp_max", 2100 * BULLET_IMPULSE_EXAGGERATION, 0, 6, 10 ); ammoDef.AddAmmoType( BULLET_PLAYER_357SIG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 4, 8 ); ammoDef.AddAmmoType( BULLET_PLAYER_57MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_57mm_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 4, 8 ); ammoDef.AddAmmoType( AMMO_TYPE_HEGRENADE, DMG_BLAST, TRACER_LINE, 0, 0, "ammo_hegrenade_max", 1, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_FLASHBANG, 0, TRACER_LINE, 0, 0, "ammo_flashbang_max", 1, 0 ); ammoDef.AddAmmoType( AMMO_TYPE_SMOKEGRENADE, 0, TRACER_LINE, 0, 0, "ammo_smokegrenade_max", 1, 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_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_57MM, 0, 50 ); }
return &ammoDef; }
#ifndef CLIENT_DLL
const char *CCSGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer ) { const 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->GetTeamNumber() == 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->GetTeamNumber() == 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->GetTeamNumber() == TEAM_SPECTATOR ) { pszFormat = "Cstrike_Chat_Spec"; } } // everyone
else { if ( pPlayer->m_lifeState == LIFE_ALIVE ) { pszFormat = "Cstrike_Chat_All"; } else { if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR ) { pszFormat = "Cstrike_Chat_AllDead"; } else { pszFormat = "Cstrike_Chat_AllSpec"; } } }
return pszFormat; }
void CCSGameRules::ClientSettingsChanged( CBasePlayer *pPlayer ) { const char *pszNewName = engine->GetClientConVarValue( pPlayer->entindex(), "name" ); const char *pszOldName = pPlayer->GetPlayerName(); CCSPlayer *pCSPlayer = (CCSPlayer*)pPlayer; if ( pszOldName[0] != 0 && Q_strncmp( pszOldName, pszNewName, MAX_PLAYER_NAME_LENGTH-1 ) ) { pCSPlayer->ChangeName( pszNewName ); }
pCSPlayer->m_bShowHints = true; if ( pCSPlayer->IsNetClient() ) { const char *pShowHints = engine->GetClientConVarValue( engine->IndexOfEdict( pCSPlayer->edict() ), "cl_autohelp" ); if ( pShowHints && atoi( pShowHints ) <= 0 ) { pCSPlayer->m_bShowHints = false; } } }
bool CCSGameRules::FAllowNPCs( void ) { return false; }
bool CCSGameRules::IsFriendlyFireOn( void ) { return friendlyfire.GetBool(); }
CON_COMMAND( map_showspawnpoints, "Shows player spawn points (red=invalid)" ) { CSGameRules()->ShowSpawnPoints(); }
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 ); }
void CreateBlackMarketString( void ) { g_StringTableBlackMarket = networkstringtable->CreateStringTable( "BlackMarketTable" , 1 ); }
int CCSGameRules::GetStartMoney( void ) { if ( IsBlackMarket() ) { return atoi( mp_startmoney.GetDefault() ); }
return mp_startmoney.GetInt(); }
//=============================================================================
// HPE_BEGIN:
// [menglish] Set up anything for all players that changes based on new players spawning mid-game
// Find and return fun fact data
//=============================================================================
//-----------------------------------------------------------------------------
// 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++; } } }
//=============================================================================
// HPE_END
//=============================================================================
//=============================================================================
// HPE_BEGIN:
// [pfreese] Test for "pistol" round, defined as the default starting round
// when players cannot purchase anything primary weapons
//=============================================================================
bool CCSGameRules::IsPistolRound() { return m_iTotalRoundsPlayed == 0 && GetStartMoney() <= 800; }
//=============================================================================
// HPE_END
//=============================================================================
//=============================================================================
// HPE_BEGIN:
// [tj] So game rules can react to damage taken
// [menglish]
//=============================================================================
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; } } }
//=============================================================================
// HPE_END
//=============================================================================
#endif
bool CCSGameRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer ) { #ifdef GAME_DLL
if( pPlayer ) { int iPlayerTeam = pPlayer->GetTeamNumber(); if( ( iPlayerTeam == TEAM_CT ) || ( iPlayerTeam == TEAM_TERRORIST ) ) return false; } #else
int iLocalPlayerTeam = GetLocalPlayerTeam(); if( ( iLocalPlayerTeam == TEAM_CT ) || ( iLocalPlayerTeam == TEAM_TERRORIST ) ) return false; #endif
return true; }
#ifdef GAME_DLL
struct convar_tags_t { const char *pszConVar; const char *pszTag; };
// The list of convars that automatically turn on tags when they're changed.
// Convars in this list need to have the FCVAR_NOTIFY flag set on them, so the
// tags are recalculated and uploaded to the master server when the convar is changed.
convar_tags_t convars_to_check_for_tags[] = { { "mp_friendlyfire", "friendlyfire" }, { "bot_quota", "bots" }, { "sv_nostats", "nostats" }, { "mp_startmoney", "startmoney" }, { "sv_allowminmodels", "nominmodels" }, { "sv_enablebunnyhopping", "bunnyhopping" }, { "sv_competitive_minspec", "compspec" }, { "mp_holiday_nogifts", "nogifts" }, };
//-----------------------------------------------------------------------------
// Purpose: Engine asks for the list of convars that should tag the server
//-----------------------------------------------------------------------------
void CCSGameRules::GetTaggedConVarList( KeyValues *pCvarTagList ) { BaseClass::GetTaggedConVarList( pCvarTagList );
for ( int i = 0; i < ARRAYSIZE(convars_to_check_for_tags); i++ ) { KeyValues *pKV = new KeyValues( "tag" ); pKV->SetString( "convar", convars_to_check_for_tags[i].pszConVar ); pKV->SetString( "tag", convars_to_check_for_tags[i].pszTag );
pCvarTagList->AddSubKey( pKV ); } }
#endif
int CCSGameRules::GetBlackMarketPriceForWeapon( int iWeaponID ) { if ( m_pPrices == NULL ) { GetBlackMarketPriceList(); }
if ( m_pPrices ) return m_pPrices->iCurrentPrice[iWeaponID]; else return 0; }
int CCSGameRules::GetBlackMarketPreviousPriceForWeapon( int iWeaponID ) { if ( m_pPrices == NULL ) { GetBlackMarketPriceList(); }
if ( m_pPrices ) return m_pPrices->iPreviousPrice[iWeaponID]; else return 0; }
const weeklyprice_t *CCSGameRules::GetBlackMarketPriceList( void ) { if ( m_StringTableBlackMarket == NULL ) { m_StringTableBlackMarket = networkstringtable->FindTable( CS_GAMERULES_BLACKMARKET_TABLE_NAME); }
if ( m_pPrices == NULL ) { int iSize = 0; INetworkStringTable *pTable = m_StringTableBlackMarket; if ( pTable && pTable->GetNumStrings() > 0 ) { m_pPrices = (const weeklyprice_t *)pTable->GetStringUserData( 0, &iSize ); } }
if ( m_pPrices ) { PrepareEquipmentInfo(); } return m_pPrices; }
void CCSGameRules::SetBlackMarketPrices( bool bSetDefaults ) { for ( int i = 1; i < WEAPON_MAX; i++ ) { if ( i == WEAPON_SHIELDGUN ) continue;
CCSWeaponInfo *info = GetWeaponInfo( (CSWeaponID)i );
if ( info == NULL ) continue;
if ( bSetDefaults == false ) { info->SetWeaponPrice( GetBlackMarketPriceForWeapon( i ) ); info->SetPreviousPrice( GetBlackMarketPreviousPriceForWeapon( i ) ); } else { info->SetWeaponPrice( info->GetDefaultPrice() ); } } }
#ifdef CLIENT_DLL
CCSGameRules::CCSGameRules() { CSGameRules()->m_StringTableBlackMarket = NULL; m_pPrices = NULL; m_bBlackMarket = false; }
void TestTable( void ) { CSGameRules()->m_StringTableBlackMarket = networkstringtable->FindTable( CS_GAMERULES_BLACKMARKET_TABLE_NAME);
if ( CSGameRules()->m_StringTableBlackMarket == NULL ) return;
int iIndex = CSGameRules()->m_StringTableBlackMarket->FindStringIndex( "blackmarket_prices" ); int iSize = 0;
const weeklyprice_t *pPrices = NULL; pPrices = (const weeklyprice_t *)(CSGameRules()->m_StringTableBlackMarket)->GetStringUserData( iIndex, &iSize ); }
#ifdef DEBUG
ConCommand cs_testtable( "cs_testtable", TestTable ); #endif
//-----------------------------------------------------------------------------
// 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 == (int)va_arg(vl,double) ) return; } va_end(vl);
if( fValue < fMinValue || fValue > fMaxValue ) { float fNewValue = MAX( MIN( fValue, fMaxValue ), fMinValue ); competitiveConvar.SetValue( fNewValue ); DevMsg( "Convar %s enforced by server (see sv_competitive_minspec.) Set to %2f.\n", szCvarName, fNewValue ); } }
//-----------------------------------------------------------------------------
// 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: // It is a best practice to always have a virtual destructor in an interface
// class. Otherwise if the derived classes have destructors they will not be
// called.
virtual ~ICompetitiveConvar() {} 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 = null; s_pConVarBackups->deleteThis(); s_pConVarBackups = null; } private: static CompetitiveConvarList_t* s_pCompetitiveConvars; static KeyValues* s_pConVarBackups; }; static CCompetitiveCvarManager *s_pCompetitiveCvarManager = new CCompetitiveCvarManager(); CCompetitiveCvarManager::CompetitiveConvarList_t* CCompetitiveCvarManager::s_pCompetitiveConvars = null; KeyValues* CCompetitiveCvarManager::s_pConVarBackups = null;
//-----------------------------------------------------------------------------
// 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 && (bool)flOldValue == false ) { // Backup the values of each cvar and enforce new ones
CCompetitiveCvarManager::BackupAllConvars(); CCompetitiveCvarManager::EnforceRestrictionsOnAllConvars(); } else if( pCvar->GetBool() == false && (bool)flOldValue == true ) { // If sv_competitive_minspec is disabled, restore old client values
CCompetitiveCvarManager::RestoreAllOriginalValues(); } } #endif
static ConVar sv_competitive_minspec( "sv_competitive_minspec", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Enable to force certain client convars to minimum/maximum values to help prevent competitive advantages:\n \
r_drawdetailprops = 1\n \ r_staticprop_lod = minimum -1 maximum 3\n \ fps_max minimum 59 (0 works too)\n \ cl_detailfade minimum 400\n \ cl_detaildist minimum 1200\n \ cl_interp_ratio = minimum 1 maximum 2\n \ cl_interp = minimum 0 maximum 0.031\n \ " #ifdef CLIENT_DLL
,sv_competitive_minspec_changed_f #endif
);
#ifdef CLIENT_DLL
ENABLE_COMPETITIVE_CONVAR( r_drawdetailprops, true, true ); // force r_drawdetailprops on
ENABLE_COMPETITIVE_CONVAR( r_staticprop_lod, -1, 3 ); // force r_staticprop_lod from -1 to 3
ENABLE_COMPETITIVE_CONVAR( fps_max, 59, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works
ENABLE_COMPETITIVE_CONVAR( cl_detailfade, 400 ); // force cl_detailfade above 400.
ENABLE_COMPETITIVE_CONVAR( cl_detaildist, 1200 ); // force cl_detaildist above 1200.
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
// Stubs for replay client code
const char *GetMapDisplayName( const char *pMapName ) { return pMapName; }
bool IsTakingAFreezecamScreenshot() { return false; }
#endif
|