//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
# include "cbase.h"
# include "hl2mp_gamerules.h"
# include "viewport_panel_names.h"
# include "gameeventdefs.h"
# include <KeyValues.h>
# include "ammodef.h"
# ifdef CLIENT_DLL
# include "c_hl2mp_player.h"
# else
# include "eventqueue.h"
# include "player.h"
# include "gamerules.h"
# include "game.h"
# include "items.h"
# include "entitylist.h"
# include "mapentities.h"
# include "in_buttons.h"
# include <ctype.h>
# include "voice_gamemgr.h"
# include "iscorer.h"
# include "hl2mp_player.h"
# include "weapon_hl2mpbasehlmpcombatweapon.h"
# include "team.h"
# include "voice_gamemgr.h"
# include "hl2mp_gameinterface.h"
# include "hl2mp_cvars.h"
# ifdef DEBUG
# include "hl2mp_bot_temp.h"
# endif
extern void respawn ( CBaseEntity * pEdict , bool fCopyCorpse ) ;
extern bool FindInList ( const char * * pStrings , const char * pToFind ) ;
ConVar sv_hl2mp_weapon_respawn_time ( " sv_hl2mp_weapon_respawn_time " , " 20 " , FCVAR_GAMEDLL | FCVAR_NOTIFY ) ;
ConVar sv_hl2mp_item_respawn_time ( " sv_hl2mp_item_respawn_time " , " 30 " , FCVAR_GAMEDLL | FCVAR_NOTIFY ) ;
ConVar sv_report_client_settings ( " sv_report_client_settings " , " 0 " , FCVAR_GAMEDLL | FCVAR_NOTIFY ) ;
extern ConVar mp_chattime ;
extern CBaseEntity * g_pLastCombineSpawn ;
extern CBaseEntity * g_pLastRebelSpawn ;
# define WEAPON_MAX_DISTANCE_FROM_SPAWN 64
# endif
REGISTER_GAMERULES_CLASS ( CHL2MPRules ) ;
BEGIN_NETWORK_TABLE_NOBASE ( CHL2MPRules , DT_HL2MPRules )
# ifdef CLIENT_DLL
RecvPropBool ( RECVINFO ( m_bTeamPlayEnabled ) ) ,
# else
SendPropBool ( SENDINFO ( m_bTeamPlayEnabled ) ) ,
# endif
END_NETWORK_TABLE ( )
LINK_ENTITY_TO_CLASS ( hl2mp_gamerules , CHL2MPGameRulesProxy ) ;
IMPLEMENT_NETWORKCLASS_ALIASED ( HL2MPGameRulesProxy , DT_HL2MPGameRulesProxy )
static HL2MPViewVectors g_HL2MPViewVectors (
Vector ( 0 , 0 , 64 ) , //VEC_VIEW (m_vView)
Vector ( - 16 , - 16 , 0 ) , //VEC_HULL_MIN (m_vHullMin)
Vector ( 16 , 16 , 72 ) , //VEC_HULL_MAX (m_vHullMax)
Vector ( - 16 , - 16 , 0 ) , //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
Vector ( 16 , 16 , 36 ) , //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
Vector ( 0 , 0 , 28 ) , //VEC_DUCK_VIEW (m_vDuckView)
Vector ( - 10 , - 10 , - 10 ) , //VEC_OBS_HULL_MIN (m_vObsHullMin)
Vector ( 10 , 10 , 10 ) , //VEC_OBS_HULL_MAX (m_vObsHullMax)
Vector ( 0 , 0 , 14 ) , //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
Vector ( - 16 , - 16 , 0 ) , //VEC_CROUCH_TRACE_MIN (m_vCrouchTraceMin)
Vector ( 16 , 16 , 60 ) //VEC_CROUCH_TRACE_MAX (m_vCrouchTraceMax)
) ;
static const char * s_PreserveEnts [ ] =
{
" ai_network " ,
" ai_hint " ,
" hl2mp_gamerules " ,
" team_manager " ,
" player_manager " ,
" env_soundscape " ,
" env_soundscape_proxy " ,
" env_soundscape_triggerable " ,
" env_sun " ,
" env_wind " ,
" env_fog_controller " ,
" func_brush " ,
" func_wall " ,
" func_buyzone " ,
" func_illusionary " ,
" infodecal " ,
" info_projecteddecal " ,
" info_node " ,
" info_target " ,
" info_node_hint " ,
" info_player_deathmatch " ,
" info_player_combine " ,
" info_player_rebel " ,
" info_map_parameters " ,
" keyframe_rope " ,
" move_rope " ,
" info_ladder " ,
" player " ,
" point_viewcontrol " ,
" scene_manager " ,
" shadow_control " ,
" sky_camera " ,
" soundent " ,
" trigger_soundscape " ,
" viewmodel " ,
" predicted_viewmodel " ,
" worldspawn " ,
" point_devshot_camera " ,
" " , // END Marker
} ;
# ifdef CLIENT_DLL
void RecvProxy_HL2MPRules ( const RecvProp * pProp , void * * pOut , void * pData , int objectID )
{
CHL2MPRules * pRules = HL2MPRules ( ) ;
Assert ( pRules ) ;
* pOut = pRules ;
}
BEGIN_RECV_TABLE ( CHL2MPGameRulesProxy , DT_HL2MPGameRulesProxy )
RecvPropDataTable ( " hl2mp_gamerules_data " , 0 , 0 , & REFERENCE_RECV_TABLE ( DT_HL2MPRules ) , RecvProxy_HL2MPRules )
END_RECV_TABLE ( )
# else
void * SendProxy_HL2MPRules ( const SendProp * pProp , const void * pStructBase , const void * pData , CSendProxyRecipients * pRecipients , int objectID )
{
CHL2MPRules * pRules = HL2MPRules ( ) ;
Assert ( pRules ) ;
return pRules ;
}
BEGIN_SEND_TABLE ( CHL2MPGameRulesProxy , DT_HL2MPGameRulesProxy )
SendPropDataTable ( " hl2mp_gamerules_data " , 0 , & REFERENCE_SEND_TABLE ( DT_HL2MPRules ) , SendProxy_HL2MPRules )
END_SEND_TABLE ( )
# endif
# ifndef CLIENT_DLL
class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
{
public :
virtual bool CanPlayerHearPlayer ( CBasePlayer * pListener , CBasePlayer * pTalker , bool & bProximity )
{
return ( pListener - > GetTeamNumber ( ) = = pTalker - > GetTeamNumber ( ) ) ;
}
} ;
CVoiceGameMgrHelper g_VoiceGameMgrHelper ;
IVoiceGameMgrHelper * g_pVoiceGameMgrHelper = & g_VoiceGameMgrHelper ;
# endif
// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
char * sTeamNames [ ] =
{
" Unassigned " ,
" Spectator " ,
" Combine " ,
" Rebels " ,
} ;
CHL2MPRules : : CHL2MPRules ( )
{
# ifndef CLIENT_DLL
// Create the team managers
for ( int i = 0 ; i < ARRAYSIZE ( sTeamNames ) ; i + + )
{
CTeam * pTeam = static_cast < CTeam * > ( CreateEntityByName ( " team_manager " ) ) ;
pTeam - > Init ( sTeamNames [ i ] , i ) ;
g_Teams . AddToTail ( pTeam ) ;
}
m_bTeamPlayEnabled = teamplay . GetBool ( ) ;
m_flIntermissionEndTime = 0.0f ;
m_flGameStartTime = 0 ;
m_hRespawnableItemsAndWeapons . RemoveAll ( ) ;
m_tmNextPeriodicThink = 0 ;
m_flRestartGameTime = 0 ;
m_bCompleteReset = false ;
m_bHeardAllPlayersReady = false ;
m_bAwaitingReadyRestart = false ;
m_bChangelevelDone = false ;
# endif
}
const CViewVectors * CHL2MPRules : : GetViewVectors ( ) const
{
return & g_HL2MPViewVectors ;
}
const HL2MPViewVectors * CHL2MPRules : : GetHL2MPViewVectors ( ) const
{
return & g_HL2MPViewVectors ;
}
CHL2MPRules : : ~ CHL2MPRules ( void )
{
# ifndef CLIENT_DLL
// Note, don't delete each team since they are in the gEntList and will
// automatically be deleted from there, instead.
g_Teams . Purge ( ) ;
# endif
}
void CHL2MPRules : : CreateStandardEntities ( void )
{
# ifndef CLIENT_DLL
// Create the entity that will send our data to the client.
BaseClass : : CreateStandardEntities ( ) ;
g_pLastCombineSpawn = NULL ;
g_pLastRebelSpawn = NULL ;
# ifdef DBGFLAG_ASSERT
CBaseEntity * pEnt =
# endif
CBaseEntity : : Create ( " hl2mp_gamerules " , vec3_origin , vec3_angle ) ;
Assert ( pEnt ) ;
# endif
}
//=========================================================
// FlWeaponRespawnTime - what is the time in the future
// at which this weapon may spawn?
//=========================================================
float CHL2MPRules : : FlWeaponRespawnTime ( CBaseCombatWeapon * pWeapon )
{
# ifndef CLIENT_DLL
if ( weaponstay . GetInt ( ) > 0 )
{
// make sure it's only certain weapons
if ( ! ( pWeapon - > GetWeaponFlags ( ) & ITEM_FLAG_LIMITINWORLD ) )
{
return 0 ; // weapon respawns almost instantly
}
}
return sv_hl2mp_weapon_respawn_time . GetFloat ( ) ;
# endif
return 0 ; // weapon respawns almost instantly
}
bool CHL2MPRules : : IsIntermission ( void )
{
# ifndef CLIENT_DLL
return m_flIntermissionEndTime > gpGlobals - > curtime ;
# endif
return false ;
}
void CHL2MPRules : : PlayerKilled ( CBasePlayer * pVictim , const CTakeDamageInfo & info )
{
# ifndef CLIENT_DLL
if ( IsIntermission ( ) )
return ;
BaseClass : : PlayerKilled ( pVictim , info ) ;
# endif
}
void CHL2MPRules : : Think ( void )
{
# ifndef CLIENT_DLL
CGameRules : : Think ( ) ;
if ( g_fGameOver ) // someone else quit the game already
{
// check to see if we should change levels now
if ( m_flIntermissionEndTime < gpGlobals - > curtime )
{
if ( ! m_bChangelevelDone )
{
ChangeLevel ( ) ; // intermission is over
m_bChangelevelDone = true ;
}
}
return ;
}
// float flTimeLimit = mp_timelimit.GetFloat() * 60;
float flFragLimit = fraglimit . GetFloat ( ) ;
if ( GetMapRemainingTime ( ) < 0 )
{
GoToIntermission ( ) ;
return ;
}
if ( flFragLimit )
{
if ( IsTeamplay ( ) = = true )
{
CTeam * pCombine = g_Teams [ TEAM_COMBINE ] ;
CTeam * pRebels = g_Teams [ TEAM_REBELS ] ;
if ( pCombine - > GetScore ( ) > = flFragLimit | | pRebels - > GetScore ( ) > = flFragLimit )
{
GoToIntermission ( ) ;
return ;
}
}
else
{
// 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 ;
}
}
}
}
if ( gpGlobals - > curtime > m_tmNextPeriodicThink )
{
CheckAllPlayersReady ( ) ;
CheckRestartGame ( ) ;
m_tmNextPeriodicThink = gpGlobals - > curtime + 1.0 ;
}
if ( m_flRestartGameTime > 0.0f & & m_flRestartGameTime < = gpGlobals - > curtime )
{
RestartGame ( ) ;
}
if ( m_bAwaitingReadyRestart & & m_bHeardAllPlayersReady )
{
UTIL_ClientPrintAll ( HUD_PRINTCENTER , " All players ready. Game will restart in 5 seconds " ) ;
UTIL_ClientPrintAll ( HUD_PRINTCONSOLE , " All players ready. Game will restart in 5 seconds " ) ;
m_flRestartGameTime = gpGlobals - > curtime + 5 ;
m_bAwaitingReadyRestart = false ;
}
ManageObjectRelocation ( ) ;
# endif
}
void CHL2MPRules : : GoToIntermission ( void )
{
# ifndef CLIENT_DLL
if ( g_fGameOver )
return ;
g_fGameOver = true ;
m_flIntermissionEndTime = gpGlobals - > curtime + mp_chattime . GetInt ( ) ;
for ( int i = 0 ; i < MAX_PLAYERS ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
pPlayer - > ShowViewPortPanel ( PANEL_SCOREBOARD ) ;
pPlayer - > AddFlag ( FL_FROZEN ) ;
}
# endif
}
bool CHL2MPRules : : CheckGameOver ( )
{
# ifndef CLIENT_DLL
if ( g_fGameOver ) // someone else quit the game already
{
// check to see if we should change levels now
if ( m_flIntermissionEndTime < gpGlobals - > curtime )
{
ChangeLevel ( ) ; // intermission is over
}
return true ;
}
# endif
return false ;
}
// 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 CHL2MPRules : : FlWeaponTryRespawn ( CBaseCombatWeapon * pWeapon )
{
# ifndef CLIENT_DLL
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 ) ;
}
# endif
return 0 ;
}
//=========================================================
// VecWeaponRespawnSpot - where should this weapon spawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CHL2MPRules : : VecWeaponRespawnSpot ( CBaseCombatWeapon * pWeapon )
{
# ifndef CLIENT_DLL
CWeaponHL2MPBase * pHL2Weapon = dynamic_cast < CWeaponHL2MPBase * > ( pWeapon ) ;
if ( pHL2Weapon )
{
return pHL2Weapon - > GetOriginalSpawnOrigin ( ) ;
}
# endif
return pWeapon - > GetAbsOrigin ( ) ;
}
# ifndef CLIENT_DLL
CItem * IsManagedObjectAnItem ( CBaseEntity * pObject )
{
return dynamic_cast < CItem * > ( pObject ) ;
}
CWeaponHL2MPBase * IsManagedObjectAWeapon ( CBaseEntity * pObject )
{
return dynamic_cast < CWeaponHL2MPBase * > ( pObject ) ;
}
bool GetObjectsOriginalParameters ( CBaseEntity * pObject , Vector & vOriginalOrigin , QAngle & vOriginalAngles )
{
if ( CItem * pItem = IsManagedObjectAnItem ( pObject ) )
{
if ( pItem - > m_flNextResetCheckTime > gpGlobals - > curtime )
return false ;
vOriginalOrigin = pItem - > GetOriginalSpawnOrigin ( ) ;
vOriginalAngles = pItem - > GetOriginalSpawnAngles ( ) ;
pItem - > m_flNextResetCheckTime = gpGlobals - > curtime + sv_hl2mp_item_respawn_time . GetFloat ( ) ;
return true ;
}
else if ( CWeaponHL2MPBase * pWeapon = IsManagedObjectAWeapon ( pObject ) )
{
if ( pWeapon - > m_flNextResetCheckTime > gpGlobals - > curtime )
return false ;
vOriginalOrigin = pWeapon - > GetOriginalSpawnOrigin ( ) ;
vOriginalAngles = pWeapon - > GetOriginalSpawnAngles ( ) ;
pWeapon - > m_flNextResetCheckTime = gpGlobals - > curtime + sv_hl2mp_weapon_respawn_time . GetFloat ( ) ;
return true ;
}
return false ;
}
void CHL2MPRules : : ManageObjectRelocation ( void )
{
int iTotal = m_hRespawnableItemsAndWeapons . Count ( ) ;
if ( iTotal > 0 )
{
for ( int i = 0 ; i < iTotal ; i + + )
{
CBaseEntity * pObject = m_hRespawnableItemsAndWeapons [ i ] . Get ( ) ;
if ( pObject )
{
Vector vSpawOrigin ;
QAngle vSpawnAngles ;
if ( GetObjectsOriginalParameters ( pObject , vSpawOrigin , vSpawnAngles ) = = true )
{
float flDistanceFromSpawn = ( pObject - > GetAbsOrigin ( ) - vSpawOrigin ) . Length ( ) ;
if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN )
{
bool shouldReset = false ;
IPhysicsObject * pPhysics = pObject - > VPhysicsGetObject ( ) ;
if ( pPhysics )
{
shouldReset = pPhysics - > IsAsleep ( ) ;
}
else
{
shouldReset = ( pObject - > GetFlags ( ) & FL_ONGROUND ) ? true : false ;
}
if ( shouldReset )
{
pObject - > Teleport ( & vSpawOrigin , & vSpawnAngles , NULL ) ;
pObject - > EmitSound ( " AlyxEmp.Charge " ) ;
IPhysicsObject * pPhys = pObject - > VPhysicsGetObject ( ) ;
if ( pPhys )
{
pPhys - > Wake ( ) ;
}
}
}
}
}
}
}
}
//=========================================================
//AddLevelDesignerPlacedWeapon
//=========================================================
void CHL2MPRules : : AddLevelDesignerPlacedObject ( CBaseEntity * pEntity )
{
if ( m_hRespawnableItemsAndWeapons . Find ( pEntity ) = = - 1 )
{
m_hRespawnableItemsAndWeapons . AddToTail ( pEntity ) ;
}
}
//=========================================================
//RemoveLevelDesignerPlacedWeapon
//=========================================================
void CHL2MPRules : : RemoveLevelDesignerPlacedObject ( CBaseEntity * pEntity )
{
if ( m_hRespawnableItemsAndWeapons . Find ( pEntity ) ! = - 1 )
{
m_hRespawnableItemsAndWeapons . FindAndRemove ( pEntity ) ;
}
}
//=========================================================
// Where should this item respawn?
// Some game variations may choose to randomize spawn locations
//=========================================================
Vector CHL2MPRules : : VecItemRespawnSpot ( CItem * pItem )
{
return pItem - > GetOriginalSpawnOrigin ( ) ;
}
//=========================================================
// What angles should this item use to respawn?
//=========================================================
QAngle CHL2MPRules : : VecItemRespawnAngles ( CItem * pItem )
{
return pItem - > GetOriginalSpawnAngles ( ) ;
}
//=========================================================
// At what time in the future may this Item respawn?
//=========================================================
float CHL2MPRules : : FlItemRespawnTime ( CItem * pItem )
{
return sv_hl2mp_item_respawn_time . GetFloat ( ) ;
}
//=========================================================
// CanHaveWeapon - returns false if the player is not allowed
// to pick up this weapon
//=========================================================
bool CHL2MPRules : : CanHavePlayerItem ( CBasePlayer * pPlayer , CBaseCombatWeapon * pItem )
{
if ( weaponstay . GetInt ( ) > 0 )
{
if ( pPlayer - > Weapon_OwnsThisType ( pItem - > GetClassname ( ) , pItem - > GetSubType ( ) ) )
return false ;
}
return BaseClass : : CanHavePlayerItem ( pPlayer , pItem ) ;
}
# endif
//=========================================================
// WeaponShouldRespawn - any conditions inhibiting the
// respawning of this weapon?
//=========================================================
int CHL2MPRules : : WeaponShouldRespawn ( CBaseCombatWeapon * pWeapon )
{
# ifndef CLIENT_DLL
if ( pWeapon - > HasSpawnFlags ( SF_NORESPAWN ) )
{
return GR_WEAPON_RESPAWN_NO ;
}
# endif
return GR_WEAPON_RESPAWN_YES ;
}
//-----------------------------------------------------------------------------
// Purpose: Player has just left the game
//-----------------------------------------------------------------------------
void CHL2MPRules : : ClientDisconnected ( edict_t * pClient )
{
# ifndef CLIENT_DLL
// Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" );
CBasePlayer * pPlayer = ( CBasePlayer * ) CBaseEntity : : Instance ( pClient ) ;
if ( pPlayer )
{
// Remove the player from his team
if ( pPlayer - > GetTeam ( ) )
{
pPlayer - > GetTeam ( ) - > RemovePlayer ( pPlayer ) ;
}
}
BaseClass : : ClientDisconnected ( pClient ) ;
# endif
}
//=========================================================
// Deathnotice.
//=========================================================
void CHL2MPRules : : DeathNotice ( CBasePlayer * pVictim , const CTakeDamageInfo & info )
{
# ifndef CLIENT_DLL
// 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 ) ;
// Custom kill 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 ( ) - > GetClassname ( ) ;
}
}
else
{
killer_weapon_name = pInflictor - > GetClassname ( ) ; // it's just that easy
}
}
}
else
{
killer_weapon_name = pInflictor - > GetClassname ( ) ;
}
// 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_ " , 4 ) = = 0 )
{
killer_weapon_name + = 4 ;
}
else if ( strncmp ( killer_weapon_name , " func_ " , 5 ) = = 0 )
{
killer_weapon_name + = 5 ;
}
else if ( strstr ( killer_weapon_name , " physics " ) )
{
killer_weapon_name = " physics " ;
}
if ( strstr ( killer_weapon_name , " physbox " ) )
{
killer_weapon_name = " physics " ;
}
if ( strcmp ( killer_weapon_name , " prop_combine_ball " ) = = 0 )
{
killer_weapon_name = " combine_ball " ;
}
else if ( strcmp ( killer_weapon_name , " grenade_ar2 " ) = = 0 )
{
killer_weapon_name = " smg1_grenade " ;
}
else if ( strcmp ( killer_weapon_name , " satchel " ) = = 0 | | strcmp ( killer_weapon_name , " tripmine " ) = = 0 )
{
killer_weapon_name = " slam " ;
}
}
IGameEvent * event = gameeventmanager - > CreateEvent ( " player_death " ) ;
if ( event )
{
event - > SetInt ( " userid " , pVictim - > GetUserID ( ) ) ;
event - > SetInt ( " attacker " , killer_ID ) ;
event - > SetString ( " weapon " , killer_weapon_name ) ;
event - > SetInt ( " priority " , 7 ) ;
gameeventmanager - > FireEvent ( event ) ;
}
# endif
}
void CHL2MPRules : : ClientSettingsChanged ( CBasePlayer * pPlayer )
{
# ifndef CLIENT_DLL
CHL2MP_Player * pHL2Player = ToHL2MPPlayer ( pPlayer ) ;
if ( pHL2Player = = NULL )
return ;
const char * pCurrentModel = modelinfo - > GetModelName ( pPlayer - > GetModel ( ) ) ;
const char * szModelName = engine - > GetClientConVarValue ( engine - > IndexOfEdict ( pPlayer - > edict ( ) ) , " cl_playermodel " ) ;
//If we're different.
if ( stricmp ( szModelName , pCurrentModel ) )
{
//Too soon, set the cvar back to what it was.
//Note: this will make this function be called again
//but since our models will match it'll just skip this whole dealio.
if ( pHL2Player - > GetNextModelChangeTime ( ) > = gpGlobals - > curtime )
{
char szReturnString [ 512 ] ;
Q_snprintf ( szReturnString , sizeof ( szReturnString ) , " cl_playermodel %s \n " , pCurrentModel ) ;
engine - > ClientCommand ( pHL2Player - > edict ( ) , szReturnString ) ;
Q_snprintf ( szReturnString , sizeof ( szReturnString ) , " Please wait %d more seconds before trying to switch. \n " , ( int ) ( pHL2Player - > GetNextModelChangeTime ( ) - gpGlobals - > curtime ) ) ;
ClientPrint ( pHL2Player , HUD_PRINTTALK , szReturnString ) ;
return ;
}
if ( HL2MPRules ( ) - > IsTeamplay ( ) = = false )
{
pHL2Player - > SetPlayerModel ( ) ;
const char * pszCurrentModelName = modelinfo - > GetModelName ( pHL2Player - > GetModel ( ) ) ;
char szReturnString [ 128 ] ;
Q_snprintf ( szReturnString , sizeof ( szReturnString ) , " Your player model is: %s \n " , pszCurrentModelName ) ;
ClientPrint ( pHL2Player , HUD_PRINTTALK , szReturnString ) ;
}
else
{
if ( Q_stristr ( szModelName , " models/human " ) )
{
pHL2Player - > ChangeTeam ( TEAM_REBELS ) ;
}
else
{
pHL2Player - > ChangeTeam ( TEAM_COMBINE ) ;
}
}
}
if ( sv_report_client_settings . GetInt ( ) = = 1 )
{
UTIL_LogPrintf ( " \" %s \" cl_cmdrate = \" %s \" \n " , pHL2Player - > GetPlayerName ( ) , engine - > GetClientConVarValue ( pHL2Player - > entindex ( ) , " cl_cmdrate " ) ) ;
}
BaseClass : : ClientSettingsChanged ( pPlayer ) ;
# endif
}
int CHL2MPRules : : PlayerRelationship ( CBaseEntity * pPlayer , CBaseEntity * pTarget )
{
# ifndef CLIENT_DLL
// half life multiplay has a simple concept of Player Relationships.
// you are either on another player's team, or you are not.
if ( ! pPlayer | | ! pTarget | | ! pTarget - > IsPlayer ( ) | | IsTeamplay ( ) = = false )
return GR_NOTTEAMMATE ;
if ( ( * GetTeamID ( pPlayer ) ! = ' \0 ' ) & & ( * GetTeamID ( pTarget ) ! = ' \0 ' ) & & ! stricmp ( GetTeamID ( pPlayer ) , GetTeamID ( pTarget ) ) )
{
return GR_TEAMMATE ;
}
# endif
return GR_NOTTEAMMATE ;
}
const char * CHL2MPRules : : GetGameDescription ( void )
{
if ( IsTeamplay ( ) )
return " Team Deathmatch " ;
return " Deathmatch " ;
}
bool CHL2MPRules : : IsConnectedUserInfoChangeAllowed ( CBasePlayer * pPlayer )
{
return true ;
}
float CHL2MPRules : : GetMapRemainingTime ( )
{
// if timelimit is disabled, return 0
if ( mp_timelimit . GetInt ( ) < = 0 )
return 0 ;
// timelimit is in minutes
float timeleft = ( m_flGameStartTime + mp_timelimit . GetInt ( ) * 60.0f ) - gpGlobals - > curtime ;
return timeleft ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2MPRules : : Precache ( void )
{
CBaseEntity : : PrecacheScriptSound ( " AlyxEmp.Charge " ) ;
}
bool CHL2MPRules : : ShouldCollide ( int collisionGroup0 , int collisionGroup1 )
{
if ( collisionGroup0 > collisionGroup1 )
{
// swap so that lowest is always first
V_swap ( collisionGroup0 , collisionGroup1 ) ;
}
if ( ( collisionGroup0 = = COLLISION_GROUP_PLAYER | | collisionGroup0 = = COLLISION_GROUP_PLAYER_MOVEMENT ) & &
collisionGroup1 = = COLLISION_GROUP_WEAPON )
{
return false ;
}
return BaseClass : : ShouldCollide ( collisionGroup0 , collisionGroup1 ) ;
}
bool CHL2MPRules : : ClientCommand ( CBaseEntity * pEdict , const CCommand & args )
{
# ifndef CLIENT_DLL
if ( BaseClass : : ClientCommand ( pEdict , args ) )
return true ;
CHL2MP_Player * pPlayer = ( CHL2MP_Player * ) pEdict ;
if ( pPlayer - > ClientCommand ( args ) )
return true ;
# endif
return false ;
}
// shared ammo definition
// JAY: Trying to make a more physical bullet response
# define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains) / 16.0f)
# define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
// exaggerate all of the forces, but use real numbers to keep them consistent
# define BULLET_IMPULSE_EXAGGERATION 3.5
// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
# define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
CAmmoDef * GetAmmoDef ( )
{
static CAmmoDef def ;
static bool bInitted = false ;
if ( ! bInitted )
{
bInitted = true ;
def . AddAmmoType ( " AR2 " , DMG_BULLET , TRACER_LINE_AND_WHIZ , 0 , 0 , 60 , BULLET_IMPULSE ( 200 , 1225 ) , 0 ) ;
def . AddAmmoType ( " AR2AltFire " , DMG_DISSOLVE , TRACER_NONE , 0 , 0 , 3 , 0 , 0 ) ;
def . AddAmmoType ( " Pistol " , DMG_BULLET , TRACER_LINE_AND_WHIZ , 0 , 0 , 150 , BULLET_IMPULSE ( 200 , 1225 ) , 0 ) ;
def . AddAmmoType ( " SMG1 " , DMG_BULLET , TRACER_LINE_AND_WHIZ , 0 , 0 , 225 , BULLET_IMPULSE ( 200 , 1225 ) , 0 ) ;
def . AddAmmoType ( " 357 " , DMG_BULLET , TRACER_LINE_AND_WHIZ , 0 , 0 , 12 , BULLET_IMPULSE ( 800 , 5000 ) , 0 ) ;
def . AddAmmoType ( " XBowBolt " , DMG_BULLET , TRACER_LINE , 0 , 0 , 10 , BULLET_IMPULSE ( 800 , 8000 ) , 0 ) ;
def . AddAmmoType ( " Buckshot " , DMG_BULLET | DMG_BUCKSHOT , TRACER_LINE , 0 , 0 , 30 , BULLET_IMPULSE ( 400 , 1200 ) , 0 ) ;
def . AddAmmoType ( " RPG_Round " , DMG_BURN , TRACER_NONE , 0 , 0 , 3 , 0 , 0 ) ;
def . AddAmmoType ( " SMG1_Grenade " , DMG_BURN , TRACER_NONE , 0 , 0 , 3 , 0 , 0 ) ;
def . AddAmmoType ( " Grenade " , DMG_BURN , TRACER_NONE , 0 , 0 , 5 , 0 , 0 ) ;
def . AddAmmoType ( " slam " , DMG_BURN , TRACER_NONE , 0 , 0 , 5 , 0 , 0 ) ;
}
return & def ;
}
# ifdef CLIENT_DLL
ConVar cl_autowepswitch (
" cl_autowepswitch " ,
" 1 " ,
FCVAR_ARCHIVE | FCVAR_USERINFO ,
" Automatically switch to picked up weapons (if more powerful) " ) ;
# else
# ifdef DEBUG
// Handler for the "bot" command.
void Bot_f ( )
{
// Look at -count.
int count = 1 ;
count = clamp ( count , 1 , 16 ) ;
int iTeam = TEAM_COMBINE ;
// Look at -frozen.
bool bFrozen = false ;
// Ok, spawn all the bots.
while ( - - count > = 0 )
{
BotPutInServer ( bFrozen , iTeam ) ;
}
}
ConCommand cc_Bot ( " bot " , Bot_f , " Add a bot. " , FCVAR_CHEAT ) ;
# endif
bool CHL2MPRules : : FShouldSwitchWeapon ( CBasePlayer * pPlayer , CBaseCombatWeapon * pWeapon )
{
if ( pPlayer - > GetActiveWeapon ( ) & & pPlayer - > IsNetClient ( ) )
{
// Player has an active item, so let's check cl_autowepswitch.
const char * cl_autowepswitch = engine - > GetClientConVarValue ( engine - > IndexOfEdict ( pPlayer - > edict ( ) ) , " cl_autowepswitch " ) ;
if ( cl_autowepswitch & & atoi ( cl_autowepswitch ) < = 0 )
{
return false ;
}
}
return BaseClass : : FShouldSwitchWeapon ( pPlayer , pWeapon ) ;
}
# endif
# ifndef CLIENT_DLL
void CHL2MPRules : : RestartGame ( )
{
// bounds check
if ( mp_timelimit . GetInt ( ) < 0 )
{
mp_timelimit . SetValue ( 0 ) ;
}
m_flGameStartTime = gpGlobals - > curtime ;
if ( ! IsFinite ( m_flGameStartTime . Get ( ) ) )
{
Warning ( " Trying to set a NaN game start time \n " ) ;
m_flGameStartTime . GetForModify ( ) = 0.0f ;
}
CleanUpMap ( ) ;
// now respawn all players
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CHL2MP_Player * pPlayer = ( CHL2MP_Player * ) UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
if ( pPlayer - > GetActiveWeapon ( ) )
{
pPlayer - > GetActiveWeapon ( ) - > Holster ( ) ;
}
pPlayer - > RemoveAllItems ( true ) ;
respawn ( pPlayer , false ) ;
pPlayer - > Reset ( ) ;
}
// Respawn entities (glass, doors, etc..)
CTeam * pRebels = GetGlobalTeam ( TEAM_REBELS ) ;
CTeam * pCombine = GetGlobalTeam ( TEAM_COMBINE ) ;
if ( pRebels )
{
pRebels - > SetScore ( 0 ) ;
}
if ( pCombine )
{
pCombine - > SetScore ( 0 ) ;
}
m_flIntermissionEndTime = 0 ;
m_flRestartGameTime = 0.0 ;
m_bCompleteReset = false ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " round_start " ) ;
if ( event )
{
event - > SetInt ( " fraglimit " , 0 ) ;
event - > SetInt ( " priority " , 6 ) ; // HLTV event priority, not transmitted
event - > SetString ( " objective " , " DEATHMATCH " ) ;
gameeventmanager - > FireEvent ( event ) ;
}
}
void CHL2MPRules : : CleanUpMap ( )
{
// Recreate all the map entities from the map data (preserving their indices),
// then remove everything else except the players.
// Get rid of all entities except players.
CBaseEntity * pCur = gEntList . FirstEnt ( ) ;
while ( pCur )
{
CBaseHL2MPCombatWeapon * pWeapon = dynamic_cast < CBaseHL2MPCombatWeapon * > ( pCur ) ;
// Weapons with owners don't want to be removed..
if ( pWeapon )
{
if ( ! pWeapon - > GetPlayerOwner ( ) )
{
UTIL_Remove ( pCur ) ;
}
}
// remove entities that has to be restored on roundrestart (breakables etc)
else if ( ! FindInList ( s_PreserveEnts , pCur - > GetClassname ( ) ) )
{
UTIL_Remove ( pCur ) ;
}
pCur = gEntList . NextEnt ( pCur ) ;
}
// Really remove the entities so we can have access to their slots below.
gEntList . CleanupDeleteList ( ) ;
// Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
// could kill respawning CTs
g_EventQueue . Clear ( ) ;
// Now reload the map entities.
class CHL2MPMapEntityFilter : public IMapEntityFilter
{
public :
virtual bool ShouldCreateEntity ( const char * pClassname )
{
// Don't recreate the preserved entities.
if ( ! FindInList ( s_PreserveEnts , pClassname ) )
{
return true ;
}
else
{
// Increment our iterator since it's not going to call CreateNextEntity for this ent.
if ( m_iIterator ! = g_MapEntityRefs . InvalidIndex ( ) )
m_iIterator = g_MapEntityRefs . Next ( m_iIterator ) ;
return false ;
}
}
virtual CBaseEntity * CreateNextEntity ( const char * pClassname )
{
if ( m_iIterator = = g_MapEntityRefs . InvalidIndex ( ) )
{
// This shouldn't be possible. When we loaded the map, it should have used
// CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
// with the same list of entities we're referring to here.
Assert ( false ) ;
return NULL ;
}
else
{
CMapEntityRef & ref = g_MapEntityRefs [ m_iIterator ] ;
m_iIterator = g_MapEntityRefs . Next ( m_iIterator ) ; // Seek to the next entity.
if ( ref . m_iEdict = = - 1 | | engine - > PEntityOfEntIndex ( ref . m_iEdict ) )
{
// Doh! The entity was delete and its slot was reused.
// Just use any old edict slot. This case sucks because we lose the baseline.
return CreateEntityByName ( pClassname ) ;
}
else
{
// Cool, the slot where this entity was is free again (most likely, the entity was
// freed above). Now create an entity with this specific index.
return CreateEntityByName ( pClassname , ref . m_iEdict ) ;
}
}
}
public :
int m_iIterator ; // Iterator into g_MapEntityRefs.
} ;
CHL2MPMapEntityFilter filter ;
filter . m_iIterator = g_MapEntityRefs . Head ( ) ;
// DO NOT CALL SPAWN ON info_node ENTITIES!
MapEntity_ParseAllEntities ( engine - > GetMapEntitiesString ( ) , & filter , true ) ;
}
void CHL2MPRules : : CheckChatForReadySignal ( CHL2MP_Player * pPlayer , const char * chatmsg )
{
if ( m_bAwaitingReadyRestart & & FStrEq ( chatmsg , mp_ready_signal . GetString ( ) ) )
{
if ( ! pPlayer - > IsReady ( ) )
{
pPlayer - > SetReady ( true ) ;
}
}
}
void CHL2MPRules : : CheckRestartGame ( void )
{
// Restart the game if specified by the server
int iRestartDelay = mp_restartgame . GetInt ( ) ;
if ( iRestartDelay > 0 )
{
if ( iRestartDelay > 60 )
iRestartDelay = 60 ;
// let the players know
char strRestartDelay [ 64 ] ;
Q_snprintf ( strRestartDelay , sizeof ( strRestartDelay ) , " %d " , iRestartDelay ) ;
UTIL_ClientPrintAll ( HUD_PRINTCENTER , " Game will restart in %s1 %s2 " , strRestartDelay , iRestartDelay = = 1 ? " SECOND " : " SECONDS " ) ;
UTIL_ClientPrintAll ( HUD_PRINTCONSOLE , " Game will restart in %s1 %s2 " , strRestartDelay , iRestartDelay = = 1 ? " SECOND " : " SECONDS " ) ;
m_flRestartGameTime = gpGlobals - > curtime + iRestartDelay ;
m_bCompleteReset = true ;
mp_restartgame . SetValue ( 0 ) ;
}
if ( mp_readyrestart . GetBool ( ) )
{
m_bAwaitingReadyRestart = true ;
m_bHeardAllPlayersReady = false ;
const char * pszReadyString = mp_ready_signal . GetString ( ) ;
// Don't let them put anything malicious in there
if ( pszReadyString = = NULL | | Q_strlen ( pszReadyString ) > 16 )
{
pszReadyString = " ready " ;
}
IGameEvent * event = gameeventmanager - > CreateEvent ( " hl2mp_ready_restart " ) ;
if ( event )
gameeventmanager - > FireEvent ( event ) ;
mp_readyrestart . SetValue ( 0 ) ;
// cancel any restart round in progress
m_flRestartGameTime = - 1 ;
}
}
void CHL2MPRules : : CheckAllPlayersReady ( void )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CHL2MP_Player * pPlayer = ( CHL2MP_Player * ) UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
if ( ! pPlayer - > IsReady ( ) )
return ;
}
m_bHeardAllPlayersReady = true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
const char * CHL2MPRules : : GetChatFormat ( bool bTeamOnly , CBasePlayer * pPlayer )
{
if ( ! pPlayer ) // dedicated server output
{
return NULL ;
}
const char * pszFormat = NULL ;
// team only
if ( bTeamOnly = = TRUE )
{
if ( pPlayer - > GetTeamNumber ( ) = = TEAM_SPECTATOR )
{
pszFormat = " HL2MP_Chat_Spec " ;
}
else
{
const char * chatLocation = GetChatLocation ( bTeamOnly , pPlayer ) ;
if ( chatLocation & & * chatLocation )
{
pszFormat = " HL2MP_Chat_Team_Loc " ;
}
else
{
pszFormat = " HL2MP_Chat_Team " ;
}
}
}
// everyone
else
{
if ( pPlayer - > GetTeamNumber ( ) ! = TEAM_SPECTATOR )
{
pszFormat = " HL2MP_Chat_All " ;
}
else
{
pszFormat = " HL2MP_Chat_AllSpec " ;
}
}
return pszFormat ;
}
# endif