You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1283 lines
35 KiB
1283 lines
35 KiB
![]()
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: The Half-Life 2 game rules, such as the relationship tables and ammo
|
||
|
// damage cvars.
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "portal_gamerules.h"
|
||
|
#include "viewport_panel_names.h"
|
||
|
#include "gameeventdefs.h"
|
||
|
#include <KeyValues.h>
|
||
|
#include "ammodef.h"
|
||
|
#include "hl2_shareddefs.h"
|
||
|
|
||
|
#ifdef CLIENT_DLL
|
||
|
#include "c_portal_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 "portal_player.h"
|
||
|
//#include "weapon_hl2mpbasehlmpcombatweapon.h"
|
||
|
#include "team.h"
|
||
|
#include "voice_gamemgr.h"
|
||
|
|
||
|
|
||
|
extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
|
||
|
|
||
|
|
||
|
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 mp_restartgame( "mp_restartgame", "0", 0, "If non-zero, game will restart in the specified number of seconds" );
|
||
|
ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY );
|
||
|
|
||
|
extern ConVar mp_chattime;
|
||
|
|
||
|
#define WEAPON_MAX_DISTANCE_FROM_SPAWN 64
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
REGISTER_GAMERULES_CLASS( CPortalMPGameRules );
|
||
|
|
||
|
BEGIN_NETWORK_TABLE_NOBASE( CPortalMPGameRules, DT_PortalMPGameRules )
|
||
|
|
||
|
#ifdef CLIENT_DLL
|
||
|
RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ),
|
||
|
#else
|
||
|
SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ),
|
||
|
#endif
|
||
|
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( portalmp_gamerules, CPortalMPGameRulesProxy );
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( PortalMPGameRulesProxy, DT_PortalMPGameRulesProxy )
|
||
|
|
||
|
static PortalMPViewVectors g_PortalMPViewVectors(
|
||
|
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_PortalMPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
|
||
|
{
|
||
|
CPortalMPGameRules *pRules = PortalMPGameRules();
|
||
|
Assert( pRules );
|
||
|
*pOut = pRules;
|
||
|
}
|
||
|
|
||
|
BEGIN_RECV_TABLE( CPortalMPGameRulesProxy, DT_PortalMPGameRulesProxy )
|
||
|
RecvPropDataTable( "portalmp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_PortalMPGameRules ), RecvProxy_PortalMPRules )
|
||
|
END_RECV_TABLE()
|
||
|
#else
|
||
|
void* SendProxy_PortalMPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
|
||
|
{
|
||
|
CPortalMPGameRules *pRules = PortalMPGameRules();
|
||
|
Assert( pRules );
|
||
|
return pRules;
|
||
|
}
|
||
|
|
||
|
BEGIN_SEND_TABLE( CPortalMPGameRulesProxy, DT_PortalMPGameRulesProxy )
|
||
|
SendPropDataTable( "portalmp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_PortalMPGameRules ), SendProxy_PortalMPRules )
|
||
|
END_SEND_TABLE()
|
||
|
#endif
|
||
|
|
||
|
#ifndef CLIENT_DLL
|
||
|
|
||
|
class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
|
||
|
{
|
||
|
public:
|
||
|
virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker )
|
||
|
{
|
||
|
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",
|
||
|
};
|
||
|
|
||
|
CPortalMPGameRules::CPortalMPGameRules()
|
||
|
{
|
||
|
#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;
|
||
|
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
const CViewVectors* CPortalMPGameRules::GetViewVectors()const
|
||
|
{
|
||
|
return &g_PortalMPViewVectors;
|
||
|
}
|
||
|
|
||
|
const PortalMPViewVectors* CPortalMPGameRules::GetPortalMPViewVectors()const
|
||
|
{
|
||
|
return &g_PortalMPViewVectors;
|
||
|
}
|
||
|
|
||
|
CPortalMPGameRules::~CPortalMPGameRules( 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 CPortalMPGameRules::CreateStandardEntities( void )
|
||
|
{
|
||
|
|
||
|
#ifndef CLIENT_DLL
|
||
|
// Create the entity that will send our data to the client.
|
||
|
|
||
|
BaseClass::CreateStandardEntities();
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
CBaseEntity *pEnt =
|
||
|
#endif
|
||
|
CBaseEntity::Create( "portalmp_gamerules", vec3_origin, vec3_angle );
|
||
|
Assert( pEnt );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// FlWeaponRespawnTime - what is the time in the future
|
||
|
// at which this weapon may spawn?
|
||
|
//=========================================================
|
||
|
float CPortalMPGameRules::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 CPortalMPGameRules::IsIntermission( void )
|
||
|
{
|
||
|
#ifndef CLIENT_DLL
|
||
|
return m_flIntermissionEndTime > gpGlobals->curtime;
|
||
|
#endif
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CPortalMPGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
|
||
|
{
|
||
|
#ifndef CLIENT_DLL
|
||
|
if ( IsIntermission() )
|
||
|
return;
|
||
|
BaseClass::PlayerKilled( pVictim, info );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
void CPortalMPGameRules::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 )
|
||
|
{
|
||
|
ChangeLevel(); // intermission is over
|
||
|
}
|
||
|
|
||
|
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 CPortalMPGameRules::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 CPortalMPGameRules::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 CPortalMPGameRules::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 CPortalMPGameRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
|
||
|
{
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled weapon respawn location code" )
|
||
|
#if 0
|
||
|
#ifndef CLIENT_DLL
|
||
|
CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon );
|
||
|
|
||
|
if ( pHL2Weapon )
|
||
|
{
|
||
|
return pHL2Weapon->GetOriginalSpawnOrigin();
|
||
|
}
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
return pWeapon->GetAbsOrigin();
|
||
|
}
|
||
|
|
||
|
#ifndef CLIENT_DLL
|
||
|
|
||
|
CItem* IsManagedObjectAnItem( CBaseEntity *pObject )
|
||
|
{
|
||
|
return dynamic_cast< CItem*>( pObject );
|
||
|
}
|
||
|
|
||
|
CWeaponPortalBase* IsManagedObjectAWeapon( CBaseEntity *pObject )
|
||
|
{
|
||
|
return dynamic_cast<CWeaponPortalBase*>( pObject );
|
||
|
}
|
||
|
|
||
|
bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles )
|
||
|
{
|
||
|
if ( CItem *pItem = IsManagedObjectAnItem( pObject ) )
|
||
|
{
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled rest time code" )
|
||
|
#if 0
|
||
|
if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime )
|
||
|
return false;
|
||
|
#endif
|
||
|
vOriginalOrigin = pItem->GetOriginalSpawnOrigin();
|
||
|
vOriginalAngles = pItem->GetOriginalSpawnAngles();
|
||
|
|
||
|
#if 0
|
||
|
pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat();
|
||
|
#endif
|
||
|
return true;
|
||
|
}
|
||
|
else if ( CWeaponPortalBase *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 CPortalMPGameRules::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 CPortalMPGameRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity )
|
||
|
{
|
||
|
if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 )
|
||
|
{
|
||
|
m_hRespawnableItemsAndWeapons.AddToTail( pEntity );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
//RemoveLevelDesignerPlacedWeapon
|
||
|
//=========================================================
|
||
|
void CPortalMPGameRules::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 CPortalMPGameRules::VecItemRespawnSpot( CItem *pItem )
|
||
|
{
|
||
|
return pItem->GetOriginalSpawnOrigin();
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// What angles should this item use to respawn?
|
||
|
//=========================================================
|
||
|
QAngle CPortalMPGameRules::VecItemRespawnAngles( CItem *pItem )
|
||
|
{
|
||
|
return pItem->GetOriginalSpawnAngles();
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// At what time in the future may this Item respawn?
|
||
|
//=========================================================
|
||
|
float CPortalMPGameRules::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 CPortalMPGameRules::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 CPortalMPGameRules::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 CPortalMPGameRules::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 CPortalMPGameRules::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 ( 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 CPortalMPGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
|
||
|
{
|
||
|
#ifndef CLIENT_DLL
|
||
|
|
||
|
CPortal_Player *pPortalPlayer = ToPortalPlayer( pPlayer );
|
||
|
//CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer );
|
||
|
|
||
|
if ( pPortalPlayer == 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.
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled model change code" )
|
||
|
#if 0
|
||
|
if ( pPortalPlayer->GetNextModelChangeTime() >= gpGlobals->curtime )
|
||
|
{
|
||
|
char szReturnString[512];
|
||
|
|
||
|
Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel );
|
||
|
engine->ClientCommand ( pPlayer->edict(), szReturnString );
|
||
|
|
||
|
Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pPlayer->GetNextModelChangeTime() - gpGlobals->curtime) );
|
||
|
ClientPrint( pPlayer, HUD_PRINTTALK, szReturnString );
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ( PortalMPGameRules()->IsTeamplay() == false )
|
||
|
{
|
||
|
pPortalPlayer->SetPlayerModel();
|
||
|
|
||
|
const char *pszCurrentModelName = modelinfo->GetModelName( pPlayer->GetModel() );
|
||
|
|
||
|
char szReturnString[128];
|
||
|
Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName );
|
||
|
|
||
|
ClientPrint( pPlayer, HUD_PRINTTALK, szReturnString );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( Q_stristr( szModelName, "models/human") )
|
||
|
{
|
||
|
pPlayer->ChangeTeam( TEAM_REBELS );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pPlayer->ChangeTeam( TEAM_COMBINE );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( sv_report_client_settings.GetInt() == 1 )
|
||
|
{
|
||
|
UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pPlayer->GetPlayerName(), engine->GetClientConVarValue( pPlayer->entindex(), "cl_cmdrate" ));
|
||
|
}
|
||
|
|
||
|
BaseClass::ClientSettingsChanged( pPlayer );
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
int CPortalMPGameRules::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 *CPortalMPGameRules::GetGameDescription( void )
|
||
|
{
|
||
|
return "PMP Test";
|
||
|
}
|
||
|
|
||
|
|
||
|
float CPortalMPGameRules::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 CPortalMPGameRules::Precache( void )
|
||
|
{
|
||
|
CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" );
|
||
|
}
|
||
|
|
||
|
bool CPortalMPGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
|
||
|
{
|
||
|
if ( collisionGroup0 > collisionGroup1 )
|
||
|
{
|
||
|
// swap so that lowest is always first
|
||
|
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 CPortalMPGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
|
||
|
{
|
||
|
|
||
|
#ifndef CLIENT_DLL
|
||
|
if( BaseClass::ClientCommand( pEdict, args ) )
|
||
|
return true;
|
||
|
|
||
|
|
||
|
CBasePlayer *pPlayer = (CBasePlayer *) 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, "sk_plr_dmg_ar2", "sk_npc_dmg_ar2", "sk_max_ar2", BULLET_IMPULSE(200, 1225), 0 );
|
||
|
def.AddAmmoType("AlyxGun", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_alyxgun", "sk_npc_dmg_alyxgun", "sk_max_alyxgun", BULLET_IMPULSE(200, 1225), 0 );
|
||
|
def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_pistol", "sk_npc_dmg_pistol", "sk_max_pistol", BULLET_IMPULSE(200, 1225), 0 );
|
||
|
def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_smg1", "sk_npc_dmg_smg1", "sk_max_smg1", BULLET_IMPULSE(200, 1225), 0 );
|
||
|
def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_plr_dmg_357", "sk_npc_dmg_357", "sk_max_357", BULLET_IMPULSE(800, 5000), 0 );
|
||
|
def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, "sk_plr_dmg_crossbow", "sk_npc_dmg_crossbow", "sk_max_crossbow", BULLET_IMPULSE(800, 8000), 0 );
|
||
|
|
||
|
def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, "sk_plr_dmg_buckshot", "sk_npc_dmg_buckshot", "sk_max_buckshot", BULLET_IMPULSE(400, 1200), 0 );
|
||
|
def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, "sk_plr_dmg_rpg_round", "sk_npc_dmg_rpg_round", "sk_max_rpg_round", 0, 0 );
|
||
|
def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_smg1_grenade", "sk_npc_dmg_smg1_grenade", "sk_max_smg1_grenade", 0, 0 );
|
||
|
def.AddAmmoType("SniperRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_plr_dmg_sniper_round", "sk_npc_dmg_sniper_round", "sk_max_sniper_round", BULLET_IMPULSE(650, 6000), 0 );
|
||
|
def.AddAmmoType("SniperPenetratedRound", DMG_BULLET | DMG_SNIPER, TRACER_NONE, "sk_dmg_sniper_penetrate_plr", "sk_dmg_sniper_penetrate_npc", "sk_max_sniper_round", BULLET_IMPULSE(150, 6000), 0 );
|
||
|
def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_grenade", 0, 0);
|
||
|
def.AddAmmoType("Thumper", DMG_SONIC, TRACER_NONE, 10, 10, 2, 0, 0 );
|
||
|
def.AddAmmoType("Gravity", DMG_CLUB, TRACER_NONE, 0, 0, 8, 0, 0 );
|
||
|
def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, NULL, NULL, NULL, 0, 0 );
|
||
|
def.AddAmmoType("GaussEnergy", DMG_SHOCK, TRACER_NONE, "sk_jeep_gauss_damage", "sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); // hit like a 10kg weight at 400 in/s
|
||
|
def.AddAmmoType("CombineCannon", DMG_BULLET, TRACER_LINE, "sk_npc_dmg_gunship_to_plr", "sk_npc_dmg_gunship", NULL, 1.5 * 750 * 12, 0 ); // hit like a 1.5kg weight at 750 ft/s
|
||
|
def.AddAmmoType("AirboatGun", DMG_AIRBOAT, TRACER_LINE, "sk_plr_dmg_airboat", "sk_npc_dmg_airboat", NULL, BULLET_IMPULSE(10, 600), 0 );
|
||
|
def.AddAmmoType("StriderMinigun", DMG_BULLET, TRACER_LINE, 5, 5, 15, 1.0 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 1.0kg weight at 750 ft/s
|
||
|
def.AddAmmoType("HelicopterGun", DMG_BULLET, TRACER_LINE_AND_WHIZ, "sk_npc_dmg_helicopter_to_plr", "sk_npc_dmg_helicopter", "sk_max_smg1", BULLET_IMPULSE(400, 1225), AMMO_FORCE_DROP_IF_CARRIED | AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER );
|
||
|
def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, "sk_max_ar2_altfire", 0, 0 );
|
||
|
#ifdef HL2_EPISODIC
|
||
|
def.AddAmmoType("Hopwire", DMG_BLAST, TRACER_NONE, "sk_plr_dmg_grenade", "sk_npc_dmg_grenade", "sk_max_hopwire", 0, 0);
|
||
|
def.AddAmmoType("HunterGun", DMG_BULLET, TRACER_LINE, "sk_hunter_dmg", "sk_hunter_dmg", "sk_hunter_max_round", BULLET_IMPULSE(200, 1225), 0 );
|
||
|
def.AddAmmoType("CombineHeavyCannon", DMG_BLAST, TRACER_LINE, 40, 40, NULL, 1.5 * 750 * 12, AMMO_FORCE_DROP_IF_CARRIED ); // hit like a 100 kg weight at 750 ft/s
|
||
|
#endif // HL2_EPISODIC
|
||
|
}
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
bool CPortalMPGameRules::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
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Damage (applied per second) value of the npc_laser_turret
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CPortalMPGameRules::GetLaserTurretDamage( void )
|
||
|
{
|
||
|
switch( GetSkillLevel() )
|
||
|
{
|
||
|
case SKILL_EASY:
|
||
|
return 120.0f;
|
||
|
|
||
|
case SKILL_MEDIUM:
|
||
|
return 200.0f;
|
||
|
|
||
|
case SKILL_HARD:
|
||
|
return 200.0f;
|
||
|
|
||
|
default:
|
||
|
return 100.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Movement speed of the turret.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CPortalMPGameRules::GetLaserTurretMoveSpeed( void )
|
||
|
{
|
||
|
switch( GetSkillLevel() )
|
||
|
{
|
||
|
case SKILL_EASY:
|
||
|
return 0.6f;
|
||
|
|
||
|
case SKILL_MEDIUM:
|
||
|
return 0.8f;
|
||
|
|
||
|
case SKILL_HARD:
|
||
|
return 1.0f;
|
||
|
|
||
|
default:
|
||
|
return 0.7f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Damage value of the npc_rocket_turret
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CPortalMPGameRules::GetRocketTurretDamage( void )
|
||
|
{
|
||
|
switch( GetSkillLevel() )
|
||
|
{
|
||
|
case SKILL_EASY:
|
||
|
return 120.0f;
|
||
|
|
||
|
case SKILL_MEDIUM:
|
||
|
return 200.0f;
|
||
|
|
||
|
case SKILL_HARD:
|
||
|
return 200.0f;
|
||
|
|
||
|
default:
|
||
|
return 100.0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool FindInList( const char **pStrings, const char *pToFind );
|
||
|
|
||
|
void CPortalMPGameRules::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++ )
|
||
|
{
|
||
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
|
||
|
|
||
|
if ( !pPlayer )
|
||
|
continue;
|
||
|
|
||
|
if ( pPlayer->GetActiveWeapon() )
|
||
|
{
|
||
|
pPlayer->GetActiveWeapon()->Holster();
|
||
|
}
|
||
|
pPlayer->RemoveAllItems( true );
|
||
|
respawn( pPlayer, false );
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled player reset" )
|
||
|
#if 0
|
||
|
pPlayer->Reset();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// 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 CPortalMPGameRules::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 )
|
||
|
{
|
||
|
CBaseCombatWeapon *pWeapon = dynamic_cast< CBaseCombatWeapon* >( pCur );
|
||
|
// Weapons with owners don't want to be removed..
|
||
|
if ( pWeapon )
|
||
|
{
|
||
|
if ( !pWeapon->GetOwner() || !pWeapon->GetOwner()->IsPlayer() )
|
||
|
{
|
||
|
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();
|
||
|
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled entity parsing" )
|
||
|
#if 0
|
||
|
// 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 );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void CPortalMPGameRules::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 );
|
||
|
}
|
||
|
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled ready restart" )
|
||
|
#if 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;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void CPortalMPGameRules::CheckAllPlayersReady( void )
|
||
|
{
|
||
|
#pragma message( __FILE__ "(" __LINE__AS_STRING ") : warning custom: Disabled ready restart" )
|
||
|
#if 0
|
||
|
for (int i = 1; i <= gpGlobals->maxClients; i++ )
|
||
|
{
|
||
|
CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
|
||
|
|
||
|
if ( !pPlayer )
|
||
|
continue;
|
||
|
if ( !pPlayer->IsReady() )
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
m_bHeardAllPlayersReady = true;
|
||
|
}
|
||
|
|
||
|
#endif
|