aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/multiplay_gamerules.cpp
diff options
context:
space:
mode:
authorNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
committerNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
commit8737f191f3b59f001a77bf6c08091109211c1c9f (patch)
treedbbf05c004d9b026f2c1f23f06600fe0add82c36 /mp/src/game/shared/multiplay_gamerules.cpp
parentUpdate .gitignore. (diff)
parentMake .xcconfigs text files too. (diff)
downloadsource-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.tar.xz
source-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'mp/src/game/shared/multiplay_gamerules.cpp')
-rw-r--r--mp/src/game/shared/multiplay_gamerules.cpp3642
1 files changed, 1821 insertions, 1821 deletions
diff --git a/mp/src/game/shared/multiplay_gamerules.cpp b/mp/src/game/shared/multiplay_gamerules.cpp
index 2cbb6f61..409f2362 100644
--- a/mp/src/game/shared/multiplay_gamerules.cpp
+++ b/mp/src/game/shared/multiplay_gamerules.cpp
@@ -1,1821 +1,1821 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Contains the implementation of game rules for multiplayer.
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "cdll_int.h"
-#include "multiplay_gamerules.h"
-#include "viewport_panel_names.h"
-#include "gameeventdefs.h"
-#include <KeyValues.h>
-#include "filesystem.h"
-#include "mp_shareddefs.h"
-#include "utlbuffer.h"
-
-#ifdef CLIENT_DLL
-
-#else
-
- #include "eventqueue.h"
- #include "player.h"
- #include "basecombatweapon.h"
- #include "gamerules.h"
- #include "game.h"
- #include "items.h"
- #include "entitylist.h"
- #include "in_buttons.h"
- #include <ctype.h>
- #include "voice_gamemgr.h"
- #include "iscorer.h"
- #include "hltvdirector.h"
- #include "AI_Criteria.h"
- #include "sceneentity.h"
- #include "basemultiplayerplayer.h"
- #include "team.h"
- #include "usermessages.h"
- #include "tier0/icommandline.h"
-
-#ifdef NEXT_BOT
- #include "NextBotManager.h"
-#endif
-
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-
-REGISTER_GAMERULES_CLASS( CMultiplayRules );
-
-ConVar mp_chattime(
- "mp_chattime",
- "10",
- FCVAR_REPLICATED,
- "amount of time players can chat after the game is over",
- true, 1,
- true, 120 );
-
-#ifdef GAME_DLL
-void MPTimeLimitCallback( IConVar *var, const char *pOldString, float flOldValue )
-{
- if ( mp_timelimit.GetInt() < 0 )
- {
- mp_timelimit.SetValue( 0 );
- }
-
- if ( MultiplayRules() )
- {
- MultiplayRules()->HandleTimeLimitChange();
- }
-}
-#endif
-
-ConVar mp_timelimit( "mp_timelimit", "0", FCVAR_NOTIFY|FCVAR_REPLICATED, "game time per map in minutes"
-#ifdef GAME_DLL
- , MPTimeLimitCallback
-#endif
- );
-
-ConVar fraglimit( "mp_fraglimit","0", FCVAR_NOTIFY|FCVAR_REPLICATED, "The number of kills at which the map ends");
-
-ConVar mp_show_voice_icons( "mp_show_voice_icons", "1", FCVAR_REPLICATED, "Show overhead player voice icons when players are speaking.\n" );
-
-#ifdef GAME_DLL
-
-ConVar tv_delaymapchange( "tv_delaymapchange", "0", 0, "Delays map change until broadcast is complete" );
-
-ConVar mp_restartgame( "mp_restartgame", "0", FCVAR_GAMEDLL, "If non-zero, game will restart in the specified number of seconds" );
-ConVar mp_restartgame_immediate( "mp_restartgame_immediate", "0", FCVAR_GAMEDLL, "If non-zero, game will restart immediately" );
-
-ConVar mp_mapcycle_empty_timeout_seconds( "mp_mapcycle_empty_timeout_seconds", "0", FCVAR_REPLICATED, "If nonzero, server will cycle to the next map if it has been empty on the current map for N seconds");
-
-void cc_SkipNextMapInCycle()
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- if ( MultiplayRules() )
- {
- MultiplayRules()->SkipNextMapInCycle();
- }
-}
-
-void cc_GotoNextMapInCycle()
-{
- if ( !UTIL_IsCommandIssuedByServerAdmin() )
- return;
-
- if ( MultiplayRules() )
- {
- MultiplayRules()->ChangeLevel();
- }
-}
-
-ConCommand skip_next_map( "skip_next_map", cc_SkipNextMapInCycle, "Skips the next map in the map rotation for the server." );
-ConCommand changelevel_next( "changelevel_next", cc_GotoNextMapInCycle, "Immediately changes to the next map in the map rotation for the server." );
-
-#ifndef TF_DLL // TF overrides the default value of this convar
-ConVar mp_waitingforplayers_time( "mp_waitingforplayers_time", "0", FCVAR_GAMEDLL, "WaitingForPlayers time length in seconds" );
-#endif
-
-ConVar mp_waitingforplayers_restart( "mp_waitingforplayers_restart", "0", FCVAR_GAMEDLL, "Set to 1 to start or restart the WaitingForPlayers period." );
-ConVar mp_waitingforplayers_cancel( "mp_waitingforplayers_cancel", "0", FCVAR_GAMEDLL, "Set to 1 to end the WaitingForPlayers period." );
-ConVar mp_clan_readyrestart( "mp_clan_readyrestart", "0", FCVAR_GAMEDLL, "If non-zero, game will restart once someone from each team gives the ready signal" );
-ConVar mp_clan_ready_signal( "mp_clan_ready_signal", "ready", FCVAR_GAMEDLL, "Text that team leader from each team must speak for the match to begin" );
-
-ConVar nextlevel( "nextlevel",
- "",
- FCVAR_GAMEDLL | FCVAR_NOTIFY,
-#if defined( CSTRIKE_DLL ) || defined( TF_DLL )
- "If set to a valid map name, will trigger a changelevel to the specified map at the end of the round" );
-#else
- "If set to a valid map name, will change to this map during the next changelevel" );
-#endif // CSTRIKE_DLL || TF_DLL
-
-#endif
-
-#ifndef CLIENT_DLL
-int CMultiplayRules::m_nMapCycleTimeStamp = 0;
-int CMultiplayRules::m_nMapCycleindex = 0;
-CUtlVector<char*> CMultiplayRules::m_MapList;
-#endif
-
-//=========================================================
-//=========================================================
-bool CMultiplayRules::IsMultiplayer( void )
-{
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CMultiplayRules::Damage_GetTimeBased( void )
-{
- int iDamage = ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN );
- return iDamage;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CMultiplayRules::Damage_GetShouldGibCorpse( void )
-{
- int iDamage = ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB );
- return iDamage;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CMultiplayRules::Damage_GetShowOnHud( void )
-{
- int iDamage = ( DMG_POISON | DMG_ACID | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK );
- return iDamage;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CMultiplayRules::Damage_GetNoPhysicsForce( void )
-{
- int iTimeBasedDamage = Damage_GetTimeBased();
- int iDamage = ( DMG_FALL | DMG_BURN | DMG_PLASMA | DMG_DROWN | iTimeBasedDamage | DMG_CRUSH | DMG_PHYSGUN | DMG_PREVENT_PHYSICS_FORCE );
- return iDamage;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-int CMultiplayRules::Damage_GetShouldNotBleed( void )
-{
- int iDamage = ( DMG_POISON | DMG_ACID );
- return iDamage;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iDmgType -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CMultiplayRules::Damage_IsTimeBased( int iDmgType )
-{
- // Damage types that are time-based.
- return ( ( iDmgType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) ) != 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iDmgType -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CMultiplayRules::Damage_ShouldGibCorpse( int iDmgType )
-{
- // Damage types that gib the corpse.
- return ( ( iDmgType & ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) ) != 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iDmgType -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CMultiplayRules::Damage_ShowOnHUD( int iDmgType )
-{
- // Damage types that have client HUD art.
- return ( ( iDmgType & ( DMG_POISON | DMG_ACID | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK ) ) != 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iDmgType -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CMultiplayRules::Damage_NoPhysicsForce( int iDmgType )
-{
- // Damage types that don't have to supply a physics force & position.
- int iTimeBasedDamage = Damage_GetTimeBased();
- return ( ( iDmgType & ( DMG_FALL | DMG_BURN | DMG_PLASMA | DMG_DROWN | iTimeBasedDamage | DMG_CRUSH | DMG_PHYSGUN | DMG_PREVENT_PHYSICS_FORCE ) ) != 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iDmgType -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CMultiplayRules::Damage_ShouldNotBleed( int iDmgType )
-{
- // Damage types that don't make the player bleed.
- return ( ( iDmgType & ( DMG_POISON | DMG_ACID ) ) != 0 );
-}
-
-//*********************************************************
-// Rules for the half-life multiplayer game.
-//*********************************************************
-CMultiplayRules::CMultiplayRules()
-{
-#ifndef CLIENT_DLL
- m_flTimeLastMapChangeOrPlayerWasConnected = 0.0f;
-
- RefreshSkillData( true );
-
- // 11/8/98
- // Modified by YWB: Server .cfg file is now a cvar, so that
- // server ops can run multiple game servers, with different server .cfg files,
- // from a single installed directory.
- // Mapcyclefile is already a cvar.
-
- // 3/31/99
- // Added lservercfg file cvar, since listen and dedicated servers should not
- // share a single config file. (sjb)
- if ( engine->IsDedicatedServer() )
- {
- // dedicated server
- const char *cfgfile = servercfgfile.GetString();
-
- if ( cfgfile && cfgfile[0] )
- {
- char szCommand[256];
-
- Log( "Executing dedicated server config file %s\n", cfgfile );
- Q_snprintf( szCommand,sizeof(szCommand), "exec %s\n", cfgfile );
- engine->ServerCommand( szCommand );
- }
- }
- else
- {
- // listen server
- const char *cfgfile = lservercfgfile.GetString();
-
- if ( cfgfile && cfgfile[0] )
- {
- char szCommand[256];
-
- Log( "Executing listen server config file %s\n", cfgfile );
- Q_snprintf( szCommand,sizeof(szCommand), "exec %s\n", cfgfile );
- engine->ServerCommand( szCommand );
- }
- }
-
- nextlevel.SetValue( "" );
- LoadMapCycleFile();
-
-#endif
-
- LoadVoiceCommandScript();
-}
-
-bool CMultiplayRules::Init()
-{
-#ifdef GAME_DLL
-
- // Initialize the custom response rule dictionaries.
- InitCustomResponseRulesDicts();
-
-#endif
-
- return BaseClass::Init();
-}
-
-
-#ifdef CLIENT_DLL
-
-
-#else
-
- extern bool g_fGameOver;
-
- #define ITEM_RESPAWN_TIME 30
- #define WEAPON_RESPAWN_TIME 20
- #define AMMO_RESPAWN_TIME 20
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::RefreshSkillData( bool forceUpdate )
- {
- // load all default values
- BaseClass::RefreshSkillData( forceUpdate );
-
- // override some values for multiplay.
-
- // suitcharger
-#ifndef TF_DLL
-//=============================================================================
-// HPE_BEGIN:
-// [menglish] CS doesn't have the suitcharger either
-//=============================================================================
-#ifndef CSTRIKE_DLL
-ConVarRef suitcharger( "sk_suitcharger" );
- suitcharger.SetValue( 30 );
- #endif
-//=============================================================================
-// HPE_END
-//=============================================================================
-#endif
- }
-
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::Think ( void )
- {
- BaseClass::Think();
-
- ///// Check game rules /////
-
- if ( g_fGameOver ) // someone else quit the game already
- {
- ChangeLevel(); // intermission is over
- return;
- }
-
- float flTimeLimit = mp_timelimit.GetFloat() * 60;
- float flFragLimit = fraglimit.GetFloat();
-
- if ( flTimeLimit != 0 && gpGlobals->curtime >= flTimeLimit )
- {
- GoToIntermission();
- return;
- }
-
- if ( flFragLimit )
- {
- // check if any player is over the frag limit
- for ( int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( pPlayer && pPlayer->FragCount() >= flFragLimit )
- {
- GoToIntermission();
- return;
- }
- }
- }
- }
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::FrameUpdatePostEntityThink()
- {
- BaseClass::FrameUpdatePostEntityThink();
-
- float flNow = Plat_FloatTime();
-
- // Update time when client was last connected
- if ( m_flTimeLastMapChangeOrPlayerWasConnected <= 0.0f )
- {
- m_flTimeLastMapChangeOrPlayerWasConnected = flNow;
- }
- else
- {
- for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
- {
- player_info_t pi;
- if ( !engine->GetPlayerInfo( iPlayerIndex, &pi ) )
- continue;
-#if defined( REPLAY_ENABLED )
- if ( pi.ishltv || pi.isreplay || pi.fakeplayer )
-#else
- if ( pi.ishltv || pi.fakeplayer )
-#endif
- continue;
-
- m_flTimeLastMapChangeOrPlayerWasConnected = flNow;
- break;
- }
- }
-
- // Check if we should cycle the map because we've been empty
- // for long enough
- if ( mp_mapcycle_empty_timeout_seconds.GetInt() > 0 )
- {
- int iIdleSeconds = (int)( flNow - m_flTimeLastMapChangeOrPlayerWasConnected );
- if ( iIdleSeconds >= mp_mapcycle_empty_timeout_seconds.GetInt() )
- {
-
- Log( "Server has been empty for %d seconds on this map, cycling map as per mp_mapcycle_empty_timeout_seconds\n", iIdleSeconds );
- ChangeLevel();
- }
- }
- }
-
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::IsDeathmatch( void )
- {
- return true;
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::IsCoOp( void )
- {
- return false;
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
- {
- if ( !pPlayer->Weapon_CanSwitchTo( pWeapon ) )
- {
- // Can't switch weapons for some reason.
- return false;
- }
-
- if ( !pPlayer->GetActiveWeapon() )
- {
- // Player doesn't have an active item, might as well switch.
- return true;
- }
-
- if ( !pWeapon->AllowsAutoSwitchTo() )
- {
- // The given weapon should not be auto switched to from another weapon.
- return false;
- }
-
- if ( !pPlayer->GetActiveWeapon()->AllowsAutoSwitchFrom() )
- {
- // The active weapon does not allow autoswitching away from it.
- return false;
- }
-
- if ( pWeapon->GetWeight() > pPlayer->GetActiveWeapon()->GetWeight() )
- {
- return true;
- }
-
- return false;
- }
-
- //-----------------------------------------------------------------------------
- // Purpose: Returns the weapon in the player's inventory that would be better than
- // the given weapon.
- //-----------------------------------------------------------------------------
- CBaseCombatWeapon *CMultiplayRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
- {
- CBaseCombatWeapon *pCheck;
- CBaseCombatWeapon *pBest;// this will be used in the event that we don't find a weapon in the same category.
-
- int iCurrentWeight = -1;
- int iBestWeight = -1;// no weapon lower than -1 can be autoswitched to
- pBest = NULL;
-
- // If I have a weapon, make sure I'm allowed to holster it
- if ( pCurrentWeapon )
- {
- if ( !pCurrentWeapon->AllowsAutoSwitchFrom() || !pCurrentWeapon->CanHolster() )
- {
- // Either this weapon doesn't allow autoswitching away from it or I
- // can't put this weapon away right now, so I can't switch.
- return NULL;
- }
-
- iCurrentWeight = pCurrentWeapon->GetWeight();
- }
-
- for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i )
- {
- pCheck = pPlayer->GetWeapon( i );
- if ( !pCheck )
- continue;
-
- // If we have an active weapon and this weapon doesn't allow autoswitching away
- // from another weapon, skip it.
- if ( pCurrentWeapon && !pCheck->AllowsAutoSwitchTo() )
- continue;
-
- if ( pCheck->GetWeight() > -1 && pCheck->GetWeight() == iCurrentWeight && pCheck != pCurrentWeapon )
- {
- // this weapon is from the same category.
- if ( pCheck->HasAnyAmmo() )
- {
- if ( pPlayer->Weapon_CanSwitchTo( pCheck ) )
- {
- return pCheck;
- }
- }
- }
- else if ( pCheck->GetWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of
- {
- //Msg( "Considering %s\n", STRING( pCheck->GetClassname() );
- // we keep updating the 'best' weapon just in case we can't find a weapon of the same weight
- // that the player was using. This will end up leaving the player with his heaviest-weighted
- // weapon.
- if ( pCheck->HasAnyAmmo() )
- {
- // if this weapon is useable, flag it as the best
- iBestWeight = pCheck->GetWeight();
- pBest = pCheck;
- }
- }
- }
-
- // if we make it here, we've checked all the weapons and found no useable
- // weapon in the same catagory as the current weapon.
-
- // if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always
- // at least get the crowbar, but ya never know.
- return pBest;
- }
-
- //-----------------------------------------------------------------------------
- // Purpose:
- // Output : Returns true on success, false on failure.
- //-----------------------------------------------------------------------------
- bool CMultiplayRules::SwitchToNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
- {
- CBaseCombatWeapon *pWeapon = GetNextBestWeapon( pPlayer, pCurrentWeapon );
-
- if ( pWeapon != NULL )
- return pPlayer->Weapon_Switch( pWeapon );
-
- return false;
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
- {
- GetVoiceGameMgr()->ClientConnected( pEntity );
- return true;
- }
-
- void CMultiplayRules::InitHUD( CBasePlayer *pl )
- {
- }
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::ClientDisconnected( edict_t *pClient )
- {
- if ( pClient )
- {
- CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
-
- if ( pPlayer )
- {
- FireTargets( "game_playerleave", pPlayer, pPlayer, USE_TOGGLE, 0 );
-
- pPlayer->RemoveAllItems( true );// destroy all of the players weapons and items
-
- // Kill off view model entities
- pPlayer->DestroyViewModels();
-
- pPlayer->SetConnected( PlayerDisconnected );
- }
- }
- }
-
- //=========================================================
- //=========================================================
- float CMultiplayRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
- {
- int iFallDamage = (int)falldamage.GetFloat();
-
- switch ( iFallDamage )
- {
- case 1://progressive
- pPlayer->m_Local.m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
- return pPlayer->m_Local.m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
- break;
- default:
- case 0:// fixed
- return 10;
- break;
- }
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::AllowDamage( CBaseEntity *pVictim, const CTakeDamageInfo &info )
- {
- return true;
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
- {
- return true;
- }
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::PlayerThink( CBasePlayer *pPlayer )
- {
- if ( g_fGameOver )
- {
- // clear attack/use commands from player
- pPlayer->m_afButtonPressed = 0;
- pPlayer->m_nButtons = 0;
- pPlayer->m_afButtonReleased = 0;
- }
- }
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::PlayerSpawn( CBasePlayer *pPlayer )
- {
- bool addDefault;
- CBaseEntity *pWeaponEntity = NULL;
-
- pPlayer->EquipSuit();
-
- addDefault = true;
-
- while ( (pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL)
- {
- pWeaponEntity->Touch( pPlayer );
- addDefault = false;
- }
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::FPlayerCanRespawn( CBasePlayer *pPlayer )
- {
- return true;
- }
-
- //=========================================================
- //=========================================================
- float CMultiplayRules::FlPlayerSpawnTime( CBasePlayer *pPlayer )
- {
- return gpGlobals->curtime;//now!
- }
-
- bool CMultiplayRules::AllowAutoTargetCrosshair( void )
- {
- return ( aimcrosshair.GetInt() != 0 );
- }
-
- //=========================================================
- // IPointsForKill - how many points awarded to anyone
- // that kills this player?
- //=========================================================
- int CMultiplayRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
- {
- return 1;
- }
-
- //-----------------------------------------------------------------------------
- // Purpose:
- //-----------------------------------------------------------------------------
- CBasePlayer *CMultiplayRules::GetDeathScorer( CBaseEntity *pKiller, CBaseEntity *pInflictor )
- {
- if ( pKiller)
- {
- if ( pKiller->Classify() == CLASS_PLAYER )
- return (CBasePlayer*)pKiller;
-
- // Killing entity might be specifying a scorer player
- IScorer *pScorerInterface = dynamic_cast<IScorer*>( pKiller );
- if ( pScorerInterface )
- {
- CBasePlayer *pPlayer = pScorerInterface->GetScorer();
- if ( pPlayer )
- return pPlayer;
- }
-
- // Inflicting entity might be specifying a scoring player
- pScorerInterface = dynamic_cast<IScorer*>( pInflictor );
- if ( pScorerInterface )
- {
- CBasePlayer *pPlayer = pScorerInterface->GetScorer();
- if ( pPlayer )
- return pPlayer;
- }
- }
-
- return NULL;
- }
-
- //-----------------------------------------------------------------------------
- // Purpose: Returns player who should receive credit for kill
- //-----------------------------------------------------------------------------
- CBasePlayer *CMultiplayRules::GetDeathScorer( CBaseEntity *pKiller, CBaseEntity *pInflictor, CBaseEntity *pVictim )
- {
- // if this method not overridden by subclass, just call our default implementation
- return GetDeathScorer( pKiller, pInflictor );
- }
-
- //=========================================================
- // PlayerKilled - someone/something killed this player
- //=========================================================
- void CMultiplayRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
- {
- DeathNotice( pVictim, info );
-
- // Find the killer & the scorer
- CBaseEntity *pInflictor = info.GetInflictor();
- CBaseEntity *pKiller = info.GetAttacker();
- CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor, pVictim );
-
- pVictim->IncrementDeathCount( 1 );
-
- // 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 );
-
- // Did the player kill himself?
- if ( pVictim == pScorer )
- {
- if ( UseSuicidePenalty() )
- {
- // Players lose a frag for killing themselves
- pVictim->IncrementFragCount( -1 );
- }
- }
- else if ( pScorer )
- {
- // if a player dies in a deathmatch game and the killer is a client, award the killer some points
- pScorer->IncrementFragCount( IPointsForKill( pScorer, pVictim ) );
-
- // Allow the scorer to immediately paint a decal
- pScorer->AllowImmediateDecalPainting();
-
- // dvsents2: uncomment when removing all FireTargets
- //variant_t value;
- //g_EventQueue.AddEvent( "game_playerkill", "Use", value, 0, pScorer, pScorer );
- FireTargets( "game_playerkill", pScorer, pScorer, USE_TOGGLE, 0 );
- }
- else
- {
- if ( UseSuicidePenalty() )
- {
- // Players lose a frag for letting the world kill them
- pVictim->IncrementFragCount( -1 );
- }
- }
- }
-
- //=========================================================
- // Deathnotice.
- //=========================================================
- void CMultiplayRules::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, pVictim );
-
- // Custom damage type?
- if ( info.GetDamageCustom() )
- {
- killer_weapon_name = GetDamageCustomString( info );
- if ( pScorer )
- {
- killer_ID = pScorer->GetUserID();
- }
- }
- else
- {
- // Is the killer a client?
- if ( pScorer )
- {
- killer_ID = pScorer->GetUserID();
-
- 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()->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;
- }
- }
-
- IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
- if ( event )
- {
- event->SetInt("userid", pVictim->GetUserID() );
- event->SetInt("attacker", killer_ID );
- event->SetInt("customkill", info.GetDamageCustom() );
- event->SetInt("priority", 7 ); // HLTV event priority, not transmitted
-
- gameeventmanager->FireEvent( event );
- }
-
- }
-
- //=========================================================
- // FlWeaponRespawnTime - what is the time in the future
- // at which this weapon may spawn?
- //=========================================================
- float CMultiplayRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon )
- {
- if ( weaponstay.GetInt() > 0 )
- {
- // make sure it's only certain weapons
- if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
- {
- return gpGlobals->curtime + 0; // weapon respawns almost instantly
- }
- }
-
- return gpGlobals->curtime + WEAPON_RESPAWN_TIME;
- }
-
- // when we are within this close to running out of entities, items
- // marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
- #define ENTITY_INTOLERANCE 100
-
- //=========================================================
- // FlWeaponRespawnTime - Returns 0 if the weapon can respawn
- // now, otherwise it returns the time at which it can try
- // to spawn again.
- //=========================================================
- float CMultiplayRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon )
- {
- if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
- {
- if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) )
- return 0;
-
- // we're past the entity tolerance level, so delay the respawn
- return FlWeaponRespawnTime( pWeapon );
- }
-
- return 0;
- }
-
- //=========================================================
- // VecWeaponRespawnSpot - where should this weapon spawn?
- // Some game variations may choose to randomize spawn locations
- //=========================================================
- Vector CMultiplayRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
- {
- return pWeapon->GetAbsOrigin();
- }
-
- //=========================================================
- // WeaponShouldRespawn - any conditions inhibiting the
- // respawning of this weapon?
- //=========================================================
- int CMultiplayRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon )
- {
- if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) )
- {
- return GR_WEAPON_RESPAWN_NO;
- }
-
- return GR_WEAPON_RESPAWN_YES;
- }
-
- //=========================================================
- // CanHaveWeapon - returns false if the player is not allowed
- // to pick up this weapon
- //=========================================================
- bool CMultiplayRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem )
- {
- if ( weaponstay.GetInt() > 0 )
- {
- if ( pItem->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD )
- return BaseClass::CanHavePlayerItem( pPlayer, pItem );
-
- // check if the player already has this weapon
- for ( int i = 0 ; i < pPlayer->WeaponCount() ; i++ )
- {
- if ( pPlayer->GetWeapon(i) == pItem )
- {
- return false;
- }
- }
- }
-
- return BaseClass::CanHavePlayerItem( pPlayer, pItem );
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem )
- {
- return true;
- }
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem )
- {
- }
-
- //=========================================================
- //=========================================================
- int CMultiplayRules::ItemShouldRespawn( CItem *pItem )
- {
- if ( pItem->HasSpawnFlags( SF_NORESPAWN ) )
- {
- return GR_ITEM_RESPAWN_NO;
- }
-
- return GR_ITEM_RESPAWN_YES;
- }
-
-
- //=========================================================
- // At what time in the future may this Item respawn?
- //=========================================================
- float CMultiplayRules::FlItemRespawnTime( CItem *pItem )
- {
- return gpGlobals->curtime + ITEM_RESPAWN_TIME;
- }
-
- //=========================================================
- // Where should this item respawn?
- // Some game variations may choose to randomize spawn locations
- //=========================================================
- Vector CMultiplayRules::VecItemRespawnSpot( CItem *pItem )
- {
- return pItem->GetAbsOrigin();
- }
-
- //=========================================================
- // What angles should this item use to respawn?
- //=========================================================
- QAngle CMultiplayRules::VecItemRespawnAngles( CItem *pItem )
- {
- return pItem->GetAbsAngles();
- }
-
- //=========================================================
- //=========================================================
- void CMultiplayRules::PlayerGotAmmo( CBaseCombatCharacter *pPlayer, char *szName, int iCount )
- {
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::IsAllowedToSpawn( CBaseEntity *pEntity )
- {
- // if ( pEntity->GetFlags() & FL_NPC )
- // return false;
-
- return true;
- }
-
-
- //=========================================================
- //=========================================================
- float CMultiplayRules::FlHealthChargerRechargeTime( void )
- {
- return 60;
- }
-
-
- float CMultiplayRules::FlHEVChargerRechargeTime( void )
- {
- return 30;
- }
-
- //=========================================================
- //=========================================================
- int CMultiplayRules::DeadPlayerWeapons( CBasePlayer *pPlayer )
- {
- return GR_PLR_DROP_GUN_ACTIVE;
- }
-
- //=========================================================
- //=========================================================
- int CMultiplayRules::DeadPlayerAmmo( CBasePlayer *pPlayer )
- {
- return GR_PLR_DROP_AMMO_ACTIVE;
- }
-
- CBaseEntity *CMultiplayRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
- {
- CBaseEntity *pentSpawnSpot = BaseClass::GetPlayerSpawnSpot( pPlayer );
-
- //!! replace this with an Event
- /*
- if ( IsMultiplayer() && pentSpawnSpot->m_target )
- {
- FireTargets( STRING(pentSpawnSpot->m_target), pPlayer, pPlayer, USE_TOGGLE, 0 ); // dvsents2: what is this code supposed to do?
- }
- */
-
- return pentSpawnSpot;
- }
-
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker )
- {
- return ( PlayerRelationship( pListener, pSpeaker ) == GR_TEAMMATE );
- }
-
- int CMultiplayRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
- {
- // half life deathmatch has only enemies
- return GR_NOTTEAMMATE;
- }
-
- bool CMultiplayRules::PlayFootstepSounds( CBasePlayer *pl )
- {
- if ( footsteps.GetInt() == 0 )
- return false;
-
- if ( pl->IsOnLadder() || pl->GetAbsVelocity().Length2D() > 220 )
- return true; // only make step sounds in multiplayer if the player is moving fast enough
-
- return false;
- }
-
- bool CMultiplayRules::FAllowFlashlight( void )
- {
- return flashlight.GetInt() != 0;
- }
-
- //=========================================================
- //=========================================================
- bool CMultiplayRules::FAllowNPCs( void )
- {
- return true; // E3 hack
- return ( allowNPCs.GetInt() != 0 );
- }
-
- //=========================================================
- //======== CMultiplayRules private functions ===========
-
- void CMultiplayRules::GoToIntermission( void )
- {
- if ( g_fGameOver )
- return;
-
- g_fGameOver = true;
-
- float flWaitTime = mp_chattime.GetInt();
-
- if ( tv_delaymapchange.GetBool() )
- {
- if ( HLTVDirector()->IsActive() )
- flWaitTime = MAX( flWaitTime, HLTVDirector()->GetDelay() );
- }
-
- m_flIntermissionEndTime = gpGlobals->curtime + flWaitTime;
-
- for ( int i = 1; i <= MAX_PLAYERS; i++ )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( !pPlayer )
- continue;
-
- pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
- }
- }
-
- void StripChar(char *szBuffer, const char cWhiteSpace )
- {
-
- while ( char *pSpace = strchr( szBuffer, cWhiteSpace ) )
- {
- char *pNextChar = pSpace + sizeof(char);
- V_strcpy( pSpace, pNextChar );
- }
- }
-
- void CMultiplayRules::GetNextLevelName( char *pszNextMap, int bufsize, bool bRandom /* = false */ )
- {
- char mapcfile[256];
- DetermineMapCycleFilename( mapcfile, sizeof(mapcfile), false );
-
- // Check the time of the mapcycle file and re-populate the list of level names if the file has been modified
- const int nMapCycleTimeStamp = filesystem->GetPathTime( mapcfile, "GAME" );
-
- if ( 0 == nMapCycleTimeStamp )
- {
- // Map cycle file does not exist, make a list containing only the current map
- char *szCurrentMapName = new char[MAX_MAP_NAME];
- Q_strncpy( szCurrentMapName, STRING(gpGlobals->mapname), MAX_MAP_NAME );
- m_MapList.AddToTail( szCurrentMapName );
- }
- else
- {
- // If map cycle file has changed or this is the first time through ...
- if ( m_nMapCycleTimeStamp != nMapCycleTimeStamp )
- {
- // Reset map index and map cycle timestamp
- m_nMapCycleTimeStamp = nMapCycleTimeStamp;
- m_nMapCycleindex = 0;
-
- LoadMapCycleFile();
- }
- }
-
- // If somehow we have no maps in the list then add the current one
- if ( 0 == m_MapList.Count() )
- {
- char *szDefaultMapName = new char[MAX_MAP_NAME];
- Q_strncpy( szDefaultMapName, STRING(gpGlobals->mapname), MAX_MAP_NAME );
- m_MapList.AddToTail( szDefaultMapName );
- }
-
- if ( bRandom )
- {
- m_nMapCycleindex = RandomInt( 0, m_MapList.Count() - 1 );
- }
-
- // Here's the return value
- Q_strncpy( pszNextMap, m_MapList[m_nMapCycleindex], bufsize);
- }
-
- void CMultiplayRules::DetermineMapCycleFilename( char *pszResult, int nSizeResult, bool bForceSpew )
- {
- static char szLastResult[ 256];
-
- const char *pszVar = mapcyclefile.GetString();
- if ( *pszVar == '\0' )
- {
- if ( bForceSpew || V_stricmp( szLastResult, "__novar") )
- {
- Msg( "mapcyclefile convar not set.\n" );
- V_strcpy_safe( szLastResult, "__novar" );
- }
- *pszResult = '\0';
- return;
- }
-
- char szRecommendedName[ 256 ];
- V_sprintf_safe( szRecommendedName, "cfg/%s", pszVar );
-
- // First, look for a mapcycle file in the cfg directory, which is preferred
- V_strncpy( pszResult, szRecommendedName, nSizeResult );
- if ( filesystem->FileExists( pszResult, "GAME" ) )
- {
- if ( bForceSpew || V_stricmp( szLastResult, pszResult) )
- {
- Msg( "Using map cycle file '%s'.\n", pszResult );
- V_strcpy_safe( szLastResult, pszResult );
- }
- return;
- }
-
- // Nope? Try the root.
- V_strncpy( pszResult, pszVar, nSizeResult );
- if ( filesystem->FileExists( pszResult, "GAME" ) )
- {
- if ( bForceSpew || V_stricmp( szLastResult, pszResult) )
- {
- Msg( "Using map cycle file '%s'. ('%s' was not found.)\n", pszResult, szRecommendedName );
- V_strcpy_safe( szLastResult, pszResult );
- }
- return;
- }
-
- // Nope? Use the default.
- if ( !V_stricmp( pszVar, "mapcycle.txt" ) )
- {
- V_strncpy( pszResult, "cfg/mapcycle_default.txt", nSizeResult );
- if ( filesystem->FileExists( pszResult, "GAME" ) )
- {
- if ( bForceSpew || V_stricmp( szLastResult, pszResult) )
- {
- Msg( "Using map cycle file '%s'. ('%s' was not found.)\n", pszResult, szRecommendedName );
- V_strcpy_safe( szLastResult, pszResult );
- }
- return;
- }
- }
-
- // Failed
- *pszResult = '\0';
- if ( bForceSpew || V_stricmp( szLastResult, "__notfound") )
- {
- Msg( "Map cycle file '%s' was not found.\n", szRecommendedName );
- V_strcpy_safe( szLastResult, "__notfound" );
- }
- }
-
- void CMultiplayRules::LoapMapCycleFileIntoVector( const char *pszMapCycleFile, CUtlVector<char *> &mapList )
- {
- CUtlBuffer buf;
- if ( !filesystem->ReadFile( pszMapCycleFile, "GAME", buf ) )
- return;
- buf.PutChar( 0 );
- V_SplitString( (char*)buf.Base(), "\n", mapList );
-
- for ( int i = 0; i < mapList.Count(); i++ )
- {
- bool bIgnore = false;
-
- // Strip out the spaces in the name
- StripChar( mapList[i] , '\r');
- StripChar( mapList[i] , ' ');
-
- if ( !Q_strncmp( mapList[i], "//", 2 ) || mapList[i][0] == '\0' )
- {
- bIgnore = true;
- }
- else if ( !engine->IsMapValid( mapList[i] ) )
- {
- bIgnore = true;
-
- // If the engine doesn't consider it a valid map remove it from the lists
- Warning( "Invalid map '%s' included in map cycle file. Ignored.\n", mapList[i] );
- }
-
- if ( bIgnore )
- {
- delete [] mapList[i];
- mapList.Remove( i );
- --i;
- }
- }
- }
-
- void CMultiplayRules::FreeMapCycleFileVector( CUtlVector<char *> &mapList )
- {
- // Clear out existing map list. Not using Purge() or PurgeAndDeleteAll() because they won't delete [] each element.
- for ( int i = 0; i < mapList.Count(); i++ )
- {
- delete [] mapList[i];
- }
-
- mapList.RemoveAll();
- }
-
- bool CMultiplayRules::IsMapInMapCycle( const char *pszName )
- {
- for ( int i = 0; i < m_MapList.Count(); i++ )
- {
- if ( V_stricmp( pszName, m_MapList[i] ) == 0 )
- {
- return true;
- }
- }
-
- return false;
- }
-
- void CMultiplayRules::ChangeLevel( void )
- {
- char szNextMap[MAX_MAP_NAME];
-
- if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
- {
- Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
- }
- else
- {
- GetNextLevelName( szNextMap, sizeof(szNextMap) );
- IncrementMapCycleIndex();
- }
-
- ChangeLevelToMap( szNextMap );
- }
-
- void CMultiplayRules::LoadMapCycleFile( void )
- {
- char mapcfile[256];
- DetermineMapCycleFilename( mapcfile, sizeof(mapcfile), false );
-
- FreeMapCycleFileVector( m_MapList );
-
- // Repopulate map list from mapcycle file
- LoapMapCycleFileIntoVector( mapcfile, m_MapList );
-
- // Load server's mapcycle into network string table for client-side voting
- if ( g_pStringTableServerMapCycle )
- {
- CUtlString sFileList;
- for ( int i = 0; i < m_MapList.Count(); i++ )
- {
- sFileList += m_MapList[i];
- sFileList += '\n';
- }
-
- g_pStringTableServerMapCycle->AddString( CBaseEntity::IsServer(), "ServerMapCycle", sFileList.Length() + 1, sFileList.String() );
- }
-
-#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
- if ( g_pStringTableServerPopFiles )
- {
- // Search for all pop files that are prefixed with the current map name
- CUtlString sFileList;
-
- char szBaseName[_MAX_PATH];
- V_snprintf( szBaseName, sizeof( szBaseName ), "scripts/population/%s*.pop", STRING(gpGlobals->mapname) );
-
- FileFindHandle_t popHandle;
- const char *pPopFileName = filesystem->FindFirst( szBaseName, &popHandle );
-
- while ( pPopFileName && pPopFileName[ 0 ] != '\0' )
- {
- // Skip it if it's a directory or is the folder info
- if ( filesystem->FindIsDirectory( popHandle ) )
- {
- pPopFileName = filesystem->FindNext( popHandle );
- continue;
- }
-
- const char *pchPopPostfix = StringAfterPrefix( pPopFileName, STRING(gpGlobals->mapname) );
- if ( pchPopPostfix )
- {
- char szShortName[_MAX_PATH];
- V_strncpy( szShortName, ( ( pchPopPostfix[ 0 ] == '_' ) ? ( pchPopPostfix + 1 ) : "normal" ), sizeof( szShortName ) ); // skip the '_'
- V_StripExtension( szShortName, szShortName, sizeof( szShortName ) );
-
- sFileList += szShortName;
- sFileList += '\n';
- }
-
- pPopFileName = filesystem->FindNext( popHandle );
- }
-
- filesystem->FindClose( popHandle );
-
- if ( sFileList.Length() > 0 )
- {
- g_pStringTableServerPopFiles->AddString( CBaseEntity::IsServer(), "ServerPopFiles", sFileList.Length() + 1, sFileList.String() );
- }
- }
-
- if ( g_pStringTableServerMapCycleMvM )
- {
- ConVarRef tf_mvm_missioncyclefile( "tf_mvm_missioncyclefile" );
- KeyValues *pKV = new KeyValues( tf_mvm_missioncyclefile.GetString() );
- if ( pKV->LoadFromFile( g_pFullFileSystem, tf_mvm_missioncyclefile.GetString(), "MOD" ) )
- {
- CUtlVector<CUtlString> mapList;
-
- // Parse the maps and send a list to each client for vote options
- int iMaxCat = pKV->GetInt( "categories", 0 );
- for ( int iCat = 1; iCat <= iMaxCat; iCat++ )
- {
- KeyValues *pCategory = pKV->FindKey( UTIL_VarArgs( "%d", iCat ), false );
- if ( pCategory )
- {
- int iMapCount = pCategory->GetInt( "count", 0 );
- for ( int iMap = 1; iMap <= iMapCount; ++iMap )
- {
- KeyValues *pMission = pCategory->FindKey( UTIL_VarArgs( "%d", iMap ), false );
- if ( pMission )
- {
- const char *pszMap = pMission->GetString( "map", "" );
- int iIdx = mapList.Find( pszMap );
- if ( !mapList.IsValidIndex( iIdx ) )
- {
- mapList.AddToTail( pszMap );
- }
- }
- }
- }
- }
-
- if ( mapList.Count() )
- {
- CUtlString sFileList;
- for ( int i = 0; i < mapList.Count(); i++ )
- {
- sFileList += mapList[i];
- sFileList += '\n';
- }
-
- g_pStringTableServerMapCycleMvM->AddString( CBaseEntity::IsServer(), "ServerMapCycleMvM", sFileList.Length() + 1, sFileList.String() );
- }
-
- pKV->deleteThis();
- }
- }
-#endif
-
- // If the current map selection is in the list, set m_nMapCycleindex to the map that follows it.
- for ( int i = 0; i < m_MapList.Count(); i++ )
- {
- if ( V_strcmp( STRING( gpGlobals->mapname ), m_MapList[i] ) == 0 )
- {
- m_nMapCycleindex = i;
- IncrementMapCycleIndex();
- break;
- }
- }
- }
-
- void CMultiplayRules::ChangeLevelToMap( const char *pszMap )
- {
- g_fGameOver = true;
- m_flTimeLastMapChangeOrPlayerWasConnected = 0.0f;
- Msg( "CHANGE LEVEL: %s\n", pszMap );
- engine->ChangeLevel( pszMap, NULL );
- }
-
-
-#endif
-
-
- //-----------------------------------------------------------------------------
- // Purpose: Shared script resource of voice menu commands and hud strings
- //-----------------------------------------------------------------------------
- void CMultiplayRules::LoadVoiceCommandScript( void )
- {
- KeyValues *pKV = new KeyValues( "VoiceCommands" );
-
- if ( pKV->LoadFromFile( filesystem, "scripts/voicecommands.txt", "GAME" ) )
- {
- for ( KeyValues *menu = pKV->GetFirstSubKey(); menu != NULL; menu = menu->GetNextKey() )
- {
- int iMenuIndex = m_VoiceCommandMenus.AddToTail();
-
- int iNumItems = 0;
-
- // for each subkey of this menu, add a menu item
- for ( KeyValues *menuitem = menu->GetFirstSubKey(); menuitem != NULL; menuitem = menuitem->GetNextKey() )
- {
- iNumItems++;
-
- if ( iNumItems > 9 )
- {
- Warning( "Trying to load more than 9 menu items in voicecommands.txt, extras ignored" );
- continue;
- }
-
- VoiceCommandMenuItem_t item;
-
-#ifndef CLIENT_DLL
- int iConcept = GetMPConceptIndexFromString( menuitem->GetString( "concept", "" ) );
- if ( iConcept == MP_CONCEPT_NONE )
- {
- Warning( "Voicecommand script attempting to use unknown concept. Need to define new concepts in code. ( %s )\n", menuitem->GetString( "concept", "" ) );
- }
- item.m_iConcept = iConcept;
-
- item.m_bShowSubtitle = ( menuitem->GetInt( "show_subtitle", 0 ) > 0 );
- item.m_bDistanceBasedSubtitle = ( menuitem->GetInt( "distance_check_subtitle", 0 ) > 0 );
-
- Q_strncpy( item.m_szGestureActivity, menuitem->GetString( "activity", "" ), sizeof( item.m_szGestureActivity ) );
-#else
- Q_strncpy( item.m_szSubtitle, menuitem->GetString( "subtitle", "" ), MAX_VOICE_COMMAND_SUBTITLE );
- Q_strncpy( item.m_szMenuLabel, menuitem->GetString( "menu_label", "" ), MAX_VOICE_COMMAND_SUBTITLE );
-
-#endif
- m_VoiceCommandMenus.Element( iMenuIndex ).AddToTail( item );
- }
- }
- }
-
- pKV->deleteThis();
- }
-
-#ifndef CLIENT_DLL
-
- void CMultiplayRules::SkipNextMapInCycle()
- {
- char szSkippedMap[MAX_MAP_NAME];
- char szNextMap[MAX_MAP_NAME];
-
- GetNextLevelName( szSkippedMap, sizeof( szSkippedMap ) );
- IncrementMapCycleIndex();
- GetNextLevelName( szNextMap, sizeof( szNextMap ) );
-
- Msg( "Skipping: %s\tNext map: %s\n", szSkippedMap, szNextMap );
-
- if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
- {
- Msg( "Warning! \"nextlevel\" is set to \"%s\" and will override the next map to be played.\n", nextlevel.GetString() );
- }
- }
-
- void CMultiplayRules::IncrementMapCycleIndex()
- {
- // Reset index if we've passed the end of the map list
- if ( ++m_nMapCycleindex >= m_MapList.Count() )
- {
- m_nMapCycleindex = 0;
- }
- }
-
- bool CMultiplayRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
- {
- CBasePlayer *pPlayer = ToBasePlayer( pEdict );
-
- const char *pcmd = args[0];
- if ( FStrEq( pcmd, "voicemenu" ) )
- {
- if ( args.ArgC() < 3 )
- return true;
-
- CBaseMultiplayerPlayer *pMultiPlayerPlayer = dynamic_cast< CBaseMultiplayerPlayer * >( pPlayer );
-
- if ( pMultiPlayerPlayer )
- {
- int iMenu = atoi( args[1] );
- int iItem = atoi( args[2] );
-
- VoiceCommand( pMultiPlayerPlayer, iMenu, iItem );
- }
-
- return true;
- }
-
- return BaseClass::ClientCommand( pEdict, args );
- }
-
- void CMultiplayRules::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
- {
- CBaseMultiplayerPlayer *pPlayer = dynamic_cast< CBaseMultiplayerPlayer * >( CBaseEntity::Instance( pEntity ) );
-
- if ( !pPlayer )
- return;
-
- char const *pszCommand = pKeyValues->GetName();
- if ( pszCommand && pszCommand[0] )
- {
- if ( FStrEq( pszCommand, "AchievementEarned" ) )
- {
- if ( pPlayer->ShouldAnnounceAchievement() )
- {
- int nAchievementID = pKeyValues->GetInt( "achievementID" );
-
- IGameEvent * event = gameeventmanager->CreateEvent( "achievement_earned" );
- if ( event )
- {
- event->SetInt( "player", pPlayer->entindex() );
- event->SetInt( "achievement", nAchievementID );
- gameeventmanager->FireEvent( event );
- }
-
- pPlayer->OnAchievementEarned( nAchievementID );
- }
- }
- else if ( FStrEq( pszCommand, "SendServerMapCycle" ) )
- {
- LoadMapCycleFile();
- }
- }
- }
-
- VoiceCommandMenuItem_t *CMultiplayRules::VoiceCommand( CBaseMultiplayerPlayer *pPlayer, int iMenu, int iItem )
- {
- // have the player speak the concept that is in a particular menu slot
- if ( !pPlayer )
- return NULL;
-
- if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
- return NULL;
-
- if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
- return NULL;
-
- VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );
-
- Assert( pItem );
-
- char szResponse[AI_Response::MAX_RESPONSE_NAME];
-
- if ( pPlayer->CanSpeakVoiceCommand() )
- {
- CMultiplayer_Expresser *pExpresser = pPlayer->GetMultiplayerExpresser();
- Assert( pExpresser );
- pExpresser->AllowMultipleScenes();
-
- if ( pPlayer->SpeakConceptIfAllowed( pItem->m_iConcept, NULL, szResponse, AI_Response::MAX_RESPONSE_NAME ) )
- {
- // show a subtitle if we need to
- if ( pItem->m_bShowSubtitle )
- {
- CRecipientFilter filter;
-
- if ( pItem->m_bDistanceBasedSubtitle )
- {
- filter.AddRecipientsByPAS( pPlayer->WorldSpaceCenter() );
-
- // further reduce the range to a certain radius
- int i;
- for ( i = filter.GetRecipientCount()-1; i >= 0; i-- )
- {
- int index = filter.GetRecipientIndex(i);
-
- CBasePlayer *pListener = UTIL_PlayerByIndex( index );
-
- if ( pListener && pListener != pPlayer )
- {
- float flDist = ( pListener->WorldSpaceCenter() - pPlayer->WorldSpaceCenter() ).Length2D();
-
- if ( flDist > VOICE_COMMAND_MAX_SUBTITLE_DIST )
- filter.RemoveRecipientByPlayerIndex( index );
- }
- }
- }
- else
- {
- filter.AddAllPlayers();
- }
-
- // if we aren't a disguised spy
- if ( !pPlayer->ShouldShowVoiceSubtitleToEnemy() )
- {
- // remove players on other teams
- filter.RemoveRecipientsNotOnTeam( pPlayer->GetTeam() );
- }
-
- // Register this event in the mod-specific usermessages .cpp file if you hit this assert
- Assert( usermessages->LookupUserMessage( "VoiceSubtitle" ) != -1 );
-
- // Send a subtitle to anyone in the PAS
- UserMessageBegin( filter, "VoiceSubtitle" );
- WRITE_BYTE( pPlayer->entindex() );
- WRITE_BYTE( iMenu );
- WRITE_BYTE( iItem );
- MessageEnd();
- }
-
- pPlayer->NoteSpokeVoiceCommand( szResponse );
-
-#ifdef NEXT_BOT
- // let bots react to player's voice commands
- CUtlVector< INextBot * > botVector;
- TheNextBots().CollectAllBots( &botVector );
-
- for( int i=0; i<botVector.Count(); ++i )
- {
- botVector[i]->OnActorEmoted( pPlayer, pItem->m_iConcept );
- }
-#endif
- }
- else
- {
- pItem = NULL;
- }
-
- pExpresser->DisallowMultipleScenes();
- return pItem;
- }
-
- return NULL;
- }
-
- bool CMultiplayRules::IsLoadingBugBaitReport()
- {
- return ( !engine->IsDedicatedServer()&& CommandLine()->CheckParm( "-bugbait" ) && sv_cheats->GetBool() );
- }
-
- void CMultiplayRules::HaveAllPlayersSpeakConceptIfAllowed( int iConcept, int iTeam /* = TEAM_UNASSIGNED */, const char *modifiers /* = NULL */ )
- {
- CBaseMultiplayerPlayer *pPlayer;
- for ( int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
-
- if ( !pPlayer )
- continue;
-
- if ( iTeam != TEAM_UNASSIGNED )
- {
- if ( pPlayer->GetTeamNumber() != iTeam )
- continue;
- }
-
- pPlayer->SpeakConceptIfAllowed( iConcept, modifiers );
- }
- }
-
- void CMultiplayRules::ClientSettingsChanged( CBasePlayer *pPlayer )
- {
- // NVNT see if this user is still or has began using a haptic device
- const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
- if( pszHH )
- {
- int iHH = atoi( pszHH );
- pPlayer->SetHaptics( iHH != 0 );
- }
-
- }
- void CMultiplayRules::GetTaggedConVarList( KeyValues *pCvarTagList )
- {
- BaseClass::GetTaggedConVarList( pCvarTagList );
-
- // sv_gravity
- KeyValues *pGravity = new KeyValues( "sv_gravity" );
- pGravity->SetString( "convar", "sv_gravity" );
- pGravity->SetString( "tag", "gravity" );
-
- pCvarTagList->AddSubKey( pGravity );
-
- // sv_alltalk
- KeyValues *pAllTalk = new KeyValues( "sv_alltalk" );
- pAllTalk->SetString( "convar", "sv_alltalk" );
- pAllTalk->SetString( "tag", "alltalk" );
-
- pCvarTagList->AddSubKey( pAllTalk );
- }
-
-#else
-
- const char *CMultiplayRules::GetVoiceCommandSubtitle( int iMenu, int iItem )
- {
- Assert( iMenu >= 0 && iMenu < m_VoiceCommandMenus.Count() );
- if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
- return "";
-
- Assert( iItem >= 0 && iItem < m_VoiceCommandMenus.Element( iMenu ).Count() );
- if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
- return "";
-
- VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );
-
- Assert( pItem );
-
- return pItem->m_szSubtitle;
- }
-
- // Returns false if no such menu is declared or if it's an empty menu
- bool CMultiplayRules::GetVoiceMenuLabels( int iMenu, KeyValues *pKV )
- {
- Assert( iMenu >= 0 && iMenu < m_VoiceCommandMenus.Count() );
- if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
- return false;
-
- int iNumItems = m_VoiceCommandMenus.Element( iMenu ).Count();
-
- for ( int i=0; i<iNumItems; i++ )
- {
- VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( i );
-
- KeyValues *pLabelKV = new KeyValues( pItem->m_szMenuLabel );
-
- pKV->AddSubKey( pLabelKV );
- }
-
- return iNumItems > 0;
- }
-
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Contains the implementation of game rules for multiplayer.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "cdll_int.h"
+#include "multiplay_gamerules.h"
+#include "viewport_panel_names.h"
+#include "gameeventdefs.h"
+#include <KeyValues.h>
+#include "filesystem.h"
+#include "mp_shareddefs.h"
+#include "utlbuffer.h"
+
+#ifdef CLIENT_DLL
+
+#else
+
+ #include "eventqueue.h"
+ #include "player.h"
+ #include "basecombatweapon.h"
+ #include "gamerules.h"
+ #include "game.h"
+ #include "items.h"
+ #include "entitylist.h"
+ #include "in_buttons.h"
+ #include <ctype.h>
+ #include "voice_gamemgr.h"
+ #include "iscorer.h"
+ #include "hltvdirector.h"
+ #include "AI_Criteria.h"
+ #include "sceneentity.h"
+ #include "basemultiplayerplayer.h"
+ #include "team.h"
+ #include "usermessages.h"
+ #include "tier0/icommandline.h"
+
+#ifdef NEXT_BOT
+ #include "NextBotManager.h"
+#endif
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+REGISTER_GAMERULES_CLASS( CMultiplayRules );
+
+ConVar mp_chattime(
+ "mp_chattime",
+ "10",
+ FCVAR_REPLICATED,
+ "amount of time players can chat after the game is over",
+ true, 1,
+ true, 120 );
+
+#ifdef GAME_DLL
+void MPTimeLimitCallback( IConVar *var, const char *pOldString, float flOldValue )
+{
+ if ( mp_timelimit.GetInt() < 0 )
+ {
+ mp_timelimit.SetValue( 0 );
+ }
+
+ if ( MultiplayRules() )
+ {
+ MultiplayRules()->HandleTimeLimitChange();
+ }
+}
+#endif
+
+ConVar mp_timelimit( "mp_timelimit", "0", FCVAR_NOTIFY|FCVAR_REPLICATED, "game time per map in minutes"
+#ifdef GAME_DLL
+ , MPTimeLimitCallback
+#endif
+ );
+
+ConVar fraglimit( "mp_fraglimit","0", FCVAR_NOTIFY|FCVAR_REPLICATED, "The number of kills at which the map ends");
+
+ConVar mp_show_voice_icons( "mp_show_voice_icons", "1", FCVAR_REPLICATED, "Show overhead player voice icons when players are speaking.\n" );
+
+#ifdef GAME_DLL
+
+ConVar tv_delaymapchange( "tv_delaymapchange", "0", 0, "Delays map change until broadcast is complete" );
+
+ConVar mp_restartgame( "mp_restartgame", "0", FCVAR_GAMEDLL, "If non-zero, game will restart in the specified number of seconds" );
+ConVar mp_restartgame_immediate( "mp_restartgame_immediate", "0", FCVAR_GAMEDLL, "If non-zero, game will restart immediately" );
+
+ConVar mp_mapcycle_empty_timeout_seconds( "mp_mapcycle_empty_timeout_seconds", "0", FCVAR_REPLICATED, "If nonzero, server will cycle to the next map if it has been empty on the current map for N seconds");
+
+void cc_SkipNextMapInCycle()
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ if ( MultiplayRules() )
+ {
+ MultiplayRules()->SkipNextMapInCycle();
+ }
+}
+
+void cc_GotoNextMapInCycle()
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ if ( MultiplayRules() )
+ {
+ MultiplayRules()->ChangeLevel();
+ }
+}
+
+ConCommand skip_next_map( "skip_next_map", cc_SkipNextMapInCycle, "Skips the next map in the map rotation for the server." );
+ConCommand changelevel_next( "changelevel_next", cc_GotoNextMapInCycle, "Immediately changes to the next map in the map rotation for the server." );
+
+#ifndef TF_DLL // TF overrides the default value of this convar
+ConVar mp_waitingforplayers_time( "mp_waitingforplayers_time", "0", FCVAR_GAMEDLL, "WaitingForPlayers time length in seconds" );
+#endif
+
+ConVar mp_waitingforplayers_restart( "mp_waitingforplayers_restart", "0", FCVAR_GAMEDLL, "Set to 1 to start or restart the WaitingForPlayers period." );
+ConVar mp_waitingforplayers_cancel( "mp_waitingforplayers_cancel", "0", FCVAR_GAMEDLL, "Set to 1 to end the WaitingForPlayers period." );
+ConVar mp_clan_readyrestart( "mp_clan_readyrestart", "0", FCVAR_GAMEDLL, "If non-zero, game will restart once someone from each team gives the ready signal" );
+ConVar mp_clan_ready_signal( "mp_clan_ready_signal", "ready", FCVAR_GAMEDLL, "Text that team leader from each team must speak for the match to begin" );
+
+ConVar nextlevel( "nextlevel",
+ "",
+ FCVAR_GAMEDLL | FCVAR_NOTIFY,
+#if defined( CSTRIKE_DLL ) || defined( TF_DLL )
+ "If set to a valid map name, will trigger a changelevel to the specified map at the end of the round" );
+#else
+ "If set to a valid map name, will change to this map during the next changelevel" );
+#endif // CSTRIKE_DLL || TF_DLL
+
+#endif
+
+#ifndef CLIENT_DLL
+int CMultiplayRules::m_nMapCycleTimeStamp = 0;
+int CMultiplayRules::m_nMapCycleindex = 0;
+CUtlVector<char*> CMultiplayRules::m_MapList;
+#endif
+
+//=========================================================
+//=========================================================
+bool CMultiplayRules::IsMultiplayer( void )
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMultiplayRules::Damage_GetTimeBased( void )
+{
+ int iDamage = ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN );
+ return iDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMultiplayRules::Damage_GetShouldGibCorpse( void )
+{
+ int iDamage = ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB );
+ return iDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMultiplayRules::Damage_GetShowOnHud( void )
+{
+ int iDamage = ( DMG_POISON | DMG_ACID | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK );
+ return iDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMultiplayRules::Damage_GetNoPhysicsForce( void )
+{
+ int iTimeBasedDamage = Damage_GetTimeBased();
+ int iDamage = ( DMG_FALL | DMG_BURN | DMG_PLASMA | DMG_DROWN | iTimeBasedDamage | DMG_CRUSH | DMG_PHYSGUN | DMG_PREVENT_PHYSICS_FORCE );
+ return iDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CMultiplayRules::Damage_GetShouldNotBleed( void )
+{
+ int iDamage = ( DMG_POISON | DMG_ACID );
+ return iDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iDmgType -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMultiplayRules::Damage_IsTimeBased( int iDmgType )
+{
+ // Damage types that are time-based.
+ return ( ( iDmgType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) ) != 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iDmgType -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMultiplayRules::Damage_ShouldGibCorpse( int iDmgType )
+{
+ // Damage types that gib the corpse.
+ return ( ( iDmgType & ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) ) != 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iDmgType -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMultiplayRules::Damage_ShowOnHUD( int iDmgType )
+{
+ // Damage types that have client HUD art.
+ return ( ( iDmgType & ( DMG_POISON | DMG_ACID | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK ) ) != 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iDmgType -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMultiplayRules::Damage_NoPhysicsForce( int iDmgType )
+{
+ // Damage types that don't have to supply a physics force & position.
+ int iTimeBasedDamage = Damage_GetTimeBased();
+ return ( ( iDmgType & ( DMG_FALL | DMG_BURN | DMG_PLASMA | DMG_DROWN | iTimeBasedDamage | DMG_CRUSH | DMG_PHYSGUN | DMG_PREVENT_PHYSICS_FORCE ) ) != 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iDmgType -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CMultiplayRules::Damage_ShouldNotBleed( int iDmgType )
+{
+ // Damage types that don't make the player bleed.
+ return ( ( iDmgType & ( DMG_POISON | DMG_ACID ) ) != 0 );
+}
+
+//*********************************************************
+// Rules for the half-life multiplayer game.
+//*********************************************************
+CMultiplayRules::CMultiplayRules()
+{
+#ifndef CLIENT_DLL
+ m_flTimeLastMapChangeOrPlayerWasConnected = 0.0f;
+
+ RefreshSkillData( true );
+
+ // 11/8/98
+ // Modified by YWB: Server .cfg file is now a cvar, so that
+ // server ops can run multiple game servers, with different server .cfg files,
+ // from a single installed directory.
+ // Mapcyclefile is already a cvar.
+
+ // 3/31/99
+ // Added lservercfg file cvar, since listen and dedicated servers should not
+ // share a single config file. (sjb)
+ if ( engine->IsDedicatedServer() )
+ {
+ // dedicated server
+ const char *cfgfile = servercfgfile.GetString();
+
+ if ( cfgfile && cfgfile[0] )
+ {
+ char szCommand[256];
+
+ Log( "Executing dedicated server config file %s\n", cfgfile );
+ Q_snprintf( szCommand,sizeof(szCommand), "exec %s\n", cfgfile );
+ engine->ServerCommand( szCommand );
+ }
+ }
+ else
+ {
+ // listen server
+ const char *cfgfile = lservercfgfile.GetString();
+
+ if ( cfgfile && cfgfile[0] )
+ {
+ char szCommand[256];
+
+ Log( "Executing listen server config file %s\n", cfgfile );
+ Q_snprintf( szCommand,sizeof(szCommand), "exec %s\n", cfgfile );
+ engine->ServerCommand( szCommand );
+ }
+ }
+
+ nextlevel.SetValue( "" );
+ LoadMapCycleFile();
+
+#endif
+
+ LoadVoiceCommandScript();
+}
+
+bool CMultiplayRules::Init()
+{
+#ifdef GAME_DLL
+
+ // Initialize the custom response rule dictionaries.
+ InitCustomResponseRulesDicts();
+
+#endif
+
+ return BaseClass::Init();
+}
+
+
+#ifdef CLIENT_DLL
+
+
+#else
+
+ extern bool g_fGameOver;
+
+ #define ITEM_RESPAWN_TIME 30
+ #define WEAPON_RESPAWN_TIME 20
+ #define AMMO_RESPAWN_TIME 20
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::RefreshSkillData( bool forceUpdate )
+ {
+ // load all default values
+ BaseClass::RefreshSkillData( forceUpdate );
+
+ // override some values for multiplay.
+
+ // suitcharger
+#ifndef TF_DLL
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] CS doesn't have the suitcharger either
+//=============================================================================
+#ifndef CSTRIKE_DLL
+ConVarRef suitcharger( "sk_suitcharger" );
+ suitcharger.SetValue( 30 );
+ #endif
+//=============================================================================
+// HPE_END
+//=============================================================================
+#endif
+ }
+
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::Think ( void )
+ {
+ BaseClass::Think();
+
+ ///// Check game rules /////
+
+ if ( g_fGameOver ) // someone else quit the game already
+ {
+ ChangeLevel(); // intermission is over
+ return;
+ }
+
+ float flTimeLimit = mp_timelimit.GetFloat() * 60;
+ float flFragLimit = fraglimit.GetFloat();
+
+ if ( flTimeLimit != 0 && gpGlobals->curtime >= flTimeLimit )
+ {
+ GoToIntermission();
+ return;
+ }
+
+ if ( flFragLimit )
+ {
+ // check if any player is over the frag limit
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( pPlayer && pPlayer->FragCount() >= flFragLimit )
+ {
+ GoToIntermission();
+ return;
+ }
+ }
+ }
+ }
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::FrameUpdatePostEntityThink()
+ {
+ BaseClass::FrameUpdatePostEntityThink();
+
+ float flNow = Plat_FloatTime();
+
+ // Update time when client was last connected
+ if ( m_flTimeLastMapChangeOrPlayerWasConnected <= 0.0f )
+ {
+ m_flTimeLastMapChangeOrPlayerWasConnected = flNow;
+ }
+ else
+ {
+ for( int iPlayerIndex = 1 ; iPlayerIndex <= MAX_PLAYERS; iPlayerIndex++ )
+ {
+ player_info_t pi;
+ if ( !engine->GetPlayerInfo( iPlayerIndex, &pi ) )
+ continue;
+#if defined( REPLAY_ENABLED )
+ if ( pi.ishltv || pi.isreplay || pi.fakeplayer )
+#else
+ if ( pi.ishltv || pi.fakeplayer )
+#endif
+ continue;
+
+ m_flTimeLastMapChangeOrPlayerWasConnected = flNow;
+ break;
+ }
+ }
+
+ // Check if we should cycle the map because we've been empty
+ // for long enough
+ if ( mp_mapcycle_empty_timeout_seconds.GetInt() > 0 )
+ {
+ int iIdleSeconds = (int)( flNow - m_flTimeLastMapChangeOrPlayerWasConnected );
+ if ( iIdleSeconds >= mp_mapcycle_empty_timeout_seconds.GetInt() )
+ {
+
+ Log( "Server has been empty for %d seconds on this map, cycling map as per mp_mapcycle_empty_timeout_seconds\n", iIdleSeconds );
+ ChangeLevel();
+ }
+ }
+ }
+
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::IsDeathmatch( void )
+ {
+ return true;
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::IsCoOp( void )
+ {
+ return false;
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
+ {
+ if ( !pPlayer->Weapon_CanSwitchTo( pWeapon ) )
+ {
+ // Can't switch weapons for some reason.
+ return false;
+ }
+
+ if ( !pPlayer->GetActiveWeapon() )
+ {
+ // Player doesn't have an active item, might as well switch.
+ return true;
+ }
+
+ if ( !pWeapon->AllowsAutoSwitchTo() )
+ {
+ // The given weapon should not be auto switched to from another weapon.
+ return false;
+ }
+
+ if ( !pPlayer->GetActiveWeapon()->AllowsAutoSwitchFrom() )
+ {
+ // The active weapon does not allow autoswitching away from it.
+ return false;
+ }
+
+ if ( pWeapon->GetWeight() > pPlayer->GetActiveWeapon()->GetWeight() )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Returns the weapon in the player's inventory that would be better than
+ // the given weapon.
+ //-----------------------------------------------------------------------------
+ CBaseCombatWeapon *CMultiplayRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
+ {
+ CBaseCombatWeapon *pCheck;
+ CBaseCombatWeapon *pBest;// this will be used in the event that we don't find a weapon in the same category.
+
+ int iCurrentWeight = -1;
+ int iBestWeight = -1;// no weapon lower than -1 can be autoswitched to
+ pBest = NULL;
+
+ // If I have a weapon, make sure I'm allowed to holster it
+ if ( pCurrentWeapon )
+ {
+ if ( !pCurrentWeapon->AllowsAutoSwitchFrom() || !pCurrentWeapon->CanHolster() )
+ {
+ // Either this weapon doesn't allow autoswitching away from it or I
+ // can't put this weapon away right now, so I can't switch.
+ return NULL;
+ }
+
+ iCurrentWeight = pCurrentWeapon->GetWeight();
+ }
+
+ for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i )
+ {
+ pCheck = pPlayer->GetWeapon( i );
+ if ( !pCheck )
+ continue;
+
+ // If we have an active weapon and this weapon doesn't allow autoswitching away
+ // from another weapon, skip it.
+ if ( pCurrentWeapon && !pCheck->AllowsAutoSwitchTo() )
+ continue;
+
+ if ( pCheck->GetWeight() > -1 && pCheck->GetWeight() == iCurrentWeight && pCheck != pCurrentWeapon )
+ {
+ // this weapon is from the same category.
+ if ( pCheck->HasAnyAmmo() )
+ {
+ if ( pPlayer->Weapon_CanSwitchTo( pCheck ) )
+ {
+ return pCheck;
+ }
+ }
+ }
+ else if ( pCheck->GetWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of
+ {
+ //Msg( "Considering %s\n", STRING( pCheck->GetClassname() );
+ // we keep updating the 'best' weapon just in case we can't find a weapon of the same weight
+ // that the player was using. This will end up leaving the player with his heaviest-weighted
+ // weapon.
+ if ( pCheck->HasAnyAmmo() )
+ {
+ // if this weapon is useable, flag it as the best
+ iBestWeight = pCheck->GetWeight();
+ pBest = pCheck;
+ }
+ }
+ }
+
+ // if we make it here, we've checked all the weapons and found no useable
+ // weapon in the same catagory as the current weapon.
+
+ // if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always
+ // at least get the crowbar, but ya never know.
+ return pBest;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool CMultiplayRules::SwitchToNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
+ {
+ CBaseCombatWeapon *pWeapon = GetNextBestWeapon( pPlayer, pCurrentWeapon );
+
+ if ( pWeapon != NULL )
+ return pPlayer->Weapon_Switch( pWeapon );
+
+ return false;
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
+ {
+ GetVoiceGameMgr()->ClientConnected( pEntity );
+ return true;
+ }
+
+ void CMultiplayRules::InitHUD( CBasePlayer *pl )
+ {
+ }
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::ClientDisconnected( edict_t *pClient )
+ {
+ if ( pClient )
+ {
+ CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
+
+ if ( pPlayer )
+ {
+ FireTargets( "game_playerleave", pPlayer, pPlayer, USE_TOGGLE, 0 );
+
+ pPlayer->RemoveAllItems( true );// destroy all of the players weapons and items
+
+ // Kill off view model entities
+ pPlayer->DestroyViewModels();
+
+ pPlayer->SetConnected( PlayerDisconnected );
+ }
+ }
+ }
+
+ //=========================================================
+ //=========================================================
+ float CMultiplayRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
+ {
+ int iFallDamage = (int)falldamage.GetFloat();
+
+ switch ( iFallDamage )
+ {
+ case 1://progressive
+ pPlayer->m_Local.m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
+ return pPlayer->m_Local.m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
+ break;
+ default:
+ case 0:// fixed
+ return 10;
+ break;
+ }
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::AllowDamage( CBaseEntity *pVictim, const CTakeDamageInfo &info )
+ {
+ return true;
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
+ {
+ return true;
+ }
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::PlayerThink( CBasePlayer *pPlayer )
+ {
+ if ( g_fGameOver )
+ {
+ // clear attack/use commands from player
+ pPlayer->m_afButtonPressed = 0;
+ pPlayer->m_nButtons = 0;
+ pPlayer->m_afButtonReleased = 0;
+ }
+ }
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::PlayerSpawn( CBasePlayer *pPlayer )
+ {
+ bool addDefault;
+ CBaseEntity *pWeaponEntity = NULL;
+
+ pPlayer->EquipSuit();
+
+ addDefault = true;
+
+ while ( (pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL)
+ {
+ pWeaponEntity->Touch( pPlayer );
+ addDefault = false;
+ }
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::FPlayerCanRespawn( CBasePlayer *pPlayer )
+ {
+ return true;
+ }
+
+ //=========================================================
+ //=========================================================
+ float CMultiplayRules::FlPlayerSpawnTime( CBasePlayer *pPlayer )
+ {
+ return gpGlobals->curtime;//now!
+ }
+
+ bool CMultiplayRules::AllowAutoTargetCrosshair( void )
+ {
+ return ( aimcrosshair.GetInt() != 0 );
+ }
+
+ //=========================================================
+ // IPointsForKill - how many points awarded to anyone
+ // that kills this player?
+ //=========================================================
+ int CMultiplayRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
+ {
+ return 1;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ CBasePlayer *CMultiplayRules::GetDeathScorer( CBaseEntity *pKiller, CBaseEntity *pInflictor )
+ {
+ if ( pKiller)
+ {
+ if ( pKiller->Classify() == CLASS_PLAYER )
+ return (CBasePlayer*)pKiller;
+
+ // Killing entity might be specifying a scorer player
+ IScorer *pScorerInterface = dynamic_cast<IScorer*>( pKiller );
+ if ( pScorerInterface )
+ {
+ CBasePlayer *pPlayer = pScorerInterface->GetScorer();
+ if ( pPlayer )
+ return pPlayer;
+ }
+
+ // Inflicting entity might be specifying a scoring player
+ pScorerInterface = dynamic_cast<IScorer*>( pInflictor );
+ if ( pScorerInterface )
+ {
+ CBasePlayer *pPlayer = pScorerInterface->GetScorer();
+ if ( pPlayer )
+ return pPlayer;
+ }
+ }
+
+ return NULL;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Returns player who should receive credit for kill
+ //-----------------------------------------------------------------------------
+ CBasePlayer *CMultiplayRules::GetDeathScorer( CBaseEntity *pKiller, CBaseEntity *pInflictor, CBaseEntity *pVictim )
+ {
+ // if this method not overridden by subclass, just call our default implementation
+ return GetDeathScorer( pKiller, pInflictor );
+ }
+
+ //=========================================================
+ // PlayerKilled - someone/something killed this player
+ //=========================================================
+ void CMultiplayRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+ {
+ DeathNotice( pVictim, info );
+
+ // Find the killer & the scorer
+ CBaseEntity *pInflictor = info.GetInflictor();
+ CBaseEntity *pKiller = info.GetAttacker();
+ CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor, pVictim );
+
+ pVictim->IncrementDeathCount( 1 );
+
+ // 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 );
+
+ // Did the player kill himself?
+ if ( pVictim == pScorer )
+ {
+ if ( UseSuicidePenalty() )
+ {
+ // Players lose a frag for killing themselves
+ pVictim->IncrementFragCount( -1 );
+ }
+ }
+ else if ( pScorer )
+ {
+ // if a player dies in a deathmatch game and the killer is a client, award the killer some points
+ pScorer->IncrementFragCount( IPointsForKill( pScorer, pVictim ) );
+
+ // Allow the scorer to immediately paint a decal
+ pScorer->AllowImmediateDecalPainting();
+
+ // dvsents2: uncomment when removing all FireTargets
+ //variant_t value;
+ //g_EventQueue.AddEvent( "game_playerkill", "Use", value, 0, pScorer, pScorer );
+ FireTargets( "game_playerkill", pScorer, pScorer, USE_TOGGLE, 0 );
+ }
+ else
+ {
+ if ( UseSuicidePenalty() )
+ {
+ // Players lose a frag for letting the world kill them
+ pVictim->IncrementFragCount( -1 );
+ }
+ }
+ }
+
+ //=========================================================
+ // Deathnotice.
+ //=========================================================
+ void CMultiplayRules::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, pVictim );
+
+ // Custom damage type?
+ if ( info.GetDamageCustom() )
+ {
+ killer_weapon_name = GetDamageCustomString( info );
+ if ( pScorer )
+ {
+ killer_ID = pScorer->GetUserID();
+ }
+ }
+ else
+ {
+ // Is the killer a client?
+ if ( pScorer )
+ {
+ killer_ID = pScorer->GetUserID();
+
+ 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()->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;
+ }
+ }
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
+ if ( event )
+ {
+ event->SetInt("userid", pVictim->GetUserID() );
+ event->SetInt("attacker", killer_ID );
+ event->SetInt("customkill", info.GetDamageCustom() );
+ event->SetInt("priority", 7 ); // HLTV event priority, not transmitted
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ }
+
+ //=========================================================
+ // FlWeaponRespawnTime - what is the time in the future
+ // at which this weapon may spawn?
+ //=========================================================
+ float CMultiplayRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon )
+ {
+ if ( weaponstay.GetInt() > 0 )
+ {
+ // make sure it's only certain weapons
+ if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
+ {
+ return gpGlobals->curtime + 0; // weapon respawns almost instantly
+ }
+ }
+
+ return gpGlobals->curtime + WEAPON_RESPAWN_TIME;
+ }
+
+ // when we are within this close to running out of entities, items
+ // marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
+ #define ENTITY_INTOLERANCE 100
+
+ //=========================================================
+ // FlWeaponRespawnTime - Returns 0 if the weapon can respawn
+ // now, otherwise it returns the time at which it can try
+ // to spawn again.
+ //=========================================================
+ float CMultiplayRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon )
+ {
+ if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
+ {
+ if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) )
+ return 0;
+
+ // we're past the entity tolerance level, so delay the respawn
+ return FlWeaponRespawnTime( pWeapon );
+ }
+
+ return 0;
+ }
+
+ //=========================================================
+ // VecWeaponRespawnSpot - where should this weapon spawn?
+ // Some game variations may choose to randomize spawn locations
+ //=========================================================
+ Vector CMultiplayRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
+ {
+ return pWeapon->GetAbsOrigin();
+ }
+
+ //=========================================================
+ // WeaponShouldRespawn - any conditions inhibiting the
+ // respawning of this weapon?
+ //=========================================================
+ int CMultiplayRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon )
+ {
+ if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) )
+ {
+ return GR_WEAPON_RESPAWN_NO;
+ }
+
+ return GR_WEAPON_RESPAWN_YES;
+ }
+
+ //=========================================================
+ // CanHaveWeapon - returns false if the player is not allowed
+ // to pick up this weapon
+ //=========================================================
+ bool CMultiplayRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem )
+ {
+ if ( weaponstay.GetInt() > 0 )
+ {
+ if ( pItem->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD )
+ return BaseClass::CanHavePlayerItem( pPlayer, pItem );
+
+ // check if the player already has this weapon
+ for ( int i = 0 ; i < pPlayer->WeaponCount() ; i++ )
+ {
+ if ( pPlayer->GetWeapon(i) == pItem )
+ {
+ return false;
+ }
+ }
+ }
+
+ return BaseClass::CanHavePlayerItem( pPlayer, pItem );
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem )
+ {
+ return true;
+ }
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem )
+ {
+ }
+
+ //=========================================================
+ //=========================================================
+ int CMultiplayRules::ItemShouldRespawn( CItem *pItem )
+ {
+ if ( pItem->HasSpawnFlags( SF_NORESPAWN ) )
+ {
+ return GR_ITEM_RESPAWN_NO;
+ }
+
+ return GR_ITEM_RESPAWN_YES;
+ }
+
+
+ //=========================================================
+ // At what time in the future may this Item respawn?
+ //=========================================================
+ float CMultiplayRules::FlItemRespawnTime( CItem *pItem )
+ {
+ return gpGlobals->curtime + ITEM_RESPAWN_TIME;
+ }
+
+ //=========================================================
+ // Where should this item respawn?
+ // Some game variations may choose to randomize spawn locations
+ //=========================================================
+ Vector CMultiplayRules::VecItemRespawnSpot( CItem *pItem )
+ {
+ return pItem->GetAbsOrigin();
+ }
+
+ //=========================================================
+ // What angles should this item use to respawn?
+ //=========================================================
+ QAngle CMultiplayRules::VecItemRespawnAngles( CItem *pItem )
+ {
+ return pItem->GetAbsAngles();
+ }
+
+ //=========================================================
+ //=========================================================
+ void CMultiplayRules::PlayerGotAmmo( CBaseCombatCharacter *pPlayer, char *szName, int iCount )
+ {
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::IsAllowedToSpawn( CBaseEntity *pEntity )
+ {
+ // if ( pEntity->GetFlags() & FL_NPC )
+ // return false;
+
+ return true;
+ }
+
+
+ //=========================================================
+ //=========================================================
+ float CMultiplayRules::FlHealthChargerRechargeTime( void )
+ {
+ return 60;
+ }
+
+
+ float CMultiplayRules::FlHEVChargerRechargeTime( void )
+ {
+ return 30;
+ }
+
+ //=========================================================
+ //=========================================================
+ int CMultiplayRules::DeadPlayerWeapons( CBasePlayer *pPlayer )
+ {
+ return GR_PLR_DROP_GUN_ACTIVE;
+ }
+
+ //=========================================================
+ //=========================================================
+ int CMultiplayRules::DeadPlayerAmmo( CBasePlayer *pPlayer )
+ {
+ return GR_PLR_DROP_AMMO_ACTIVE;
+ }
+
+ CBaseEntity *CMultiplayRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
+ {
+ CBaseEntity *pentSpawnSpot = BaseClass::GetPlayerSpawnSpot( pPlayer );
+
+ //!! replace this with an Event
+ /*
+ if ( IsMultiplayer() && pentSpawnSpot->m_target )
+ {
+ FireTargets( STRING(pentSpawnSpot->m_target), pPlayer, pPlayer, USE_TOGGLE, 0 ); // dvsents2: what is this code supposed to do?
+ }
+ */
+
+ return pentSpawnSpot;
+ }
+
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker )
+ {
+ return ( PlayerRelationship( pListener, pSpeaker ) == GR_TEAMMATE );
+ }
+
+ int CMultiplayRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
+ {
+ // half life deathmatch has only enemies
+ return GR_NOTTEAMMATE;
+ }
+
+ bool CMultiplayRules::PlayFootstepSounds( CBasePlayer *pl )
+ {
+ if ( footsteps.GetInt() == 0 )
+ return false;
+
+ if ( pl->IsOnLadder() || pl->GetAbsVelocity().Length2D() > 220 )
+ return true; // only make step sounds in multiplayer if the player is moving fast enough
+
+ return false;
+ }
+
+ bool CMultiplayRules::FAllowFlashlight( void )
+ {
+ return flashlight.GetInt() != 0;
+ }
+
+ //=========================================================
+ //=========================================================
+ bool CMultiplayRules::FAllowNPCs( void )
+ {
+ return true; // E3 hack
+ return ( allowNPCs.GetInt() != 0 );
+ }
+
+ //=========================================================
+ //======== CMultiplayRules private functions ===========
+
+ void CMultiplayRules::GoToIntermission( void )
+ {
+ if ( g_fGameOver )
+ return;
+
+ g_fGameOver = true;
+
+ float flWaitTime = mp_chattime.GetInt();
+
+ if ( tv_delaymapchange.GetBool() )
+ {
+ if ( HLTVDirector()->IsActive() )
+ flWaitTime = MAX( flWaitTime, HLTVDirector()->GetDelay() );
+ }
+
+ m_flIntermissionEndTime = gpGlobals->curtime + flWaitTime;
+
+ for ( int i = 1; i <= MAX_PLAYERS; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
+ }
+ }
+
+ void StripChar(char *szBuffer, const char cWhiteSpace )
+ {
+
+ while ( char *pSpace = strchr( szBuffer, cWhiteSpace ) )
+ {
+ char *pNextChar = pSpace + sizeof(char);
+ V_strcpy( pSpace, pNextChar );
+ }
+ }
+
+ void CMultiplayRules::GetNextLevelName( char *pszNextMap, int bufsize, bool bRandom /* = false */ )
+ {
+ char mapcfile[256];
+ DetermineMapCycleFilename( mapcfile, sizeof(mapcfile), false );
+
+ // Check the time of the mapcycle file and re-populate the list of level names if the file has been modified
+ const int nMapCycleTimeStamp = filesystem->GetPathTime( mapcfile, "GAME" );
+
+ if ( 0 == nMapCycleTimeStamp )
+ {
+ // Map cycle file does not exist, make a list containing only the current map
+ char *szCurrentMapName = new char[MAX_MAP_NAME];
+ Q_strncpy( szCurrentMapName, STRING(gpGlobals->mapname), MAX_MAP_NAME );
+ m_MapList.AddToTail( szCurrentMapName );
+ }
+ else
+ {
+ // If map cycle file has changed or this is the first time through ...
+ if ( m_nMapCycleTimeStamp != nMapCycleTimeStamp )
+ {
+ // Reset map index and map cycle timestamp
+ m_nMapCycleTimeStamp = nMapCycleTimeStamp;
+ m_nMapCycleindex = 0;
+
+ LoadMapCycleFile();
+ }
+ }
+
+ // If somehow we have no maps in the list then add the current one
+ if ( 0 == m_MapList.Count() )
+ {
+ char *szDefaultMapName = new char[MAX_MAP_NAME];
+ Q_strncpy( szDefaultMapName, STRING(gpGlobals->mapname), MAX_MAP_NAME );
+ m_MapList.AddToTail( szDefaultMapName );
+ }
+
+ if ( bRandom )
+ {
+ m_nMapCycleindex = RandomInt( 0, m_MapList.Count() - 1 );
+ }
+
+ // Here's the return value
+ Q_strncpy( pszNextMap, m_MapList[m_nMapCycleindex], bufsize);
+ }
+
+ void CMultiplayRules::DetermineMapCycleFilename( char *pszResult, int nSizeResult, bool bForceSpew )
+ {
+ static char szLastResult[ 256];
+
+ const char *pszVar = mapcyclefile.GetString();
+ if ( *pszVar == '\0' )
+ {
+ if ( bForceSpew || V_stricmp( szLastResult, "__novar") )
+ {
+ Msg( "mapcyclefile convar not set.\n" );
+ V_strcpy_safe( szLastResult, "__novar" );
+ }
+ *pszResult = '\0';
+ return;
+ }
+
+ char szRecommendedName[ 256 ];
+ V_sprintf_safe( szRecommendedName, "cfg/%s", pszVar );
+
+ // First, look for a mapcycle file in the cfg directory, which is preferred
+ V_strncpy( pszResult, szRecommendedName, nSizeResult );
+ if ( filesystem->FileExists( pszResult, "GAME" ) )
+ {
+ if ( bForceSpew || V_stricmp( szLastResult, pszResult) )
+ {
+ Msg( "Using map cycle file '%s'.\n", pszResult );
+ V_strcpy_safe( szLastResult, pszResult );
+ }
+ return;
+ }
+
+ // Nope? Try the root.
+ V_strncpy( pszResult, pszVar, nSizeResult );
+ if ( filesystem->FileExists( pszResult, "GAME" ) )
+ {
+ if ( bForceSpew || V_stricmp( szLastResult, pszResult) )
+ {
+ Msg( "Using map cycle file '%s'. ('%s' was not found.)\n", pszResult, szRecommendedName );
+ V_strcpy_safe( szLastResult, pszResult );
+ }
+ return;
+ }
+
+ // Nope? Use the default.
+ if ( !V_stricmp( pszVar, "mapcycle.txt" ) )
+ {
+ V_strncpy( pszResult, "cfg/mapcycle_default.txt", nSizeResult );
+ if ( filesystem->FileExists( pszResult, "GAME" ) )
+ {
+ if ( bForceSpew || V_stricmp( szLastResult, pszResult) )
+ {
+ Msg( "Using map cycle file '%s'. ('%s' was not found.)\n", pszResult, szRecommendedName );
+ V_strcpy_safe( szLastResult, pszResult );
+ }
+ return;
+ }
+ }
+
+ // Failed
+ *pszResult = '\0';
+ if ( bForceSpew || V_stricmp( szLastResult, "__notfound") )
+ {
+ Msg( "Map cycle file '%s' was not found.\n", szRecommendedName );
+ V_strcpy_safe( szLastResult, "__notfound" );
+ }
+ }
+
+ void CMultiplayRules::LoapMapCycleFileIntoVector( const char *pszMapCycleFile, CUtlVector<char *> &mapList )
+ {
+ CUtlBuffer buf;
+ if ( !filesystem->ReadFile( pszMapCycleFile, "GAME", buf ) )
+ return;
+ buf.PutChar( 0 );
+ V_SplitString( (char*)buf.Base(), "\n", mapList );
+
+ for ( int i = 0; i < mapList.Count(); i++ )
+ {
+ bool bIgnore = false;
+
+ // Strip out the spaces in the name
+ StripChar( mapList[i] , '\r');
+ StripChar( mapList[i] , ' ');
+
+ if ( !Q_strncmp( mapList[i], "//", 2 ) || mapList[i][0] == '\0' )
+ {
+ bIgnore = true;
+ }
+ else if ( !engine->IsMapValid( mapList[i] ) )
+ {
+ bIgnore = true;
+
+ // If the engine doesn't consider it a valid map remove it from the lists
+ Warning( "Invalid map '%s' included in map cycle file. Ignored.\n", mapList[i] );
+ }
+
+ if ( bIgnore )
+ {
+ delete [] mapList[i];
+ mapList.Remove( i );
+ --i;
+ }
+ }
+ }
+
+ void CMultiplayRules::FreeMapCycleFileVector( CUtlVector<char *> &mapList )
+ {
+ // Clear out existing map list. Not using Purge() or PurgeAndDeleteAll() because they won't delete [] each element.
+ for ( int i = 0; i < mapList.Count(); i++ )
+ {
+ delete [] mapList[i];
+ }
+
+ mapList.RemoveAll();
+ }
+
+ bool CMultiplayRules::IsMapInMapCycle( const char *pszName )
+ {
+ for ( int i = 0; i < m_MapList.Count(); i++ )
+ {
+ if ( V_stricmp( pszName, m_MapList[i] ) == 0 )
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void CMultiplayRules::ChangeLevel( void )
+ {
+ char szNextMap[MAX_MAP_NAME];
+
+ if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
+ {
+ Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
+ }
+ else
+ {
+ GetNextLevelName( szNextMap, sizeof(szNextMap) );
+ IncrementMapCycleIndex();
+ }
+
+ ChangeLevelToMap( szNextMap );
+ }
+
+ void CMultiplayRules::LoadMapCycleFile( void )
+ {
+ char mapcfile[256];
+ DetermineMapCycleFilename( mapcfile, sizeof(mapcfile), false );
+
+ FreeMapCycleFileVector( m_MapList );
+
+ // Repopulate map list from mapcycle file
+ LoapMapCycleFileIntoVector( mapcfile, m_MapList );
+
+ // Load server's mapcycle into network string table for client-side voting
+ if ( g_pStringTableServerMapCycle )
+ {
+ CUtlString sFileList;
+ for ( int i = 0; i < m_MapList.Count(); i++ )
+ {
+ sFileList += m_MapList[i];
+ sFileList += '\n';
+ }
+
+ g_pStringTableServerMapCycle->AddString( CBaseEntity::IsServer(), "ServerMapCycle", sFileList.Length() + 1, sFileList.String() );
+ }
+
+#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
+ if ( g_pStringTableServerPopFiles )
+ {
+ // Search for all pop files that are prefixed with the current map name
+ CUtlString sFileList;
+
+ char szBaseName[_MAX_PATH];
+ V_snprintf( szBaseName, sizeof( szBaseName ), "scripts/population/%s*.pop", STRING(gpGlobals->mapname) );
+
+ FileFindHandle_t popHandle;
+ const char *pPopFileName = filesystem->FindFirst( szBaseName, &popHandle );
+
+ while ( pPopFileName && pPopFileName[ 0 ] != '\0' )
+ {
+ // Skip it if it's a directory or is the folder info
+ if ( filesystem->FindIsDirectory( popHandle ) )
+ {
+ pPopFileName = filesystem->FindNext( popHandle );
+ continue;
+ }
+
+ const char *pchPopPostfix = StringAfterPrefix( pPopFileName, STRING(gpGlobals->mapname) );
+ if ( pchPopPostfix )
+ {
+ char szShortName[_MAX_PATH];
+ V_strncpy( szShortName, ( ( pchPopPostfix[ 0 ] == '_' ) ? ( pchPopPostfix + 1 ) : "normal" ), sizeof( szShortName ) ); // skip the '_'
+ V_StripExtension( szShortName, szShortName, sizeof( szShortName ) );
+
+ sFileList += szShortName;
+ sFileList += '\n';
+ }
+
+ pPopFileName = filesystem->FindNext( popHandle );
+ }
+
+ filesystem->FindClose( popHandle );
+
+ if ( sFileList.Length() > 0 )
+ {
+ g_pStringTableServerPopFiles->AddString( CBaseEntity::IsServer(), "ServerPopFiles", sFileList.Length() + 1, sFileList.String() );
+ }
+ }
+
+ if ( g_pStringTableServerMapCycleMvM )
+ {
+ ConVarRef tf_mvm_missioncyclefile( "tf_mvm_missioncyclefile" );
+ KeyValues *pKV = new KeyValues( tf_mvm_missioncyclefile.GetString() );
+ if ( pKV->LoadFromFile( g_pFullFileSystem, tf_mvm_missioncyclefile.GetString(), "MOD" ) )
+ {
+ CUtlVector<CUtlString> mapList;
+
+ // Parse the maps and send a list to each client for vote options
+ int iMaxCat = pKV->GetInt( "categories", 0 );
+ for ( int iCat = 1; iCat <= iMaxCat; iCat++ )
+ {
+ KeyValues *pCategory = pKV->FindKey( UTIL_VarArgs( "%d", iCat ), false );
+ if ( pCategory )
+ {
+ int iMapCount = pCategory->GetInt( "count", 0 );
+ for ( int iMap = 1; iMap <= iMapCount; ++iMap )
+ {
+ KeyValues *pMission = pCategory->FindKey( UTIL_VarArgs( "%d", iMap ), false );
+ if ( pMission )
+ {
+ const char *pszMap = pMission->GetString( "map", "" );
+ int iIdx = mapList.Find( pszMap );
+ if ( !mapList.IsValidIndex( iIdx ) )
+ {
+ mapList.AddToTail( pszMap );
+ }
+ }
+ }
+ }
+ }
+
+ if ( mapList.Count() )
+ {
+ CUtlString sFileList;
+ for ( int i = 0; i < mapList.Count(); i++ )
+ {
+ sFileList += mapList[i];
+ sFileList += '\n';
+ }
+
+ g_pStringTableServerMapCycleMvM->AddString( CBaseEntity::IsServer(), "ServerMapCycleMvM", sFileList.Length() + 1, sFileList.String() );
+ }
+
+ pKV->deleteThis();
+ }
+ }
+#endif
+
+ // If the current map selection is in the list, set m_nMapCycleindex to the map that follows it.
+ for ( int i = 0; i < m_MapList.Count(); i++ )
+ {
+ if ( V_strcmp( STRING( gpGlobals->mapname ), m_MapList[i] ) == 0 )
+ {
+ m_nMapCycleindex = i;
+ IncrementMapCycleIndex();
+ break;
+ }
+ }
+ }
+
+ void CMultiplayRules::ChangeLevelToMap( const char *pszMap )
+ {
+ g_fGameOver = true;
+ m_flTimeLastMapChangeOrPlayerWasConnected = 0.0f;
+ Msg( "CHANGE LEVEL: %s\n", pszMap );
+ engine->ChangeLevel( pszMap, NULL );
+ }
+
+
+#endif
+
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Shared script resource of voice menu commands and hud strings
+ //-----------------------------------------------------------------------------
+ void CMultiplayRules::LoadVoiceCommandScript( void )
+ {
+ KeyValues *pKV = new KeyValues( "VoiceCommands" );
+
+ if ( pKV->LoadFromFile( filesystem, "scripts/voicecommands.txt", "GAME" ) )
+ {
+ for ( KeyValues *menu = pKV->GetFirstSubKey(); menu != NULL; menu = menu->GetNextKey() )
+ {
+ int iMenuIndex = m_VoiceCommandMenus.AddToTail();
+
+ int iNumItems = 0;
+
+ // for each subkey of this menu, add a menu item
+ for ( KeyValues *menuitem = menu->GetFirstSubKey(); menuitem != NULL; menuitem = menuitem->GetNextKey() )
+ {
+ iNumItems++;
+
+ if ( iNumItems > 9 )
+ {
+ Warning( "Trying to load more than 9 menu items in voicecommands.txt, extras ignored" );
+ continue;
+ }
+
+ VoiceCommandMenuItem_t item;
+
+#ifndef CLIENT_DLL
+ int iConcept = GetMPConceptIndexFromString( menuitem->GetString( "concept", "" ) );
+ if ( iConcept == MP_CONCEPT_NONE )
+ {
+ Warning( "Voicecommand script attempting to use unknown concept. Need to define new concepts in code. ( %s )\n", menuitem->GetString( "concept", "" ) );
+ }
+ item.m_iConcept = iConcept;
+
+ item.m_bShowSubtitle = ( menuitem->GetInt( "show_subtitle", 0 ) > 0 );
+ item.m_bDistanceBasedSubtitle = ( menuitem->GetInt( "distance_check_subtitle", 0 ) > 0 );
+
+ Q_strncpy( item.m_szGestureActivity, menuitem->GetString( "activity", "" ), sizeof( item.m_szGestureActivity ) );
+#else
+ Q_strncpy( item.m_szSubtitle, menuitem->GetString( "subtitle", "" ), MAX_VOICE_COMMAND_SUBTITLE );
+ Q_strncpy( item.m_szMenuLabel, menuitem->GetString( "menu_label", "" ), MAX_VOICE_COMMAND_SUBTITLE );
+
+#endif
+ m_VoiceCommandMenus.Element( iMenuIndex ).AddToTail( item );
+ }
+ }
+ }
+
+ pKV->deleteThis();
+ }
+
+#ifndef CLIENT_DLL
+
+ void CMultiplayRules::SkipNextMapInCycle()
+ {
+ char szSkippedMap[MAX_MAP_NAME];
+ char szNextMap[MAX_MAP_NAME];
+
+ GetNextLevelName( szSkippedMap, sizeof( szSkippedMap ) );
+ IncrementMapCycleIndex();
+ GetNextLevelName( szNextMap, sizeof( szNextMap ) );
+
+ Msg( "Skipping: %s\tNext map: %s\n", szSkippedMap, szNextMap );
+
+ if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
+ {
+ Msg( "Warning! \"nextlevel\" is set to \"%s\" and will override the next map to be played.\n", nextlevel.GetString() );
+ }
+ }
+
+ void CMultiplayRules::IncrementMapCycleIndex()
+ {
+ // Reset index if we've passed the end of the map list
+ if ( ++m_nMapCycleindex >= m_MapList.Count() )
+ {
+ m_nMapCycleindex = 0;
+ }
+ }
+
+ bool CMultiplayRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( pEdict );
+
+ const char *pcmd = args[0];
+ if ( FStrEq( pcmd, "voicemenu" ) )
+ {
+ if ( args.ArgC() < 3 )
+ return true;
+
+ CBaseMultiplayerPlayer *pMultiPlayerPlayer = dynamic_cast< CBaseMultiplayerPlayer * >( pPlayer );
+
+ if ( pMultiPlayerPlayer )
+ {
+ int iMenu = atoi( args[1] );
+ int iItem = atoi( args[2] );
+
+ VoiceCommand( pMultiPlayerPlayer, iMenu, iItem );
+ }
+
+ return true;
+ }
+
+ return BaseClass::ClientCommand( pEdict, args );
+ }
+
+ void CMultiplayRules::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
+ {
+ CBaseMultiplayerPlayer *pPlayer = dynamic_cast< CBaseMultiplayerPlayer * >( CBaseEntity::Instance( pEntity ) );
+
+ if ( !pPlayer )
+ return;
+
+ char const *pszCommand = pKeyValues->GetName();
+ if ( pszCommand && pszCommand[0] )
+ {
+ if ( FStrEq( pszCommand, "AchievementEarned" ) )
+ {
+ if ( pPlayer->ShouldAnnounceAchievement() )
+ {
+ int nAchievementID = pKeyValues->GetInt( "achievementID" );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "achievement_earned" );
+ if ( event )
+ {
+ event->SetInt( "player", pPlayer->entindex() );
+ event->SetInt( "achievement", nAchievementID );
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->OnAchievementEarned( nAchievementID );
+ }
+ }
+ else if ( FStrEq( pszCommand, "SendServerMapCycle" ) )
+ {
+ LoadMapCycleFile();
+ }
+ }
+ }
+
+ VoiceCommandMenuItem_t *CMultiplayRules::VoiceCommand( CBaseMultiplayerPlayer *pPlayer, int iMenu, int iItem )
+ {
+ // have the player speak the concept that is in a particular menu slot
+ if ( !pPlayer )
+ return NULL;
+
+ if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
+ return NULL;
+
+ if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
+ return NULL;
+
+ VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );
+
+ Assert( pItem );
+
+ char szResponse[AI_Response::MAX_RESPONSE_NAME];
+
+ if ( pPlayer->CanSpeakVoiceCommand() )
+ {
+ CMultiplayer_Expresser *pExpresser = pPlayer->GetMultiplayerExpresser();
+ Assert( pExpresser );
+ pExpresser->AllowMultipleScenes();
+
+ if ( pPlayer->SpeakConceptIfAllowed( pItem->m_iConcept, NULL, szResponse, AI_Response::MAX_RESPONSE_NAME ) )
+ {
+ // show a subtitle if we need to
+ if ( pItem->m_bShowSubtitle )
+ {
+ CRecipientFilter filter;
+
+ if ( pItem->m_bDistanceBasedSubtitle )
+ {
+ filter.AddRecipientsByPAS( pPlayer->WorldSpaceCenter() );
+
+ // further reduce the range to a certain radius
+ int i;
+ for ( i = filter.GetRecipientCount()-1; i >= 0; i-- )
+ {
+ int index = filter.GetRecipientIndex(i);
+
+ CBasePlayer *pListener = UTIL_PlayerByIndex( index );
+
+ if ( pListener && pListener != pPlayer )
+ {
+ float flDist = ( pListener->WorldSpaceCenter() - pPlayer->WorldSpaceCenter() ).Length2D();
+
+ if ( flDist > VOICE_COMMAND_MAX_SUBTITLE_DIST )
+ filter.RemoveRecipientByPlayerIndex( index );
+ }
+ }
+ }
+ else
+ {
+ filter.AddAllPlayers();
+ }
+
+ // if we aren't a disguised spy
+ if ( !pPlayer->ShouldShowVoiceSubtitleToEnemy() )
+ {
+ // remove players on other teams
+ filter.RemoveRecipientsNotOnTeam( pPlayer->GetTeam() );
+ }
+
+ // Register this event in the mod-specific usermessages .cpp file if you hit this assert
+ Assert( usermessages->LookupUserMessage( "VoiceSubtitle" ) != -1 );
+
+ // Send a subtitle to anyone in the PAS
+ UserMessageBegin( filter, "VoiceSubtitle" );
+ WRITE_BYTE( pPlayer->entindex() );
+ WRITE_BYTE( iMenu );
+ WRITE_BYTE( iItem );
+ MessageEnd();
+ }
+
+ pPlayer->NoteSpokeVoiceCommand( szResponse );
+
+#ifdef NEXT_BOT
+ // let bots react to player's voice commands
+ CUtlVector< INextBot * > botVector;
+ TheNextBots().CollectAllBots( &botVector );
+
+ for( int i=0; i<botVector.Count(); ++i )
+ {
+ botVector[i]->OnActorEmoted( pPlayer, pItem->m_iConcept );
+ }
+#endif
+ }
+ else
+ {
+ pItem = NULL;
+ }
+
+ pExpresser->DisallowMultipleScenes();
+ return pItem;
+ }
+
+ return NULL;
+ }
+
+ bool CMultiplayRules::IsLoadingBugBaitReport()
+ {
+ return ( !engine->IsDedicatedServer()&& CommandLine()->CheckParm( "-bugbait" ) && sv_cheats->GetBool() );
+ }
+
+ void CMultiplayRules::HaveAllPlayersSpeakConceptIfAllowed( int iConcept, int iTeam /* = TEAM_UNASSIGNED */, const char *modifiers /* = NULL */ )
+ {
+ CBaseMultiplayerPlayer *pPlayer;
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
+
+ if ( !pPlayer )
+ continue;
+
+ if ( iTeam != TEAM_UNASSIGNED )
+ {
+ if ( pPlayer->GetTeamNumber() != iTeam )
+ continue;
+ }
+
+ pPlayer->SpeakConceptIfAllowed( iConcept, modifiers );
+ }
+ }
+
+ void CMultiplayRules::ClientSettingsChanged( CBasePlayer *pPlayer )
+ {
+ // NVNT see if this user is still or has began using a haptic device
+ const char *pszHH = engine->GetClientConVarValue( pPlayer->entindex(), "hap_HasDevice" );
+ if( pszHH )
+ {
+ int iHH = atoi( pszHH );
+ pPlayer->SetHaptics( iHH != 0 );
+ }
+
+ }
+ void CMultiplayRules::GetTaggedConVarList( KeyValues *pCvarTagList )
+ {
+ BaseClass::GetTaggedConVarList( pCvarTagList );
+
+ // sv_gravity
+ KeyValues *pGravity = new KeyValues( "sv_gravity" );
+ pGravity->SetString( "convar", "sv_gravity" );
+ pGravity->SetString( "tag", "gravity" );
+
+ pCvarTagList->AddSubKey( pGravity );
+
+ // sv_alltalk
+ KeyValues *pAllTalk = new KeyValues( "sv_alltalk" );
+ pAllTalk->SetString( "convar", "sv_alltalk" );
+ pAllTalk->SetString( "tag", "alltalk" );
+
+ pCvarTagList->AddSubKey( pAllTalk );
+ }
+
+#else
+
+ const char *CMultiplayRules::GetVoiceCommandSubtitle( int iMenu, int iItem )
+ {
+ Assert( iMenu >= 0 && iMenu < m_VoiceCommandMenus.Count() );
+ if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
+ return "";
+
+ Assert( iItem >= 0 && iItem < m_VoiceCommandMenus.Element( iMenu ).Count() );
+ if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
+ return "";
+
+ VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );
+
+ Assert( pItem );
+
+ return pItem->m_szSubtitle;
+ }
+
+ // Returns false if no such menu is declared or if it's an empty menu
+ bool CMultiplayRules::GetVoiceMenuLabels( int iMenu, KeyValues *pKV )
+ {
+ Assert( iMenu >= 0 && iMenu < m_VoiceCommandMenus.Count() );
+ if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
+ return false;
+
+ int iNumItems = m_VoiceCommandMenus.Element( iMenu ).Count();
+
+ for ( int i=0; i<iNumItems; i++ )
+ {
+ VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( i );
+
+ KeyValues *pLabelKV = new KeyValues( pItem->m_szMenuLabel );
+
+ pKV->AddSubKey( pLabelKV );
+ }
+
+ return iNumItems > 0;
+ }
+
+#endif