|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Player for HL2.
//
//=============================================================================//
#include "cbase.h"
#include "weapon_hl2mpbasehlmpcombatweapon.h"
#include "hl2mp_player.h"
#include "globalstate.h"
#include "game.h"
#include "gamerules.h"
#include "hl2mp_player_shared.h"
#include "predicted_viewmodel.h"
#include "in_buttons.h"
#include "hl2mp_gamerules.h"
#include "KeyValues.h"
#include "team.h"
#include "weapon_hl2mpbase.h"
#include "grenade_satchel.h"
#include "eventqueue.h"
#include "gamestats.h"
#include "engine/IEngineSound.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "ilagcompensationmanager.h"
int g_iLastCitizenModel = 0; int g_iLastCombineModel = 0;
CBaseEntity *g_pLastCombineSpawn = NULL; CBaseEntity *g_pLastRebelSpawn = NULL; extern CBaseEntity *g_pLastSpawn;
#define HL2MP_COMMAND_MAX_RATE 0.3
void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade );
LINK_ENTITY_TO_CLASS( player, CHL2MP_Player );
LINK_ENTITY_TO_CLASS( info_player_combine, CPointEntity ); LINK_ENTITY_TO_CLASS( info_player_rebel, CPointEntity );
IMPLEMENT_SERVERCLASS_ST(CHL2MP_Player, DT_HL2MP_Player) SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ), SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ), SendPropEHandle( SENDINFO( m_hRagdoll ) ), SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ), SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ), SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), SendPropExclude( "DT_BaseFlex", "m_viewtarget" ),
// SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
// SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
END_SEND_TABLE()
BEGIN_DATADESC( CHL2MP_Player ) END_DATADESC()
const char *g_ppszRandomCitizenModels[] = { "models/humans/group03/male_01.mdl", "models/humans/group03/male_02.mdl", "models/humans/group03/female_01.mdl", "models/humans/group03/male_03.mdl", "models/humans/group03/female_02.mdl", "models/humans/group03/male_04.mdl", "models/humans/group03/female_03.mdl", "models/humans/group03/male_05.mdl", "models/humans/group03/female_04.mdl", "models/humans/group03/male_06.mdl", "models/humans/group03/female_06.mdl", "models/humans/group03/male_07.mdl", "models/humans/group03/female_07.mdl", "models/humans/group03/male_08.mdl", "models/humans/group03/male_09.mdl", };
const char *g_ppszRandomCombineModels[] = { "models/combine_soldier.mdl", "models/combine_soldier_prisonguard.mdl", "models/combine_super_soldier.mdl", "models/police.mdl", };
#define MAX_COMBINE_MODELS 4
#define MODEL_CHANGE_INTERVAL 5.0f
#define TEAM_CHANGE_INTERVAL 5.0f
#define HL2MPPLAYER_PHYSDAMAGE_SCALE 4.0f
#pragma warning( disable : 4355 )
CHL2MP_Player::CHL2MP_Player() : m_PlayerAnimState( this ) { m_angEyeAngles.Init();
m_iLastWeaponFireUsercmd = 0;
m_flNextModelChangeTime = 0.0f; m_flNextTeamChangeTime = 0.0f;
m_iSpawnInterpCounter = 0;
m_bEnterObserver = false; m_bReady = false;
BaseClass::ChangeTeam( 0 ); // UseClientSideAnimation();
}
CHL2MP_Player::~CHL2MP_Player( void ) {
}
void CHL2MP_Player::UpdateOnRemove( void ) { if ( m_hRagdoll ) { UTIL_RemoveImmediate( m_hRagdoll ); m_hRagdoll = NULL; }
BaseClass::UpdateOnRemove(); }
void CHL2MP_Player::Precache( void ) { BaseClass::Precache();
PrecacheModel ( "sprites/glow01.vmt" );
//Precache Citizen models
int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); int i;
for ( i = 0; i < nHeads; ++i ) PrecacheModel( g_ppszRandomCitizenModels[i] );
//Precache Combine Models
nHeads = ARRAYSIZE( g_ppszRandomCombineModels );
for ( i = 0; i < nHeads; ++i ) PrecacheModel( g_ppszRandomCombineModels[i] );
PrecacheFootStepSounds();
PrecacheScriptSound( "NPC_MetroPolice.Die" ); PrecacheScriptSound( "NPC_CombineS.Die" ); PrecacheScriptSound( "NPC_Citizen.die" ); }
void CHL2MP_Player::GiveAllItems( void ) { EquipSuit();
CBasePlayer::GiveAmmo( 255, "Pistol"); CBasePlayer::GiveAmmo( 255, "AR2" ); CBasePlayer::GiveAmmo( 5, "AR2AltFire" ); CBasePlayer::GiveAmmo( 255, "SMG1"); CBasePlayer::GiveAmmo( 1, "smg1_grenade"); CBasePlayer::GiveAmmo( 255, "Buckshot"); CBasePlayer::GiveAmmo( 32, "357" ); CBasePlayer::GiveAmmo( 3, "rpg_round");
CBasePlayer::GiveAmmo( 1, "grenade" ); CBasePlayer::GiveAmmo( 2, "slam" );
GiveNamedItem( "weapon_crowbar" ); GiveNamedItem( "weapon_stunstick" ); GiveNamedItem( "weapon_pistol" ); GiveNamedItem( "weapon_357" );
GiveNamedItem( "weapon_smg1" ); GiveNamedItem( "weapon_ar2" ); GiveNamedItem( "weapon_shotgun" ); GiveNamedItem( "weapon_frag" ); GiveNamedItem( "weapon_crossbow" ); GiveNamedItem( "weapon_rpg" );
GiveNamedItem( "weapon_slam" );
GiveNamedItem( "weapon_physcannon" ); }
void CHL2MP_Player::GiveDefaultItems( void ) { EquipSuit();
CBasePlayer::GiveAmmo( 255, "Pistol"); CBasePlayer::GiveAmmo( 45, "SMG1"); CBasePlayer::GiveAmmo( 1, "grenade" ); CBasePlayer::GiveAmmo( 6, "Buckshot"); CBasePlayer::GiveAmmo( 6, "357" );
if ( GetPlayerModelType() == PLAYER_SOUNDS_METROPOLICE || GetPlayerModelType() == PLAYER_SOUNDS_COMBINESOLDIER ) { GiveNamedItem( "weapon_stunstick" ); } else if ( GetPlayerModelType() == PLAYER_SOUNDS_CITIZEN ) { GiveNamedItem( "weapon_crowbar" ); } GiveNamedItem( "weapon_pistol" ); GiveNamedItem( "weapon_smg1" ); GiveNamedItem( "weapon_frag" ); GiveNamedItem( "weapon_physcannon" );
const char *szDefaultWeaponName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_defaultweapon" );
CBaseCombatWeapon *pDefaultWeapon = Weapon_OwnsThisType( szDefaultWeaponName );
if ( pDefaultWeapon ) { Weapon_Switch( pDefaultWeapon ); } else { Weapon_Switch( Weapon_OwnsThisType( "weapon_physcannon" ) ); } }
void CHL2MP_Player::PickDefaultSpawnTeam( void ) { if ( GetTeamNumber() == 0 ) { if ( HL2MPRules()->IsTeamplay() == false ) { if ( GetModelPtr() == NULL ) { const char *szModelName = NULL; szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" );
if ( ValidatePlayerModel( szModelName ) == false ) { char szReturnString[512];
Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel models/combine_soldier.mdl\n" ); engine->ClientCommand ( edict(), szReturnString ); }
ChangeTeam( TEAM_UNASSIGNED ); } } else { CTeam *pCombine = g_Teams[TEAM_COMBINE]; CTeam *pRebels = g_Teams[TEAM_REBELS];
if ( pCombine == NULL || pRebels == NULL ) { ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); } else { if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() ) { ChangeTeam( TEAM_REBELS ); } else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() ) { ChangeTeam( TEAM_COMBINE ); } else { ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); } } } } }
//-----------------------------------------------------------------------------
// Purpose: Sets HL2 specific defaults.
//-----------------------------------------------------------------------------
void CHL2MP_Player::Spawn(void) { m_flNextModelChangeTime = 0.0f; m_flNextTeamChangeTime = 0.0f;
PickDefaultSpawnTeam();
BaseClass::Spawn(); if ( !IsObserver() ) { pl.deadflag = false; RemoveSolidFlags( FSOLID_NOT_SOLID );
RemoveEffects( EF_NODRAW ); GiveDefaultItems(); }
SetNumAnimOverlays( 3 ); ResetAnimation();
m_nRenderFX = kRenderNormal;
m_Local.m_iHideHUD = 0; AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round.
m_impactEnergyScale = HL2MPPLAYER_PHYSDAMAGE_SCALE;
if ( HL2MPRules()->IsIntermission() ) { AddFlag( FL_FROZEN ); } else { RemoveFlag( FL_FROZEN ); }
m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
m_Local.m_bDucked = false;
SetPlayerUnderwater(false);
m_bReady = false; }
void CHL2MP_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize ) { }
bool CHL2MP_Player::ValidatePlayerModel( const char *pModel ) { int iModels = ARRAYSIZE( g_ppszRandomCitizenModels ); int i;
for ( i = 0; i < iModels; ++i ) { if ( !Q_stricmp( g_ppszRandomCitizenModels[i], pModel ) ) { return true; } }
iModels = ARRAYSIZE( g_ppszRandomCombineModels );
for ( i = 0; i < iModels; ++i ) { if ( !Q_stricmp( g_ppszRandomCombineModels[i], pModel ) ) { return true; } }
return false; }
void CHL2MP_Player::SetPlayerTeamModel( void ) { const char *szModelName = NULL; szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" );
int modelIndex = modelinfo->GetModelIndex( szModelName );
if ( modelIndex == -1 || ValidatePlayerModel( szModelName ) == false ) { szModelName = "models/Combine_Soldier.mdl"; m_iModelType = TEAM_COMBINE;
char szReturnString[512];
Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); engine->ClientCommand ( edict(), szReturnString ); }
if ( GetTeamNumber() == TEAM_COMBINE ) { if ( Q_stristr( szModelName, "models/human") ) { int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; }
m_iModelType = TEAM_COMBINE; } else if ( GetTeamNumber() == TEAM_REBELS ) { if ( !Q_stristr( szModelName, "models/human") ) { int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels );
g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; }
m_iModelType = TEAM_REBELS; } SetModel( szModelName ); SetupPlayerSoundsByModel( szModelName );
m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; }
void CHL2MP_Player::SetPlayerModel( void ) { const char *szModelName = NULL; const char *pszCurrentModelName = modelinfo->GetModelName( GetModel());
szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" );
if ( ValidatePlayerModel( szModelName ) == false ) { char szReturnString[512];
if ( ValidatePlayerModel( pszCurrentModelName ) == false ) { pszCurrentModelName = "models/Combine_Soldier.mdl"; }
Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName ); engine->ClientCommand ( edict(), szReturnString );
szModelName = pszCurrentModelName; }
if ( GetTeamNumber() == TEAM_COMBINE ) { int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; szModelName = g_ppszRandomCombineModels[g_iLastCombineModel];
m_iModelType = TEAM_COMBINE; } else if ( GetTeamNumber() == TEAM_REBELS ) { int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels );
g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel];
m_iModelType = TEAM_REBELS; } else { if ( Q_strlen( szModelName ) == 0 ) { szModelName = g_ppszRandomCitizenModels[0]; }
if ( Q_stristr( szModelName, "models/human") ) { m_iModelType = TEAM_REBELS; } else { m_iModelType = TEAM_COMBINE; } }
int modelIndex = modelinfo->GetModelIndex( szModelName );
if ( modelIndex == -1 ) { szModelName = "models/Combine_Soldier.mdl"; m_iModelType = TEAM_COMBINE;
char szReturnString[512];
Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); engine->ClientCommand ( edict(), szReturnString ); }
SetModel( szModelName ); SetupPlayerSoundsByModel( szModelName );
m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; }
void CHL2MP_Player::SetupPlayerSoundsByModel( const char *pModelName ) { if ( Q_stristr( pModelName, "models/human") ) { m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN; } else if ( Q_stristr(pModelName, "police" ) ) { m_iPlayerSoundType = (int)PLAYER_SOUNDS_METROPOLICE; } else if ( Q_stristr(pModelName, "combine" ) ) { m_iPlayerSoundType = (int)PLAYER_SOUNDS_COMBINESOLDIER; } }
void CHL2MP_Player::ResetAnimation( void ) { if ( IsAlive() ) { SetSequence ( -1 ); SetActivity( ACT_INVALID );
if (!GetAbsVelocity().x && !GetAbsVelocity().y) SetAnimation( PLAYER_IDLE ); else if ((GetAbsVelocity().x || GetAbsVelocity().y) && ( GetFlags() & FL_ONGROUND )) SetAnimation( PLAYER_WALK ); else if (GetWaterLevel() > 1) SetAnimation( PLAYER_WALK ); } }
bool CHL2MP_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex ) { bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
if ( bRet == true ) { ResetAnimation(); }
return bRet; }
void CHL2MP_Player::PreThink( void ) { QAngle vOldAngles = GetLocalAngles(); QAngle vTempAngles = GetLocalAngles();
vTempAngles = EyeAngles();
if ( vTempAngles[PITCH] > 180.0f ) { vTempAngles[PITCH] -= 360.0f; }
SetLocalAngles( vTempAngles );
BaseClass::PreThink(); State_PreThink();
//Reset bullet force accumulator, only lasts one frame
m_vecTotalBulletForce = vec3_origin; SetLocalAngles( vOldAngles ); }
void CHL2MP_Player::PostThink( void ) { BaseClass::PostThink(); if ( GetFlags() & FL_DUCKING ) { SetCollisionBounds( VEC_CROUCH_TRACE_MIN, VEC_CROUCH_TRACE_MAX ); }
m_PlayerAnimState.Update();
// Store the eye angles pitch so the client can compute its animation state correctly.
m_angEyeAngles = EyeAngles();
QAngle angles = GetLocalAngles(); angles[PITCH] = 0; SetLocalAngles( angles ); }
void CHL2MP_Player::PlayerDeathThink() { if( !IsObserver() ) { BaseClass::PlayerDeathThink(); } }
void CHL2MP_Player::FireBullets ( const FireBulletsInfo_t &info ) { // Move other players back to history positions based on local player's lag
lagcompensation->StartLagCompensation( this, this->GetCurrentCommand() );
FireBulletsInfo_t modinfo = info;
CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>( GetActiveWeapon() );
if ( pWeapon ) { modinfo.m_iPlayerDamage = modinfo.m_flDamage = pWeapon->GetHL2MPWpnData().m_iPlayerDamage; }
NoteWeaponFired();
BaseClass::FireBullets( modinfo );
// Move other players back to history positions based on local player's lag
lagcompensation->FinishLagCompensation( this ); }
void CHL2MP_Player::NoteWeaponFired( void ) { Assert( m_pCurrentCommand ); if( m_pCurrentCommand ) { m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number; } }
extern ConVar sv_maxunlag;
bool CHL2MP_Player::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const { // No need to lag compensate at all if we're not attacking in this command and
// we haven't attacked recently.
if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) ) return false;
// If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it.
if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) return false;
const Vector &vMyOrigin = GetAbsOrigin(); const Vector &vHisOrigin = pPlayer->GetAbsOrigin();
// get max distance player could have moved within max lag compensation time,
// multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value)
float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat();
// If the player is within this distance, lag compensate them in case they're running past us.
if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance ) return true;
// If their origin is not within a 45 degree cone in front of us, no need to lag compensate.
Vector vForward; AngleVectors( pCmd->viewangles, &vForward ); Vector vDiff = vHisOrigin - vMyOrigin; VectorNormalize( vDiff );
float flCosAngle = 0.707107f; // 45 degree angle
if ( vForward.Dot( vDiff ) < flCosAngle ) return false;
return true; }
Activity CHL2MP_Player::TranslateTeamActivity( Activity ActToTranslate ) { if ( m_iModelType == TEAM_COMBINE ) return ActToTranslate; if ( ActToTranslate == ACT_RUN ) return ACT_RUN_AIM_AGITATED;
if ( ActToTranslate == ACT_IDLE ) return ACT_IDLE_AIM_AGITATED;
if ( ActToTranslate == ACT_WALK ) return ACT_WALK_AIM_AGITATED;
return ActToTranslate; }
extern ConVar hl2_normspeed;
// Set the activity based on an event or current state
void CHL2MP_Player::SetAnimation( PLAYER_ANIM playerAnim ) { int animDesired;
float speed;
speed = GetAbsVelocity().Length2D();
// bool bRunning = true;
//Revisit!
/* if ( ( m_nButtons & ( IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) ) )
{ if ( speed > 1.0f && speed < hl2_normspeed.GetFloat() - 20.0f ) { bRunning = false; } }*/
if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) ) { speed = 0; playerAnim = PLAYER_IDLE; }
Activity idealActivity = ACT_HL2MP_RUN;
// This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
if ( playerAnim == PLAYER_JUMP ) { idealActivity = ACT_HL2MP_JUMP; } else if ( playerAnim == PLAYER_DIE ) { if ( m_lifeState == LIFE_ALIVE ) { return; } } else if ( playerAnim == PLAYER_ATTACK1 ) { if ( GetActivity( ) == ACT_HOVER || GetActivity( ) == ACT_SWIM || GetActivity( ) == ACT_HOP || GetActivity( ) == ACT_LEAP || GetActivity( ) == ACT_DIESIMPLE ) { idealActivity = GetActivity( ); } else { idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK; } } else if ( playerAnim == PLAYER_RELOAD ) { idealActivity = ACT_HL2MP_GESTURE_RELOAD; } else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK ) { if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_HL2MP_JUMP ) // Still jumping
{ idealActivity = GetActivity( ); } /*
else if ( GetWaterLevel() > 1 ) { if ( speed == 0 ) idealActivity = ACT_HOVER; else idealActivity = ACT_SWIM; } */ else { if ( GetFlags() & FL_DUCKING ) { if ( speed > 0 ) { idealActivity = ACT_HL2MP_WALK_CROUCH; } else { idealActivity = ACT_HL2MP_IDLE_CROUCH; } } else { if ( speed > 0 ) { /*
if ( bRunning == false ) { idealActivity = ACT_WALK; } else */ { idealActivity = ACT_HL2MP_RUN; } } else { idealActivity = ACT_HL2MP_IDLE; } } }
idealActivity = TranslateTeamActivity( idealActivity ); } if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK ) { RestartGesture( Weapon_TranslateActivity( idealActivity ) );
// FIXME: this seems a bit wacked
Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );
return; } else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD ) { RestartGesture( Weapon_TranslateActivity( idealActivity ) ); return; } else { SetActivity( idealActivity );
animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) );
if (animDesired == -1) { animDesired = SelectWeightedSequence( idealActivity );
if ( animDesired == -1 ) { animDesired = 0; } } // Already using the desired animation?
if ( GetSequence() == animDesired ) return;
m_flPlaybackRate = 1.0; ResetSequence( animDesired ); SetCycle( 0 ); return; }
// Already using the desired animation?
if ( GetSequence() == animDesired ) return;
//Msg( "Set animation to %d\n", animDesired );
// Reset to first frame of desired animation
ResetSequence( animDesired ); SetCycle( 0 ); }
extern int gEvilImpulse101; //-----------------------------------------------------------------------------
// Purpose: Player reacts to bumping a weapon.
// Input : pWeapon - the weapon that the player bumped into.
// Output : Returns true if player picked up the weapon
//-----------------------------------------------------------------------------
bool CHL2MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) { CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
// Can I have this weapon type?
if ( !IsAllowedToPickupWeapons() ) return false;
if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) { if ( gEvilImpulse101 ) { UTIL_Remove( pWeapon ); } return false; }
// Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows)
if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) ) { return false; }
bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType());
if ( bOwnsWeaponAlready == true ) { //If we have room for the ammo, then "take" the weapon too.
if ( Weapon_EquipAmmoOnly( pWeapon ) ) { pWeapon->CheckRespawn();
UTIL_Remove( pWeapon ); return true; } else { return false; } }
pWeapon->CheckRespawn(); Weapon_Equip( pWeapon );
return true; }
void CHL2MP_Player::ChangeTeam( int iTeam ) { /* if ( GetNextTeamChangeTime() >= gpGlobals->curtime )
{ char szReturnString[128]; Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch teams again.\n", (int)(GetNextTeamChangeTime() - gpGlobals->curtime) );
ClientPrint( this, HUD_PRINTTALK, szReturnString ); return; }*/
bool bKill = false;
if ( HL2MPRules()->IsTeamplay() != true && iTeam != TEAM_SPECTATOR ) { //don't let them try to join combine or rebels during deathmatch.
iTeam = TEAM_UNASSIGNED; }
if ( HL2MPRules()->IsTeamplay() == true ) { if ( iTeam != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED ) { bKill = true; } }
BaseClass::ChangeTeam( iTeam );
m_flNextTeamChangeTime = gpGlobals->curtime + TEAM_CHANGE_INTERVAL;
if ( HL2MPRules()->IsTeamplay() == true ) { SetPlayerTeamModel(); } else { SetPlayerModel(); }
if ( iTeam == TEAM_SPECTATOR ) { RemoveAllItems( true );
State_Transition( STATE_OBSERVER_MODE ); }
if ( bKill == true ) { CommitSuicide(); } }
bool CHL2MP_Player::HandleCommand_JoinTeam( int team ) { if ( !GetGlobalTeam( team ) || team == 0 ) { Warning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team ); return false; }
if ( team == TEAM_SPECTATOR ) { // Prevent this is the cvar is set
if ( !mp_allowspectators.GetInt() ) { ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" ); return false; }
if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() ) { m_fNextSuicideTime = gpGlobals->curtime; // allow the suicide to work
CommitSuicide();
// add 1 to frags to balance out the 1 subtracted for killing yourself
IncrementFragCount( 1 ); }
ChangeTeam( TEAM_SPECTATOR );
return true; } else { StopObserverMode(); State_Transition(STATE_ACTIVE); }
// Switch their actual team...
ChangeTeam( team );
return true; }
bool CHL2MP_Player::ClientCommand( const CCommand &args ) { if ( FStrEq( args[0], "spectate" ) ) { if ( ShouldRunRateLimitedCommand( args ) ) { // instantly join spectators
HandleCommand_JoinTeam( TEAM_SPECTATOR ); } return true; } else if ( FStrEq( args[0], "jointeam" ) ) { if ( args.ArgC() < 2 ) { Warning( "Player sent bad jointeam syntax\n" ); }
if ( ShouldRunRateLimitedCommand( args ) ) { int iTeam = atoi( args[1] ); HandleCommand_JoinTeam( iTeam ); } return true; } else if ( FStrEq( args[0], "joingame" ) ) { return true; }
return BaseClass::ClientCommand( args ); }
void CHL2MP_Player::CheatImpulseCommands( int iImpulse ) { switch ( iImpulse ) { case 101: { if( sv_cheats->GetBool() ) { GiveAllItems(); } } break;
default: BaseClass::CheatImpulseCommands( iImpulse ); } }
bool CHL2MP_Player::ShouldRunRateLimitedCommand( const CCommand &args ) { int i = m_RateLimitLastCommandTimes.Find( args[0] ); if ( i == m_RateLimitLastCommandTimes.InvalidIndex() ) { m_RateLimitLastCommandTimes.Insert( args[0], gpGlobals->curtime ); return true; } else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < HL2MP_COMMAND_MAX_RATE ) { // Too fast.
return false; } else { m_RateLimitLastCommandTimes[i] = gpGlobals->curtime; return true; } }
void CHL2MP_Player::CreateViewModel( int index /*=0*/ ) { Assert( index >= 0 && index < MAX_VIEWMODELS );
if ( GetViewModel( index ) ) return;
CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" ); if ( vm ) { vm->SetAbsOrigin( GetAbsOrigin() ); vm->SetOwner( this ); vm->SetIndex( index ); DispatchSpawn( vm ); vm->FollowEntity( this, false ); m_hViewModel.Set( index, vm ); } }
bool CHL2MP_Player::BecomeRagdollOnClient( const Vector &force ) { return true; }
// -------------------------------------------------------------------------------- //
// Ragdoll entities.
// -------------------------------------------------------------------------------- //
class CHL2MPRagdoll : public CBaseAnimatingOverlay { public: DECLARE_CLASS( CHL2MPRagdoll, CBaseAnimatingOverlay ); DECLARE_SERVERCLASS();
// Transmit ragdolls to everyone.
virtual int UpdateTransmitState() { return SetTransmitState( FL_EDICT_ALWAYS ); }
public: // In case the client has the player entity, we transmit the player index.
// In case the client doesn't have it, we transmit the player's model index, origin, and angles
// so they can create a ragdoll in the right place.
CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
CNetworkVector( m_vecRagdollVelocity ); CNetworkVector( m_vecRagdollOrigin ); };
LINK_ENTITY_TO_CLASS( hl2mp_ragdoll, CHL2MPRagdoll );
IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL2MPRagdoll, DT_HL2MPRagdoll ) SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ), SendPropEHandle( SENDINFO( m_hPlayer ) ), SendPropModelIndex( SENDINFO( m_nModelIndex ) ), SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ), SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ), SendPropVector( SENDINFO( m_vecRagdollVelocity ) ) END_SEND_TABLE()
void CHL2MP_Player::CreateRagdollEntity( void ) { if ( m_hRagdoll ) { UTIL_RemoveImmediate( m_hRagdoll ); m_hRagdoll = NULL; }
// If we already have a ragdoll, don't make another one.
CHL2MPRagdoll *pRagdoll = dynamic_cast< CHL2MPRagdoll* >( m_hRagdoll.Get() ); if ( !pRagdoll ) { // create a new one
pRagdoll = dynamic_cast< CHL2MPRagdoll* >( CreateEntityByName( "hl2mp_ragdoll" ) ); }
if ( pRagdoll ) { pRagdoll->m_hPlayer = this; pRagdoll->m_vecRagdollOrigin = GetAbsOrigin(); pRagdoll->m_vecRagdollVelocity = GetAbsVelocity(); pRagdoll->m_nModelIndex = m_nModelIndex; pRagdoll->m_nForceBone = m_nForceBone; pRagdoll->m_vecForce = m_vecTotalBulletForce; pRagdoll->SetAbsOrigin( GetAbsOrigin() ); }
// ragdolls will be removed on round restart automatically
m_hRagdoll = pRagdoll; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CHL2MP_Player::FlashlightIsOn( void ) { return IsEffectActive( EF_DIMLIGHT ); }
extern ConVar flashlight;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2MP_Player::FlashlightTurnOn( void ) { if( flashlight.GetInt() > 0 && IsAlive() ) { AddEffects( EF_DIMLIGHT ); EmitSound( "HL2Player.FlashlightOn" ); } }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2MP_Player::FlashlightTurnOff( void ) { RemoveEffects( EF_DIMLIGHT ); if( IsAlive() ) { EmitSound( "HL2Player.FlashlightOff" ); } }
void CHL2MP_Player::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity ) { //Drop a grenade if it's primed.
if ( GetActiveWeapon() ) { CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType("weapon_frag");
if ( GetActiveWeapon() == pGrenade ) { if ( ( m_nButtons & IN_ATTACK ) || (m_nButtons & IN_ATTACK2) ) { DropPrimedFragGrenade( this, pGrenade ); return; } } }
BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); }
void CHL2MP_Player::DetonateTripmines( void ) { CBaseEntity *pEntity = NULL;
while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL) { CSatchelCharge *pSatchel = dynamic_cast<CSatchelCharge *>(pEntity); if (pSatchel->m_bIsLive && pSatchel->GetThrower() == this ) { g_EventQueue.AddEvent( pSatchel, "Explode", 0.20, this, this ); } }
// Play sound for pressing the detonator
EmitSound( "Weapon_SLAM.SatchelDetonate" ); }
void CHL2MP_Player::Event_Killed( const CTakeDamageInfo &info ) { //update damage info with our accumulated physics force
CTakeDamageInfo subinfo = info; subinfo.SetDamageForce( m_vecTotalBulletForce );
SetNumAnimOverlays( 0 );
// Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
// because we still want to transmit to the clients in our PVS.
CreateRagdollEntity();
DetonateTripmines();
BaseClass::Event_Killed( subinfo );
if ( info.GetDamageType() & DMG_DISSOLVE ) { if ( m_hRagdoll ) { m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); } }
CBaseEntity *pAttacker = info.GetAttacker();
if ( pAttacker ) { int iScoreToAdd = 1;
if ( pAttacker == this ) { iScoreToAdd = -1; }
GetGlobalTeam( pAttacker->GetTeamNumber() )->AddScore( iScoreToAdd ); }
FlashlightTurnOff();
m_lifeState = LIFE_DEAD;
RemoveEffects( EF_NODRAW ); // still draw player body
StopZooming(); }
int CHL2MP_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo ) { //return here if the player is in the respawn grace period vs. slams.
if ( gpGlobals->curtime < m_flSlamProtectTime && (inputInfo.GetDamageType() == DMG_BLAST ) ) return 0;
m_vecTotalBulletForce += inputInfo.GetDamageForce(); gamestats->Event_PlayerDamage( this, inputInfo );
return BaseClass::OnTakeDamage( inputInfo ); }
void CHL2MP_Player::DeathSound( const CTakeDamageInfo &info ) { if ( m_hRagdoll && m_hRagdoll->GetBaseAnimating()->IsDissolving() ) return;
char szStepSound[128];
Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.Die", GetPlayerModelSoundPrefix() );
const char *pModelName = STRING( GetModelName() );
CSoundParameters params; if ( GetParametersForSound( szStepSound, params, pModelName ) == false ) return;
Vector vecOrigin = GetAbsOrigin(); CRecipientFilter filter; filter.AddRecipientsByPAS( vecOrigin );
EmitSound_t ep; ep.m_nChannel = params.channel; ep.m_pSoundName = params.soundname; ep.m_flVolume = params.volume; ep.m_SoundLevel = params.soundlevel; ep.m_nFlags = 0; ep.m_nPitch = params.pitch; ep.m_pOrigin = &vecOrigin;
EmitSound( filter, entindex(), ep ); }
CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void ) { CBaseEntity *pSpot = NULL; CBaseEntity *pLastSpawnPoint = g_pLastSpawn; edict_t *player = edict(); const char *pSpawnpointName = "info_player_deathmatch";
if ( HL2MPRules()->IsTeamplay() == true ) { if ( GetTeamNumber() == TEAM_COMBINE ) { pSpawnpointName = "info_player_combine"; pLastSpawnPoint = g_pLastCombineSpawn; } else if ( GetTeamNumber() == TEAM_REBELS ) { pSpawnpointName = "info_player_rebel"; pLastSpawnPoint = g_pLastRebelSpawn; }
if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL ) { pSpawnpointName = "info_player_deathmatch"; pLastSpawnPoint = g_pLastSpawn; } }
pSpot = pLastSpawnPoint; // Randomize the start spot
for ( int i = random->RandomInt(1,5); i > 0; i-- ) pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); if ( !pSpot ) // skip over the null point
pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName );
CBaseEntity *pFirstSpot = pSpot;
do { if ( pSpot ) { // check if pSpot is valid
if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) { if ( pSpot->GetLocalOrigin() == vec3_origin ) { pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); continue; }
// if so, go to pSpot
goto ReturnSpot; } } // increment pSpot
pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); } while ( pSpot != pFirstSpot ); // loop if we're not back to the start
// we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
if ( pSpot ) { CBaseEntity *ent = NULL; for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) { // if ent is a client, kill em (unless they are ourselves)
if ( ent->IsPlayer() && !(ent->edict() == player) ) ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); } goto ReturnSpot; }
if ( !pSpot ) { pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" );
if ( pSpot ) goto ReturnSpot; }
ReturnSpot:
if ( HL2MPRules()->IsTeamplay() == true ) { if ( GetTeamNumber() == TEAM_COMBINE ) { g_pLastCombineSpawn = pSpot; } else if ( GetTeamNumber() == TEAM_REBELS ) { g_pLastRebelSpawn = pSpot; } }
g_pLastSpawn = pSpot;
m_flSlamProtectTime = gpGlobals->curtime + 0.5;
return pSpot; }
CON_COMMAND( timeleft, "prints the time remaining in the match" ) { CHL2MP_Player *pPlayer = ToHL2MPPlayer( UTIL_GetCommandClient() );
int iTimeRemaining = (int)HL2MPRules()->GetMapRemainingTime(); if ( iTimeRemaining == 0 ) { if ( pPlayer ) { ClientPrint( pPlayer, HUD_PRINTTALK, "This game has no timelimit." ); } else { Msg( "* No Time Limit *\n" ); } } else { int iMinutes, iSeconds; iMinutes = iTimeRemaining / 60; iSeconds = iTimeRemaining % 60;
char minutes[8]; char seconds[8];
Q_snprintf( minutes, sizeof(minutes), "%d", iMinutes ); Q_snprintf( seconds, sizeof(seconds), "%2.2d", iSeconds );
if ( pPlayer ) { ClientPrint( pPlayer, HUD_PRINTTALK, "Time left in map: %s1:%s2", minutes, seconds ); } else { Msg( "Time Remaining: %s:%s\n", minutes, seconds ); } } }
void CHL2MP_Player::Reset() { ResetDeathCount(); ResetFragCount(); }
bool CHL2MP_Player::IsReady() { return m_bReady; }
void CHL2MP_Player::SetReady( bool bReady ) { m_bReady = bReady; }
void CHL2MP_Player::CheckChatText( char *p, int bufsize ) { //Look for escape sequences and replace
char *buf = new char[bufsize]; int pos = 0;
// Parse say text for escape sequences
for ( char *pSrc = p; pSrc != NULL && *pSrc != 0 && pos < bufsize-1; pSrc++ ) { // copy each char across
buf[pos] = *pSrc; pos++; }
buf[pos] = '\0';
// copy buf back into p
Q_strncpy( p, buf, bufsize );
delete[] buf;
const char *pReadyCheck = p;
HL2MPRules()->CheckChatForReadySignal( this, pReadyCheck ); }
void CHL2MP_Player::State_Transition( HL2MPPlayerState newState ) { State_Leave(); State_Enter( newState ); }
void CHL2MP_Player::State_Enter( HL2MPPlayerState newState ) { m_iPlayerState = newState; m_pCurStateInfo = State_LookupInfo( newState );
// Initialize the new state.
if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) (this->*m_pCurStateInfo->pfnEnterState)(); }
void CHL2MP_Player::State_Leave() { if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) { (this->*m_pCurStateInfo->pfnLeaveState)(); } }
void CHL2MP_Player::State_PreThink() { if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink ) { (this->*m_pCurStateInfo->pfnPreThink)(); } }
CHL2MPPlayerStateInfo *CHL2MP_Player::State_LookupInfo( HL2MPPlayerState state ) { // This table MUST match the
static CHL2MPPlayerStateInfo playerStateInfos[] = { { STATE_ACTIVE, "STATE_ACTIVE", &CHL2MP_Player::State_Enter_ACTIVE, NULL, &CHL2MP_Player::State_PreThink_ACTIVE }, { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CHL2MP_Player::State_Enter_OBSERVER_MODE, NULL, &CHL2MP_Player::State_PreThink_OBSERVER_MODE } };
for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) { if ( playerStateInfos[i].m_iPlayerState == state ) return &playerStateInfos[i]; }
return NULL; }
bool CHL2MP_Player::StartObserverMode(int mode) { //we only want to go into observer mode if the player asked to, not on a death timeout
if ( m_bEnterObserver == true ) { VPhysicsDestroyObject(); return BaseClass::StartObserverMode( mode ); } return false; }
void CHL2MP_Player::StopObserverMode() { m_bEnterObserver = false; BaseClass::StopObserverMode(); }
void CHL2MP_Player::State_Enter_OBSERVER_MODE() { int observerMode = m_iObserverLastMode; if ( IsNetClient() ) { const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" ); if ( pIdealMode ) { observerMode = atoi( pIdealMode ); if ( observerMode <= OBS_MODE_FIXED || observerMode > OBS_MODE_ROAMING ) { observerMode = m_iObserverLastMode; } } } m_bEnterObserver = true; StartObserverMode( observerMode ); }
void CHL2MP_Player::State_PreThink_OBSERVER_MODE() { // Make sure nobody has changed any of our state.
// Assert( GetMoveType() == MOVETYPE_FLY );
Assert( m_takedamage == DAMAGE_NO ); Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) ); // Assert( IsEffectActive( EF_NODRAW ) );
// Must be dead.
Assert( m_lifeState == LIFE_DEAD ); Assert( pl.deadflag ); }
void CHL2MP_Player::State_Enter_ACTIVE() { SetMoveType( MOVETYPE_WALK ); // md 8/15/07 - They'll get set back to solid when they actually respawn. If we set them solid now and mp_forcerespawn
// is false, then they'll be spectating but blocking live players from moving.
// RemoveSolidFlags( FSOLID_NOT_SOLID );
m_Local.m_iHideHUD = 0; }
void CHL2MP_Player::State_PreThink_ACTIVE() { //we don't really need to do anything here.
//This state_prethink structure came over from CS:S and was doing an assert check that fails the way hl2dm handles death
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHL2MP_Player::CanHearAndReadChatFrom( CBasePlayer *pPlayer ) { // can always hear the console unless we're ignoring all chat
if ( !pPlayer ) return false;
return true; }
|