//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Functions dealing with the player.
//
//===========================================================================//
# include "cbase.h"
# include "const.h"
# include "baseplayer_shared.h"
# include "trains.h"
# include "soundent.h"
# include "gib.h"
# include "shake.h"
# include "decals.h"
# include "gamerules.h"
# include "game.h"
# include "entityapi.h"
# include "entitylist.h"
# include "eventqueue.h"
# include "worldsize.h"
# include "isaverestore.h"
# include "globalstate.h"
# include "basecombatweapon.h"
# include "ai_basenpc.h"
# include "ai_network.h"
# include "ai_node.h"
# include "ai_networkmanager.h"
# include "ammodef.h"
# include "mathlib/mathlib.h"
# include "ndebugoverlay.h"
# include "baseviewmodel.h"
# include "in_buttons.h"
# include "client.h"
# include "team.h"
# include "particle_smokegrenade.h"
# include "IEffects.h"
# include "vstdlib/random.h"
# include "engine/IEngineSound.h"
# include "movehelper_server.h"
# include "igamemovement.h"
# include "saverestoretypes.h"
# include "iservervehicle.h"
# include "movevars_shared.h"
# include "vcollide_parse.h"
# include "player_command.h"
# include "vehicle_base.h"
# include "AI_Criteria.h"
# include "globals.h"
# include "usermessages.h"
# include "gamevars_shared.h"
# include "world.h"
# include "physobj.h"
# include "KeyValues.h"
# include "coordsize.h"
# include "vphysics/player_controller.h"
# include "saverestore_utlvector.h"
# include "hltvdirector.h"
# include "nav_mesh.h"
# include "env_zoom.h"
# include "rumble_shared.h"
# include "gamestats.h"
# include "npcevent.h"
# include "datacache/imdlcache.h"
# include "hintsystem.h"
# include "env_debughistory.h"
# include "fogcontroller.h"
# include "gameinterface.h"
# include "hl2orange.spa.h"
# include "dt_utlvector_send.h"
# include "vote_controller.h"
# include "ai_speech.h"
# if defined USES_ECON_ITEMS
# include "econ_wearable.h"
# endif
// NVNT haptic utils
# include "haptics/haptic_utils.h"
# ifdef HL2_DLL
# include "combine_mine.h"
# include "weapon_physcannon.h"
# endif
ConVar autoaim_max_dist ( " autoaim_max_dist " , " 2160 " ) ; // 2160 = 180 feet
ConVar autoaim_max_deflect ( " autoaim_max_deflect " , " 0.99 " ) ;
# ifdef CSTRIKE_DLL
ConVar spec_freeze_time ( " spec_freeze_time " , " 5.0 " , FCVAR_CHEAT | FCVAR_REPLICATED , " Time spend frozen in observer freeze cam. " ) ;
ConVar spec_freeze_traveltime ( " spec_freeze_traveltime " , " 0.7 " , FCVAR_CHEAT | FCVAR_REPLICATED , " Time taken to zoom in to frame a target in observer freeze cam. " , true , 0.01 , false , 0 ) ;
# else
ConVar spec_freeze_time ( " spec_freeze_time " , " 4.0 " , FCVAR_CHEAT | FCVAR_REPLICATED , " Time spend frozen in observer freeze cam. " ) ;
ConVar spec_freeze_traveltime ( " spec_freeze_traveltime " , " 0.4 " , FCVAR_CHEAT | FCVAR_REPLICATED , " Time taken to zoom in to frame a target in observer freeze cam. " , true , 0.01 , false , 0 ) ;
# endif
ConVar sv_bonus_challenge ( " sv_bonus_challenge " , " 0 " , FCVAR_REPLICATED , " Set to values other than 0 to select a bonus map challenge type. " ) ;
static ConVar sv_maxusrcmdprocessticks ( " sv_maxusrcmdprocessticks " , " 24 " , FCVAR_NOTIFY , " Maximum number of client-issued usrcmd ticks that can be replayed in packet loss conditions, 0 to allow no restrictions " ) ;
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
static ConVar old_armor ( " player_old_armor " , " 0 " ) ;
static ConVar physicsshadowupdate_render ( " physicsshadowupdate_render " , " 0 " ) ;
bool IsInCommentaryMode ( void ) ;
bool IsListeningToCommentary ( void ) ;
# if !defined( CSTRIKE_DLL )
ConVar cl_sidespeed ( " cl_sidespeed " , " 450 " , FCVAR_REPLICATED | FCVAR_CHEAT ) ;
ConVar cl_upspeed ( " cl_upspeed " , " 320 " , FCVAR_REPLICATED | FCVAR_CHEAT ) ;
ConVar cl_forwardspeed ( " cl_forwardspeed " , " 450 " , FCVAR_REPLICATED | FCVAR_CHEAT ) ;
ConVar cl_backspeed ( " cl_backspeed " , " 450 " , FCVAR_REPLICATED | FCVAR_CHEAT ) ;
# endif // CSTRIKE_DLL
// This is declared in the engine, too
ConVar sv_noclipduringpause ( " sv_noclipduringpause " , " 0 " , FCVAR_REPLICATED | FCVAR_CHEAT , " If cheats are enabled, then you can noclip with the game paused (for doing screenshots, etc.) . " ) ;
extern ConVar sv_maxunlag ;
extern ConVar sv_turbophysics ;
extern ConVar * sv_maxreplay ;
extern CServerGameDLL g_ServerGameDLL ;
// TIME BASED DAMAGE AMOUNT
// tweak these values based on gameplay feedback:
# define PARALYZE_DURATION 2 // number of 2 second intervals to take damage
# define PARALYZE_DAMAGE 1.0 // damage to take each 2 second interval
# define NERVEGAS_DURATION 2
# define NERVEGAS_DAMAGE 5.0
# define POISON_DURATION 5
# define POISON_DAMAGE 2.0
# define RADIATION_DURATION 2
# define RADIATION_DAMAGE 1.0
# define ACID_DURATION 2
# define ACID_DAMAGE 5.0
# define SLOWBURN_DURATION 2
# define SLOWBURN_DAMAGE 1.0
# define SLOWFREEZE_DURATION 2
# define SLOWFREEZE_DAMAGE 1.0
//----------------------------------------------------
// Player Physics Shadow
//----------------------------------------------------
# define VPHYS_MAX_DISTANCE 2.0
# define VPHYS_MAX_VEL 10
# define VPHYS_MAX_DISTSQR (VPHYS_MAX_DISTANCE*VPHYS_MAX_DISTANCE)
# define VPHYS_MAX_VELSQR (VPHYS_MAX_VEL*VPHYS_MAX_VEL)
extern bool g_fDrawLines ;
int gEvilImpulse101 ;
bool gInitHUD = true ;
extern void respawn ( CBaseEntity * pEdict , bool fCopyCorpse ) ;
int MapTextureTypeStepType ( char chTextureType ) ;
extern void SpawnBlood ( Vector vecSpot , const Vector & vecDir , int bloodColor , float flDamage ) ;
extern void AddMultiDamage ( const CTakeDamageInfo & info , CBaseEntity * pEntity ) ;
# define CMD_MOSTRECENT 0
//#define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes
//#define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit)
//#define PLAYER_MAX_SAFE_FALL_DIST 20// falling any farther than this many feet will inflict damage
//#define PLAYER_FATAL_FALL_DIST 60// 100% damage inflicted if player falls this many feet
//#define DAMAGE_PER_UNIT_FALLEN (float)( 100 ) / ( ( PLAYER_FATAL_FALL_DIST - PLAYER_MAX_SAFE_FALL_DIST ) * 12 )
//#define MAX_SAFE_FALL_UNITS ( PLAYER_MAX_SAFE_FALL_DIST * 12 )
// player damage adjusters
ConVar sk_player_head ( " sk_player_head " , " 2 " ) ;
ConVar sk_player_chest ( " sk_player_chest " , " 1 " ) ;
ConVar sk_player_stomach ( " sk_player_stomach " , " 1 " ) ;
ConVar sk_player_arm ( " sk_player_arm " , " 1 " ) ;
ConVar sk_player_leg ( " sk_player_leg " , " 1 " ) ;
//ConVar player_usercommand_timeout( "player_usercommand_timeout", "10", 0, "After this many seconds without a usercommand from a player, the client is kicked." );
# ifdef _DEBUG
ConVar sv_player_net_suppress_usercommands ( " sv_player_net_suppress_usercommands " , " 0 " , FCVAR_CHEAT , " For testing usercommand hacking sideeffects. DO NOT SHIP " ) ;
# endif // _DEBUG
ConVar sv_player_display_usercommand_errors ( " sv_player_display_usercommand_errors " , " 0 " , FCVAR_CHEAT , " 1 = Display warning when command values are out-of-range. 2 = Spew invalid ranges. " ) ;
ConVar player_debug_print_damage ( " player_debug_print_damage " , " 0 " , FCVAR_CHEAT , " When true, print amount and type of all damage received by player to console. " ) ;
void CC_GiveCurrentAmmo ( void )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( 1 ) ;
if ( pPlayer )
{
CBaseCombatWeapon * pWeapon = pPlayer - > GetActiveWeapon ( ) ;
if ( pWeapon )
{
if ( pWeapon - > UsesPrimaryAmmo ( ) )
{
int ammoIndex = pWeapon - > GetPrimaryAmmoType ( ) ;
if ( ammoIndex ! = - 1 )
{
int giveAmount ;
giveAmount = GetAmmoDef ( ) - > MaxCarry ( ammoIndex ) ;
pPlayer - > GiveAmmo ( giveAmount , GetAmmoDef ( ) - > GetAmmoOfIndex ( ammoIndex ) - > pName ) ;
}
}
if ( pWeapon - > UsesSecondaryAmmo ( ) & & pWeapon - > HasSecondaryAmmo ( ) )
{
// Give secondary ammo out, as long as the player already has some
// from a presumeably natural source. This prevents players on XBox
// having Combine Balls and so forth in areas of the game that
// were not tested with these items.
int ammoIndex = pWeapon - > GetSecondaryAmmoType ( ) ;
if ( ammoIndex ! = - 1 )
{
int giveAmount ;
giveAmount = GetAmmoDef ( ) - > MaxCarry ( ammoIndex ) ;
pPlayer - > GiveAmmo ( giveAmount , GetAmmoDef ( ) - > GetAmmoOfIndex ( ammoIndex ) - > pName ) ;
}
}
}
}
}
static ConCommand givecurrentammo ( " givecurrentammo " , CC_GiveCurrentAmmo , " Give a supply of ammo for current weapon.. \n " , FCVAR_CHEAT ) ;
// pl
BEGIN_SIMPLE_DATADESC ( CPlayerState )
// DEFINE_FIELD( netname, FIELD_STRING ), // Don't stomp player name with what's in save/restore
DEFINE_FIELD ( v_angle , FIELD_VECTOR ) ,
DEFINE_FIELD ( deadflag , FIELD_BOOLEAN ) ,
// this is always set to true on restore, don't bother saving it.
// DEFINE_FIELD( fixangle, FIELD_INTEGER ),
// DEFINE_FIELD( anglechange, FIELD_FLOAT ),
// DEFINE_FIELD( hltv, FIELD_BOOLEAN ),
// DEFINE_FIELD( replay, FIELD_BOOLEAN ),
// DEFINE_FIELD( frags, FIELD_INTEGER ),
// DEFINE_FIELD( deaths, FIELD_INTEGER ),
END_DATADESC ( )
// Global Savedata for player
BEGIN_DATADESC ( CBasePlayer )
DEFINE_EMBEDDED ( m_Local ) ,
# if defined USES_ECON_ITEMS
DEFINE_EMBEDDED ( m_AttributeList ) ,
# endif
DEFINE_UTLVECTOR ( m_hTriggerSoundscapeList , FIELD_EHANDLE ) ,
DEFINE_EMBEDDED ( pl ) ,
DEFINE_FIELD ( m_StuckLast , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_nButtons , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_afButtonLast , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_afButtonPressed , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_afButtonReleased , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_afButtonDisabled , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_afButtonForced , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iFOV , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iFOVStart , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_flFOVTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_iDefaultFOV , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_flVehicleViewFOV , FIELD_FLOAT ) ,
//DEFINE_FIELD( m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore
DEFINE_FIELD ( m_iObserverMode , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iObserverLastMode , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_hObserverTarget , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_bForcedObserverMode , FIELD_BOOLEAN ) ,
DEFINE_AUTO_ARRAY ( m_szAnimExtension , FIELD_CHARACTER ) ,
// DEFINE_CUSTOM_FIELD( m_Activity, ActivityDataOps() ),
DEFINE_FIELD ( m_nUpdateRate , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_fLerpTime , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_bLagCompensation , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_bPredictWeapons , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_vecAdditionalPVSOrigin , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_vecCameraPVSOrigin , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_hUseEntity , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_iTrain , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iRespawnFrames , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_afPhysicsFlags , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_hVehicle , FIELD_EHANDLE ) ,
// recreate, don't restore
// DEFINE_FIELD( m_CommandContext, CUtlVector < CCommandContext > ),
//DEFINE_FIELD( m_pPhysicsController, FIELD_POINTER ),
//DEFINE_FIELD( m_pShadowStand, FIELD_POINTER ),
//DEFINE_FIELD( m_pShadowCrouch, FIELD_POINTER ),
//DEFINE_FIELD( m_vphysicsCollisionState, FIELD_INTEGER ),
DEFINE_ARRAY ( m_szNetworkIDString , FIELD_CHARACTER , MAX_NETWORKID_LENGTH ) ,
DEFINE_FIELD ( m_oldOrigin , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_vecSmoothedVelocity , FIELD_VECTOR ) ,
//DEFINE_FIELD( m_touchedPhysObject, FIELD_BOOLEAN ),
//DEFINE_FIELD( m_bPhysicsWasFrozen, FIELD_BOOLEAN ),
//DEFINE_FIELD( m_iPlayerSound, FIELD_INTEGER ), // Don't restore, set in Precache()
DEFINE_FIELD ( m_iTargetVolume , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( m_rgItems , FIELD_INTEGER ) ,
//DEFINE_FIELD( m_fNextSuicideTime, FIELD_TIME ),
// DEFINE_FIELD( m_PlayerInfo, CPlayerInfo ),
DEFINE_FIELD ( m_flSwimTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_flDuckTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_flDuckJumpTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_flSuitUpdate , FIELD_TIME ) ,
DEFINE_AUTO_ARRAY ( m_rgSuitPlayList , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iSuitPlayNext , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( m_rgiSuitNoRepeat , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( m_rgflSuitNoRepeatTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_bPauseBonusProgress , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_iBonusProgress , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iBonusChallenge , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_lastDamageAmount , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_tbdPrev , FIELD_TIME ) ,
DEFINE_FIELD ( m_flStepSoundTime , FIELD_FLOAT ) ,
DEFINE_ARRAY ( m_szNetname , FIELD_CHARACTER , MAX_PLAYER_NAME_LENGTH ) ,
//DEFINE_FIELD( m_flgeigerRange, FIELD_FLOAT ), // Don't restore, reset in Precache()
//DEFINE_FIELD( m_flgeigerDelay, FIELD_FLOAT ), // Don't restore, reset in Precache()
//DEFINE_FIELD( m_igeigerRangePrev, FIELD_FLOAT ), // Don't restore, reset in Precache()
//DEFINE_FIELD( m_iStepLeft, FIELD_INTEGER ), // Don't need to restore
//DEFINE_FIELD( m_chTextureType, FIELD_CHARACTER ), // Don't need to restore
//DEFINE_FIELD( m_surfaceProps, FIELD_INTEGER ), // don't need to restore, reset by gamemovement
// DEFINE_FIELD( m_pSurfaceData, surfacedata_t* ),
//DEFINE_FIELD( m_surfaceFriction, FIELD_FLOAT ),
//DEFINE_FIELD( m_chPreviousTextureType, FIELD_CHARACTER ),
DEFINE_FIELD ( m_idrowndmg , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_idrownrestored , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_nPoisonDmg , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_nPoisonRestored , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_bitsHUDDamage , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_fInitHUD , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_flDeathTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_flDeathAnimTime , FIELD_TIME ) ,
//DEFINE_FIELD( m_fGameHUDInitialized, FIELD_BOOLEAN ), // only used in multiplayer games
//DEFINE_FIELD( m_fWeapon, FIELD_BOOLEAN ), // Don't restore, client needs reset
//DEFINE_FIELD( m_iUpdateTime, FIELD_INTEGER ), // Don't need to restore
//DEFINE_FIELD( m_iClientBattery, FIELD_INTEGER ), // Don't restore, client needs reset
//DEFINE_FIELD( m_iClientHideHUD, FIELD_INTEGER ), // Don't restore, client needs reset
//DEFINE_FIELD( m_vecAutoAim, FIELD_VECTOR ), // Don't save/restore - this is recomputed
//DEFINE_FIELD( m_lastx, FIELD_INTEGER ),
//DEFINE_FIELD( m_lasty, FIELD_INTEGER ),
DEFINE_FIELD ( m_iFrags , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iDeaths , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_bAllowInstantSpawn , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_flNextDecalTime , FIELD_TIME ) ,
//DEFINE_AUTO_ARRAY( m_szTeamName, FIELD_STRING ), // mp
//DEFINE_FIELD( m_iConnected, FIELD_INTEGER ),
// from edict_t
DEFINE_FIELD ( m_ArmorValue , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_DmgOrigin , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_DmgTake , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_DmgSave , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_AirFinished , FIELD_TIME ) ,
DEFINE_FIELD ( m_PainFinished , FIELD_TIME ) ,
DEFINE_FIELD ( m_iPlayerLocked , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( m_hViewModel , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_flMaxspeed , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flWaterJumpTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_vecWaterJumpVel , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_nImpulse , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_flSwimSoundTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_vecLadderNormal , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_flFlashTime , FIELD_TIME ) ,
DEFINE_FIELD ( m_nDrownDmgRate , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_iSuicideCustomKillFlags , FIELD_INTEGER ) ,
// NOT SAVED
//DEFINE_FIELD( m_vForcedOrigin, FIELD_VECTOR ),
//DEFINE_FIELD( m_bForceOrigin, FIELD_BOOLEAN ),
//DEFINE_FIELD( m_nTickBase, FIELD_INTEGER ),
//DEFINE_FIELD( m_LastCmd, FIELD_ ),
// DEFINE_FIELD( m_pCurrentCommand, CUserCmd ),
//DEFINE_FIELD( m_bGamePaused, FIELD_BOOLEAN ),
// DEFINE_FIELD( m_iVehicleAnalogBias, FIELD_INTEGER ),
// m_flVehicleViewFOV
// m_vecVehicleViewOrigin
// m_vecVehicleViewAngles
// m_nVehicleViewSavedFrame
DEFINE_FIELD ( m_bitsDamageType , FIELD_INTEGER ) ,
DEFINE_AUTO_ARRAY ( m_rgbTimeBasedDamage , FIELD_CHARACTER ) ,
DEFINE_FIELD ( m_fLastPlayerTalkTime , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_hLastWeapon , FIELD_EHANDLE ) ,
# if !defined( NO_ENTITY_PREDICTION )
// DEFINE_FIELD( m_SimulatedByThisPlayer, CUtlVector < CHandle < CBaseEntity > > ),
# endif
DEFINE_FIELD ( m_flOldPlayerZ , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flOldPlayerViewOffsetZ , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_bPlayerUnderwater , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_hViewEntity , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_hConstraintEntity , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_vecConstraintCenter , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_flConstraintRadius , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flConstraintWidth , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flConstraintSpeedFactor , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_hZoomOwner , FIELD_EHANDLE ) ,
DEFINE_FIELD ( m_flLaggedMovementValue , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_vNewVPhysicsPosition , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_vNewVPhysicsVelocity , FIELD_VECTOR ) ,
DEFINE_FIELD ( m_bSinglePlayerGameEnding , FIELD_BOOLEAN ) ,
DEFINE_ARRAY ( m_szLastPlaceName , FIELD_CHARACTER , MAX_PLACE_NAME_LENGTH ) ,
DEFINE_FIELD ( m_autoKickDisabled , FIELD_BOOLEAN ) ,
// Function Pointers
DEFINE_FUNCTION ( PlayerDeathThink ) ,
// Inputs
DEFINE_INPUTFUNC ( FIELD_INTEGER , " SetHealth " , InputSetHealth ) ,
DEFINE_INPUTFUNC ( FIELD_BOOLEAN , " SetHUDVisibility " , InputSetHUDVisibility ) ,
DEFINE_INPUTFUNC ( FIELD_STRING , " SetFogController " , InputSetFogController ) ,
DEFINE_INPUTFUNC ( FIELD_STRING , " HandleMapEvent " , InputHandleMapEvent ) ,
DEFINE_FIELD ( m_nNumCrouches , FIELD_INTEGER ) ,
DEFINE_FIELD ( m_bDuckToggled , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( m_flForwardMove , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_flSideMove , FIELD_FLOAT ) ,
DEFINE_FIELD ( m_vecPreviouslyPredictedOrigin , FIELD_POSITION_VECTOR ) ,
DEFINE_FIELD ( m_nNumCrateHudHints , FIELD_INTEGER ) ,
// DEFINE_FIELD( m_nBodyPitchPoseParam, FIELD_INTEGER ),
// DEFINE_ARRAY( m_StepSoundCache, StepSoundCache_t, 2 ),
// DEFINE_UTLVECTOR( m_vecPlayerCmdInfo ),
// DEFINE_UTLVECTOR( m_vecPlayerSimInfo ),
END_DATADESC ( )
int giPrecacheGrunt = 0 ;
edict_t * CBasePlayer : : s_PlayerEdict = NULL ;
inline bool ShouldRunCommandsInContext ( const CCommandContext * ctx )
{
// TODO: This should be enabled at some point. If usercmds can run while paused, then
// they can create entities which will never die and it will fill up the entity list.
# ifdef NO_USERCMDS_DURING_PAUSE
return ! ctx - > paused | | sv_noclipduringpause . GetInt ( ) ;
# else
return true ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CBaseViewModel
//-----------------------------------------------------------------------------
CBaseViewModel * CBasePlayer : : GetViewModel ( int index /*= 0*/ , bool bObserverOK )
{
Assert ( index > = 0 & & index < MAX_VIEWMODELS ) ;
return m_hViewModel [ index ] . Get ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : CreateViewModel ( int index /*=0*/ )
{
Assert ( index > = 0 & & index < MAX_VIEWMODELS ) ;
if ( GetViewModel ( index ) )
return ;
CBaseViewModel * vm = ( CBaseViewModel * ) CreateEntityByName ( " viewmodel " ) ;
if ( vm )
{
vm - > SetAbsOrigin ( GetAbsOrigin ( ) ) ;
vm - > SetOwner ( this ) ;
vm - > SetIndex ( index ) ;
DispatchSpawn ( vm ) ;
vm - > FollowEntity ( this ) ;
m_hViewModel . Set ( index , vm ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : DestroyViewModels ( void )
{
int i ;
for ( i = MAX_VIEWMODELS - 1 ; i > = 0 ; i - - )
{
CBaseViewModel * vm = GetViewModel ( i ) ;
if ( ! vm )
continue ;
UTIL_Remove ( vm ) ;
m_hViewModel . Set ( i , NULL ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Static member function to create a player of the specified class
// Input : *className -
// *ed -
// Output : CBasePlayer
//-----------------------------------------------------------------------------
CBasePlayer * CBasePlayer : : CreatePlayer ( const char * className , edict_t * ed )
{
CBasePlayer * player ;
CBasePlayer : : s_PlayerEdict = ed ;
player = ( CBasePlayer * ) CreateEntityByName ( className ) ;
return player ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CBasePlayer : : CBasePlayer ( )
{
AddEFlags ( EFL_NO_AUTO_EDICT_ATTACH ) ;
# ifdef _DEBUG
m_vecAutoAim . Init ( ) ;
m_vecAdditionalPVSOrigin . Init ( ) ;
m_vecCameraPVSOrigin . Init ( ) ;
m_DmgOrigin . Init ( ) ;
m_vecLadderNormal . Init ( ) ;
m_oldOrigin . Init ( ) ;
m_vecSmoothedVelocity . Init ( ) ;
# endif
if ( s_PlayerEdict )
{
// take the assigned edict_t and attach it
Assert ( s_PlayerEdict ! = NULL ) ;
NetworkProp ( ) - > AttachEdict ( s_PlayerEdict ) ;
s_PlayerEdict = NULL ;
}
m_flFlashTime = - 1 ;
pl . fixangle = FIXANGLE_ABSOLUTE ;
pl . hltv = false ;
pl . replay = false ;
pl . frags = 0 ;
pl . deaths = 0 ;
m_szNetname [ 0 ] = ' \0 ' ;
m_iHealth = 0 ;
Weapon_SetLast ( NULL ) ;
m_bitsDamageType = 0 ;
m_bForceOrigin = false ;
m_hVehicle = NULL ;
m_pCurrentCommand = NULL ;
// Setup our default FOV
m_iDefaultFOV = g_pGameRules - > DefaultFOV ( ) ;
m_hZoomOwner = NULL ;
m_nUpdateRate = 20 ; // cl_updaterate defualt
m_fLerpTime = 0.1f ; // cl_interp default
m_bPredictWeapons = true ;
m_bLagCompensation = false ;
m_flLaggedMovementValue = 1.0f ;
m_StuckLast = 0 ;
m_impactEnergyScale = 1.0f ;
m_fLastPlayerTalkTime = 0.0f ;
m_PlayerInfo . SetParent ( this ) ;
ResetObserverMode ( ) ;
m_surfaceProps = 0 ;
m_pSurfaceData = NULL ;
m_surfaceFriction = 1.0f ;
m_chTextureType = 0 ;
m_chPreviousTextureType = 0 ;
m_iSuicideCustomKillFlags = 0 ;
m_fDelay = 0.0f ;
m_fReplayEnd = - 1 ;
m_iReplayEntity = 0 ;
m_autoKickDisabled = false ;
m_nNumCrouches = 0 ;
m_bDuckToggled = false ;
m_bPhysicsWasFrozen = false ;
// Used to mask off buttons
m_afButtonDisabled = 0 ;
m_afButtonForced = 0 ;
m_nBodyPitchPoseParam = - 1 ;
m_flForwardMove = 0 ;
m_flSideMove = 0 ;
// NVNT default to no haptics
m_bhasHaptics = false ;
m_vecConstraintCenter = vec3_origin ;
m_flLastUserCommandTime = 0.f ;
m_flMovementTimeForUserCmdProcessingRemaining = 0.0f ;
}
CBasePlayer : : ~ CBasePlayer ( )
{
VPhysicsDestroyObject ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CBasePlayer : : UpdateOnRemove ( void )
{
VPhysicsDestroyObject ( ) ;
// Remove him from his current team
if ( GetTeam ( ) )
{
GetTeam ( ) - > RemovePlayer ( this ) ;
}
// Chain at end to mimic destructor unwind order
BaseClass : : UpdateOnRemove ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : **pvs -
// **pas -
//-----------------------------------------------------------------------------
void CBasePlayer : : SetupVisibility ( CBaseEntity * pViewEntity , unsigned char * pvs , int pvssize )
{
// If we have a viewentity, we don't add the player's origin.
if ( pViewEntity )
return ;
Vector org ;
org = EyePosition ( ) ;
engine - > AddOriginToPVS ( org ) ;
}
int CBasePlayer : : UpdateTransmitState ( )
{
// always call ShouldTransmit() for players
return SetTransmitState ( FL_EDICT_FULLCHECK ) ;
}
int CBasePlayer : : ShouldTransmit ( const CCheckTransmitInfo * pInfo )
{
// Allow me to introduce myself to, err, myself.
// I.e., always update the recipient player data even if it's nodraw (first person mode)
if ( pInfo - > m_pClientEnt = = edict ( ) )
{
return FL_EDICT_ALWAYS ;
}
// when HLTV/Replay is connected and spectators press +USE, they
// signal that they are recording a interesting scene
// so transmit these 'cameramans' to the HLTV or Replay client
if ( HLTVDirector ( ) - > GetCameraMan ( ) = = entindex ( ) )
{
CBaseEntity * pRecipientEntity = CBaseEntity : : Instance ( pInfo - > m_pClientEnt ) ;
Assert ( pRecipientEntity - > IsPlayer ( ) ) ;
CBasePlayer * pRecipientPlayer = static_cast < CBasePlayer * > ( pRecipientEntity ) ;
if ( pRecipientPlayer - > IsHLTV ( ) | |
pRecipientPlayer - > IsReplay ( ) )
{
// HACK force calling RecomputePVSInformation to update PVS data
NetworkProp ( ) - > AreaNum ( ) ;
return FL_EDICT_ALWAYS ;
}
}
// Transmit for a short time after death and our death anim finishes so ragdolls can access reliable player data.
// Note that if m_flDeathAnimTime is never set, as long as m_lifeState is set to LIFE_DEAD after dying, this
// test will act as if the death anim is finished.
if ( IsEffectActive ( EF_NODRAW ) | | ( IsObserver ( ) & & ( gpGlobals - > curtime - m_flDeathTime > 0.5 ) & &
( m_lifeState = = LIFE_DEAD ) & & ( gpGlobals - > curtime - m_flDeathAnimTime > 0.5 ) ) )
{
return FL_EDICT_DONTSEND ;
}
return BaseClass : : ShouldTransmit ( pInfo ) ;
}
bool CBasePlayer : : WantsLagCompensationOnEntity ( const CBasePlayer * pPlayer , const CUserCmd * pCmd , const CBitVec < MAX_EDICTS > * pEntityTransmitBits ) const
{
// Team members shouldn't be adjusted unless friendly fire is on.
if ( ! friendlyfire . GetInt ( ) & & pPlayer - > GetTeamNumber ( ) = = GetTeamNumber ( ) )
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 ;
}
void CBasePlayer : : PauseBonusProgress ( bool bPause )
{
m_bPauseBonusProgress = bPause ;
}
void CBasePlayer : : SetBonusProgress ( int iBonusProgress )
{
if ( ! m_bPauseBonusProgress )
m_iBonusProgress = iBonusProgress ;
}
void CBasePlayer : : SetBonusChallenge ( int iBonusChallenge )
{
m_iBonusChallenge = iBonusChallenge ;
}
//-----------------------------------------------------------------------------
// Sets the view angles
//-----------------------------------------------------------------------------
void CBasePlayer : : SnapEyeAngles ( const QAngle & viewAngles )
{
pl . v_angle = viewAngles ;
pl . fixangle = FIXANGLE_ABSOLUTE ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iSpeed -
// iMax -
// Output : int
//-----------------------------------------------------------------------------
int TrainSpeed ( int iSpeed , int iMax )
{
float fSpeed , fMax ;
int iRet = 0 ;
fMax = ( float ) iMax ;
fSpeed = iSpeed ;
fSpeed = fSpeed / fMax ;
if ( iSpeed < 0 )
iRet = TRAIN_BACK ;
else if ( iSpeed = = 0 )
iRet = TRAIN_NEUTRAL ;
else if ( fSpeed < 0.33 )
iRet = TRAIN_SLOW ;
else if ( fSpeed < 0.66 )
iRet = TRAIN_MEDIUM ;
else
iRet = TRAIN_FAST ;
return iRet ;
}
void CBasePlayer : : DeathSound ( const CTakeDamageInfo & info )
{
// temporarily using pain sounds for death sounds
// Did we die from falling?
if ( m_bitsDamageType & DMG_FALL )
{
// They died in the fall. Play a splat sound.
EmitSound ( " Player.FallGib " ) ;
}
else
{
EmitSound ( " Player.Death " ) ;
}
// play one of the suit death alarms
if ( IsSuitEquipped ( ) )
{
UTIL_EmitGroupnameSuit ( edict ( ) , " HEV_DEAD " ) ;
}
}
// override takehealth
// bitsDamageType indicates type of damage healed.
int CBasePlayer : : TakeHealth ( float flHealth , int bitsDamageType )
{
// clear out any damage types we healed.
// UNDONE: generic health should not heal any
// UNDONE: time-based damage
if ( m_takedamage )
{
int bitsDmgTimeBased = g_pGameRules - > Damage_GetTimeBased ( ) ;
m_bitsDamageType & = ~ ( bitsDamageType & ~ bitsDmgTimeBased ) ;
}
// I disabled reporting history into the dbghist because it was super spammy.
// But, if you need to reenable it, the code is below in the "else" clause.
# if 1 // #ifdef DISABLE_DEBUG_HISTORY
return BaseClass : : TakeHealth ( flHealth , bitsDamageType ) ;
# else
const int healingTaken = BaseClass : : TakeHealth ( flHealth , bitsDamageType ) ;
char buf [ 256 ] ;
Q_snprintf ( buf , 256 , " [%f] Player %s healed for %d with damagetype %X \n " , gpGlobals - > curtime , GetDebugName ( ) , healingTaken , bitsDamageType ) ;
ADD_DEBUG_HISTORY ( HISTORY_PLAYER_DAMAGE , buf ) ;
return healingTaken ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Draw all overlays (should be implemented in cascade by subclass to add
// any additional non-text overlays)
// Input :
// Output : Current text offset from the top
//-----------------------------------------------------------------------------
void CBasePlayer : : DrawDebugGeometryOverlays ( void )
{
// --------------------------------------------------------
// If in buddha mode and dead draw lines to indicate death
// --------------------------------------------------------
if ( ( m_debugOverlays & OVERLAY_BUDDHA_MODE ) & & m_iHealth = = 1 )
{
Vector vBodyDir = BodyDirection2D ( ) ;
Vector eyePos = EyePosition ( ) + vBodyDir * 10.0 ;
Vector vUp = Vector ( 0 , 0 , 8 ) ;
Vector vSide ;
CrossProduct ( vBodyDir , vUp , vSide ) ;
NDebugOverlay : : Line ( eyePos + vSide + vUp , eyePos - vSide - vUp , 255 , 0 , 0 , false , 0 ) ;
NDebugOverlay : : Line ( eyePos + vSide - vUp , eyePos - vSide + vUp , 255 , 0 , 0 , false , 0 ) ;
}
BaseClass : : DrawDebugGeometryOverlays ( ) ;
}
//=========================================================
// TraceAttack
//=========================================================
void CBasePlayer : : TraceAttack ( const CTakeDamageInfo & inputInfo , const Vector & vecDir , trace_t * ptr , CDmgAccumulator * pAccumulator )
{
if ( m_takedamage )
{
CTakeDamageInfo info = inputInfo ;
if ( info . GetAttacker ( ) )
{
// --------------------------------------------------
// If an NPC check if friendly fire is disallowed
// --------------------------------------------------
CAI_BaseNPC * pNPC = info . GetAttacker ( ) - > MyNPCPointer ( ) ;
if ( pNPC & & ( pNPC - > CapabilitiesGet ( ) & bits_CAP_NO_HIT_PLAYER ) & & pNPC - > IRelationType ( this ) ! = D_HT )
return ;
// Prevent team damage here so blood doesn't appear
if ( info . GetAttacker ( ) - > IsPlayer ( ) )
{
if ( ! g_pGameRules - > FPlayerCanTakeDamage ( this , info . GetAttacker ( ) , info ) )
return ;
}
}
SetLastHitGroup ( ptr - > hitgroup ) ;
switch ( ptr - > hitgroup )
{
case HITGROUP_GENERIC :
break ;
case HITGROUP_HEAD :
info . ScaleDamage ( sk_player_head . GetFloat ( ) ) ;
break ;
case HITGROUP_CHEST :
info . ScaleDamage ( sk_player_chest . GetFloat ( ) ) ;
break ;
case HITGROUP_STOMACH :
info . ScaleDamage ( sk_player_stomach . GetFloat ( ) ) ;
break ;
case HITGROUP_LEFTARM :
case HITGROUP_RIGHTARM :
info . ScaleDamage ( sk_player_arm . GetFloat ( ) ) ;
break ;
case HITGROUP_LEFTLEG :
case HITGROUP_RIGHTLEG :
info . ScaleDamage ( sk_player_leg . GetFloat ( ) ) ;
break ;
default :
break ;
}
# ifdef HL2_EPISODIC
// If this damage type makes us bleed, then do so
bool bShouldBleed = ! g_pGameRules - > Damage_ShouldNotBleed ( info . GetDamageType ( ) ) ;
if ( bShouldBleed )
# endif
{
SpawnBlood ( ptr - > endpos , vecDir , BloodColor ( ) , info . GetDamage ( ) ) ; // a little surface blood.
TraceBleed ( info . GetDamage ( ) , vecDir , ptr , info . GetDamageType ( ) ) ;
}
AddMultiDamage ( info , this ) ;
}
}
//------------------------------------------------------------------------------
// Purpose : Do some kind of damage effect for the type of damage
// Input :
// Output :
//------------------------------------------------------------------------------
void CBasePlayer : : DamageEffect ( float flDamage , int fDamageType )
{
if ( fDamageType & DMG_CRUSH )
{
//Red damage indicator
color32 red = { 128 , 0 , 0 , 128 } ;
UTIL_ScreenFade ( this , red , 1.0f , 0.1f , FFADE_IN ) ;
}
else if ( fDamageType & DMG_DROWN )
{
//Red damage indicator
color32 blue = { 0 , 0 , 128 , 128 } ;
UTIL_ScreenFade ( this , blue , 1.0f , 0.1f , FFADE_IN ) ;
}
else if ( fDamageType & DMG_SLASH )
{
// If slash damage shoot some blood
SpawnBlood ( EyePosition ( ) , g_vecAttackDir , BloodColor ( ) , flDamage ) ;
}
else if ( fDamageType & DMG_PLASMA )
{
// Blue screen fade
color32 blue = { 0 , 0 , 255 , 100 } ;
UTIL_ScreenFade ( this , blue , 0.2 , 0.4 , FFADE_MODULATE ) ;
// Very small screen shake
// Both -0.1 and 0.1 map to 0 when converted to integer, so all of these RandomInt
// calls are just expensive ways of returning zero. This code has always been this
// way and has never had any value. clang complains about the conversion from a
// literal floating-point number to an integer.
//ViewPunch(QAngle(random->RandomInt(-0.1,0.1), random->RandomInt(-0.1,0.1), random->RandomInt(-0.1,0.1)));
// Burn sound
EmitSound ( " Player.PlasmaDamage " ) ;
}
else if ( fDamageType & DMG_SONIC )
{
// Sonic damage sound
EmitSound ( " Player.SonicDamage " ) ;
}
else if ( fDamageType & DMG_BULLET )
{
EmitSound ( " Flesh.BulletImpact " ) ;
}
}
/*
Take some damage .
NOTE : each call to OnTakeDamage with bitsDamageType set to a time - based damage
type will cause the damage time countdown to be reset . Thus the ongoing effects of poison , radiation
etc are implemented with subsequent calls to OnTakeDamage using DMG_GENERIC .
*/
// Old values
# define OLD_ARMOR_RATIO 0.2 // Armor Takes 80% of the damage
# define OLD_ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health
// New values
# define ARMOR_RATIO 0.2
# define ARMOR_BONUS 1.0
//---------------------------------------------------------
//---------------------------------------------------------
bool CBasePlayer : : ShouldTakeDamageInCommentaryMode ( const CTakeDamageInfo & inputInfo )
{
// Only ignore damage when we're listening to a commentary node
if ( ! IsListeningToCommentary ( ) )
return true ;
// Allow SetHealth inputs to kill player.
if ( inputInfo . GetInflictor ( ) = = this & & inputInfo . GetAttacker ( ) = = this )
return true ;
# ifdef PORTAL
if ( inputInfo . GetDamageType ( ) & DMG_ACID )
return true ;
# endif
// In commentary, ignore all damage except for falling and leeches
if ( ! ( inputInfo . GetDamageType ( ) & ( DMG_BURN | DMG_PLASMA | DMG_FALL | DMG_CRUSH ) ) & & inputInfo . GetDamageType ( ) ! = DMG_GENERIC )
return false ;
// We let DMG_CRUSH pass the check above so that we can check here for stress damage. Deny the CRUSH damage if there is no attacker,
// or if the attacker isn't a BSP model. Therefore, we're allowing any CRUSH damage done by a BSP model.
if ( ( inputInfo . GetDamageType ( ) & DMG_CRUSH ) & & ( inputInfo . GetAttacker ( ) = = NULL | | ! inputInfo . GetAttacker ( ) - > IsBSPModel ( ) ) )
return false ;
return true ;
}
int CBasePlayer : : OnTakeDamage ( const CTakeDamageInfo & inputInfo )
{
// have suit diagnose the problem - ie: report damage type
int bitsDamage = inputInfo . GetDamageType ( ) ;
int ffound = true ;
int fmajor ;
int fcritical ;
int fTookDamage ;
int ftrivial ;
float flRatio ;
float flBonus ;
float flHealthPrev = m_iHealth ;
CTakeDamageInfo info = inputInfo ;
IServerVehicle * pVehicle = GetVehicle ( ) ;
if ( pVehicle )
{
// Let the vehicle decide if we should take this damage or not
if ( pVehicle - > PassengerShouldReceiveDamage ( info ) = = false )
return 0 ;
}
if ( IsInCommentaryMode ( ) )
{
if ( ! ShouldTakeDamageInCommentaryMode ( info ) )
return 0 ;
}
if ( GetFlags ( ) & FL_GODMODE )
return 0 ;
if ( m_debugOverlays & OVERLAY_BUDDHA_MODE )
{
if ( ( m_iHealth - info . GetDamage ( ) ) < = 0 )
{
m_iHealth = 1 ;
return 0 ;
}
}
// Early out if there's no damage
if ( ! info . GetDamage ( ) )
return 0 ;
if ( old_armor . GetBool ( ) )
{
flBonus = OLD_ARMOR_BONUS ;
flRatio = OLD_ARMOR_RATIO ;
}
else
{
flBonus = ARMOR_BONUS ;
flRatio = ARMOR_RATIO ;
}
if ( ( info . GetDamageType ( ) & DMG_BLAST ) & & g_pGameRules - > IsMultiplayer ( ) )
{
// blasts damage armor more.
flBonus * = 2 ;
}
// Already dead
if ( ! IsAlive ( ) )
return 0 ;
// go take the damage first
if ( ! g_pGameRules - > FPlayerCanTakeDamage ( this , info . GetAttacker ( ) , inputInfo ) )
{
// Refuse the damage
return 0 ;
}
// print to console if the appropriate cvar is set
# ifdef DISABLE_DEBUG_HISTORY
if ( player_debug_print_damage . GetBool ( ) & & info . GetDamage ( ) > 0 )
# endif
{
char dmgtype [ 64 ] ;
CTakeDamageInfo : : DebugGetDamageTypeString ( info . GetDamageType ( ) , dmgtype , 512 ) ;
char outputString [ 256 ] ;
Q_snprintf ( outputString , 256 , " %f: Player %s at [%0.2f %0.2f %0.2f] took %f damage from %s, type %s \n " , gpGlobals - > curtime , GetDebugName ( ) ,
GetAbsOrigin ( ) . x , GetAbsOrigin ( ) . y , GetAbsOrigin ( ) . z , info . GetDamage ( ) , info . GetInflictor ( ) - > GetDebugName ( ) , dmgtype ) ;
//Msg( "%f: Player %s at [%0.2f %0.2f %0.2f] took %f damage from %s, type %s\n", gpGlobals->curtime, GetDebugName(),
// GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z, info.GetDamage(), info.GetInflictor()->GetDebugName(), dmgtype );
ADD_DEBUG_HISTORY ( HISTORY_PLAYER_DAMAGE , outputString ) ;
# ifndef DISABLE_DEBUG_HISTORY
if ( player_debug_print_damage . GetBool ( ) ) // if we're not in here just for the debug history
# endif
{
Msg ( " %s " , outputString ) ;
}
}
// keep track of amount of damage last sustained
m_lastDamageAmount = info . GetDamage ( ) ;
// Armor.
if ( m_ArmorValue & & ! ( info . GetDamageType ( ) & ( DMG_FALL | DMG_DROWN | DMG_POISON | DMG_RADIATION ) ) ) // armor doesn't protect against fall or drown damage!
{
float flNew = info . GetDamage ( ) * flRatio ;
float flArmor ;
flArmor = ( info . GetDamage ( ) - flNew ) * flBonus ;
if ( ! old_armor . GetBool ( ) )
{
if ( flArmor < 1.0 )
{
flArmor = 1.0 ;
}
}
// Does this use more armor than we have?
if ( flArmor > m_ArmorValue )
{
flArmor = m_ArmorValue ;
flArmor * = ( 1 / flBonus ) ;
flNew = info . GetDamage ( ) - flArmor ;
m_DmgSave = m_ArmorValue ;
m_ArmorValue = 0 ;
}
else
{
m_DmgSave = flArmor ;
m_ArmorValue - = flArmor ;
}
info . SetDamage ( flNew ) ;
}
# if defined( WIN32 ) && !defined( _X360 )
// NVNT if player's client has a haptic device send them a user message with the damage.
if ( HasHaptics ( ) )
HapticsDamage ( this , info ) ;
# endif
// this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that
// as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc)
// NOTENOTE: jdw - We are now capable of retaining the mantissa of this damage value and deferring its application
// info.SetDamage( (int)info.GetDamage() );
// Call up to the base class
fTookDamage = BaseClass : : OnTakeDamage ( info ) ;
// Early out if the base class took no damage
if ( ! fTookDamage )
return 0 ;
// add to the damage total for clients, which will be sent as a single
// message at the end of the frame
// todo: remove after combining shotgun blasts?
if ( info . GetInflictor ( ) & & info . GetInflictor ( ) - > edict ( ) )
m_DmgOrigin = info . GetInflictor ( ) - > GetAbsOrigin ( ) ;
m_DmgTake + = ( int ) info . GetDamage ( ) ;
// Reset damage time countdown for each type of time based damage player just sustained
for ( int i = 0 ; i < CDMG_TIMEBASED ; i + + )
{
// Make sure the damage type is really time-based.
// This is kind of hacky but necessary until we setup DamageType as an enum.
int iDamage = ( DMG_PARALYZE < < i ) ;
if ( ( info . GetDamageType ( ) & iDamage ) & & g_pGameRules - > Damage_IsTimeBased ( iDamage ) )
{
m_rgbTimeBasedDamage [ i ] = 0 ;
}
}
// Display any effect associate with this damage type
DamageEffect ( info . GetDamage ( ) , bitsDamage ) ;
// how bad is it, doc?
ftrivial = ( m_iHealth > 75 | | m_lastDamageAmount < 5 ) ;
fmajor = ( m_lastDamageAmount > 25 ) ;
fcritical = ( m_iHealth < 30 ) ;
// handle all bits set in this damage message,
// let the suit give player the diagnosis
// UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash )
// UNDONE: still need to record damage and heal messages for the following types
// DMG_BURN
// DMG_FREEZE
// DMG_BLAST
// DMG_SHOCK
m_bitsDamageType | = bitsDamage ; // Save this so we can report it to the client
m_bitsHUDDamage = - 1 ; // make sure the damage bits get resent
while ( fTookDamage & & ( ! ftrivial | | g_pGameRules - > Damage_IsTimeBased ( bitsDamage ) ) & & ffound & & bitsDamage )
{
ffound = false ;
if ( bitsDamage & DMG_CLUB )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG4 " , false , SUIT_NEXT_IN_30SEC ) ; // minor fracture
bitsDamage & = ~ DMG_CLUB ;
ffound = true ;
}
if ( bitsDamage & ( DMG_FALL | DMG_CRUSH ) )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG5 " , false , SUIT_NEXT_IN_30SEC ) ; // major fracture
else
SetSuitUpdate ( " !HEV_DMG4 " , false , SUIT_NEXT_IN_30SEC ) ; // minor fracture
bitsDamage & = ~ ( DMG_FALL | DMG_CRUSH ) ;
ffound = true ;
}
if ( bitsDamage & DMG_BULLET )
{
if ( m_lastDamageAmount > 5 )
SetSuitUpdate ( " !HEV_DMG6 " , false , SUIT_NEXT_IN_30SEC ) ; // blood loss detected
//else
// SetSuitUpdate("!HEV_DMG0", false, SUIT_NEXT_IN_30SEC); // minor laceration
bitsDamage & = ~ DMG_BULLET ;
ffound = true ;
}
if ( bitsDamage & DMG_SLASH )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG1 " , false , SUIT_NEXT_IN_30SEC ) ; // major laceration
else
SetSuitUpdate ( " !HEV_DMG0 " , false , SUIT_NEXT_IN_30SEC ) ; // minor laceration
bitsDamage & = ~ DMG_SLASH ;
ffound = true ;
}
if ( bitsDamage & DMG_SONIC )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG2 " , false , SUIT_NEXT_IN_1MIN ) ; // internal bleeding
bitsDamage & = ~ DMG_SONIC ;
ffound = true ;
}
if ( bitsDamage & ( DMG_POISON | DMG_PARALYZE ) )
{
if ( bitsDamage & DMG_POISON )
{
m_nPoisonDmg + = info . GetDamage ( ) ;
m_tbdPrev = gpGlobals - > curtime ;
m_rgbTimeBasedDamage [ itbd_PoisonRecover ] = 0 ;
}
SetSuitUpdate ( " !HEV_DMG3 " , false , SUIT_NEXT_IN_1MIN ) ; // blood toxins detected
bitsDamage & = ~ ( DMG_POISON | DMG_PARALYZE ) ;
ffound = true ;
}
if ( bitsDamage & DMG_ACID )
{
SetSuitUpdate ( " !HEV_DET1 " , false , SUIT_NEXT_IN_1MIN ) ; // hazardous chemicals detected
bitsDamage & = ~ DMG_ACID ;
ffound = true ;
}
if ( bitsDamage & DMG_NERVEGAS )
{
SetSuitUpdate ( " !HEV_DET0 " , false , SUIT_NEXT_IN_1MIN ) ; // biohazard detected
bitsDamage & = ~ DMG_NERVEGAS ;
ffound = true ;
}
if ( bitsDamage & DMG_RADIATION )
{
SetSuitUpdate ( " !HEV_DET2 " , false , SUIT_NEXT_IN_1MIN ) ; // radiation detected
bitsDamage & = ~ DMG_RADIATION ;
ffound = true ;
}
if ( bitsDamage & DMG_SHOCK )
{
bitsDamage & = ~ DMG_SHOCK ;
ffound = true ;
}
}
float flPunch = - 2 ;
if ( hl2_episodic . GetBool ( ) & & info . GetAttacker ( ) & & ! FInViewCone ( info . GetAttacker ( ) ) )
{
if ( info . GetDamage ( ) > 10.0f )
flPunch = - 10 ;
else
flPunch = RandomFloat ( - 5 , - 7 ) ;
}
m_Local . m_vecPunchAngle . SetX ( flPunch ) ;
if ( fTookDamage & & ! ftrivial & & fmajor & & flHealthPrev > = 75 )
{
// first time we take major damage...
// turn automedic on if not on
SetSuitUpdate ( " !HEV_MED1 " , false , SUIT_NEXT_IN_30MIN ) ; // automedic on
// give morphine shot if not given recently
SetSuitUpdate ( " !HEV_HEAL7 " , false , SUIT_NEXT_IN_30MIN ) ; // morphine shot
}
if ( fTookDamage & & ! ftrivial & & fcritical & & flHealthPrev < 75 )
{
// already took major damage, now it's critical...
if ( m_iHealth < 6 )
SetSuitUpdate ( " !HEV_HLTH3 " , false , SUIT_NEXT_IN_10MIN ) ; // near death
else if ( m_iHealth < 20 )
SetSuitUpdate ( " !HEV_HLTH2 " , false , SUIT_NEXT_IN_10MIN ) ; // health critical
// give critical health warnings
if ( ! random - > RandomInt ( 0 , 3 ) & & flHealthPrev < 50 )
SetSuitUpdate ( " !HEV_DMG7 " , false , SUIT_NEXT_IN_5MIN ) ; //seek medical attention
}
// if we're taking time based damage, warn about its continuing effects
if ( fTookDamage & & g_pGameRules - > Damage_IsTimeBased ( info . GetDamageType ( ) ) & & flHealthPrev < 75 )
{
if ( flHealthPrev < 50 )
{
if ( ! random - > RandomInt ( 0 , 3 ) )
SetSuitUpdate ( " !HEV_DMG7 " , false , SUIT_NEXT_IN_5MIN ) ; //seek medical attention
}
else
SetSuitUpdate ( " !HEV_HLTH1 " , false , SUIT_NEXT_IN_10MIN ) ; // health dropping
}
// Do special explosion damage effect
if ( bitsDamage & DMG_BLAST )
{
OnDamagedByExplosion ( info ) ;
}
return fTookDamage ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &info -
// damageAmount -
//-----------------------------------------------------------------------------
# define MIN_SHOCK_AND_CONFUSION_DAMAGE 30.0f
# define MIN_EAR_RINGING_DISTANCE 240.0f // 20 feet
//-----------------------------------------------------------------------------
// Purpose:
// Input : &info -
//-----------------------------------------------------------------------------
void CBasePlayer : : OnDamagedByExplosion ( const CTakeDamageInfo & info )
{
float lastDamage = info . GetDamage ( ) ;
float distanceFromPlayer = 9999.0f ;
CBaseEntity * inflictor = info . GetInflictor ( ) ;
if ( inflictor )
{
Vector delta = GetAbsOrigin ( ) - inflictor - > GetAbsOrigin ( ) ;
distanceFromPlayer = delta . Length ( ) ;
}
bool ear_ringing = distanceFromPlayer < MIN_EAR_RINGING_DISTANCE ? true : false ;
bool shock = lastDamage > = MIN_SHOCK_AND_CONFUSION_DAMAGE ;
if ( ! shock & & ! ear_ringing )
return ;
int effect = shock ?
random - > RandomInt ( 35 , 37 ) :
random - > RandomInt ( 32 , 34 ) ;
CSingleUserRecipientFilter user ( this ) ;
enginesound - > SetPlayerDSP ( user , effect , false ) ;
}
//=========================================================
// PackDeadPlayerItems - call this when a player dies to
// pack up the appropriate weapons and ammo items, and to
// destroy anything that shouldn't be packed.
//
// This is pretty brute force :(
//=========================================================
void CBasePlayer : : PackDeadPlayerItems ( void )
{
int iWeaponRules ;
int iAmmoRules ;
int i ;
CBaseCombatWeapon * rgpPackWeapons [ 20 ] ; // 20 hardcoded for now. How to determine exactly how many weapons we have?
int iPackAmmo [ MAX_AMMO_SLOTS + 1 ] ;
int iPW = 0 ; // index into packweapons array
int iPA = 0 ; // index into packammo array
memset ( rgpPackWeapons , NULL , sizeof ( rgpPackWeapons ) ) ;
memset ( iPackAmmo , - 1 , sizeof ( iPackAmmo ) ) ;
// get the game rules
iWeaponRules = g_pGameRules - > DeadPlayerWeapons ( this ) ;
iAmmoRules = g_pGameRules - > DeadPlayerAmmo ( this ) ;
if ( iWeaponRules = = GR_PLR_DROP_GUN_NO & & iAmmoRules = = GR_PLR_DROP_AMMO_NO )
{
// nothing to pack. Remove the weapons and return. Don't call create on the box!
RemoveAllItems ( true ) ;
return ;
}
// go through all of the weapons and make a list of the ones to pack
for ( i = 0 ; i < WeaponCount ( ) ; i + + )
{
// there's a weapon here. Should I pack it?
CBaseCombatWeapon * pPlayerItem = GetWeapon ( i ) ;
if ( pPlayerItem )
{
switch ( iWeaponRules )
{
case GR_PLR_DROP_GUN_ACTIVE :
if ( GetActiveWeapon ( ) & & pPlayerItem = = GetActiveWeapon ( ) )
{
// this is the active item. Pack it.
rgpPackWeapons [ iPW + + ] = pPlayerItem ;
}
break ;
case GR_PLR_DROP_GUN_ALL :
rgpPackWeapons [ iPW + + ] = pPlayerItem ;
break ;
default :
break ;
}
}
}
// now go through ammo and make a list of which types to pack.
if ( iAmmoRules ! = GR_PLR_DROP_AMMO_NO )
{
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
{
if ( GetAmmoCount ( i ) > 0 )
{
// player has some ammo of this type.
switch ( iAmmoRules )
{
case GR_PLR_DROP_AMMO_ALL :
iPackAmmo [ iPA + + ] = i ;
break ;
case GR_PLR_DROP_AMMO_ACTIVE :
// WEAPONTODO: Make this work
/*
if ( GetActiveWeapon ( ) & & i = = GetActiveWeapon ( ) - > m_iPrimaryAmmoType )
{
// this is the primary ammo type for the active weapon
iPackAmmo [ iPA + + ] = i ;
}
else if ( GetActiveWeapon ( ) & & i = = GetActiveWeapon ( ) - > m_iSecondaryAmmoType )
{
// this is the secondary ammo type for the active weapon
iPackAmmo [ iPA + + ] = i ;
}
*/
break ;
default :
break ;
}
}
}
}
RemoveAllItems ( true ) ; // now strip off everything that wasn't handled by the code above.
}
void CBasePlayer : : RemoveAllItems ( bool removeSuit )
{
if ( GetActiveWeapon ( ) )
{
ResetAutoaim ( ) ;
GetActiveWeapon ( ) - > Holster ( ) ;
}
Weapon_SetLast ( NULL ) ;
RemoveAllWeapons ( ) ;
RemoveAllAmmo ( ) ;
if ( removeSuit )
{
RemoveSuit ( ) ;
}
UpdateClientData ( ) ;
}
bool CBasePlayer : : IsDead ( ) const
{
return m_lifeState = = LIFE_DEAD ;
}
static float DamageForce ( const Vector & size , float damage )
{
float force = damage * ( ( 32 * 32 * 72.0 ) / ( size . x * size . y * size . z ) ) * 5 ;
if ( force > 1000.0 )
{
force = 1000.0 ;
}
return force ;
}
const impactdamagetable_t & CBasePlayer : : GetPhysicsImpactDamageTable ( )
{
return gDefaultPlayerImpactDamageTable ;
}
int CBasePlayer : : OnTakeDamage_Alive ( const CTakeDamageInfo & info )
{
// set damage type sustained
m_bitsDamageType | = info . GetDamageType ( ) ;
if ( ! BaseClass : : OnTakeDamage_Alive ( info ) )
return 0 ;
CBaseEntity * attacker = info . GetAttacker ( ) ;
if ( ! attacker )
return 0 ;
Vector vecDir = vec3_origin ;
if ( info . GetInflictor ( ) )
{
vecDir = info . GetInflictor ( ) - > WorldSpaceCenter ( ) - Vector ( 0 , 0 , 10 ) - WorldSpaceCenter ( ) ;
VectorNormalize ( vecDir ) ;
}
if ( info . GetInflictor ( ) & & ( GetMoveType ( ) = = MOVETYPE_WALK ) & &
( ! attacker - > IsSolidFlagSet ( FSOLID_TRIGGER ) ) )
{
Vector force = vecDir * - DamageForce ( WorldAlignSize ( ) , info . GetBaseDamage ( ) ) ;
if ( force . z > 250.0f )
{
force . z = 250.0f ;
}
ApplyAbsVelocityImpulse ( force ) ;
}
// fire global game event
IGameEvent * event = gameeventmanager - > CreateEvent ( " player_hurt " ) ;
if ( event )
{
event - > SetInt ( " userid " , GetUserID ( ) ) ;
event - > SetInt ( " health " , MAX ( 0 , m_iHealth ) ) ;
event - > SetInt ( " priority " , 5 ) ; // HLTV event priority, not transmitted
if ( attacker - > IsPlayer ( ) )
{
CBasePlayer * player = ToBasePlayer ( attacker ) ;
event - > SetInt ( " attacker " , player - > GetUserID ( ) ) ; // hurt by other player
}
else
{
event - > SetInt ( " attacker " , 0 ) ; // hurt by "world"
}
gameeventmanager - > FireEvent ( event ) ;
}
// Insert a combat sound so that nearby NPCs hear battle
if ( attacker - > IsNPC ( ) )
{
CSoundEnt : : InsertSound ( SOUND_COMBAT , GetAbsOrigin ( ) , 512 , 0.5 , this ) ; //<<TODO>>//magic number
}
return 1 ;
}
void CBasePlayer : : Event_Killed ( const CTakeDamageInfo & info )
{
CSound * pSound ;
if ( Hints ( ) )
{
Hints ( ) - > ResetHintTimers ( ) ;
}
g_pGameRules - > PlayerKilled ( this , info ) ;
gamestats - > Event_PlayerKilled ( this , info ) ;
RumbleEffect ( RUMBLE_STOP_ALL , 0 , RUMBLE_FLAGS_NONE ) ;
# if defined( WIN32 ) && !defined( _X360 )
// NVNT set the drag to zero in the case of underwater death.
HapticSetDrag ( this , 0 ) ;
# endif
ClearUseEntity ( ) ;
// this client isn't going to be thinking for a while, so reset the sound until they respawn
pSound = CSoundEnt : : SoundPointerForIndex ( CSoundEnt : : ClientSoundIndex ( edict ( ) ) ) ;
{
if ( pSound )
{
pSound - > Reset ( ) ;
}
}
// don't let the status bar glitch for players with <0 health.
if ( m_iHealth < - 99 )
{
m_iHealth = 0 ;
}
// holster the current weapon
if ( GetActiveWeapon ( ) )
{
GetActiveWeapon ( ) - > Holster ( ) ;
}
SetAnimation ( PLAYER_DIE ) ;
if ( ! IsObserver ( ) )
{
SetViewOffset ( VEC_DEAD_VIEWHEIGHT_SCALED ( this ) ) ;
}
m_lifeState = LIFE_DYING ;
pl . deadflag = true ;
AddSolidFlags ( FSOLID_NOT_SOLID ) ;
// force contact points to get flushed if no longer valid
// UNDONE: Always do this on RecheckCollisionFilter() ?
IPhysicsObject * pObject = VPhysicsGetObject ( ) ;
if ( pObject )
{
pObject - > RecheckContactPoints ( ) ;
}
SetMoveType ( MOVETYPE_FLYGRAVITY ) ;
SetGroundEntity ( NULL ) ;
// clear out the suit message cache so we don't keep chattering
SetSuitUpdate ( NULL , false , 0 ) ;
// reset FOV
SetFOV ( this , 0 ) ;
if ( FlashlightIsOn ( ) )
{
FlashlightTurnOff ( ) ;
}
m_flDeathTime = gpGlobals - > curtime ;
ClearLastKnownArea ( ) ;
BaseClass : : Event_Killed ( info ) ;
}
void CBasePlayer : : Event_Dying ( const CTakeDamageInfo & info )
{
// NOT GIBBED, RUN THIS CODE
DeathSound ( info ) ;
// The dead body rolls out of the vehicle.
if ( IsInAVehicle ( ) )
{
LeaveVehicle ( ) ;
}
QAngle angles = GetLocalAngles ( ) ;
angles . x = 0 ;
angles . z = 0 ;
SetLocalAngles ( angles ) ;
SetThink ( & CBasePlayer : : PlayerDeathThink ) ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
BaseClass : : Event_Dying ( info ) ;
}
// Set the activity based on an event or current state
void CBasePlayer : : SetAnimation ( PLAYER_ANIM playerAnim )
{
int animDesired ;
char szAnim [ 64 ] ;
float speed ;
speed = GetAbsVelocity ( ) . Length2D ( ) ;
if ( GetFlags ( ) & ( FL_FROZEN | FL_ATCONTROLS ) )
{
speed = 0 ;
playerAnim = PLAYER_IDLE ;
}
Activity idealActivity = ACT_WALK ; // TEMP!!!!!
// This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
if ( playerAnim = = PLAYER_JUMP )
{
idealActivity = ACT_HOP ;
}
else if ( playerAnim = = PLAYER_SUPERJUMP )
{
idealActivity = ACT_LEAP ;
}
else if ( playerAnim = = PLAYER_DIE )
{
if ( m_lifeState = = LIFE_ALIVE )
{
idealActivity = GetDeathActivity ( ) ;
}
}
else if ( playerAnim = = PLAYER_ATTACK1 )
{
if ( m_Activity = = ACT_HOVER | |
m_Activity = = ACT_SWIM | |
m_Activity = = ACT_HOP | |
m_Activity = = ACT_LEAP | |
m_Activity = = ACT_DIESIMPLE )
{
idealActivity = m_Activity ;
}
else
{
idealActivity = ACT_RANGE_ATTACK1 ;
}
}
else if ( playerAnim = = PLAYER_IDLE | | playerAnim = = PLAYER_WALK )
{
if ( ! ( GetFlags ( ) & FL_ONGROUND ) & & ( m_Activity = = ACT_HOP | | m_Activity = = ACT_LEAP ) ) // Still jumping
{
idealActivity = m_Activity ;
}
else if ( GetWaterLevel ( ) > 1 )
{
if ( speed = = 0 )
idealActivity = ACT_HOVER ;
else
idealActivity = ACT_SWIM ;
}
else
{
idealActivity = ACT_WALK ;
}
}
if ( idealActivity = = ACT_RANGE_ATTACK1 )
{
if ( GetFlags ( ) & FL_DUCKING ) // crouching
{
Q_strncpy ( szAnim , " crouch_shoot_ " , sizeof ( szAnim ) ) ;
}
else
{
Q_strncpy ( szAnim , " ref_shoot_ " , sizeof ( szAnim ) ) ;
}
Q_strncat ( szAnim , m_szAnimExtension , sizeof ( szAnim ) , COPY_ALL_CHARACTERS ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( animDesired = = - 1 )
animDesired = 0 ;
if ( GetSequence ( ) ! = animDesired | | ! SequenceLoops ( ) )
{
SetCycle ( 0 ) ;
}
// Tracker 24588: In single player when firing own weapon this causes eye and punchangle to jitter
//if (!SequenceLoops())
//{
// IncrementInterpolationFrame();
//}
SetActivity ( idealActivity ) ;
ResetSequence ( animDesired ) ;
}
else if ( idealActivity = = ACT_WALK )
{
if ( GetActivity ( ) ! = ACT_RANGE_ATTACK1 | | IsActivityFinished ( ) )
{
if ( GetFlags ( ) & FL_DUCKING ) // crouching
{
Q_strncpy ( szAnim , " crouch_aim_ " , sizeof ( szAnim ) ) ;
}
else
{
Q_strncpy ( szAnim , " ref_aim_ " , sizeof ( szAnim ) ) ;
}
Q_strncat ( szAnim , m_szAnimExtension , sizeof ( szAnim ) , COPY_ALL_CHARACTERS ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( animDesired = = - 1 )
animDesired = 0 ;
SetActivity ( ACT_WALK ) ;
}
else
{
animDesired = GetSequence ( ) ;
}
}
else
{
if ( GetActivity ( ) = = idealActivity )
return ;
SetActivity ( idealActivity ) ;
animDesired = SelectWeightedSequence ( m_Activity ) ;
// Already using the desired animation?
if ( GetSequence ( ) = = animDesired )
return ;
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 ) ;
}
/*
= = = = = = = = = = =
WaterMove
= = = = = = = = = = = =
*/
# ifdef HL2_DLL
// test for HL2 drowning damage increase (aux power used instead)
# define AIRTIME 7 // lung full of air lasts this many seconds
# define DROWNING_DAMAGE_INITIAL 10
# define DROWNING_DAMAGE_MAX 10
# else
# define AIRTIME 12 // lung full of air lasts this many seconds
# define DROWNING_DAMAGE_INITIAL 2
# define DROWNING_DAMAGE_MAX 5
# endif
void CBasePlayer : : WaterMove ( )
{
if ( ( GetMoveType ( ) = = MOVETYPE_NOCLIP ) & & ! GetMoveParent ( ) )
{
m_AirFinished = gpGlobals - > curtime + AIRTIME ;
return ;
}
if ( m_iHealth < 0 | | ! IsAlive ( ) )
{
UpdateUnderwaterState ( ) ;
return ;
}
// waterlevel 0 - not in water (WL_NotInWater)
// waterlevel 1 - feet in water (WL_Feet)
// waterlevel 2 - waist in water (WL_Waist)
// waterlevel 3 - head in water (WL_Eyes)
if ( GetWaterLevel ( ) ! = WL_Eyes | | CanBreatheUnderwater ( ) )
{
// not underwater
// play 'up for air' sound
if ( m_AirFinished < gpGlobals - > curtime )
{
EmitSound ( " Player.DrownStart " ) ;
}
m_AirFinished = gpGlobals - > curtime + AIRTIME ;
m_nDrownDmgRate = DROWNING_DAMAGE_INITIAL ;
// if we took drowning damage, give it back slowly
if ( m_idrowndmg > m_idrownrestored )
{
// set drowning damage bit. hack - dmg_drownrecover actually
// makes the time based damage code 'give back' health over time.
// make sure counter is cleared so we start count correctly.
// NOTE: this actually causes the count to continue restarting
// until all drowning damage is healed.
m_bitsDamageType | = DMG_DROWNRECOVER ;
m_bitsDamageType & = ~ DMG_DROWN ;
m_rgbTimeBasedDamage [ itbd_DrownRecover ] = 0 ;
}
}
else
{ // fully under water
// stop restoring damage while underwater
m_bitsDamageType & = ~ DMG_DROWNRECOVER ;
m_rgbTimeBasedDamage [ itbd_DrownRecover ] = 0 ;
if ( m_AirFinished < gpGlobals - > curtime & & ! ( GetFlags ( ) & FL_GODMODE ) ) // drown!
{
if ( m_PainFinished < gpGlobals - > curtime )
{
// take drowning damage
m_nDrownDmgRate + = 1 ;
if ( m_nDrownDmgRate > DROWNING_DAMAGE_MAX )
{
m_nDrownDmgRate = DROWNING_DAMAGE_MAX ;
}
OnTakeDamage ( CTakeDamageInfo ( GetContainingEntity ( INDEXENT ( 0 ) ) , GetContainingEntity ( INDEXENT ( 0 ) ) , m_nDrownDmgRate , DMG_DROWN ) ) ;
m_PainFinished = gpGlobals - > curtime + 1 ;
// track drowning damage, give it back when
// player finally takes a breath
m_idrowndmg + = m_nDrownDmgRate ;
}
}
else
{
m_bitsDamageType & = ~ DMG_DROWN ;
}
}
UpdateUnderwaterState ( ) ;
}
// true if the player is attached to a ladder
bool CBasePlayer : : IsOnLadder ( void )
{
return ( GetMoveType ( ) = = MOVETYPE_LADDER ) ;
}
float CBasePlayer : : GetWaterJumpTime ( ) const
{
return m_flWaterJumpTime ;
}
void CBasePlayer : : SetWaterJumpTime ( float flWaterJumpTime )
{
m_flWaterJumpTime = flWaterJumpTime ;
}
float CBasePlayer : : GetSwimSoundTime ( void ) const
{
return m_flSwimSoundTime ;
}
void CBasePlayer : : SetSwimSoundTime ( float flSwimSoundTime )
{
m_flSwimSoundTime = flSwimSoundTime ;
}
void CBasePlayer : : ShowViewPortPanel ( const char * name , bool bShow , KeyValues * data )
{
CSingleUserRecipientFilter filter ( this ) ;
filter . MakeReliable ( ) ;
int count = 0 ;
KeyValues * subkey = NULL ;
if ( data )
{
subkey = data - > GetFirstSubKey ( ) ;
while ( subkey )
{
count + + ; subkey = subkey - > GetNextKey ( ) ;
}
subkey = data - > GetFirstSubKey ( ) ; // reset
}
UserMessageBegin ( filter , " VGUIMenu " ) ;
WRITE_STRING ( name ) ; // menu name
WRITE_BYTE ( bShow ? 1 : 0 ) ;
WRITE_BYTE ( count ) ;
// write additional data (be careful not more than 192 bytes!)
while ( subkey )
{
WRITE_STRING ( subkey - > GetName ( ) ) ;
WRITE_STRING ( subkey - > GetString ( ) ) ;
subkey = subkey - > GetNextKey ( ) ;
}
MessageEnd ( ) ;
}
void CBasePlayer : : PlayerDeathThink ( void )
{
float flForward ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
if ( GetFlags ( ) & FL_ONGROUND )
{
flForward = GetAbsVelocity ( ) . Length ( ) - 20 ;
if ( flForward < = 0 )
{
SetAbsVelocity ( vec3_origin ) ;
}
else
{
Vector vecNewVelocity = GetAbsVelocity ( ) ;
VectorNormalize ( vecNewVelocity ) ;
vecNewVelocity * = flForward ;
SetAbsVelocity ( vecNewVelocity ) ;
}
}
if ( HasWeapons ( ) )
{
// we drop the guns here because weapons that have an area effect and can kill their user
// will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
// player class sometimes is freed. It's safer to manipulate the weapons once we know
// we aren't calling into any of their code anymore through the player pointer.
PackDeadPlayerItems ( ) ;
}
if ( GetModelIndex ( ) & & ( ! IsSequenceFinished ( ) ) & & ( m_lifeState = = LIFE_DYING ) )
{
StudioFrameAdvance ( ) ;
m_iRespawnFrames + + ;
if ( m_iRespawnFrames < 60 ) // animations should be no longer than this
return ;
}
if ( m_lifeState = = LIFE_DYING )
{
m_lifeState = LIFE_DEAD ;
m_flDeathAnimTime = gpGlobals - > curtime ;
}
StopAnimation ( ) ;
IncrementInterpolationFrame ( ) ;
m_flPlaybackRate = 0.0 ;
int fAnyButtonDown = ( m_nButtons & ~ IN_SCORE ) ;
// Strip out the duck key from this check if it's toggled
if ( ( fAnyButtonDown & IN_DUCK ) & & GetToggledDuckState ( ) )
{
fAnyButtonDown & = ~ IN_DUCK ;
}
// wait for all buttons released
if ( m_lifeState = = LIFE_DEAD )
{
if ( fAnyButtonDown )
return ;
if ( g_pGameRules - > FPlayerCanRespawn ( this ) )
{
m_lifeState = LIFE_RESPAWNABLE ;
}
return ;
}
// if the player has been dead for one second longer than allowed by forcerespawn,
// forcerespawn isn't on. Send the player off to an intermission camera until they
// choose to respawn.
if ( g_pGameRules - > IsMultiplayer ( ) & & ( gpGlobals - > curtime > ( m_flDeathTime + DEATH_ANIMATION_TIME ) ) & & ! IsObserver ( ) )
{
// go to dead camera.
StartObserverMode ( m_iObserverLastMode ) ;
}
// wait for any button down, or mp_forcerespawn is set and the respawn time is up
if ( ! fAnyButtonDown
& & ! ( g_pGameRules - > IsMultiplayer ( ) & & forcerespawn . GetInt ( ) > 0 & & ( gpGlobals - > curtime > ( m_flDeathTime + 5 ) ) ) )
return ;
m_nButtons = 0 ;
m_iRespawnFrames = 0 ;
//Msg( "Respawn\n");
respawn ( this , ! IsObserver ( ) ) ; // don't copy a corpse if we're in deathcam.
SetNextThink ( TICK_NEVER_THINK ) ;
}
/*
//=========================================================
// StartDeathCam - find an intermission spot and send the
// player off into observer mode
//=========================================================
void CBasePlayer : : StartDeathCam ( void )
{
CBaseEntity * pSpot , * pNewSpot ;
int iRand ;
if ( GetViewOffset ( ) = = vec3_origin )
{
// don't accept subsequent attempts to StartDeathCam()
return ;
}
pSpot = gEntList . FindEntityByClassname ( NULL , " info_intermission " ) ;
if ( pSpot )
{
// at least one intermission spot in the world.
iRand = random - > RandomInt ( 0 , 3 ) ;
while ( iRand > 0 )
{
pNewSpot = gEntList . FindEntityByClassname ( pSpot , " info_intermission " ) ;
if ( pNewSpot )
{
pSpot = pNewSpot ;
}
iRand - - ;
}
CreateCorpse ( ) ;
StartObserverMode ( pSpot - > GetAbsOrigin ( ) , pSpot - > GetAbsAngles ( ) ) ;
}
else
{
// no intermission spot. Push them up in the air, looking down at their corpse
trace_t tr ;
CreateCorpse ( ) ;
UTIL_TraceLine ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + Vector ( 0 , 0 , 128 ) ,
MASK_PLAYERSOLID_BRUSHONLY , this , COLLISION_GROUP_NONE , & tr ) ;
QAngle angles ;
VectorAngles ( GetAbsOrigin ( ) - tr . endpos , angles ) ;
StartObserverMode ( tr . endpos , angles ) ;
return ;
}
} */
void CBasePlayer : : StopObserverMode ( )
{
m_bForcedObserverMode = false ;
m_afPhysicsFlags & = ~ PFLAG_OBSERVER ;
if ( m_iObserverMode = = OBS_MODE_NONE )
return ;
if ( m_iObserverMode > OBS_MODE_DEATHCAM )
{
m_iObserverLastMode = m_iObserverMode ;
}
m_iObserverMode . Set ( OBS_MODE_NONE ) ;
ShowViewPortPanel ( " specmenu " , false ) ;
ShowViewPortPanel ( " specgui " , false ) ;
ShowViewPortPanel ( " overview " , false ) ;
}
bool CBasePlayer : : StartObserverMode ( int mode )
{
if ( ! IsObserver ( ) )
{
// set position to last view offset
SetAbsOrigin ( GetAbsOrigin ( ) + GetViewOffset ( ) ) ;
SetViewOffset ( vec3_origin ) ;
}
Assert ( mode > OBS_MODE_NONE ) ;
m_afPhysicsFlags | = PFLAG_OBSERVER ;
// Holster weapon immediately, to allow it to cleanup
if ( GetActiveWeapon ( ) )
GetActiveWeapon ( ) - > Holster ( ) ;
// clear out the suit message cache so we don't keep chattering
SetSuitUpdate ( NULL , FALSE , 0 ) ;
SetGroundEntity ( ( CBaseEntity * ) NULL ) ;
RemoveFlag ( FL_DUCKING ) ;
AddSolidFlags ( FSOLID_NOT_SOLID ) ;
SetObserverMode ( mode ) ;
if ( gpGlobals - > eLoadType ! = MapLoad_Background )
{
ShowViewPortPanel ( " specgui " , ModeWantsSpectatorGUI ( mode ) ) ;
}
// Setup flags
m_Local . m_iHideHUD = HIDEHUD_HEALTH ;
m_takedamage = DAMAGE_NO ;
// Become invisible
AddEffects ( EF_NODRAW ) ;
m_iHealth = 1 ;
m_lifeState = LIFE_DEAD ; // Can't be dead, otherwise movement doesn't work right.
m_flDeathAnimTime = gpGlobals - > curtime ;
pl . deadflag = true ;
return true ;
}
bool CBasePlayer : : SetObserverMode ( int mode )
{
if ( mode < OBS_MODE_NONE | | mode > = NUM_OBSERVER_MODES )
return false ;
// check mp_forcecamera settings for dead players
if ( mode > OBS_MODE_FIXED & & GetTeamNumber ( ) > TEAM_SPECTATOR )
{
switch ( mp_forcecamera . GetInt ( ) )
{
case OBS_ALLOW_ALL : break ; // no restrictions
case OBS_ALLOW_TEAM : mode = OBS_MODE_IN_EYE ; break ;
case OBS_ALLOW_NONE : mode = OBS_MODE_FIXED ; break ; // don't allow anything
}
}
if ( m_iObserverMode > OBS_MODE_DEATHCAM )
{
// remember mode if we were really spectating before
m_iObserverLastMode = m_iObserverMode ;
}
m_iObserverMode = mode ;
switch ( mode )
{
case OBS_MODE_NONE :
case OBS_MODE_FIXED :
case OBS_MODE_DEATHCAM :
SetFOV ( this , 0 ) ; // Reset FOV
SetViewOffset ( vec3_origin ) ;
SetMoveType ( MOVETYPE_NONE ) ;
break ;
case OBS_MODE_CHASE :
case OBS_MODE_IN_EYE :
// udpate FOV and viewmodels
SetObserverTarget ( m_hObserverTarget ) ;
SetMoveType ( MOVETYPE_OBSERVER ) ;
break ;
//=============================================================================
// HPE_BEGIN:
// [menglish] Added freeze cam to the setter. Uses same setup as the roaming mode
//=============================================================================
case OBS_MODE_ROAMING :
case OBS_MODE_FREEZECAM :
SetFOV ( this , 0 ) ; // Reset FOV
SetObserverTarget ( m_hObserverTarget ) ;
SetViewOffset ( vec3_origin ) ;
SetMoveType ( MOVETYPE_OBSERVER ) ;
break ;
//=============================================================================
// HPE_END
//=============================================================================
}
CheckObserverSettings ( ) ;
return true ;
}
int CBasePlayer : : GetObserverMode ( )
{
return m_iObserverMode ;
}
void CBasePlayer : : ForceObserverMode ( int mode )
{
int tempMode = OBS_MODE_ROAMING ;
if ( m_iObserverMode = = mode )
return ;
// don't change last mode if already in forced mode
if ( m_bForcedObserverMode )
{
tempMode = m_iObserverLastMode ;
}
SetObserverMode ( mode ) ;
if ( m_bForcedObserverMode )
{
m_iObserverLastMode = tempMode ;
}
m_bForcedObserverMode = true ;
}
void CBasePlayer : : CheckObserverSettings ( )
{
// check if we are in forced mode and may go back to old mode
if ( m_bForcedObserverMode )
{
CBaseEntity * target = m_hObserverTarget ;
if ( ! IsValidObserverTarget ( target ) )
{
// if old target is still invalid, try to find valid one
target = FindNextObserverTarget ( false ) ;
}
if ( target )
{
// we found a valid target
m_bForcedObserverMode = false ; // disable force mode
SetObserverMode ( m_iObserverLastMode ) ; // switch to last mode
SetObserverTarget ( target ) ; // goto target
// TODO check for HUD icons
return ;
}
else
{
// else stay in forced mode, no changes
return ;
}
}
// make sure our last mode is valid
if ( m_iObserverLastMode < OBS_MODE_FIXED )
{
m_iObserverLastMode = OBS_MODE_ROAMING ;
}
// check if our spectating target is still a valid one
if ( m_iObserverMode = = OBS_MODE_IN_EYE | | m_iObserverMode = = OBS_MODE_CHASE | | m_iObserverMode = = OBS_MODE_FIXED )
{
ValidateCurrentObserverTarget ( ) ;
CBasePlayer * target = ToBasePlayer ( m_hObserverTarget . Get ( ) ) ;
// for ineye mode we have to copy several data to see exactly the same
if ( target & & m_iObserverMode = = OBS_MODE_IN_EYE )
{
int flagMask = FL_ONGROUND | FL_DUCKING ;
int flags = target - > GetFlags ( ) & flagMask ;
if ( ( GetFlags ( ) & flagMask ) ! = flags )
{
flags | = GetFlags ( ) & ( ~ flagMask ) ; // keep other flags
ClearFlags ( ) ;
AddFlag ( flags ) ;
}
if ( target - > GetViewOffset ( ) ! = GetViewOffset ( ) )
{
SetViewOffset ( target - > GetViewOffset ( ) ) ;
}
}
// Update the fog.
if ( target )
{
if ( target - > m_Local . m_PlayerFog . m_hCtrl . Get ( ) ! = m_Local . m_PlayerFog . m_hCtrl . Get ( ) )
{
m_Local . m_PlayerFog . m_hCtrl . Set ( target - > m_Local . m_PlayerFog . m_hCtrl . Get ( ) ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : ValidateCurrentObserverTarget ( void )
{
if ( ! IsValidObserverTarget ( m_hObserverTarget . Get ( ) ) )
{
// our target is not valid, try to find new target
CBaseEntity * target = FindNextObserverTarget ( false ) ;
if ( target )
{
// switch to new valid target
SetObserverTarget ( target ) ;
}
else
{
// couldn't find new target, switch to temporary mode
if ( mp_forcecamera . GetInt ( ) = = OBS_ALLOW_ALL )
{
// let player roam around
ForceObserverMode ( OBS_MODE_ROAMING ) ;
}
else
{
// fix player view right where it is
ForceObserverMode ( OBS_MODE_FIXED ) ;
m_hObserverTarget . Set ( NULL ) ; // no traget to follow
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : AttemptToExitFreezeCam ( void )
{
StartObserverMode ( OBS_MODE_DEATHCAM ) ;
}
bool CBasePlayer : : StartReplayMode ( float fDelay , float fDuration , int iEntity )
{
if ( ( sv_maxreplay = = NULL ) | | ( sv_maxreplay - > GetFloat ( ) < = 0 ) )
return false ;
m_fDelay = fDelay ;
m_fReplayEnd = gpGlobals - > curtime + fDuration ;
m_iReplayEntity = iEntity ;
return true ;
}
void CBasePlayer : : StopReplayMode ( )
{
m_fDelay = 0.0f ;
m_fReplayEnd = - 1 ;
m_iReplayEntity = 0 ;
}
int CBasePlayer : : GetDelayTicks ( )
{
if ( m_fReplayEnd > gpGlobals - > curtime )
{
return TIME_TO_TICKS ( m_fDelay ) ;
}
else
{
if ( m_fDelay > 0.0f )
StopReplayMode ( ) ;
return 0 ;
}
}
int CBasePlayer : : GetReplayEntity ( )
{
return m_iReplayEntity ;
}
CBaseEntity * CBasePlayer : : GetObserverTarget ( )
{
return m_hObserverTarget . Get ( ) ;
}
void CBasePlayer : : ObserverUse ( bool bIsPressed )
{
# ifndef _XBOX
if ( ! HLTVDirector ( ) - > IsActive ( ) )
return ;
if ( GetTeamNumber ( ) ! = TEAM_SPECTATOR )
return ; // only pure spectators can play cameraman
if ( ! bIsPressed )
return ;
bool bIsHLTV = HLTVDirector ( ) - > IsActive ( ) ;
if ( bIsHLTV )
{
int iCameraManIndex = HLTVDirector ( ) - > GetCameraMan ( ) ;
if ( iCameraManIndex = = 0 )
{
// turn camera on
HLTVDirector ( ) - > SetCameraMan ( entindex ( ) ) ;
}
else if ( iCameraManIndex = = entindex ( ) )
{
// turn camera off
HLTVDirector ( ) - > SetCameraMan ( 0 ) ;
}
else
{
ClientPrint ( this , HUD_PRINTTALK , " Camera in use by other player. " ) ;
}
}
/* UTIL_SayText( "Spectator can not USE anything", this );
Vector dir , end ;
Vector start = GetAbsOrigin ( ) ;
AngleVectors ( GetAbsAngles ( ) , & dir ) ;
VectorNormalize ( dir ) ;
VectorMA ( start , 32.0f , dir , end ) ;
trace_t tr ;
UTIL_TraceLine ( start , end , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & tr ) ;
if ( tr . fraction = = 1.0f )
return ; // no obstacles in spectators way
VectorMA ( start , 128.0f , dir , end ) ;
Ray_t ray ;
ray . Init ( end , start , VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ) ;
UTIL_TraceRay ( ray , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & tr ) ;
if ( tr . startsolid | | tr . allsolid )
return ;
SetAbsOrigin ( tr . endpos ) ; */
# endif
}
void CBasePlayer : : JumptoPosition ( const Vector & origin , const QAngle & angles )
{
Vector neworigin ;
QAngle newangles ;
// Clamp the position and angles to prevent crashes
neworigin . x = clamp ( origin . x , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ;
neworigin . y = clamp ( origin . y , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ;
neworigin . z = clamp ( origin . z , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ;
newangles . x = clamp ( angles . x , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ;
newangles . y = clamp ( angles . y , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ;
newangles . z = clamp ( angles . z , MIN_COORD_FLOAT , MAX_COORD_FLOAT ) ; // not clamped in original valve's code, idk why
SetAbsOrigin ( neworigin ) ;
SetAbsVelocity ( vec3_origin ) ; // stop movement
SetLocalAngles ( newangles ) ;
SnapEyeAngles ( newangles ) ;
}
bool CBasePlayer : : SetObserverTarget ( CBaseEntity * target )
{
if ( ! IsValidObserverTarget ( target ) )
return false ;
// set new target
m_hObserverTarget . Set ( target ) ;
// reset fov to default
SetFOV ( this , 0 ) ;
if ( m_iObserverMode = = OBS_MODE_ROAMING )
{
Vector dir , end ;
Vector start = target - > EyePosition ( ) ;
AngleVectors ( target - > EyeAngles ( ) , & dir ) ;
VectorNormalize ( dir ) ;
VectorMA ( start , - 64.0f , dir , end ) ;
Ray_t ray ;
ray . Init ( start , end , VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ) ;
trace_t tr ;
UTIL_TraceRay ( ray , MASK_PLAYERSOLID , target , COLLISION_GROUP_PLAYER_MOVEMENT , & tr ) ;
JumptoPosition ( tr . endpos , target - > EyeAngles ( ) ) ;
}
return true ;
}
bool CBasePlayer : : IsValidObserverTarget ( CBaseEntity * target )
{
if ( target = = NULL )
return false ;
// MOD AUTHORS: Add checks on target here or in derived method
if ( ! target - > IsPlayer ( ) ) // only track players
return false ;
CBasePlayer * player = ToBasePlayer ( target ) ;
/* Don't spec observers or players who haven't picked a class yet
if ( player - > IsObserver ( ) )
return false ; */
if ( player = = this )
return false ; // We can't observe ourselves.
if ( player - > IsEffectActive ( EF_NODRAW ) ) // don't watch invisible players
return false ;
if ( player - > m_lifeState = = LIFE_RESPAWNABLE ) // target is dead, waiting for respawn
return false ;
if ( player - > m_lifeState = = LIFE_DEAD | | player - > m_lifeState = = LIFE_DYING )
{
if ( ( player - > m_flDeathTime + DEATH_ANIMATION_TIME ) < gpGlobals - > curtime )
{
return false ; // allow watching until 3 seconds after death to see death animation
}
}
// check forcecamera settings for active players
if ( GetTeamNumber ( ) ! = TEAM_SPECTATOR )
{
switch ( mp_forcecamera . GetInt ( ) )
{
case OBS_ALLOW_ALL : break ;
case OBS_ALLOW_TEAM : if ( GetTeamNumber ( ) ! = target - > GetTeamNumber ( ) )
return false ;
break ;
case OBS_ALLOW_NONE : return false ;
}
}
return true ; // passed all test
}
int CBasePlayer : : GetNextObserverSearchStartPoint ( bool bReverse )
{
int iDir = bReverse ? - 1 : 1 ;
int startIndex ;
if ( m_hObserverTarget )
{
// start using last followed player
startIndex = m_hObserverTarget - > entindex ( ) ;
}
else
{
// start using own player index
startIndex = this - > entindex ( ) ;
}
startIndex + = iDir ;
if ( startIndex > gpGlobals - > maxClients )
startIndex = 1 ;
else if ( startIndex < 1 )
startIndex = gpGlobals - > maxClients ;
return startIndex ;
}
CBaseEntity * CBasePlayer : : FindNextObserverTarget ( bool bReverse )
{
// MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching
// only a subset of the players. e.g. Make it check the target's team.
/* if ( m_flNextFollowTime && m_flNextFollowTime > gpGlobals->time )
{
return ;
}
m_flNextFollowTime = gpGlobals - > time + 0.25 ;
*/ // TODO move outside this function
int startIndex = GetNextObserverSearchStartPoint ( bReverse ) ;
int currentIndex = startIndex ;
int iDir = bReverse ? - 1 : 1 ;
do
{
CBaseEntity * nextTarget = UTIL_PlayerByIndex ( currentIndex ) ;
if ( IsValidObserverTarget ( nextTarget ) )
{
return nextTarget ; // found next valid player
}
currentIndex + = iDir ;
// Loop through the clients
if ( currentIndex > gpGlobals - > maxClients )
currentIndex = 1 ;
else if ( currentIndex < 1 )
currentIndex = gpGlobals - > maxClients ;
} while ( currentIndex ! = startIndex ) ;
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this object can be +used by the player
//-----------------------------------------------------------------------------
bool CBasePlayer : : IsUseableEntity ( CBaseEntity * pEntity , unsigned int requiredCaps )
{
if ( pEntity )
{
int caps = pEntity - > ObjectCaps ( ) ;
if ( caps & ( FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE | FCAP_DIRECTIONAL_USE ) )
{
if ( ( caps & requiredCaps ) = = requiredCaps )
{
return true ;
}
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBasePlayer : : CanPickupObject ( CBaseEntity * pObject , float massLimit , float sizeLimit )
{
// UNDONE: Make this virtual and move to HL2 player
# ifdef HL2_DLL
//Must be valid
if ( pObject = = NULL )
return false ;
//Must move with physics
if ( pObject - > GetMoveType ( ) ! = MOVETYPE_VPHYSICS )
return false ;
IPhysicsObject * pList [ VPHYSICS_MAX_OBJECT_LIST_COUNT ] ;
int count = pObject - > VPhysicsGetObjectList ( pList , ARRAYSIZE ( pList ) ) ;
//Must have a physics object
if ( ! count )
return false ;
float objectMass = 0 ;
bool checkEnable = false ;
for ( int i = 0 ; i < count ; i + + )
{
objectMass + = pList [ i ] - > GetMass ( ) ;
if ( ! pList [ i ] - > IsMoveable ( ) )
{
checkEnable = true ;
}
if ( pList [ i ] - > GetGameFlags ( ) & FVPHYSICS_NO_PLAYER_PICKUP )
return false ;
if ( pList [ i ] - > IsHinged ( ) )
return false ;
}
//Msg( "Target mass: %f\n", pPhys->GetMass() );
//Must be under our threshold weight
if ( massLimit > 0 & & objectMass > massLimit )
return false ;
if ( checkEnable )
{
// Allowing picking up of bouncebombs.
CBounceBomb * pBomb = dynamic_cast < CBounceBomb * > ( pObject ) ;
if ( pBomb )
return true ;
// Allow pickup of phys props that are motion enabled on player pickup
CPhysicsProp * pProp = dynamic_cast < CPhysicsProp * > ( pObject ) ;
CPhysBox * pBox = dynamic_cast < CPhysBox * > ( pObject ) ;
if ( ! pProp & & ! pBox )
return false ;
if ( pProp & & ! ( pProp - > HasSpawnFlags ( SF_PHYSPROP_ENABLE_ON_PHYSCANNON ) ) )
return false ;
if ( pBox & & ! ( pBox - > HasSpawnFlags ( SF_PHYSBOX_ENABLE_ON_PHYSCANNON ) ) )
return false ;
}
if ( sizeLimit > 0 )
{
const Vector & size = pObject - > CollisionProp ( ) - > OBBSize ( ) ;
if ( size . x > sizeLimit | | size . y > sizeLimit | | size . z > sizeLimit )
return false ;
}
return true ;
# else
return false ;
# endif
}
float CBasePlayer : : GetHeldObjectMass ( IPhysicsObject * pHeldObject )
{
return 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: Server side of jumping rules. Most jumping logic is already
// handled in shared gamemovement code. Put stuff here that should
// only be done server side.
//-----------------------------------------------------------------------------
void CBasePlayer : : Jump ( )
{
}
void CBasePlayer : : Duck ( )
{
if ( m_nButtons & IN_DUCK )
{
if ( m_Activity ! = ACT_LEAP )
{
SetAnimation ( PLAYER_WALK ) ;
}
}
}
//
// ID's player as such.
//
Class_T CBasePlayer : : Classify ( void )
{
return CLASS_PLAYER ;
}
void CBasePlayer : : ResetFragCount ( )
{
m_iFrags = 0 ;
pl . frags = m_iFrags ;
}
void CBasePlayer : : IncrementFragCount ( int nCount )
{
m_iFrags + = nCount ;
pl . frags = m_iFrags ;
}
void CBasePlayer : : ResetDeathCount ( )
{
m_iDeaths = 0 ;
pl . deaths = m_iDeaths ;
}
void CBasePlayer : : IncrementDeathCount ( int nCount )
{
m_iDeaths + = nCount ;
pl . deaths = m_iDeaths ;
}
void CBasePlayer : : AddPoints ( int score , bool bAllowNegativeScore )
{
// Positive score always adds
if ( score < 0 )
{
if ( ! bAllowNegativeScore )
{
if ( m_iFrags < 0 ) // Can't go more negative
return ;
if ( - score > m_iFrags ) // Will this go negative?
{
score = - m_iFrags ; // Sum will be 0
}
}
}
m_iFrags + = score ;
pl . frags = m_iFrags ;
}
void CBasePlayer : : AddPointsToTeam ( int score , bool bAllowNegativeScore )
{
if ( GetTeam ( ) )
{
GetTeam ( ) - > AddScore ( score ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CBasePlayer : : GetCommandContextCount ( void ) const
{
return m_CommandContext . Count ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : index -
// Output : CCommandContext
//-----------------------------------------------------------------------------
CCommandContext * CBasePlayer : : GetCommandContext ( int index )
{
if ( index < 0 | | index > = m_CommandContext . Count ( ) )
return NULL ;
return & m_CommandContext [ index ] ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CCommandContext * CBasePlayer : : AllocCommandContext ( void )
{
int idx = m_CommandContext . AddToTail ( ) ;
if ( m_CommandContext . Count ( ) > 1000 )
{
Assert ( 0 ) ;
}
return & m_CommandContext [ idx ] ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : index -
//-----------------------------------------------------------------------------
void CBasePlayer : : RemoveCommandContext ( int index )
{
m_CommandContext . Remove ( index ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : RemoveAllCommandContexts ( )
{
m_CommandContext . RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Removes all existing contexts, but leaves the last one around ( or creates it if it doesn't exist -- which would be a bug )
//-----------------------------------------------------------------------------
CCommandContext * CBasePlayer : : RemoveAllCommandContextsExceptNewest ( void )
{
int count = m_CommandContext . Count ( ) ;
int toRemove = count - 1 ;
if ( toRemove > 0 )
{
m_CommandContext . RemoveMultiple ( 0 , toRemove ) ;
}
if ( ! m_CommandContext . Count ( ) )
{
Assert ( 0 ) ;
CCommandContext * ctx = AllocCommandContext ( ) ;
Q_memset ( ctx , 0 , sizeof ( * ctx ) ) ;
}
return & m_CommandContext [ 0 ] ;
}
//-----------------------------------------------------------------------------
// Purpose: Replaces the first nCommands CUserCmds in the context with the ones passed in -- this is used to help meter out CUserCmds over the number of simulation ticks on the server
//-----------------------------------------------------------------------------
void CBasePlayer : : ReplaceContextCommands ( CCommandContext * ctx , CUserCmd * pCommands , int nCommands )
{
// Blow away all of the commands
ctx - > cmds . RemoveAll ( ) ;
ctx - > numcmds = nCommands ;
ctx - > totalcmds = nCommands ;
ctx - > dropped_packets = 0 ; // meaningless in this context
// Add them in so the most recent is at slot 0
for ( int i = nCommands - 1 ; i > = 0 ; - - i )
{
ctx - > cmds . AddToTail ( pCommands [ i ] ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Determine how much time we will be running this frame
// Output : float
//-----------------------------------------------------------------------------
int CBasePlayer : : DetermineSimulationTicks ( void )
{
int command_context_count = GetCommandContextCount ( ) ;
int context_number ;
int simulation_ticks = 0 ;
// Determine how much time we will be running this frame and fixup player clock as needed
for ( context_number = 0 ; context_number < command_context_count ; context_number + + )
{
CCommandContext const * ctx = GetCommandContext ( context_number ) ;
Assert ( ctx ) ;
Assert ( ctx - > numcmds > 0 ) ;
Assert ( ctx - > dropped_packets > = 0 ) ;
// Determine how long it will take to run those packets
simulation_ticks + = ctx - > numcmds + ctx - > dropped_packets ;
}
return simulation_ticks ;
}
// 2 ticks ahead or behind current clock means we need to fix clock on client
static ConVar sv_clockcorrection_msecs ( " sv_clockcorrection_msecs " , " 60 " , 0 , " The server tries to keep each player's m_nTickBase withing this many msecs of the server absolute tickcount " ) ;
static ConVar sv_playerperfhistorycount ( " sv_playerperfhistorycount " , " 60 " , 0 , " Number of samples to maintain in player perf history " , true , 1.0f , true , 128.0 ) ;
//-----------------------------------------------------------------------------
// Purpose: Based upon amount of time in simulation time, adjust m_nTickBase so that
// we just end at the end of the current frame (so the player is basically on clock
// with the server)
// Input : simulation_ticks -
//-----------------------------------------------------------------------------
void CBasePlayer : : AdjustPlayerTimeBase ( int simulation_ticks )
{
Assert ( simulation_ticks > = 0 ) ;
if ( simulation_ticks < 0 )
return ;
CPlayerSimInfo * pi = NULL ;
if ( sv_playerperfhistorycount . GetInt ( ) > 0 )
{
while ( m_vecPlayerSimInfo . Count ( ) > sv_playerperfhistorycount . GetInt ( ) )
{
m_vecPlayerSimInfo . Remove ( m_vecPlayerSimInfo . Head ( ) ) ;
}
pi = & m_vecPlayerSimInfo [ m_vecPlayerSimInfo . AddToTail ( ) ] ;
}
// Start in the past so that we get to the sv.time that we'll hit at the end of the
// frame, just as we process the final command
if ( gpGlobals - > maxClients = = 1 )
{
// set TickBase so that player simulation tick matches gpGlobals->tickcount after
// all commands have been executed
m_nTickBase = gpGlobals - > tickcount - simulation_ticks + gpGlobals - > simTicksThisFrame ;
}
else // multiplayer
{
float flCorrectionSeconds = clamp ( sv_clockcorrection_msecs . GetFloat ( ) / 1000.0f , 0.0f , 1.0f ) ;
int nCorrectionTicks = TIME_TO_TICKS ( flCorrectionSeconds ) ;
// Set the target tick flCorrectionSeconds (rounded to ticks) ahead in the future. this way the client can
// alternate around this target tick without getting smaller than gpGlobals->tickcount.
// After running the commands simulation time should be equal or after current gpGlobals->tickcount,
// otherwise the simulation time drops out of the client side interpolated var history window.
int nIdealFinalTick = gpGlobals - > tickcount + nCorrectionTicks ;
int nEstimatedFinalTick = m_nTickBase + simulation_ticks ;
// If client gets ahead of this, we'll need to correct
int too_fast_limit = nIdealFinalTick + nCorrectionTicks ;
// If client falls behind this, we'll also need to correct
int too_slow_limit = nIdealFinalTick - nCorrectionTicks ;
// See if we are too fast
if ( nEstimatedFinalTick > too_fast_limit | |
nEstimatedFinalTick < too_slow_limit )
{
int nCorrectedTick = nIdealFinalTick - simulation_ticks + gpGlobals - > simTicksThisFrame ;
if ( pi )
{
pi - > m_nTicksCorrected = nCorrectionTicks ;
}
m_nTickBase = nCorrectedTick ;
}
}
if ( pi )
{
pi - > m_flFinalSimulationTime = TICKS_TO_TIME ( m_nTickBase + simulation_ticks + gpGlobals - > simTicksThisFrame ) ;
}
}
void CBasePlayer : : RunNullCommand ( void )
{
CUserCmd cmd ; // NULL command
// Store off the globals.. they're gonna get whacked
float flOldFrametime = gpGlobals - > frametime ;
float flOldCurtime = gpGlobals - > curtime ;
pl . fixangle = FIXANGLE_NONE ;
if ( IsReplay ( ) )
{
cmd . viewangles = QAngle ( 0 , 0 , 0 ) ;
}
else
{
cmd . viewangles = EyeAngles ( ) ;
}
float flTimeBase = gpGlobals - > curtime ;
SetTimeBase ( flTimeBase ) ;
MoveHelperServer ( ) - > SetHost ( this ) ;
PlayerRunCommand ( & cmd , MoveHelperServer ( ) ) ;
// save off the last good usercmd
SetLastUserCommand ( cmd ) ;
// Restore the globals..
gpGlobals - > frametime = flOldFrametime ;
gpGlobals - > curtime = flOldCurtime ;
MoveHelperServer ( ) - > SetHost ( NULL ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Note, don't chain to BaseClass::PhysicsSimulate
//-----------------------------------------------------------------------------
void CBasePlayer : : PhysicsSimulate ( void )
{
VPROF_BUDGET ( " CBasePlayer::PhysicsSimulate " , VPROF_BUDGETGROUP_PLAYER ) ;
// If we've got a moveparent, we must simulate that first.
CBaseEntity * pMoveParent = GetMoveParent ( ) ;
if ( pMoveParent )
{
pMoveParent - > PhysicsSimulate ( ) ;
}
// Make sure not to simulate this guy twice per frame
if ( m_nSimulationTick = = gpGlobals - > tickcount )
{
return ;
}
m_nSimulationTick = gpGlobals - > tickcount ;
// See how many CUserCmds are queued up for running
int simulation_ticks = DetermineSimulationTicks ( ) ;
// If some time will elapse, make sure our clock (m_nTickBase) starts at the correct time
if ( simulation_ticks > 0 )
{
AdjustPlayerTimeBase ( simulation_ticks ) ;
}
if ( IsHLTV ( ) | | IsReplay ( ) )
{
// just run a single, empty command to make sure
// all PreThink/PostThink functions are called as usual
Assert ( GetCommandContextCount ( ) = = 0 ) ;
RunNullCommand ( ) ;
RemoveAllCommandContexts ( ) ;
return ;
}
// Store off true server timestamps
float savetime = gpGlobals - > curtime ;
float saveframetime = gpGlobals - > frametime ;
int command_context_count = GetCommandContextCount ( ) ;
// Build a list of all available commands
CUtlVector < CUserCmd > vecAvailCommands ;
// Contexts go from oldest to newest
for ( int context_number = 0 ; context_number < command_context_count ; context_number + + )
{
// Get oldest ( newer are added to tail )
CCommandContext * ctx = GetCommandContext ( context_number ) ;
if ( ! ShouldRunCommandsInContext ( ctx ) )
continue ;
if ( ! ctx - > cmds . Count ( ) )
continue ;
int numbackup = ctx - > totalcmds - ctx - > numcmds ;
// If we haven't dropped too many packets, then run some commands
if ( ctx - > dropped_packets < 24 )
{
int droppedcmds = ctx - > dropped_packets ;
// run the last known cmd for each dropped cmd we don't have a backup for
while ( droppedcmds > numbackup )
{
m_LastCmd . tick_count + + ;
vecAvailCommands . AddToTail ( m_LastCmd ) ;
droppedcmds - - ;
}
// Now run the "history" commands if we still have dropped packets
while ( droppedcmds > 0 )
{
int cmdnum = ctx - > numcmds + droppedcmds - 1 ;
vecAvailCommands . AddToTail ( ctx - > cmds [ cmdnum ] ) ;
droppedcmds - - ;
}
}
// Now run any new command(s). Go backward because the most recent command is at index 0.
for ( int i = ctx - > numcmds - 1 ; i > = 0 ; i - - )
{
vecAvailCommands . AddToTail ( ctx - > cmds [ i ] ) ;
}
// Save off the last good command in case we drop > numbackup packets and need to rerun them
// we'll use this to "guess" at what was in the missing packets
m_LastCmd = ctx - > cmds [ CMD_MOSTRECENT ] ;
}
// gpGlobals->simTicksThisFrame == number of ticks remaining to be run, so we should take the last N CUserCmds and postpone them until the next frame
// If we're running multiple ticks this frame, don't peel off all of the commands, spread them out over
// the server ticks. Use blocks of two in alternate ticks
int commandLimit = CBaseEntity : : IsSimulatingOnAlternateTicks ( ) ? 2 : 1 ;
int commandsToRun = vecAvailCommands . Count ( ) ;
if ( gpGlobals - > simTicksThisFrame > = commandLimit & & vecAvailCommands . Count ( ) > commandLimit )
{
int commandsToRollOver = MIN ( vecAvailCommands . Count ( ) , ( gpGlobals - > simTicksThisFrame - 1 ) ) ;
commandsToRun = vecAvailCommands . Count ( ) - commandsToRollOver ;
Assert ( commandsToRun > = 0 ) ;
// Clear all contexts except the last one
if ( commandsToRollOver > 0 )
{
CCommandContext * ctx = RemoveAllCommandContextsExceptNewest ( ) ;
ReplaceContextCommands ( ctx , & vecAvailCommands [ commandsToRun ] , commandsToRollOver ) ;
}
else
{
// Clear all contexts
RemoveAllCommandContexts ( ) ;
}
}
else
{
// Clear all contexts
RemoveAllCommandContexts ( ) ;
}
float vphysicsArrivalTime = TICK_INTERVAL ;
# ifdef _DEBUG
if ( sv_player_net_suppress_usercommands . GetBool ( ) )
{
commandsToRun = 0 ;
}
# endif // _DEBUG
int numUsrCmdProcessTicksMax = sv_maxusrcmdprocessticks . GetInt ( ) ;
if ( gpGlobals - > maxClients ! = 1 & & numUsrCmdProcessTicksMax ) // don't apply this filter in SP games
{
// Grant the client some time buffer to execute user commands
m_flMovementTimeForUserCmdProcessingRemaining + = TICK_INTERVAL ;
// but never accumulate more than N ticks
if ( m_flMovementTimeForUserCmdProcessingRemaining > numUsrCmdProcessTicksMax * TICK_INTERVAL )
m_flMovementTimeForUserCmdProcessingRemaining = numUsrCmdProcessTicksMax * TICK_INTERVAL ;
}
else
{
// Otherwise we don't care to track time
m_flMovementTimeForUserCmdProcessingRemaining = FLT_MAX ;
}
// Now run the commands
if ( commandsToRun > 0 )
{
m_flLastUserCommandTime = savetime ;
MoveHelperServer ( ) - > SetHost ( this ) ;
// Suppress predicted events, etc.
if ( IsPredictingWeapons ( ) )
{
IPredictionSystem : : SuppressHostEvents ( this ) ;
}
for ( int i = 0 ; i < commandsToRun ; + + i )
{
PlayerRunCommand ( & vecAvailCommands [ i ] , MoveHelperServer ( ) ) ;
// Update our vphysics object.
if ( m_pPhysicsController )
{
VPROF ( " CBasePlayer::PhysicsSimulate-UpdateVPhysicsPosition " ) ;
// If simulating at 2 * TICK_INTERVAL, add an extra TICK_INTERVAL to position arrival computation
UpdateVPhysicsPosition ( m_vNewVPhysicsPosition , m_vNewVPhysicsVelocity , vphysicsArrivalTime ) ;
vphysicsArrivalTime + = TICK_INTERVAL ;
}
}
// Always reset after running commands
IPredictionSystem : : SuppressHostEvents ( NULL ) ;
MoveHelperServer ( ) - > SetHost ( NULL ) ;
// Copy in final origin from simulation
CPlayerSimInfo * pi = NULL ;
if ( m_vecPlayerSimInfo . Count ( ) > 0 )
{
pi = & m_vecPlayerSimInfo [ m_vecPlayerSimInfo . Tail ( ) ] ;
pi - > m_flTime = Plat_FloatTime ( ) ;
pi - > m_vecAbsOrigin = GetAbsOrigin ( ) ;
pi - > m_flGameSimulationTime = gpGlobals - > curtime ;
pi - > m_nNumCmds = commandsToRun ;
}
}
// Restore the true server clock
// FIXME: Should this occur after simulation of children so
// that they are in the timespace of the player?
gpGlobals - > curtime = savetime ;
gpGlobals - > frametime = saveframetime ;
// // Kick the player if they haven't sent a user command in awhile in order to prevent clients
// // from using packet-level manipulation to mess with gamestate. Not sending usercommands seems
// // to have all kinds of bad effects, such as stalling a bunch of Think()'s and gamestate handling.
// // An example from TF: A medic stops sending commands after deploying an uber on another player.
// // As a result, invuln is permanently on the heal target because the maintenance code is stalled.
// if ( GetTimeSinceLastUserCommand() > player_usercommand_timeout.GetFloat() )
// {
// // If they have an active netchan, they're almost certainly messing with usercommands?
// INetChannelInfo *pNetChanInfo = engine->GetPlayerNetInfo( entindex() );
// if ( pNetChanInfo && pNetChanInfo->GetTimeSinceLastReceived() < 5.f )
// {
// engine->ServerCommand( UTIL_VarArgs( "kickid %d %s\n", GetUserID(), "UserCommand Timeout" ) );
// }
// }
}
unsigned int CBasePlayer : : PhysicsSolidMaskForEntity ( ) const
{
return MASK_PLAYERSOLID ;
}
//-----------------------------------------------------------------------------
// Purpose: This will force usercmd processing to actually consume commands even if the global tick counter isn't incrementing
//-----------------------------------------------------------------------------
void CBasePlayer : : ForceSimulation ( )
{
m_nSimulationTick = - 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *buf -
// totalcmds -
// dropped_packets -
// ignore -
// paused -
// Output : float -- Time in seconds of last movement command
//-----------------------------------------------------------------------------
void CBasePlayer : : ProcessUsercmds ( CUserCmd * cmds , int numcmds , int totalcmds ,
int dropped_packets , bool paused )
{
CCommandContext * ctx = AllocCommandContext ( ) ;
Assert ( ctx ) ;
int i ;
for ( i = totalcmds - 1 ; i > = 0 ; i - - )
{
CUserCmd * pCmd = & cmds [ totalcmds - 1 - i ] ;
// Validate values
if ( ! IsUserCmdDataValid ( pCmd ) )
{
pCmd - > MakeInert ( ) ;
}
ctx - > cmds . AddToTail ( * pCmd ) ;
}
ctx - > numcmds = numcmds ;
ctx - > totalcmds = totalcmds ,
ctx - > dropped_packets = dropped_packets ;
ctx - > paused = paused ;
// If the server is paused, zero out motion,buttons,view changes
if ( ctx - > paused )
{
bool clear_angles = true ;
// If no clipping and cheats enabled and sv_noclipduringpause enabled, then don't zero out movement part of CUserCmd
if ( GetMoveType ( ) = = MOVETYPE_NOCLIP & &
sv_cheats - > GetBool ( ) & &
sv_noclipduringpause . GetBool ( ) )
{
clear_angles = false ;
}
for ( i = 0 ; i < ctx - > numcmds ; i + + )
{
ctx - > cmds [ i ] . buttons = 0 ;
if ( clear_angles )
{
ctx - > cmds [ i ] . forwardmove = 0 ;
ctx - > cmds [ i ] . sidemove = 0 ;
ctx - > cmds [ i ] . upmove = 0 ;
VectorCopy ( pl . v_angle , ctx - > cmds [ i ] . viewangles ) ;
}
}
ctx - > dropped_packets = 0 ;
}
// Set global pause state for this player
m_bGamePaused = paused ;
if ( paused )
{
ForceSimulation ( ) ;
// Just run the commands right away if paused
PhysicsSimulate ( ) ;
}
if ( sv_playerperfhistorycount . GetInt ( ) > 0 )
{
CPlayerCmdInfo pi ;
pi . m_flTime = Plat_FloatTime ( ) ;
pi . m_nDroppedPackets = dropped_packets ;
pi . m_nNumCmds = numcmds ;
while ( m_vecPlayerCmdInfo . Count ( ) > = sv_playerperfhistorycount . GetInt ( ) )
{
m_vecPlayerCmdInfo . Remove ( m_vecPlayerCmdInfo . Head ( ) ) ;
}
m_vecPlayerCmdInfo . AddToTail ( pi ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Check that command values are reasonable
//-----------------------------------------------------------------------------
bool CBasePlayer : : IsUserCmdDataValid ( CUserCmd * pCmd )
{
if ( IsBot ( ) | | IsFakeClient ( ) )
return true ;
// Maximum difference between client's and server's tick_count
const int nCmdMaxTickDelta = ( 1.f / gpGlobals - > interval_per_tick ) * 2.5f ;
const int nMinDelta = Max ( 0 , gpGlobals - > tickcount - nCmdMaxTickDelta ) ;
const int nMaxDelta = gpGlobals - > tickcount + nCmdMaxTickDelta ;
bool bValid = ( pCmd - > tick_count > = nMinDelta & & pCmd - > tick_count < nMaxDelta ) & &
// Prevent clients from sending invalid view angles to try to get leaf server code to crash
( pCmd - > viewangles . IsValid ( ) & & IsEntityQAngleReasonable ( pCmd - > viewangles ) ) & &
// Movement ranges
( IsFinite ( pCmd - > forwardmove ) & & IsEntityCoordinateReasonable ( pCmd - > forwardmove ) ) & &
( IsFinite ( pCmd - > sidemove ) & & IsEntityCoordinateReasonable ( pCmd - > sidemove ) ) & &
( IsFinite ( pCmd - > upmove ) & & IsEntityCoordinateReasonable ( pCmd - > upmove ) ) ;
int nWarningLevel = sv_player_display_usercommand_errors . GetInt ( ) ;
if ( ! bValid & & nWarningLevel > 0 )
{
DevMsg ( " UserCommand out-of-range for userid %i \n " , GetUserID ( ) ) ;
if ( nWarningLevel = = 2 )
{
DevMsg ( " tick_count: %i \n viewangles: %5.2f %5.2f %5.2f \n forward: %5.2f \n side: \t %5.2f \n up: \t %5.2f \n " ,
pCmd - > tick_count ,
pCmd - > viewangles . x ,
pCmd - > viewangles . y ,
pCmd - > viewangles . x ,
pCmd - > forwardmove ,
pCmd - > sidemove ,
pCmd - > upmove ) ;
}
}
return bValid ;
}
void CBasePlayer : : DumpPerfToRecipient ( CBasePlayer * pRecipient , int nMaxRecords )
{
if ( ! pRecipient )
return ;
char buf [ 256 ] = { 0 } ;
int curpos = 0 ;
int nDumped = 0 ;
Vector prevo ( 0 , 0 , 0 ) ;
float prevt = 0.0f ;
for ( int i = m_vecPlayerSimInfo . Tail ( ) ; i ! = m_vecPlayerSimInfo . InvalidIndex ( ) ; i = m_vecPlayerSimInfo . Previous ( i ) )
{
const CPlayerSimInfo * pi = & m_vecPlayerSimInfo [ i ] ;
float vel = 0.0f ;
// Note we're walking from newest backward
float dt = prevt - pi - > m_flFinalSimulationTime ;
if ( nDumped > 0 & & dt > 0.0f )
{
Vector d = pi - > m_vecAbsOrigin - prevo ;
vel = d . Length ( ) / dt ;
}
char line [ 128 ] ;
int len = Q_snprintf ( line , sizeof ( line ) , " %.3f %d %d %.3f %.3f vel %.2f \n " ,
pi - > m_flTime ,
pi - > m_nNumCmds ,
pi - > m_nTicksCorrected ,
pi - > m_flFinalSimulationTime ,
pi - > m_flGameSimulationTime ,
vel ) ;
if ( curpos + len > 200 )
{
ClientPrint ( pRecipient , HUD_PRINTCONSOLE , ( char const * ) buf ) ;
buf [ 0 ] = 0 ;
curpos = 0 ;
}
Q_strncpy ( & buf [ curpos ] , line , sizeof ( buf ) - curpos ) ;
curpos + = len ;
+ + nDumped ;
if ( nMaxRecords ! = - 1 & & nDumped > = nMaxRecords )
break ;
prevo = pi - > m_vecAbsOrigin ;
prevt = pi - > m_flFinalSimulationTime ;
}
if ( curpos > 0 )
{
ClientPrint ( pRecipient , HUD_PRINTCONSOLE , buf ) ;
}
nDumped = 0 ;
curpos = 0 ;
for ( int i = m_vecPlayerCmdInfo . Tail ( ) ; i ! = m_vecPlayerCmdInfo . InvalidIndex ( ) ; i = m_vecPlayerCmdInfo . Previous ( i ) )
{
const CPlayerCmdInfo * pi = & m_vecPlayerCmdInfo [ i ] ;
char line [ 128 ] ;
int len = Q_snprintf ( line , sizeof ( line ) , " %.3f %d %d \n " ,
pi - > m_flTime ,
pi - > m_nNumCmds ,
pi - > m_nDroppedPackets ) ;
if ( curpos + len > 200 )
{
ClientPrint ( pRecipient , HUD_PRINTCONSOLE , ( char const * ) buf ) ;
buf [ 0 ] = 0 ;
curpos = 0 ;
}
Q_strncpy ( & buf [ curpos ] , line , sizeof ( buf ) - curpos ) ;
curpos + = len ;
+ + nDumped ;
if ( nMaxRecords ! = - 1 & & nDumped > = nMaxRecords )
break ;
}
if ( curpos > 0 )
{
ClientPrint ( pRecipient , HUD_PRINTCONSOLE , buf ) ;
}
}
// Duck debouncing code to stop menu changes from disallowing crouch/uncrouch
ConVar xc_crouch_debounce ( " xc_crouch_debounce " , " 0 " , FCVAR_NONE ) ;
//-----------------------------------------------------------------------------
// Purpose:
// Input : *ucmd -
// *moveHelper -
//-----------------------------------------------------------------------------
void CBasePlayer : : PlayerRunCommand ( CUserCmd * ucmd , IMoveHelper * moveHelper )
{
m_touchedPhysObject = false ;
if ( pl . fixangle = = FIXANGLE_NONE )
{
VectorCopy ( ucmd - > viewangles , pl . v_angle ) ;
}
// Handle FL_FROZEN.
// Prevent player moving for some seconds after New Game, so that they pick up everything
if ( GetFlags ( ) & FL_FROZEN | |
( developer . GetInt ( ) = = 0 & & gpGlobals - > eLoadType = = MapLoad_NewGame & & gpGlobals - > curtime < 3.0 ) )
{
ucmd - > forwardmove = 0 ;
ucmd - > sidemove = 0 ;
ucmd - > upmove = 0 ;
ucmd - > buttons = 0 ;
ucmd - > impulse = 0 ;
VectorCopy ( pl . v_angle , ucmd - > viewangles ) ;
}
else
{
// Force a duck if we're toggled
if ( GetToggledDuckState ( ) )
{
// If this is set, we've altered our menu options and need to debounce the duck
if ( xc_crouch_debounce . GetBool ( ) )
{
ToggleDuck ( ) ;
// Mark it as handled
xc_crouch_debounce . SetValue ( 0 ) ;
}
else
{
ucmd - > buttons | = IN_DUCK ;
}
}
}
PlayerMove ( ) - > RunCommand ( this , ucmd , moveHelper ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Strips off IN_xxx flags from the player's input
//-----------------------------------------------------------------------------
void CBasePlayer : : DisableButtons ( int nButtons )
{
m_afButtonDisabled | = nButtons ;
}
//-----------------------------------------------------------------------------
// Purpose: Re-enables stripped IN_xxx flags to the player's input
//-----------------------------------------------------------------------------
void CBasePlayer : : EnableButtons ( int nButtons )
{
m_afButtonDisabled & = ~ nButtons ;
}
//-----------------------------------------------------------------------------
// Purpose: Strips off IN_xxx flags from the player's input
//-----------------------------------------------------------------------------
void CBasePlayer : : ForceButtons ( int nButtons )
{
m_afButtonForced | = nButtons ;
}
//-----------------------------------------------------------------------------
// Purpose: Re-enables stripped IN_xxx flags to the player's input
//-----------------------------------------------------------------------------
void CBasePlayer : : UnforceButtons ( int nButtons )
{
m_afButtonForced & = ~ nButtons ;
}
void CBasePlayer : : HandleFuncTrain ( void )
{
if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
AddFlag ( FL_ONTRAIN ) ;
else
RemoveFlag ( FL_ONTRAIN ) ;
// Train speed control
if ( ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) = = 0 )
{
if ( m_iTrain & TRAIN_ACTIVE )
{
m_iTrain = TRAIN_NEW ; // turn off train
}
return ;
}
CBaseEntity * pTrain = GetGroundEntity ( ) ;
float vel ;
if ( pTrain )
{
if ( ! ( pTrain - > ObjectCaps ( ) & FCAP_DIRECTIONAL_USE ) )
pTrain = NULL ;
}
if ( ! pTrain )
{
if ( GetActiveWeapon ( ) - > ObjectCaps ( ) & FCAP_DIRECTIONAL_USE )
{
m_iTrain = TRAIN_ACTIVE | TRAIN_NEW ;
if ( m_nButtons & IN_FORWARD )
{
m_iTrain | = TRAIN_FAST ;
}
else if ( m_nButtons & IN_BACK )
{
m_iTrain | = TRAIN_BACK ;
}
else
{
m_iTrain | = TRAIN_NEUTRAL ;
}
return ;
}
else
{
trace_t trainTrace ;
// Maybe this is on the other side of a level transition
UTIL_TraceLine ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + Vector ( 0 , 0 , - 38 ) ,
MASK_PLAYERSOLID_BRUSHONLY , this , COLLISION_GROUP_NONE , & trainTrace ) ;
if ( trainTrace . fraction ! = 1.0 & & trainTrace . m_pEnt )
pTrain = trainTrace . m_pEnt ;
if ( ! pTrain | | ! ( pTrain - > ObjectCaps ( ) & FCAP_DIRECTIONAL_USE ) | | ! pTrain - > OnControls ( this ) )
{
m_afPhysicsFlags & = ~ PFLAG_DIROVERRIDE ;
m_iTrain = TRAIN_NEW | TRAIN_OFF ;
return ;
}
}
}
else if ( ! ( GetFlags ( ) & FL_ONGROUND ) | | pTrain - > HasSpawnFlags ( SF_TRACKTRAIN_NOCONTROL ) | | ( m_nButtons & ( IN_MOVELEFT | IN_MOVERIGHT ) ) )
{
// Turn off the train if you jump, strafe, or the train controls go dead
m_afPhysicsFlags & = ~ PFLAG_DIROVERRIDE ;
m_iTrain = TRAIN_NEW | TRAIN_OFF ;
return ;
}
SetAbsVelocity ( vec3_origin ) ;
vel = 0 ;
if ( m_afButtonPressed & IN_FORWARD )
{
vel = 1 ;
pTrain - > Use ( this , this , USE_SET , ( float ) vel ) ;
}
else if ( m_afButtonPressed & IN_BACK )
{
vel = - 1 ;
pTrain - > Use ( this , this , USE_SET , ( float ) vel ) ;
}
if ( vel )
{
m_iTrain = TrainSpeed ( pTrain - > m_flSpeed , ( ( CFuncTrackTrain * ) pTrain ) - > GetMaxSpeed ( ) ) ;
m_iTrain | = TRAIN_ACTIVE | TRAIN_NEW ;
}
}
void CBasePlayer : : PreThink ( void )
{
if ( g_fGameOver | | m_iPlayerLocked )
return ; // intermission or finale
if ( Hints ( ) )
{
Hints ( ) - > Update ( ) ;
}
ItemPreFrame ( ) ;
WaterMove ( ) ;
if ( g_pGameRules & & g_pGameRules - > FAllowFlashlight ( ) )
m_Local . m_iHideHUD & = ~ HIDEHUD_FLASHLIGHT ;
else
m_Local . m_iHideHUD | = HIDEHUD_FLASHLIGHT ;
// checks if new client data (for HUD and view control) needs to be sent to the client
UpdateClientData ( ) ;
CheckTimeBasedDamage ( ) ;
CheckSuitUpdate ( ) ;
if ( GetObserverMode ( ) > OBS_MODE_FREEZECAM )
{
CheckObserverSettings ( ) ; // do this each frame
}
if ( m_lifeState > = LIFE_DYING )
{
// track where we are in the nav mesh even when dead
UpdateLastKnownArea ( ) ;
return ;
}
HandleFuncTrain ( ) ;
if ( m_nButtons & IN_JUMP )
{
// If on a ladder, jump off the ladder
// else Jump
Jump ( ) ;
}
// If trying to duck, already ducked, or in the process of ducking
if ( ( m_nButtons & IN_DUCK ) | | ( GetFlags ( ) & FL_DUCKING ) | | ( m_afPhysicsFlags & PFLAG_DUCKING ) )
Duck ( ) ;
//
// If we're not on the ground, we're falling. Update our falling velocity.
//
if ( ! ( GetFlags ( ) & FL_ONGROUND ) )
{
m_Local . m_flFallVelocity = - GetAbsVelocity ( ) . z ;
}
// track where we are in the nav mesh
UpdateLastKnownArea ( ) ;
// StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating?
}
/* Time based Damage works as follows:
1 ) There are several types of timebased damage :
# define DMG_PARALYZE (1 << 14) // slows affected creature down
# define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad
# define DMG_POISON (1 << 16) // blood poisioning
# define DMG_RADIATION (1 << 17) // radiation exposure
# define DMG_DROWNRECOVER (1 << 18) // drown recovery
# define DMG_ACID (1 << 19) // toxic chemicals or acid burns
# define DMG_SLOWBURN (1 << 20) // in an oven
2 ) A new hit inflicting tbd restarts the tbd counter - each NPC has an 8 bit counter ,
per damage type . The counter is decremented every second , so the maximum time
an effect will last is 255 / 60 = 4.25 minutes . Of course , staying within the radius
of a damaging effect like fire , nervegas , radiation will continually reset the counter to max .
3 ) Every second that a tbd counter is running , the player takes damage . The damage
is determined by the type of tdb .
Paralyze - 1 / 2 movement rate , 30 second duration .
Nervegas - 5 points per second , 16 second duration = 80 points max dose .
Poison - 2 points per second , 25 second duration = 50 points max dose .
Radiation - 1 point per second , 50 second duration = 50 points max dose .
Drown - 5 points per second , 2 second duration .
Acid / Chemical - 5 points per second , 10 second duration = 50 points max .
Burn - 10 points per second , 2 second duration .
Freeze - 3 points per second , 10 second duration = 30 points max .
4 ) Certain actions or countermeasures counteract the damaging effects of tbds :
Armor / Heater / Cooler - Chemical ( acid ) , burn , freeze all do damage to armor power , then to body
- recharged by suit recharger
Air In Lungs - drowning damage is done to air in lungs first , then to body
- recharged by poking head out of water
- 10 seconds if swiming fast
Air In SCUBA - drowning damage is done to air in tanks first , then to body
- 2 minutes in tanks . Need new tank once empty .
Radiation Syringe - Each syringe full provides protection vs one radiation dosage
Antitoxin Syringe - Each syringe full provides protection vs one poisoning ( nervegas or poison ) .
Health kit - Immediate stop to acid / chemical , fire or freeze damage .
Radiation Shower - Immediate stop to radiation damage , acid / chemical or fire damage .
*/
// If player is taking time based damage, continue doing damage to player -
// this simulates the effect of being poisoned, gassed, dosed with radiation etc -
// anything that continues to do damage even after the initial contact stops.
// Update all time based damage counters, and shut off any that are done.
// The m_bitsDamageType bit MUST be set if any damage is to be taken.
// This routine will detect the initial on value of the m_bitsDamageType
// and init the appropriate counter. Only processes damage every second.
//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage
//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval
//#define NERVEGAS_DURATION 16
//#define NERVEGAS_DAMAGE 5.0
//#define POISON_DURATION 25
//#define POISON_DAMAGE 2.0
//#define RADIATION_DURATION 50
//#define RADIATION_DAMAGE 1.0
//#define ACID_DURATION 10
//#define ACID_DAMAGE 5.0
//#define SLOWBURN_DURATION 2
//#define SLOWBURN_DAMAGE 1.0
//#define SLOWFREEZE_DURATION 1.0
//#define SLOWFREEZE_DAMAGE 3.0
/* */
void CBasePlayer : : CheckTimeBasedDamage ( )
{
int i ;
byte bDuration = 0 ;
static float gtbdPrev = 0.0 ;
// If we don't have any time based damage return.
if ( ! g_pGameRules - > Damage_IsTimeBased ( m_bitsDamageType ) )
return ;
// only check for time based damage approx. every 2 seconds
if ( abs ( gpGlobals - > curtime - m_tbdPrev ) < 2.0 )
return ;
m_tbdPrev = gpGlobals - > curtime ;
for ( i = 0 ; i < CDMG_TIMEBASED ; i + + )
{
// Make sure the damage type is really time-based.
// This is kind of hacky but necessary until we setup DamageType as an enum.
int iDamage = ( DMG_PARALYZE < < i ) ;
if ( ! g_pGameRules - > Damage_IsTimeBased ( iDamage ) )
continue ;
// make sure bit is set for damage type
if ( m_bitsDamageType & iDamage )
{
switch ( i )
{
case itbd_Paralyze :
// UNDONE - flag movement as half-speed
bDuration = PARALYZE_DURATION ;
break ;
case itbd_NerveGas :
// OnTakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC);
bDuration = NERVEGAS_DURATION ;
break ;
// case itbd_Poison:
// OnTakeDamage( CTakeDamageInfo( this, this, POISON_DAMAGE, DMG_GENERIC ) );
// bDuration = POISON_DURATION;
// break;
case itbd_Radiation :
// OnTakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC);
bDuration = RADIATION_DURATION ;
break ;
case itbd_DrownRecover :
// NOTE: this hack is actually used to RESTORE health
// after the player has been drowning and finally takes a breath
if ( m_idrowndmg > m_idrownrestored )
{
int idif = MIN ( m_idrowndmg - m_idrownrestored , 10 ) ;
TakeHealth ( idif , DMG_GENERIC ) ;
m_idrownrestored + = idif ;
}
bDuration = 4 ; // get up to 5*10 = 50 points back
break ;
case itbd_PoisonRecover :
{
// NOTE: this hack is actually used to RESTORE health
// after the player has been poisoned.
if ( m_nPoisonDmg > m_nPoisonRestored )
{
int nDif = MIN ( m_nPoisonDmg - m_nPoisonRestored , 10 ) ;
TakeHealth ( nDif , DMG_GENERIC ) ;
m_nPoisonRestored + = nDif ;
}
bDuration = 9 ; // get up to 10*10 = 100 points back
break ;
}
case itbd_Acid :
// OnTakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC);
bDuration = ACID_DURATION ;
break ;
case itbd_SlowBurn :
// OnTakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC);
bDuration = SLOWBURN_DURATION ;
break ;
case itbd_SlowFreeze :
// OnTakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC);
bDuration = SLOWFREEZE_DURATION ;
break ;
default :
bDuration = 0 ;
}
if ( m_rgbTimeBasedDamage [ i ] )
{
// decrement damage duration, detect when done.
if ( ! m_rgbTimeBasedDamage [ i ] | | - - m_rgbTimeBasedDamage [ i ] = = 0 )
{
m_rgbTimeBasedDamage [ i ] = 0 ;
// if we're done, clear damage bits
m_bitsDamageType & = ~ ( DMG_PARALYZE < < i ) ;
}
}
else
// first time taking this damage type - init damage duration
m_rgbTimeBasedDamage [ i ] = bDuration ;
}
}
}
/*
THE POWER SUIT
The Suit provides 3 main functions : Protection , Notification and Augmentation .
Some functions are automatic , some require power .
The player gets the suit shortly after getting off the train in C1A0 and it stays
with him for the entire game .
Protection
Heat / Cold
When the player enters a hot / cold area , the heating / cooling indicator on the suit
will come on and the battery will drain while the player stays in the area .
After the battery is dead , the player starts to take damage .
This feature is built into the suit and is automatically engaged .
Radiation Syringe
This will cause the player to be immune from the effects of radiation for N seconds . Single use item .
Anti - Toxin Syringe
This will cure the player from being poisoned . Single use item .
Health
Small ( 1 st aid kits , food , etc . )
Large ( boxes on walls )
Armor
The armor works using energy to create a protective field that deflects a
percentage of damage projectile and explosive attacks . After the armor has been deployed ,
it will attempt to recharge itself to full capacity with the energy reserves from the battery .
It takes the armor N seconds to fully charge .
Notification ( via the HUD )
x Health
x Ammo
x Automatic Health Care
Notifies the player when automatic healing has been engaged .
x Geiger counter
Classic Geiger counter sound and status bar at top of HUD
alerts player to dangerous levels of radiation . This is not visible when radiation levels are normal .
x Poison
Armor
Displays the current level of armor .
Augmentation
Reanimation ( w / adrenaline )
Causes the player to come back to life after he has been dead for 3 seconds .
Will not work if player was gibbed . Single use .
Long Jump
Used by hitting the ? ? ? key ( s ) . Caused the player to further than normal .
SCUBA
Used automatically after picked up and after player enters the water .
Works for N seconds . Single use .
Things powered by the battery
Armor
Uses N watts for every M units of damage .
Heat / Cool
Uses N watts for every second in hot / cold area .
Long Jump
Uses N watts for every jump .
Alien Cloak
Uses N watts for each use . Each use lasts M seconds .
Alien Shield
Augments armor . Reduces Armor drain by one half
*/
// if in range of radiation source, ping geiger counter
# define GEIGERDELAY 0.25
void CBasePlayer : : UpdateGeigerCounter ( void )
{
byte range ;
// delay per update ie: don't flood net with these msgs
if ( gpGlobals - > curtime < m_flgeigerDelay )
return ;
m_flgeigerDelay = gpGlobals - > curtime + GEIGERDELAY ;
// send range to radition source to client
range = ( byte ) clamp ( Floor2Int ( m_flgeigerRange / 4 ) , 0 , 255 ) ;
// This is to make sure you aren't driven crazy by geiger while in the airboat
if ( IsInAVehicle ( ) )
{
range = clamp ( ( int ) range * 4 , 0 , 255 ) ;
}
if ( range ! = m_igeigerRangePrev )
{
m_igeigerRangePrev = range ;
CSingleUserRecipientFilter user ( this ) ;
user . MakeReliable ( ) ;
UserMessageBegin ( user , " Geiger " ) ;
WRITE_BYTE ( range ) ;
MessageEnd ( ) ;
}
// reset counter and semaphore
if ( ! random - > RandomInt ( 0 , 3 ) )
{
m_flgeigerRange = 1000 ;
}
}
/*
= = = = = = = = = = = = = = = =
CheckSuitUpdate
Play suit update if it ' s time
= = = = = = = = = = = = = = = =
*/
# define SUITUPDATETIME 3.5
# define SUITFIRSTUPDATETIME 0.1
void CBasePlayer : : CheckSuitUpdate ( )
{
int i ;
int isentence = 0 ;
int isearch = m_iSuitPlayNext ;
// Ignore suit updates if no suit
if ( ! IsSuitEquipped ( ) )
return ;
// if in range of radiation source, ping geiger counter
UpdateGeigerCounter ( ) ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
// don't bother updating HEV voice in multiplayer.
return ;
}
if ( gpGlobals - > curtime > = m_flSuitUpdate & & m_flSuitUpdate > 0 )
{
// play a sentence off of the end of the queue
for ( i = 0 ; i < CSUITPLAYLIST ; i + + )
{
if ( ( isentence = m_rgSuitPlayList [ isearch ] ) ! = 0 )
break ;
if ( + + isearch = = CSUITPLAYLIST )
isearch = 0 ;
}
if ( isentence )
{
m_rgSuitPlayList [ isearch ] = 0 ;
if ( isentence > 0 )
{
// play sentence number
char sentence [ 512 ] ;
Q_snprintf ( sentence , sizeof ( sentence ) , " !%s " , engine - > SentenceNameFromIndex ( isentence ) ) ;
UTIL_EmitSoundSuit ( edict ( ) , sentence ) ;
}
else
{
// play sentence group
UTIL_EmitGroupIDSuit ( edict ( ) , - isentence ) ;
}
m_flSuitUpdate = gpGlobals - > curtime + SUITUPDATETIME ;
}
else
// queue is empty, don't check
m_flSuitUpdate = 0 ;
}
}
// add sentence to suit playlist queue. if fgroup is true, then
// name is a sentence group (HEV_AA), otherwise name is a specific
// sentence name ie: !HEV_AA0. If iNoRepeat is specified in
// seconds, then we won't repeat playback of this word or sentence
// for at least that number of seconds.
void CBasePlayer : : SetSuitUpdate ( const char * name , int fgroup , int iNoRepeatTime )
{
int i ;
int isentence ;
int iempty = - 1 ;
// Ignore suit updates if no suit
if ( ! IsSuitEquipped ( ) )
return ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
// due to static channel design, etc. We don't play HEV sounds in multiplayer right now.
return ;
}
// if name == NULL, then clear out the queue
if ( ! name )
{
for ( i = 0 ; i < CSUITPLAYLIST ; i + + )
m_rgSuitPlayList [ i ] = 0 ;
return ;
}
// get sentence or group number
if ( ! fgroup )
{
isentence = SENTENCEG_Lookup ( name ) ; // Lookup sentence index (not group) by name
if ( isentence < 0 )
return ;
}
else
// mark group number as negative
isentence = - SENTENCEG_GetIndex ( name ) ; // Lookup group index by name
// check norepeat list - this list lets us cancel
// the playback of words or sentences that have already
// been played within a certain time.
for ( i = 0 ; i < CSUITNOREPEAT ; i + + )
{
if ( isentence = = m_rgiSuitNoRepeat [ i ] )
{
// this sentence or group is already in
// the norepeat list
if ( m_rgflSuitNoRepeatTime [ i ] < gpGlobals - > curtime )
{
// norepeat time has expired, clear it out
m_rgiSuitNoRepeat [ i ] = 0 ;
m_rgflSuitNoRepeatTime [ i ] = 0.0 ;
iempty = i ;
break ;
}
else
{
// don't play, still marked as norepeat
return ;
}
}
// keep track of empty slot
if ( ! m_rgiSuitNoRepeat [ i ] )
iempty = i ;
}
// sentence is not in norepeat list, save if norepeat time was given
if ( iNoRepeatTime )
{
if ( iempty < 0 )
iempty = random - > RandomInt ( 0 , CSUITNOREPEAT - 1 ) ; // pick random slot to take over
m_rgiSuitNoRepeat [ iempty ] = isentence ;
m_rgflSuitNoRepeatTime [ iempty ] = iNoRepeatTime + gpGlobals - > curtime ;
}
// find empty spot in queue, or overwrite last spot
m_rgSuitPlayList [ m_iSuitPlayNext + + ] = isentence ;
if ( m_iSuitPlayNext = = CSUITPLAYLIST )
m_iSuitPlayNext = 0 ;
if ( m_flSuitUpdate < = gpGlobals - > curtime )
{
if ( m_flSuitUpdate = = 0 )
// play queue is empty, don't delay too long before playback
m_flSuitUpdate = gpGlobals - > curtime + SUITFIRSTUPDATETIME ;
else
m_flSuitUpdate = gpGlobals - > curtime + SUITUPDATETIME ;
}
}
//=========================================================
// UpdatePlayerSound - updates the position of the player's
// reserved sound slot in the sound list.
//=========================================================
void CBasePlayer : : UpdatePlayerSound ( void )
{
int iBodyVolume ;
int iVolume ;
CSound * pSound ;
pSound = CSoundEnt : : SoundPointerForIndex ( CSoundEnt : : ClientSoundIndex ( edict ( ) ) ) ;
if ( ! pSound )
{
Msg ( " Client lost reserved sound! \n " ) ;
return ;
}
if ( GetFlags ( ) & FL_NOTARGET )
{
pSound - > m_iVolume = 0 ;
return ;
}
// now figure out how loud the player's movement is.
if ( GetFlags ( ) & FL_ONGROUND )
{
iBodyVolume = GetAbsVelocity ( ) . Length ( ) ;
// clamp the noise that can be made by the body, in case a push trigger,
// weapon recoil, or anything shoves the player abnormally fast.
// NOTE: 512 units is a pretty large radius for a sound made by the player's body.
// then again, I think some materials are pretty loud.
if ( iBodyVolume > 512 )
{
iBodyVolume = 512 ;
}
}
else
{
iBodyVolume = 0 ;
}
if ( m_nButtons & IN_JUMP )
{
// Jumping is a little louder.
iBodyVolume + = 100 ;
}
m_iTargetVolume = iBodyVolume ;
// if target volume is greater than the player sound's current volume, we paste the new volume in
// immediately. If target is less than the current volume, current volume is not set immediately to the
// lower volume, rather works itself towards target volume over time. This gives NPCs a much better chance
// to hear a sound, especially if they don't listen every frame.
iVolume = pSound - > Volume ( ) ;
if ( m_iTargetVolume > iVolume )
{
iVolume = m_iTargetVolume ;
}
else if ( iVolume > m_iTargetVolume )
{
iVolume - = 250 * gpGlobals - > frametime ;
if ( iVolume < m_iTargetVolume )
{
iVolume = 0 ;
}
}
if ( pSound )
{
pSound - > SetSoundOrigin ( GetAbsOrigin ( ) ) ;
pSound - > m_iType = SOUND_PLAYER ;
pSound - > m_iVolume = iVolume ;
}
// Below are a couple of useful little bits that make it easier to visualize just how much noise the
// player is making.
//Vector forward = UTIL_YawToVector( pl.v_angle.y );
//UTIL_Sparks( GetAbsOrigin() + forward * iVolume );
//Msg( "%d/%d\n", iVolume, m_iTargetVolume );
}
// This is a glorious hack to find free space when you've crouched into some solid space
// Our crouching collisions do not work correctly for some reason and this is easier
// than fixing the problem :(
void FixPlayerCrouchStuck ( CBasePlayer * pPlayer )
{
trace_t trace ;
// Move up as many as 18 pixels if the player is stuck.
int i ;
Vector org = pPlayer - > GetAbsOrigin ( ) ; ;
for ( i = 0 ; i < 18 ; i + + )
{
UTIL_TraceHull ( pPlayer - > GetAbsOrigin ( ) , pPlayer - > GetAbsOrigin ( ) ,
VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX , MASK_PLAYERSOLID , pPlayer , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
if ( trace . startsolid )
{
Vector origin = pPlayer - > GetAbsOrigin ( ) ;
origin . z + = 1.0f ;
pPlayer - > SetLocalOrigin ( origin ) ;
}
else
return ;
}
pPlayer - > SetAbsOrigin ( org ) ;
for ( i = 0 ; i < 18 ; i + + )
{
UTIL_TraceHull ( pPlayer - > GetAbsOrigin ( ) , pPlayer - > GetAbsOrigin ( ) ,
VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX , MASK_PLAYERSOLID , pPlayer , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
if ( trace . startsolid )
{
Vector origin = pPlayer - > GetAbsOrigin ( ) ;
origin . z - = 1.0f ;
pPlayer - > SetLocalOrigin ( origin ) ;
}
else
return ;
}
}
# define SMOOTHING_FACTOR 0.9
extern CMoveData * g_pMoveData ;
// UNDONE: Look and see if the ground entity is in hierarchy with a MOVETYPE_VPHYSICS?
// Behavior in that case is not as good currently when the parent is rideable
bool CBasePlayer : : IsRideablePhysics ( IPhysicsObject * pPhysics )
{
if ( pPhysics )
{
if ( pPhysics - > GetMass ( ) > ( VPhysicsGetObject ( ) - > GetMass ( ) * 2 ) )
return true ;
}
return false ;
}
IPhysicsObject * CBasePlayer : : GetGroundVPhysics ( )
{
CBaseEntity * pGroundEntity = GetGroundEntity ( ) ;
if ( pGroundEntity & & pGroundEntity - > GetMoveType ( ) = = MOVETYPE_VPHYSICS )
{
IPhysicsObject * pPhysGround = pGroundEntity - > VPhysicsGetObject ( ) ;
if ( pPhysGround & & pPhysGround - > IsMoveable ( ) )
return pPhysGround ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// For debugging...
//-----------------------------------------------------------------------------
void CBasePlayer : : ForceOrigin ( const Vector & vecOrigin )
{
m_bForceOrigin = true ;
m_vForcedOrigin = vecOrigin ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : PostThink ( )
{
m_vecSmoothedVelocity = m_vecSmoothedVelocity * SMOOTHING_FACTOR + GetAbsVelocity ( ) * ( 1 - SMOOTHING_FACTOR ) ;
if ( ! g_fGameOver & & ! m_iPlayerLocked )
{
if ( IsAlive ( ) )
{
// set correct collision bounds (may have changed in player movement code)
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-Bounds " ) ;
if ( GetFlags ( ) & FL_DUCKING )
{
SetCollisionBounds ( VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ) ;
}
else
{
SetCollisionBounds ( VEC_HULL_MIN , VEC_HULL_MAX ) ;
}
VPROF_SCOPE_END ( ) ;
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-Use " ) ;
// Handle controlling an entity
if ( m_hUseEntity ! = NULL )
{
// if they've moved too far from the gun, or deployed another weapon, unuse the gun
if ( m_hUseEntity - > OnControls ( this ) & &
( ! GetActiveWeapon ( ) | | GetActiveWeapon ( ) - > IsEffectActive ( EF_NODRAW ) | |
( GetActiveWeapon ( ) - > GetActivity ( ) = = ACT_VM_HOLSTER )
# ifdef PORTAL // Portalgun view model stays up when holding an object -Jeep
| | FClassnameIs ( GetActiveWeapon ( ) , " weapon_portalgun " )
# endif //#ifdef PORTAL
) )
{
m_hUseEntity - > Use ( this , this , USE_SET , 2 ) ; // try fire the gun
}
else
{
// they've moved off the controls
ClearUseEntity ( ) ;
}
}
VPROF_SCOPE_END ( ) ;
// do weapon stuff
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-ItemPostFrame " ) ;
ItemPostFrame ( ) ;
VPROF_SCOPE_END ( ) ;
if ( GetFlags ( ) & FL_ONGROUND )
{
if ( m_Local . m_flFallVelocity > 64 & & ! g_pGameRules - > IsMultiplayer ( ) )
{
CSoundEnt : : InsertSound ( SOUND_PLAYER , GetAbsOrigin ( ) , m_Local . m_flFallVelocity , 0.2 , this ) ;
// Msg( "fall %f\n", m_Local.m_flFallVelocity );
}
m_Local . m_flFallVelocity = 0 ;
}
// select the proper animation for the player character
VPROF ( " CBasePlayer::PostThink-Animation " ) ;
// If he's in a vehicle, sit down
if ( IsInAVehicle ( ) )
SetAnimation ( PLAYER_IN_VEHICLE ) ;
else 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 ) ;
}
// Don't allow bogus sequence on player
if ( GetSequence ( ) = = - 1 )
{
SetSequence ( 0 ) ;
}
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-StudioFrameAdvance " ) ;
StudioFrameAdvance ( ) ;
VPROF_SCOPE_END ( ) ;
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-DispatchAnimEvents " ) ;
DispatchAnimEvents ( this ) ;
VPROF_SCOPE_END ( ) ;
SetSimulationTime ( gpGlobals - > curtime ) ;
//Let the weapon update as well
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-Weapon_FrameUpdate " ) ;
Weapon_FrameUpdate ( ) ;
VPROF_SCOPE_END ( ) ;
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-UpdatePlayerSound " ) ;
UpdatePlayerSound ( ) ;
VPROF_SCOPE_END ( ) ;
if ( m_bForceOrigin )
{
SetLocalOrigin ( m_vForcedOrigin ) ;
SetLocalAngles ( m_Local . m_vecPunchAngle ) ;
m_Local . m_vecPunchAngle = RandomAngle ( - 25 , 25 ) ;
m_Local . m_vecPunchAngleVel . Init ( ) ;
}
VPROF_SCOPE_BEGIN ( " CBasePlayer::PostThink-PostThinkVPhysics " ) ;
PostThinkVPhysics ( ) ;
VPROF_SCOPE_END ( ) ;
}
# if !defined( NO_ENTITY_PREDICTION )
// Even if dead simulate entities
SimulatePlayerSimulatedEntities ( ) ;
# endif
}
// handles touching physics objects
void CBasePlayer : : Touch ( CBaseEntity * pOther )
{
if ( pOther = = GetGroundEntity ( ) )
return ;
if ( pOther - > GetMoveType ( ) ! = MOVETYPE_VPHYSICS | | pOther - > GetSolid ( ) ! = SOLID_VPHYSICS | | ( pOther - > GetSolidFlags ( ) & FSOLID_TRIGGER ) )
return ;
IPhysicsObject * pPhys = pOther - > VPhysicsGetObject ( ) ;
if ( ! pPhys | | ! pPhys - > IsMoveable ( ) )
return ;
SetTouchedPhysics ( true ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : PostThinkVPhysics ( void )
{
// Check to see if things are initialized!
if ( ! m_pPhysicsController )
return ;
Vector newPosition = GetAbsOrigin ( ) ;
float frametime = gpGlobals - > frametime ;
if ( frametime < = 0 | | frametime > 0.1f )
frametime = 0.1f ;
IPhysicsObject * pPhysGround = GetGroundVPhysics ( ) ;
if ( ! pPhysGround & & m_touchedPhysObject & & g_pMoveData - > m_outStepHeight < = 0.f & & ( GetFlags ( ) & FL_ONGROUND ) )
{
newPosition = m_oldOrigin + frametime * g_pMoveData - > m_outWishVel ;
newPosition = ( GetAbsOrigin ( ) * 0.5f ) + ( newPosition * 0.5f ) ;
}
int collisionState = VPHYS_WALK ;
if ( GetMoveType ( ) = = MOVETYPE_NOCLIP | | GetMoveType ( ) = = MOVETYPE_OBSERVER )
{
collisionState = VPHYS_NOCLIP ;
}
else if ( GetFlags ( ) & FL_DUCKING )
{
collisionState = VPHYS_CROUCH ;
}
if ( collisionState ! = m_vphysicsCollisionState )
{
SetVCollisionState ( GetAbsOrigin ( ) , GetAbsVelocity ( ) , collisionState ) ;
}
if ( ! ( TouchedPhysics ( ) | | pPhysGround ) )
{
float maxSpeed = m_flMaxspeed > 0.0f ? m_flMaxspeed : sv_maxspeed . GetFloat ( ) ;
g_pMoveData - > m_outWishVel . Init ( maxSpeed , maxSpeed , maxSpeed ) ;
}
// teleport the physics object up by stepheight (game code does this - reflect in the physics)
if ( g_pMoveData - > m_outStepHeight > 0.1f )
{
if ( g_pMoveData - > m_outStepHeight > 4.0f )
{
VPhysicsGetObject ( ) - > SetPosition ( GetAbsOrigin ( ) , vec3_angle , true ) ;
}
else
{
// don't ever teleport into solid
Vector position , end ;
VPhysicsGetObject ( ) - > GetPosition ( & position , NULL ) ;
end = position ;
end . z + = g_pMoveData - > m_outStepHeight ;
trace_t trace ;
UTIL_TraceEntity ( this , position , end , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
if ( trace . DidHit ( ) )
{
g_pMoveData - > m_outStepHeight = trace . endpos . z - position . z ;
}
m_pPhysicsController - > StepUp ( g_pMoveData - > m_outStepHeight ) ;
}
m_pPhysicsController - > Jump ( ) ;
}
g_pMoveData - > m_outStepHeight = 0.0f ;
// Store these off because after running the usercmds, it'll pass them
// to UpdateVPhysicsPosition.
m_vNewVPhysicsPosition = newPosition ;
m_vNewVPhysicsVelocity = g_pMoveData - > m_outWishVel ;
m_oldOrigin = GetAbsOrigin ( ) ;
}
void CBasePlayer : : UpdateVPhysicsPosition ( const Vector & position , const Vector & velocity , float secondsToArrival )
{
bool onground = ( GetFlags ( ) & FL_ONGROUND ) ? true : false ;
IPhysicsObject * pPhysGround = GetGroundVPhysics ( ) ;
// if the object is much heavier than the player, treat it as a local coordinate system
// the player controller will solve movement differently in this case.
if ( ! IsRideablePhysics ( pPhysGround ) )
{
pPhysGround = NULL ;
}
m_pPhysicsController - > Update ( position , velocity , secondsToArrival , onground , pPhysGround ) ;
}
void CBasePlayer : : UpdatePhysicsShadowToCurrentPosition ( )
{
UpdateVPhysicsPosition ( GetAbsOrigin ( ) , vec3_origin , gpGlobals - > frametime ) ;
}
void CBasePlayer : : UpdatePhysicsShadowToPosition ( const Vector & vecAbsOrigin )
{
UpdateVPhysicsPosition ( vecAbsOrigin , vec3_origin , gpGlobals - > frametime ) ;
}
Vector CBasePlayer : : GetSmoothedVelocity ( void )
{
if ( IsInAVehicle ( ) )
{
return GetVehicle ( ) - > GetVehicleEnt ( ) - > GetSmoothedVelocity ( ) ;
}
return m_vecSmoothedVelocity ;
}
CBaseEntity * g_pLastSpawn = NULL ;
//-----------------------------------------------------------------------------
// Purpose: Finds a player start entity of the given classname. If any entity of
// of the given classname has the SF_PLAYER_START_MASTER flag set, that
// is the entity that will be returned. Otherwise, the first entity of
// the given classname is returned.
// Input : pszClassName - should be "info_player_start", "info_player_coop", or
// "info_player_deathmatch"
//-----------------------------------------------------------------------------
CBaseEntity * FindPlayerStart ( const char * pszClassName )
{
# define SF_PLAYER_START_MASTER 1
CBaseEntity * pStart = gEntList . FindEntityByClassname ( NULL , pszClassName ) ;
CBaseEntity * pStartFirst = pStart ;
while ( pStart ! = NULL )
{
if ( pStart - > HasSpawnFlags ( SF_PLAYER_START_MASTER ) )
{
return pStart ;
}
pStart = gEntList . FindEntityByClassname ( pStart , pszClassName ) ;
}
return pStartFirst ;
}
/*
= = = = = = = = = = = =
EntSelectSpawnPoint
Returns the entity to spawn at
USES AND SETS GLOBAL g_pLastSpawn
= = = = = = = = = = = =
*/
CBaseEntity * CBasePlayer : : EntSelectSpawnPoint ( )
{
CBaseEntity * pSpot ;
edict_t * player ;
player = edict ( ) ;
// choose a info_player_deathmatch point
if ( g_pGameRules - > IsCoOp ( ) )
{
pSpot = gEntList . FindEntityByClassname ( g_pLastSpawn , " info_player_coop " ) ;
if ( pSpot )
goto ReturnSpot ;
pSpot = gEntList . FindEntityByClassname ( g_pLastSpawn , " info_player_start " ) ;
if ( pSpot )
goto ReturnSpot ;
}
else if ( g_pGameRules - > IsDeathmatch ( ) )
{
pSpot = g_pLastSpawn ;
// Randomize the start spot
for ( int i = random - > RandomInt ( 1 , 5 ) ; i > 0 ; i - - )
pSpot = gEntList . FindEntityByClassname ( pSpot , " info_player_deathmatch " ) ;
if ( ! pSpot ) // skip over the null point
pSpot = gEntList . FindEntityByClassname ( pSpot , " info_player_deathmatch " ) ;
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 , " info_player_deathmatch " ) ;
continue ;
}
// if so, go to pSpot
goto ReturnSpot ;
}
}
// increment pSpot
pSpot = gEntList . FindEntityByClassname ( pSpot , " info_player_deathmatch " ) ;
} 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 startspot is set, (re)spawn there.
if ( ! gpGlobals - > startspot | | ! strlen ( STRING ( gpGlobals - > startspot ) ) )
{
pSpot = FindPlayerStart ( " info_player_start " ) ;
if ( pSpot )
goto ReturnSpot ;
}
else
{
pSpot = gEntList . FindEntityByName ( NULL , gpGlobals - > startspot ) ;
if ( pSpot )
goto ReturnSpot ;
}
ReturnSpot :
if ( ! pSpot )
{
Warning ( " PutClientInServer: no info_player_start on level \n " ) ;
return CBaseEntity : : Instance ( INDEXENT ( 0 ) ) ;
}
g_pLastSpawn = pSpot ;
return pSpot ;
}
//-----------------------------------------------------------------------------
// Purpose: Called the first time the player's created
//-----------------------------------------------------------------------------
void CBasePlayer : : InitialSpawn ( void )
{
m_iConnected = PlayerConnected ;
gamestats - > Event_PlayerConnected ( this ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Called everytime the player respawns
//-----------------------------------------------------------------------------
void CBasePlayer : : Spawn ( void )
{
// Needs to be done before weapons are given
if ( Hints ( ) )
{
Hints ( ) - > ResetHints ( ) ;
}
SetClassname ( " player " ) ;
// Shared spawning code..
SharedSpawn ( ) ;
SetSimulatedEveryTick ( true ) ;
SetAnimatedEveryTick ( true ) ;
m_ArmorValue = SpawnArmorValue ( ) ;
SetBlocksLOS ( false ) ;
m_iMaxHealth = m_iHealth ;
// Clear all flags except for FL_FULLEDICT
if ( GetFlags ( ) & FL_FAKECLIENT )
{
ClearFlags ( ) ;
AddFlag ( FL_CLIENT | FL_FAKECLIENT ) ;
}
else
{
ClearFlags ( ) ;
AddFlag ( FL_CLIENT ) ;
}
AddFlag ( FL_AIMTARGET ) ;
m_AirFinished = gpGlobals - > curtime + AIRTIME ;
m_nDrownDmgRate = DROWNING_DAMAGE_INITIAL ;
// only preserve the shadow flag
int effects = GetEffects ( ) & EF_NOSHADOW ;
SetEffects ( effects ) ;
IncrementInterpolationFrame ( ) ;
// Initialize the fog and postprocess controllers.
InitFogController ( ) ;
m_DmgTake = 0 ;
m_DmgSave = 0 ;
m_bitsHUDDamage = - 1 ;
m_bitsDamageType = 0 ;
m_afPhysicsFlags = 0 ;
m_idrownrestored = m_idrowndmg ;
SetFOV ( this , 0 ) ;
m_flNextDecalTime = 0 ; // let this player decal as soon as he spawns.
m_flgeigerDelay = gpGlobals - > curtime + 2.0 ; // wait a few seconds until user-defined message registrations
// are recieved by all clients
m_flFieldOfView = 0.766 ; // some NPCs use this to determine whether or not the player is looking at them.
m_vecAdditionalPVSOrigin = vec3_origin ;
m_vecCameraPVSOrigin = vec3_origin ;
if ( ! m_fGameHUDInitialized )
g_pGameRules - > SetDefaultPlayerTeam ( this ) ;
g_pGameRules - > GetPlayerSpawnSpot ( this ) ;
m_Local . m_bDucked = false ; // This will persist over round restart if you hold duck otherwise.
m_Local . m_bDucking = false ;
SetViewOffset ( VEC_VIEW_SCALED ( this ) ) ;
Precache ( ) ;
m_bitsDamageType = 0 ;
m_bitsHUDDamage = - 1 ;
SetPlayerUnderwater ( false ) ;
m_iTrain = TRAIN_NEW ;
m_HackedGunPos = Vector ( 0 , 32 , 0 ) ;
m_iBonusChallenge = sv_bonus_challenge . GetInt ( ) ;
sv_bonus_challenge . SetValue ( 0 ) ;
if ( m_iPlayerSound = = SOUNDLIST_EMPTY )
{
Msg ( " Couldn't alloc player sound slot! \n " ) ;
}
SetThink ( NULL ) ;
m_fInitHUD = true ;
m_fWeapon = false ;
m_iClientBattery = - 1 ;
m_lastx = m_lasty = 0 ;
Q_strncpy ( m_szLastPlaceName . GetForModify ( ) , " " , MAX_PLACE_NAME_LENGTH ) ;
CSingleUserRecipientFilter user ( this ) ;
enginesound - > SetPlayerDSP ( user , 0 , false ) ;
CreateViewModel ( ) ;
SetCollisionGroup ( COLLISION_GROUP_PLAYER ) ;
// if the player is locked, make sure he stays locked
if ( m_iPlayerLocked )
{
m_iPlayerLocked = false ;
LockPlayerInPlace ( ) ;
}
if ( GetTeamNumber ( ) ! = TEAM_SPECTATOR )
{
StopObserverMode ( ) ;
}
else
{
StartObserverMode ( m_iObserverLastMode ) ;
}
StopReplayMode ( ) ;
// Clear any screenfade
color32 nothing = { 0 , 0 , 0 , 255 } ;
UTIL_ScreenFade ( this , nothing , 0 , 0 , FFADE_IN | FFADE_PURGE ) ;
g_pGameRules - > PlayerSpawn ( this ) ;
m_flLaggedMovementValue = 1.0f ;
m_vecSmoothedVelocity = vec3_origin ;
InitVCollision ( GetAbsOrigin ( ) , GetAbsVelocity ( ) ) ;
# if !defined( TF_DLL )
IGameEvent * event = gameeventmanager - > CreateEvent ( " player_spawn " ) ;
if ( event )
{
event - > SetInt ( " userid " , GetUserID ( ) ) ;
gameeventmanager - > FireEvent ( event ) ;
}
# endif
RumbleEffect ( RUMBLE_STOP_ALL , 0 , RUMBLE_FLAGS_NONE ) ;
// Calculate this immediately
m_nVehicleViewSavedFrame = 0 ;
// track where we are in the nav mesh
UpdateLastKnownArea ( ) ;
BaseClass : : Spawn ( ) ;
// track where we are in the nav mesh
UpdateLastKnownArea ( ) ;
m_weaponFiredTimer . Invalidate ( ) ;
}
void CBasePlayer : : Activate ( void )
{
BaseClass : : Activate ( ) ;
AimTarget_ForceRepopulateList ( ) ;
RumbleEffect ( RUMBLE_STOP_ALL , 0 , RUMBLE_FLAGS_NONE ) ;
// Reset the analog bias. If the player is in a vehicle when the game
// reloads, it will autosense and apply the correct bias.
m_iVehicleAnalogBias = VEHICLE_ANALOG_BIAS_NONE ;
}
void CBasePlayer : : Precache ( void )
{
BaseClass : : Precache ( ) ;
PrecacheScriptSound ( " Player.FallGib " ) ;
PrecacheScriptSound ( " Player.Death " ) ;
PrecacheScriptSound ( " Player.PlasmaDamage " ) ;
PrecacheScriptSound ( " Player.SonicDamage " ) ;
PrecacheScriptSound ( " Player.DrownStart " ) ;
PrecacheScriptSound ( " Player.DrownContinue " ) ;
PrecacheScriptSound ( " Player.Wade " ) ;
PrecacheScriptSound ( " Player.AmbientUnderWater " ) ;
enginesound - > PrecacheSentenceGroup ( " HEV " ) ;
// These are always needed
# ifndef TF_DLL
PrecacheParticleSystem ( " slime_splash_01 " ) ;
PrecacheParticleSystem ( " slime_splash_02 " ) ;
PrecacheParticleSystem ( " slime_splash_03 " ) ;
# endif
// in the event that the player JUST spawned, and the level node graph
// was loaded, fix all of the node graph pointers before the game starts.
// !!!BUGBUG - now that we have multiplayer, this needs to be moved!
/* todo - put in better spot and use new ainetowrk stuff
if ( WorldGraph . m_fGraphPresent & & ! WorldGraph . m_fGraphPointersSet )
{
if ( ! WorldGraph . FSetGraphPointers ( ) )
{
Msg ( " **Graph pointers were not set! \n " ) ;
}
else
{
Msg ( " **Graph Pointers Set! \n " ) ;
}
}
*/
// SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific)
// because they need to precache before any clients have connected
// init geiger counter vars during spawn and each time
// we cross a level transition
m_flgeigerRange = 1000 ;
m_igeigerRangePrev = 1000 ;
#if 0
// @Note (toml 04-19-04): These are saved, used to be slammed here
m_bitsDamageType = 0 ;
m_bitsHUDDamage = - 1 ;
SetPlayerUnderwter ( false ) ;
m_iTrain = TRAIN_NEW ;
# endif
m_iClientBattery = - 1 ;
m_iUpdateTime = 5 ; // won't update for 1/2 a second
if ( gInitHUD )
m_fInitHUD = true ;
}
//-----------------------------------------------------------------------------
// Purpose: Force this player to immediately respawn
//-----------------------------------------------------------------------------
void CBasePlayer : : ForceRespawn ( void )
{
RemoveAllItems ( true ) ;
// Reset ground state for airwalk animations
SetGroundEntity ( NULL ) ;
// Stop any firing that was taking place before respawn.
m_nButtons = 0 ;
Spawn ( ) ;
}
int CBasePlayer : : Save ( ISave & save )
{
if ( ! BaseClass : : Save ( save ) )
return 0 ;
return 1 ;
}
// Friend class of CBaseEntity to access private member data.
class CPlayerRestoreHelper
{
public :
const Vector & GetAbsOrigin ( CBaseEntity * pent )
{
return pent - > m_vecAbsOrigin ;
}
const Vector & GetAbsVelocity ( CBaseEntity * pent )
{
return pent - > m_vecAbsVelocity ;
}
} ;
int CBasePlayer : : Restore ( IRestore & restore )
{
int status = BaseClass : : Restore ( restore ) ;
if ( ! status )
return 0 ;
CSaveRestoreData * pSaveData = gpGlobals - > pSaveData ;
// landmark isn't present.
if ( ! pSaveData - > levelInfo . fUseLandmark )
{
Msg ( " No Landmark:%s \n " , pSaveData - > levelInfo . szLandmarkName ) ;
// default to normal spawn
CBaseEntity * pSpawnSpot = EntSelectSpawnPoint ( ) ;
SetLocalOrigin ( pSpawnSpot - > GetLocalOrigin ( ) + Vector ( 0 , 0 , 1 ) ) ;
SetLocalAngles ( pSpawnSpot - > GetLocalAngles ( ) ) ;
}
QAngle newViewAngles = pl . v_angle ;
newViewAngles . z = 0 ; // Clear out roll
SetLocalAngles ( newViewAngles ) ;
SnapEyeAngles ( newViewAngles ) ;
// Copied from spawn() for now
SetBloodColor ( BLOOD_COLOR_RED ) ;
// clear this - it will get reset by touching the trigger again
m_afPhysicsFlags & = ~ PFLAG_VPHYSICS_MOTIONCONTROLLER ;
if ( GetFlags ( ) & FL_DUCKING )
{
// Use the crouch HACK
FixPlayerCrouchStuck ( this ) ;
UTIL_SetSize ( this , VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ) ;
m_Local . m_bDucked = true ;
}
else
{
m_Local . m_bDucked = false ;
UTIL_SetSize ( this , VEC_HULL_MIN , VEC_HULL_MAX ) ;
}
// We need to get at m_vecAbsOrigin as it was restored but can't let it be
// recalculated by a call to GetAbsOrigin because hierarchy isn't fully restored yet,
// so we use this backdoor to get at the private data in CBaseEntity.
CPlayerRestoreHelper helper ;
InitVCollision ( helper . GetAbsOrigin ( this ) , helper . GetAbsVelocity ( this ) ) ;
// success
return 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : OnRestore ( void )
{
BaseClass : : OnRestore ( ) ;
SetViewEntity ( m_hViewEntity ) ;
SetDefaultFOV ( m_iDefaultFOV ) ; // force this to reset if zero
// Calculate this immediately
m_nVehicleViewSavedFrame = 0 ;
m_nBodyPitchPoseParam = LookupPoseParameter ( " body_pitch " ) ;
}
/* void CBasePlayer::SetTeamName( const char *pTeamName )
{
Q_strncpy ( m_szTeamName , pTeamName , TEAM_NAME_LENGTH ) ;
} */
void CBasePlayer : : SetArmorValue ( int value )
{
m_ArmorValue = value ;
}
void CBasePlayer : : IncrementArmorValue ( int nCount , int nMaxValue )
{
m_ArmorValue + = nCount ;
if ( nMaxValue > 0 )
{
if ( m_ArmorValue > nMaxValue )
m_ArmorValue = nMaxValue ;
}
}
// used by the physics gun and game physics... is there a better interface?
void CBasePlayer : : SetPhysicsFlag ( int nFlag , bool bSet )
{
if ( bSet )
m_afPhysicsFlags | = nFlag ;
else
m_afPhysicsFlags & = ~ nFlag ;
}
void CBasePlayer : : NotifyNearbyRadiationSource ( float flRange )
{
// if player's current geiger counter range is larger
// than range to this trigger hurt, reset player's
// geiger counter range
if ( m_flgeigerRange > = flRange )
m_flgeigerRange = flRange ;
}
void CBasePlayer : : AllowImmediateDecalPainting ( )
{
m_flNextDecalTime = gpGlobals - > curtime ;
}
// Suicide...
void CBasePlayer : : CommitSuicide ( bool bExplode /*= false*/ , bool bForce /*= false*/ )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
if ( ! IsAlive ( ) )
return ;
// prevent suiciding too often
if ( m_fNextSuicideTime > gpGlobals - > curtime & & ! bForce )
return ;
// don't let them suicide for 5 seconds after suiciding
m_fNextSuicideTime = gpGlobals - > curtime + 5 ;
int fDamage = DMG_PREVENT_PHYSICS_FORCE | ( bExplode ? ( DMG_BLAST | DMG_ALWAYSGIB ) : DMG_NEVERGIB ) ;
// have the player kill themself
m_iHealth = 0 ;
CTakeDamageInfo info ( this , this , 0 , fDamage , m_iSuicideCustomKillFlags ) ;
Event_Killed ( info ) ;
Event_Dying ( info ) ;
m_iSuicideCustomKillFlags = 0 ;
}
// Suicide with style...
void CBasePlayer : : CommitSuicide ( const Vector & vecForce , bool bExplode /*= false*/ , bool bForce /*= false*/ )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
// Already dead.
if ( ! IsAlive ( ) )
return ;
// Prevent suicides for a time.
if ( m_fNextSuicideTime > gpGlobals - > curtime & & ! bForce )
return ;
m_fNextSuicideTime = gpGlobals - > curtime + 5 ;
// Apply the force.
int nHealth = GetHealth ( ) ;
// Kill the player.
CTakeDamageInfo info ;
info . SetDamage ( nHealth + 10 ) ;
info . SetAttacker ( this ) ;
info . SetDamageType ( bExplode ? DMG_ALWAYSGIB : DMG_GENERIC ) ;
info . SetDamageForce ( vecForce ) ;
info . SetDamagePosition ( WorldSpaceCenter ( ) ) ;
TakeDamage ( info ) ;
}
//==============================================
// HasWeapons - do I have any weapons at all?
//==============================================
bool CBasePlayer : : HasWeapons ( void )
{
int i ;
for ( i = 0 ; i < WeaponCount ( ) ; i + + )
{
if ( GetWeapon ( i ) )
{
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vecForce -
//-----------------------------------------------------------------------------
void CBasePlayer : : VelocityPunch ( const Vector & vecForce )
{
// Clear onground and add velocity.
SetGroundEntity ( NULL ) ;
ApplyAbsVelocityImpulse ( vecForce ) ;
}
//--------------------------------------------------------------------------------------------------------------
// VEHICLES
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Purpose: Whether or not the player is currently able to enter the vehicle
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBasePlayer : : CanEnterVehicle ( IServerVehicle * pVehicle , int nRole )
{
// Must not have a passenger there already
if ( pVehicle - > GetPassenger ( nRole ) )
return false ;
// Must be able to holster our current weapon (ie. grav gun!)
if ( pVehicle - > IsPassengerUsingStandardWeapons ( nRole ) = = false )
{
//Must be able to stow our weapon
CBaseCombatWeapon * pWeapon = GetActiveWeapon ( ) ;
if ( ( pWeapon ! = NULL ) & & ( pWeapon - > CanHolster ( ) = = false ) )
return false ;
}
// Must be alive
if ( IsAlive ( ) = = false )
return false ;
// Can't be pulled by a barnacle
if ( IsEFlagSet ( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Put this player in a vehicle
//-----------------------------------------------------------------------------
bool CBasePlayer : : GetInVehicle ( IServerVehicle * pVehicle , int nRole )
{
Assert ( NULL = = m_hVehicle . Get ( ) ) ;
Assert ( nRole > = 0 ) ;
// Make sure we can enter the vehicle
if ( CanEnterVehicle ( pVehicle , nRole ) = = false )
return false ;
CBaseEntity * pEnt = pVehicle - > GetVehicleEnt ( ) ;
Assert ( pEnt ) ;
// Try to stow weapons
if ( pVehicle - > IsPassengerUsingStandardWeapons ( nRole ) = = false )
{
CBaseCombatWeapon * pWeapon = GetActiveWeapon ( ) ;
if ( pWeapon ! = NULL )
{
pWeapon - > Holster ( NULL ) ;
}
# ifndef HL2_DLL
m_Local . m_iHideHUD | = HIDEHUD_WEAPONSELECTION ;
# endif
m_Local . m_iHideHUD | = HIDEHUD_INVEHICLE ;
}
if ( ! pVehicle - > IsPassengerVisible ( nRole ) )
{
AddEffects ( EF_NODRAW ) ;
}
// Put us in the vehicle
pVehicle - > SetPassenger ( nRole , this ) ;
ViewPunchReset ( ) ;
// Setting the velocity to 0 will cause the IDLE animation to play
SetAbsVelocity ( vec3_origin ) ;
SetMoveType ( MOVETYPE_NOCLIP ) ;
// This is a hack to fixup the player's stats since they really didn't "cheat" and enter noclip from the console
gamestats - > Event_DecrementPlayerEnteredNoClip ( this ) ;
// Get the seat position we'll be at in this vehicle
Vector vSeatOrigin ;
QAngle qSeatAngles ;
pVehicle - > GetPassengerSeatPoint ( nRole , & vSeatOrigin , & qSeatAngles ) ;
// Set us to that position
SetAbsOrigin ( vSeatOrigin ) ;
SetAbsAngles ( qSeatAngles ) ;
// Parent to the vehicle
SetParent ( pEnt ) ;
SetCollisionGroup ( COLLISION_GROUP_IN_VEHICLE ) ;
// We cannot be ducking -- do all this before SetPassenger because it
// saves our view offset for restoration when we exit the vehicle.
RemoveFlag ( FL_DUCKING ) ;
SetViewOffset ( VEC_VIEW_SCALED ( this ) ) ;
m_Local . m_bDucked = false ;
m_Local . m_bDucking = false ;
m_Local . m_flDucktime = 0.0f ;
m_Local . m_flDuckJumpTime = 0.0f ;
m_Local . m_flJumpTime = 0.0f ;
// Turn our toggled duck off
if ( GetToggledDuckState ( ) )
{
ToggleDuck ( ) ;
}
m_hVehicle = pEnt ;
// Throw an event indicating that the player entered the vehicle.
g_pNotify - > ReportNamedEvent ( this , " PlayerEnteredVehicle " ) ;
m_iVehicleAnalogBias = VEHICLE_ANALOG_BIAS_NONE ;
OnVehicleStart ( ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Remove this player from a vehicle
//-----------------------------------------------------------------------------
void CBasePlayer : : LeaveVehicle ( const Vector & vecExitPoint , const QAngle & vecExitAngles )
{
if ( NULL = = m_hVehicle . Get ( ) )
return ;
IServerVehicle * pVehicle = GetVehicle ( ) ;
Assert ( pVehicle ) ;
int nRole = pVehicle - > GetPassengerRole ( this ) ;
Assert ( nRole > = 0 ) ;
SetParent ( NULL ) ;
// Find the first non-blocked exit point:
Vector vNewPos = GetAbsOrigin ( ) ;
QAngle qAngles = GetAbsAngles ( ) ;
if ( vecExitPoint = = vec3_origin )
{
// FIXME: this might fail to find a safe exit point!!
pVehicle - > GetPassengerExitPoint ( nRole , & vNewPos , & qAngles ) ;
}
else
{
vNewPos = vecExitPoint ;
qAngles = vecExitAngles ;
}
OnVehicleEnd ( vNewPos ) ;
SetAbsOrigin ( vNewPos ) ;
SetAbsAngles ( qAngles ) ;
// Clear out any leftover velocity
SetAbsVelocity ( vec3_origin ) ;
qAngles [ ROLL ] = 0 ;
SnapEyeAngles ( qAngles ) ;
# ifndef HL2_DLL
m_Local . m_iHideHUD & = ~ HIDEHUD_WEAPONSELECTION ;
# endif
m_Local . m_iHideHUD & = ~ HIDEHUD_INVEHICLE ;
RemoveEffects ( EF_NODRAW ) ;
SetMoveType ( MOVETYPE_WALK ) ;
SetCollisionGroup ( COLLISION_GROUP_PLAYER ) ;
if ( VPhysicsGetObject ( ) )
{
VPhysicsGetObject ( ) - > SetPosition ( vNewPos , vec3_angle , true ) ;
}
m_hVehicle = NULL ;
pVehicle - > SetPassenger ( nRole , NULL ) ;
// Re-deploy our weapon
if ( IsAlive ( ) )
{
if ( GetActiveWeapon ( ) & & GetActiveWeapon ( ) - > IsWeaponVisible ( ) = = false )
{
GetActiveWeapon ( ) - > Deploy ( ) ;
ShowCrosshair ( true ) ;
}
}
// Just cut all of the rumble effects.
RumbleEffect ( RUMBLE_STOP_ALL , 0 , RUMBLE_FLAGS_NONE ) ;
}
//==============================================
// !!!UNDONE:ultra temporary SprayCan entity to apply
// decal frame at a time. For PreAlpha CD
//==============================================
class CSprayCan : public CPointEntity
{
public :
DECLARE_CLASS ( CSprayCan , CPointEntity ) ;
void Spawn ( CBasePlayer * pOwner ) ;
void Think ( void ) ;
virtual void Precache ( ) ;
virtual int ObjectCaps ( void ) { return FCAP_DONT_SAVE ; }
} ;
LINK_ENTITY_TO_CLASS ( spraycan , CSprayCan ) ;
PRECACHE_REGISTER ( spraycan ) ;
void CSprayCan : : Spawn ( CBasePlayer * pOwner )
{
SetLocalOrigin ( pOwner - > WorldSpaceCenter ( ) + Vector ( 0 , 0 , 32 ) ) ;
SetLocalAngles ( pOwner - > EyeAngles ( ) ) ;
SetOwnerEntity ( pOwner ) ;
SetNextThink ( gpGlobals - > curtime ) ;
EmitSound ( " SprayCan.Paint " ) ;
}
void CSprayCan : : Precache ( )
{
BaseClass : : Precache ( ) ;
PrecacheScriptSound ( " SprayCan.Paint " ) ;
}
void CSprayCan : : Think ( void )
{
CBasePlayer * pPlayer = ToBasePlayer ( GetOwnerEntity ( ) ) ;
if ( pPlayer )
{
int playernum = pPlayer - > entindex ( ) ;
Vector forward ;
trace_t tr ;
AngleVectors ( GetAbsAngles ( ) , & forward ) ;
UTIL_TraceLine ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + forward * 128 ,
MASK_SOLID_BRUSHONLY , pPlayer , COLLISION_GROUP_NONE , & tr ) ;
UTIL_PlayerDecalTrace ( & tr , playernum ) ;
}
// Just painted last custom frame.
UTIL_Remove ( this ) ;
}
class CBloodSplat : public CPointEntity
{
public :
DECLARE_CLASS ( CBloodSplat , CPointEntity ) ;
void Spawn ( CBaseEntity * pOwner ) ;
void Think ( void ) ;
} ;
void CBloodSplat : : Spawn ( CBaseEntity * pOwner )
{
SetLocalOrigin ( pOwner - > WorldSpaceCenter ( ) + Vector ( 0 , 0 , 32 ) ) ;
SetLocalAngles ( pOwner - > GetLocalAngles ( ) ) ;
SetOwnerEntity ( pOwner ) ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
}
void CBloodSplat : : Think ( void )
{
trace_t tr ;
if ( g_Language . GetInt ( ) ! = LANGUAGE_GERMAN )
{
CBasePlayer * pPlayer ;
pPlayer = ToBasePlayer ( GetOwnerEntity ( ) ) ;
Vector forward ;
AngleVectors ( GetAbsAngles ( ) , & forward ) ;
UTIL_TraceLine ( GetAbsOrigin ( ) , GetAbsOrigin ( ) + forward * 128 ,
MASK_SOLID_BRUSHONLY , pPlayer , COLLISION_GROUP_NONE , & tr ) ;
UTIL_BloodDecalTrace ( & tr , BLOOD_COLOR_RED ) ;
}
UTIL_Remove ( this ) ;
}
//==============================================
//-----------------------------------------------------------------------------
// Purpose: Create and give the named item to the player. Then return it.
//-----------------------------------------------------------------------------
CBaseEntity * CBasePlayer : : GiveNamedItem ( const char * pszName , int iSubType )
{
// If I already own this type don't create one
if ( Weapon_OwnsThisType ( pszName , iSubType ) )
return NULL ;
// Msg( "giving %s\n", pszName );
EHANDLE pent ;
pent = CreateEntityByName ( pszName ) ;
if ( pent = = NULL )
{
Msg ( " NULL Ent in GiveNamedItem! \n " ) ;
return NULL ;
}
pent - > SetLocalOrigin ( GetLocalOrigin ( ) ) ;
pent - > AddSpawnFlags ( SF_NORESPAWN ) ;
CBaseCombatWeapon * pWeapon = dynamic_cast < CBaseCombatWeapon * > ( ( CBaseEntity * ) pent ) ;
if ( pWeapon )
{
pWeapon - > SetSubType ( iSubType ) ;
}
DispatchSpawn ( pent ) ;
if ( pent ! = NULL & & ! ( pent - > IsMarkedForDeletion ( ) ) )
{
pent - > Touch ( this ) ;
}
return pent ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the nearest COLLIBALE entity in front of the player
// that has a clear line of sight with the given classname
// Input :
// Output :
//-----------------------------------------------------------------------------
CBaseEntity * FindEntityClassForward ( CBasePlayer * pMe , char * classname )
{
trace_t tr ;
Vector forward ;
pMe - > EyeVectors ( & forward ) ;
UTIL_TraceLine ( pMe - > EyePosition ( ) ,
pMe - > EyePosition ( ) + forward * MAX_COORD_RANGE ,
MASK_SOLID , pMe , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . fraction ! = 1.0 & & tr . DidHitNonWorldEntity ( ) )
{
CBaseEntity * pHit = tr . m_pEnt ;
if ( FClassnameIs ( pHit , classname ) )
{
return pHit ;
}
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the nearest COLLIBALE entity in front of the player
// that has a clear line of sight. If HULL is true, the trace will
// hit the collision hull of entities. Otherwise, the trace will hit
// hitboxes.
// Input :
// Output :
//-----------------------------------------------------------------------------
CBaseEntity * FindEntityForward ( CBasePlayer * pMe , bool fHull )
{
if ( pMe )
{
trace_t tr ;
Vector forward ;
int mask ;
if ( fHull )
{
mask = MASK_SOLID ;
}
else
{
mask = MASK_SHOT ;
}
pMe - > EyeVectors ( & forward ) ;
UTIL_TraceLine ( pMe - > EyePosition ( ) ,
pMe - > EyePosition ( ) + forward * MAX_COORD_RANGE ,
mask , pMe , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . fraction ! = 1.0 & & tr . DidHitNonWorldEntity ( ) )
{
return tr . m_pEnt ;
}
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Finds the nearest entity in front of the player of the given
// classname, preferring collidable entities, but allows selection of
// enities that are on the other side of walls or objects
//
// Input :
// Output :
//-----------------------------------------------------------------------------
CBaseEntity * FindPickerEntityClass ( CBasePlayer * pPlayer , char * classname )
{
// First try to trace a hull to an entity
CBaseEntity * pEntity = FindEntityClassForward ( pPlayer , classname ) ;
// If that fails just look for the nearest facing entity
if ( ! pEntity )
{
Vector forward ;
Vector origin ;
pPlayer - > EyeVectors ( & forward ) ;
origin = pPlayer - > WorldSpaceCenter ( ) ;
pEntity = gEntList . FindEntityClassNearestFacing ( origin , forward , 0.95 , classname ) ;
}
return pEntity ;
}
//-----------------------------------------------------------------------------
// Purpose: Finds the nearest entity in front of the player, preferring
// collidable entities, but allows selection of enities that are
// on the other side of walls or objects
// Input :
// Output :
//-----------------------------------------------------------------------------
CBaseEntity * FindPickerEntity ( CBasePlayer * pPlayer )
{
MDLCACHE_CRITICAL_SECTION ( ) ;
// First try to trace a hull to an entity
CBaseEntity * pEntity = FindEntityForward ( pPlayer , true ) ;
// If that fails just look for the nearest facing entity
if ( ! pEntity )
{
Vector forward ;
Vector origin ;
pPlayer - > EyeVectors ( & forward ) ;
origin = pPlayer - > WorldSpaceCenter ( ) ;
pEntity = gEntList . FindEntityNearestFacing ( origin , forward , 0.95 ) ;
}
return pEntity ;
}
//-----------------------------------------------------------------------------
// Purpose: Finds the nearest node in front of the player
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_Node * FindPickerAINode ( CBasePlayer * pPlayer , NodeType_e nNodeType )
{
Vector forward ;
Vector origin ;
pPlayer - > EyeVectors ( & forward ) ;
origin = pPlayer - > EyePosition ( ) ;
return g_pAINetworkManager - > GetEditOps ( ) - > FindAINodeNearestFacing ( origin , forward , 0.90 , nNodeType ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Finds the nearest link in front of the player
// Input :
// Output :
//-----------------------------------------------------------------------------
CAI_Link * FindPickerAILink ( CBasePlayer * pPlayer )
{
Vector forward ;
Vector origin ;
pPlayer - > EyeVectors ( & forward ) ;
origin = pPlayer - > EyePosition ( ) ;
return g_pAINetworkManager - > GetEditOps ( ) - > FindAILinkNearestFacing ( origin , forward , 0.90 ) ;
}
/*
= = = = = = = = = = = = = = =
ForceClientDllUpdate
When recording a demo , we need to have the server tell us the entire client state
so that the client side . dll can behave correctly .
Reset stuff so that the state is transmitted .
= = = = = = = = = = = = = = =
*/
void CBasePlayer : : ForceClientDllUpdate ( void )
{
m_iClientBattery = - 1 ;
m_iTrain | = TRAIN_NEW ; // Force new train message.
m_fWeapon = false ; // Force weapon send
// Force all HUD data to be resent to client
m_fInitHUD = true ;
// Now force all the necessary messages
// to be sent.
UpdateClientData ( ) ;
UTIL_RestartAmbientSounds ( ) ; // MOTODO that updates the sounds for everybody
}
/*
= = = = = = = = = = = =
ImpulseCommands
= = = = = = = = = = = =
*/
void CBasePlayer : : ImpulseCommands ( )
{
trace_t tr ;
int iImpulse = ( int ) m_nImpulse ;
switch ( iImpulse )
{
case 100 :
// temporary flashlight for level designers
if ( FlashlightIsOn ( ) )
{
FlashlightTurnOff ( ) ;
}
else
{
FlashlightTurnOn ( ) ;
}
break ;
case 200 :
if ( sv_cheats - > GetBool ( ) )
{
CBaseCombatWeapon * pWeapon ;
pWeapon = GetActiveWeapon ( ) ;
if ( pWeapon - > IsEffectActive ( EF_NODRAW ) )
{
pWeapon - > Deploy ( ) ;
}
else
{
pWeapon - > Holster ( ) ;
}
}
break ;
case 201 : // paint decal
if ( gpGlobals - > curtime < m_flNextDecalTime )
{
// too early!
break ;
}
{
Vector forward ;
EyeVectors ( & forward ) ;
UTIL_TraceLine ( EyePosition ( ) ,
EyePosition ( ) + forward * 128 ,
MASK_SOLID_BRUSHONLY , this , COLLISION_GROUP_NONE , & tr ) ;
}
if ( tr . fraction ! = 1.0 )
{ // line hit something, so paint a decal
m_flNextDecalTime = gpGlobals - > curtime + decalfrequency . GetFloat ( ) ;
CSprayCan * pCan = CREATE_UNSAVED_ENTITY ( CSprayCan , " spraycan " ) ;
pCan - > Spawn ( this ) ;
# ifdef CSTRIKE_DLL
//=============================================================================
// HPE_BEGIN:
// [pfreese] Fire off a game event - the Counter-Strike stats manager listens
// to these achievements for one of the CS achievements.
//=============================================================================
IGameEvent * event = gameeventmanager - > CreateEvent ( " player_decal " ) ;
if ( event )
{
event - > SetInt ( " userid " , GetUserID ( ) ) ;
gameeventmanager - > FireEvent ( event ) ;
}
//=============================================================================
// HPE_END
//=============================================================================
# endif
}
break ;
case 202 : // player jungle sound
if ( gpGlobals - > curtime < m_flNextDecalTime )
{
// too early!
break ;
}
EntityMessageBegin ( this ) ;
WRITE_BYTE ( PLAY_PLAYER_JINGLE ) ;
MessageEnd ( ) ;
m_flNextDecalTime = gpGlobals - > curtime + decalfrequency . GetFloat ( ) ;
break ;
default :
// check all of the cheat impulse commands now
CheatImpulseCommands ( iImpulse ) ;
break ;
}
m_nImpulse = 0 ;
}
# ifdef HL2_EPISODIC
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static void CreateJalopy ( CBasePlayer * pPlayer )
{
// Cheat to create a jeep in front of the player
Vector vecForward ;
AngleVectors ( pPlayer - > EyeAngles ( ) , & vecForward ) ;
CBaseEntity * pJeep = ( CBaseEntity * ) CreateEntityByName ( " prop_vehicle_jeep " ) ;
if ( pJeep )
{
Vector vecOrigin = pPlayer - > GetAbsOrigin ( ) + vecForward * 256 + Vector ( 0 , 0 , 64 ) ;
QAngle vecAngles ( 0 , pPlayer - > GetAbsAngles ( ) . y - 90 , 0 ) ;
pJeep - > SetAbsOrigin ( vecOrigin ) ;
pJeep - > SetAbsAngles ( vecAngles ) ;
pJeep - > KeyValue ( " model " , " models/vehicle.mdl " ) ;
pJeep - > KeyValue ( " solid " , " 6 " ) ;
pJeep - > KeyValue ( " targetname " , " jeep " ) ;
pJeep - > KeyValue ( " vehiclescript " , " scripts/vehicles/jalopy.txt " ) ;
DispatchSpawn ( pJeep ) ;
pJeep - > Activate ( ) ;
pJeep - > Teleport ( & vecOrigin , & vecAngles , NULL ) ;
}
}
void CC_CH_CreateJalopy ( void )
{
CBasePlayer * pPlayer = UTIL_GetCommandClient ( ) ;
if ( ! pPlayer )
return ;
CreateJalopy ( pPlayer ) ;
}
static ConCommand ch_createjalopy ( " ch_createjalopy " , CC_CH_CreateJalopy , " Spawn jalopy in front of the player. " , FCVAR_CHEAT ) ;
# endif // HL2_EPISODIC
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
static void CreateJeep ( CBasePlayer * pPlayer )
{
// Cheat to create a jeep in front of the player
Vector vecForward ;
AngleVectors ( pPlayer - > EyeAngles ( ) , & vecForward ) ;
CBaseEntity * pJeep = ( CBaseEntity * ) CreateEntityByName ( " prop_vehicle_jeep " ) ;
if ( pJeep )
{
Vector vecOrigin = pPlayer - > GetAbsOrigin ( ) + vecForward * 256 + Vector ( 0 , 0 , 64 ) ;
QAngle vecAngles ( 0 , pPlayer - > GetAbsAngles ( ) . y - 90 , 0 ) ;
pJeep - > SetAbsOrigin ( vecOrigin ) ;
pJeep - > SetAbsAngles ( vecAngles ) ;
pJeep - > KeyValue ( " model " , " models/buggy.mdl " ) ;
pJeep - > KeyValue ( " solid " , " 6 " ) ;
pJeep - > KeyValue ( " targetname " , " jeep " ) ;
pJeep - > KeyValue ( " vehiclescript " , " scripts/vehicles/jeep_test.txt " ) ;
DispatchSpawn ( pJeep ) ;
pJeep - > Activate ( ) ;
pJeep - > Teleport ( & vecOrigin , & vecAngles , NULL ) ;
}
}
void CC_CH_CreateJeep ( void )
{
CBasePlayer * pPlayer = UTIL_GetCommandClient ( ) ;
if ( ! pPlayer )
return ;
CreateJeep ( pPlayer ) ;
}
static ConCommand ch_createjeep ( " ch_createjeep " , CC_CH_CreateJeep , " Spawn jeep in front of the player. " , FCVAR_CHEAT ) ;
//-----------------------------------------------------------------------------
// Create an airboat in front of the specified player
//-----------------------------------------------------------------------------
static void CreateAirboat ( CBasePlayer * pPlayer )
{
// Cheat to create a jeep in front of the player
Vector vecForward ;
AngleVectors ( pPlayer - > EyeAngles ( ) , & vecForward ) ;
CBaseEntity * pJeep = ( CBaseEntity * ) CreateEntityByName ( " prop_vehicle_airboat " ) ;
if ( pJeep )
{
Vector vecOrigin = pPlayer - > GetAbsOrigin ( ) + vecForward * 256 + Vector ( 0 , 0 , 64 ) ;
QAngle vecAngles ( 0 , pPlayer - > GetAbsAngles ( ) . y - 90 , 0 ) ;
pJeep - > SetAbsOrigin ( vecOrigin ) ;
pJeep - > SetAbsAngles ( vecAngles ) ;
pJeep - > KeyValue ( " model " , " models/airboat.mdl " ) ;
pJeep - > KeyValue ( " solid " , " 6 " ) ;
pJeep - > KeyValue ( " targetname " , " airboat " ) ;
pJeep - > KeyValue ( " vehiclescript " , " scripts/vehicles/airboat.txt " ) ;
DispatchSpawn ( pJeep ) ;
pJeep - > Activate ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CC_CH_CreateAirboat ( void )
{
CBasePlayer * pPlayer = UTIL_GetCommandClient ( ) ;
if ( ! pPlayer )
return ;
CreateAirboat ( pPlayer ) ;
}
static ConCommand ch_createairboat ( " ch_createairboat " , CC_CH_CreateAirboat , " Spawn airboat in front of the player. " , FCVAR_CHEAT ) ;
//=========================================================
//=========================================================
void CBasePlayer : : CheatImpulseCommands ( int iImpulse )
{
# if !defined( HLDEMO_BUILD )
if ( ! sv_cheats - > GetBool ( ) )
{
return ;
}
CBaseEntity * pEntity ;
trace_t tr ;
switch ( iImpulse )
{
case 76 :
{
if ( ! giPrecacheGrunt )
{
giPrecacheGrunt = 1 ;
Msg ( " You must now restart to use Grunt-o-matic. \n " ) ;
}
else
{
Vector forward = UTIL_YawToVector ( EyeAngles ( ) . y ) ;
Create ( " NPC_human_grunt " , GetLocalOrigin ( ) + forward * 128 , GetLocalAngles ( ) ) ;
}
break ;
}
case 81 :
GiveNamedItem ( " weapon_cubemap " ) ;
break ;
case 82 :
// Cheat to create a jeep in front of the player
CreateJeep ( this ) ;
break ;
case 83 :
// Cheat to create a airboat in front of the player
CreateAirboat ( this ) ;
break ;
case 101 :
gEvilImpulse101 = true ;
EquipSuit ( ) ;
// Give the player everything!
GiveAmmo ( 255 , " Pistol " ) ;
GiveAmmo ( 255 , " AR2 " ) ;
GiveAmmo ( 5 , " AR2AltFire " ) ;
GiveAmmo ( 255 , " SMG1 " ) ;
GiveAmmo ( 255 , " Buckshot " ) ;
GiveAmmo ( 3 , " smg1_grenade " ) ;
GiveAmmo ( 3 , " rpg_round " ) ;
GiveAmmo ( 5 , " grenade " ) ;
GiveAmmo ( 32 , " 357 " ) ;
GiveAmmo ( 16 , " XBowBolt " ) ;
# ifdef HL2_EPISODIC
GiveAmmo ( 5 , " Hopwire " ) ;
# endif
GiveNamedItem ( " weapon_smg1 " ) ;
GiveNamedItem ( " weapon_frag " ) ;
GiveNamedItem ( " weapon_crowbar " ) ;
GiveNamedItem ( " weapon_pistol " ) ;
GiveNamedItem ( " weapon_ar2 " ) ;
GiveNamedItem ( " weapon_shotgun " ) ;
GiveNamedItem ( " weapon_physcannon " ) ;
GiveNamedItem ( " weapon_bugbait " ) ;
GiveNamedItem ( " weapon_rpg " ) ;
GiveNamedItem ( " weapon_357 " ) ;
GiveNamedItem ( " weapon_crossbow " ) ;
# ifdef HL2_EPISODIC
// GiveNamedItem( "weapon_magnade" );
# endif
if ( GetHealth ( ) < 100 )
{
TakeHealth ( 25 , DMG_GENERIC ) ;
}
gEvilImpulse101 = false ;
break ;
case 102 :
// Gibbage!!!
CGib : : SpawnRandomGibs ( this , 1 , GIB_HUMAN ) ;
break ;
case 103 :
// What the hell are you doing?
pEntity = FindEntityForward ( this , true ) ;
if ( pEntity )
{
CAI_BaseNPC * pNPC = pEntity - > MyNPCPointer ( ) ;
if ( pNPC )
pNPC - > ReportAIState ( ) ;
}
break ;
case 106 :
// Give me the classname and targetname of this entity.
pEntity = FindEntityForward ( this , true ) ;
if ( pEntity )
{
Msg ( " Classname: %s " , pEntity - > GetClassname ( ) ) ;
if ( pEntity - > GetEntityName ( ) ! = NULL_STRING )
{
Msg ( " - Name: %s \n " , STRING ( pEntity - > GetEntityName ( ) ) ) ;
}
else
{
Msg ( " - Name: No Targetname \n " ) ;
}
if ( pEntity - > m_iParent ! = NULL_STRING )
Msg ( " Parent: %s \n " , STRING ( pEntity - > m_iParent ) ) ;
Msg ( " Model: %s \n " , STRING ( pEntity - > GetModelName ( ) ) ) ;
if ( pEntity - > m_iGlobalname ! = NULL_STRING )
Msg ( " Globalname: %s \n " , STRING ( pEntity - > m_iGlobalname ) ) ;
}
break ;
case 107 :
{
trace_t tr ;
edict_t * pWorld = engine - > PEntityOfEntIndex ( 0 ) ;
Vector start = EyePosition ( ) ;
Vector forward ;
EyeVectors ( & forward ) ;
Vector end = start + forward * 1024 ;
UTIL_TraceLine ( start , end , MASK_SOLID_BRUSHONLY , this , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . m_pEnt )
pWorld = tr . m_pEnt - > edict ( ) ;
const char * pTextureName = tr . surface . name ;
if ( pTextureName )
Msg ( " Texture: %s \n " , pTextureName ) ;
}
break ;
//
// Sets the debug NPC to be the NPC under the crosshair.
//
case 108 :
{
pEntity = FindEntityForward ( this , true ) ;
if ( pEntity )
{
CAI_BaseNPC * pNPC = pEntity - > MyNPCPointer ( ) ;
if ( pNPC ! = NULL )
{
Msg ( " Debugging %s (0x%p) \n " , pNPC - > GetClassname ( ) , pNPC ) ;
CAI_BaseNPC : : SetDebugNPC ( pNPC ) ;
}
}
break ;
}
case 195 : // show shortest paths for entire level to nearest node
{
Create ( " node_viewer_fly " , GetLocalOrigin ( ) , GetLocalAngles ( ) ) ;
}
break ;
case 196 : // show shortest paths for entire level to nearest node
{
Create ( " node_viewer_large " , GetLocalOrigin ( ) , GetLocalAngles ( ) ) ;
}
break ;
case 197 : // show shortest paths for entire level to nearest node
{
Create ( " node_viewer_human " , GetLocalOrigin ( ) , GetLocalAngles ( ) ) ;
}
break ;
case 202 : // Random blood splatter
{
Vector forward ;
EyeVectors ( & forward ) ;
UTIL_TraceLine ( EyePosition ( ) ,
EyePosition ( ) + forward * 128 ,
MASK_SOLID_BRUSHONLY , this , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . fraction ! = 1.0 )
{ // line hit something, so paint a decal
CBloodSplat * pBlood = CREATE_UNSAVED_ENTITY ( CBloodSplat , " bloodsplat " ) ;
pBlood - > Spawn ( this ) ;
}
}
break ;
case 203 : // remove creature.
pEntity = FindEntityForward ( this , true ) ;
if ( pEntity )
{
UTIL_Remove ( pEntity ) ;
// if ( pEntity->m_takedamage )
// pEntity->SetThink(SUB_Remove);
}
break ;
}
# endif // HLDEMO_BUILD
}
bool CBasePlayer : : ClientCommand ( const CCommand & args )
{
const char * cmd = args [ 0 ] ;
# ifdef _DEBUG
if ( stricmp ( cmd , " test_SmokeGrenade " ) = = 0 )
{
if ( sv_cheats & & sv_cheats - > GetBool ( ) )
{
ParticleSmokeGrenade * pSmoke = dynamic_cast < ParticleSmokeGrenade * > ( CreateEntityByName ( PARTICLESMOKEGRENADE_ENTITYNAME ) ) ;
if ( pSmoke )
{
Vector vForward ;
AngleVectors ( GetLocalAngles ( ) , & vForward ) ;
vForward . z = 0 ;
VectorNormalize ( vForward ) ;
pSmoke - > SetLocalOrigin ( GetLocalOrigin ( ) + vForward * 100 ) ;
pSmoke - > SetFadeTime ( 25 , 30 ) ; // Fade out between 25 seconds and 30 seconds.
pSmoke - > Activate ( ) ;
pSmoke - > SetLifetime ( 30 ) ;
pSmoke - > FillVolume ( ) ;
return true ;
}
}
}
else
# endif // _DEBUG
if ( stricmp ( cmd , " vehicleRole " ) = = 0 )
{
// Get the vehicle role value.
if ( args . ArgC ( ) = = 2 )
{
// Check to see if a player is in a vehicle.
if ( IsInAVehicle ( ) )
{
int nRole = atoi ( args [ 1 ] ) ;
IServerVehicle * pVehicle = GetVehicle ( ) ;
if ( pVehicle )
{
// Only switch roles if role is empty!
if ( ! pVehicle - > GetPassenger ( nRole ) )
{
LeaveVehicle ( ) ;
GetInVehicle ( pVehicle , nRole ) ;
}
}
}
return true ;
}
}
else if ( HandleVoteCommands ( args ) )
{
return true ;
}
else if ( stricmp ( cmd , " spectate " ) = = 0 ) // join spectator team & start observer mode
{
if ( GetTeamNumber ( ) = = TEAM_SPECTATOR )
return true ;
ConVarRef mp_allowspectators ( " mp_allowspectators " ) ;
if ( mp_allowspectators . IsValid ( ) )
{
if ( ( mp_allowspectators . GetBool ( ) = = false ) & & ! IsHLTV ( ) & & ! IsReplay ( ) )
{
ClientPrint ( this , HUD_PRINTCENTER , " #Cannot_Be_Spectator " ) ;
return true ;
}
}
if ( ! IsDead ( ) )
{
CommitSuicide ( ) ; // kill player
}
RemoveAllItems ( true ) ;
ChangeTeam ( TEAM_SPECTATOR ) ;
StartObserverMode ( OBS_MODE_ROAMING ) ;
return true ;
}
else if ( stricmp ( cmd , " spec_mode " ) = = 0 ) // new observer mode
{
int mode ;
if ( GetObserverMode ( ) = = OBS_MODE_FREEZECAM )
{
AttemptToExitFreezeCam ( ) ;
return true ;
}
// not allowed to change spectator modes when mp_fadetoblack is being used
if ( mp_fadetoblack . GetBool ( ) )
{
if ( GetTeamNumber ( ) > TEAM_SPECTATOR )
return true ;
}
// check for parameters.
if ( args . ArgC ( ) > = 2 )
{
mode = atoi ( args [ 1 ] ) ;
if ( mode < OBS_MODE_IN_EYE | | mode > LAST_PLAYER_OBSERVERMODE )
mode = OBS_MODE_IN_EYE ;
}
else
{
// switch to next spec mode if no parameter given
mode = GetObserverMode ( ) + 1 ;
if ( mode > LAST_PLAYER_OBSERVERMODE )
{
mode = OBS_MODE_IN_EYE ;
}
else if ( mode < OBS_MODE_IN_EYE )
{
mode = OBS_MODE_ROAMING ;
}
}
// don't allow input while player or death cam animation
if ( GetObserverMode ( ) > OBS_MODE_DEATHCAM )
{
// set new spectator mode, don't allow OBS_MODE_NONE
if ( ! SetObserverMode ( mode ) )
ClientPrint ( this , HUD_PRINTCONSOLE , " #Spectator_Mode_Unkown " ) ;
else
engine - > ClientCommand ( edict ( ) , " cl_spec_mode %d " , mode ) ;
}
else
{
// remember spectator mode for later use
m_iObserverLastMode = mode ;
engine - > ClientCommand ( edict ( ) , " cl_spec_mode %d " , mode ) ;
}
return true ;
}
else if ( stricmp ( cmd , " spec_next " ) = = 0 ) // chase next player
{
if ( GetObserverMode ( ) > OBS_MODE_FIXED )
{
// set new spectator mode
CBaseEntity * target = FindNextObserverTarget ( false ) ;
if ( target )
{
SetObserverTarget ( target ) ;
}
}
else if ( GetObserverMode ( ) = = OBS_MODE_FREEZECAM )
{
AttemptToExitFreezeCam ( ) ;
}
return true ;
}
else if ( stricmp ( cmd , " spec_prev " ) = = 0 ) // chase prevoius player
{
if ( GetObserverMode ( ) > OBS_MODE_FIXED )
{
// set new spectator mode
CBaseEntity * target = FindNextObserverTarget ( true ) ;
if ( target )
{
SetObserverTarget ( target ) ;
}
}
else if ( GetObserverMode ( ) = = OBS_MODE_FREEZECAM )
{
AttemptToExitFreezeCam ( ) ;
}
return true ;
}
else if ( stricmp ( cmd , " spec_player " ) = = 0 ) // chase next player
{
if ( GetObserverMode ( ) > OBS_MODE_FIXED & & args . ArgC ( ) = = 2 )
{
int index = atoi ( args [ 1 ] ) ;
CBasePlayer * target ;
if ( index = = 0 )
{
target = UTIL_PlayerByName ( args [ 1 ] ) ;
}
else
{
target = UTIL_PlayerByIndex ( index ) ;
}
if ( IsValidObserverTarget ( target ) )
{
SetObserverTarget ( target ) ;
}
}
return true ;
}
else if ( stricmp ( cmd , " spec_goto " ) = = 0 ) // chase next player
{
if ( ( GetObserverMode ( ) = = OBS_MODE_FIXED | |
GetObserverMode ( ) = = OBS_MODE_ROAMING ) & &
args . ArgC ( ) = = 6 )
{
Vector origin ;
origin . x = atof ( args [ 1 ] ) ;
origin . y = atof ( args [ 2 ] ) ;
origin . z = atof ( args [ 3 ] ) ;
QAngle angle ;
angle . x = atof ( args [ 4 ] ) ;
angle . y = atof ( args [ 5 ] ) ;
angle . z = 0.0f ;
JumptoPosition ( origin , angle ) ;
}
return true ;
}
else if ( stricmp ( cmd , " playerperf " ) = = 0 )
{
int nRecip = entindex ( ) ;
if ( args . ArgC ( ) > = 2 )
{
nRecip = clamp ( Q_atoi ( args . Arg ( 1 ) ) , 1 , gpGlobals - > maxClients ) ;
}
int nRecords = - 1 ; // all
if ( args . ArgC ( ) > = 3 )
{
nRecords = MAX ( Q_atoi ( args . Arg ( 2 ) ) , 1 ) ;
}
CBasePlayer * pl = UTIL_PlayerByIndex ( nRecip ) ;
if ( pl )
{
pl - > DumpPerfToRecipient ( this , nRecords ) ;
}
return true ;
}
return false ;
}
extern bool UTIL_ItemCanBeTouchedByPlayer ( CBaseEntity * pItem , CBasePlayer * pPlayer ) ;
//-----------------------------------------------------------------------------
// 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 CBasePlayer : : 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 ;
}
// Act differently in the episodes
if ( hl2_episodic . GetBool ( ) )
{
// Don't let the player touch the item unless unobstructed
if ( ! UTIL_ItemCanBeTouchedByPlayer ( pWeapon , this ) & & ! gEvilImpulse101 )
return false ;
}
else
{
// 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 ) = = false & & ! ( GetFlags ( ) & FL_NOTARGET ) )
return false ;
}
// ----------------------------------------
// If I already have it just take the ammo
// ----------------------------------------
if ( Weapon_OwnsThisType ( pWeapon - > GetClassname ( ) , pWeapon - > GetSubType ( ) ) )
{
if ( Weapon_EquipAmmoOnly ( pWeapon ) )
{
// Only remove me if I have no ammo left
if ( pWeapon - > HasPrimaryAmmo ( ) )
return false ;
UTIL_Remove ( pWeapon ) ;
return true ;
}
else
{
return false ;
}
}
// -------------------------
// Otherwise take the weapon
// -------------------------
else
{
pWeapon - > CheckRespawn ( ) ;
pWeapon - > AddSolidFlags ( FSOLID_NOT_SOLID ) ;
pWeapon - > AddEffects ( EF_NODRAW ) ;
Weapon_Equip ( pWeapon ) ;
if ( IsInAVehicle ( ) )
{
pWeapon - > Holster ( ) ;
}
else
{
# ifdef HL2_DLL
if ( IsX360 ( ) )
{
CFmtStr hint ;
hint . sprintf ( " #valve_hint_select_%s " , pWeapon - > GetClassname ( ) ) ;
UTIL_HudHintText ( this , hint . Access ( ) ) ;
}
// Always switch to a newly-picked up weapon
if ( ! PlayerHasMegaPhysCannon ( ) )
{
// If it uses clips, load it full. (this is the first time you've picked up this type of weapon)
if ( pWeapon - > UsesClipsForAmmo1 ( ) )
{
pWeapon - > m_iClip1 = pWeapon - > GetMaxClip1 ( ) ;
}
Weapon_Switch ( pWeapon ) ;
}
# endif
}
return true ;
}
}
bool CBasePlayer : : RemovePlayerItem ( CBaseCombatWeapon * pItem )
{
if ( GetActiveWeapon ( ) = = pItem )
{
ResetAutoaim ( ) ;
pItem - > Holster ( ) ;
pItem - > SetNextThink ( TICK_NEVER_THINK ) ; ; // crowbar may be trying to swing again, etc
pItem - > SetThink ( NULL ) ;
}
if ( m_hLastWeapon . Get ( ) = = pItem )
{
Weapon_SetLast ( NULL ) ;
}
return Weapon_Detach ( pItem ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Hides or shows the player's view model. The "r_drawviewmodel" cvar
// can still hide the viewmodel even if this is set to true.
// Input : bShow - true to show, false to hide the view model.
//-----------------------------------------------------------------------------
void CBasePlayer : : ShowViewModel ( bool bShow )
{
m_Local . m_bDrawViewmodel = bShow ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bDraw -
//-----------------------------------------------------------------------------
void CBasePlayer : : ShowCrosshair ( bool bShow )
{
if ( bShow )
{
m_Local . m_iHideHUD & = ~ HIDEHUD_CROSSHAIR ;
}
else
{
m_Local . m_iHideHUD | = HIDEHUD_CROSSHAIR ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
QAngle CBasePlayer : : BodyAngles ( )
{
return EyeAngles ( ) ;
}
//------------------------------------------------------------------------------
// Purpose : Add noise to BodyTarget() to give enemy a better chance of
// getting a clear shot when the player is peeking above a hole
// or behind a ladder (eventually the randomly-picked point
// along the spine will be one that is exposed above the hole or
// between rungs of a ladder.)
// Input :
// Output :
//------------------------------------------------------------------------------
Vector CBasePlayer : : BodyTarget ( const Vector & posSrc , bool bNoisy )
{
if ( IsInAVehicle ( ) )
{
return GetVehicle ( ) - > GetVehicleEnt ( ) - > BodyTarget ( posSrc , bNoisy ) ;
}
if ( bNoisy )
{
return GetAbsOrigin ( ) + ( GetViewOffset ( ) * random - > RandomFloat ( 0.7 , 1.0 ) ) ;
}
else
{
return EyePosition ( ) ;
}
} ;
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
UpdateClientData
resends any changed player HUD info to the client .
Called every frame by PlayerPreThink
Also called at start of demo recording and playback by
ForceClientDllUpdate to ensure the demo gets messages
reflecting all of the HUD state info .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
void CBasePlayer : : UpdateClientData ( void )
{
CSingleUserRecipientFilter user ( this ) ;
user . MakeReliable ( ) ;
if ( m_fInitHUD )
{
m_fInitHUD = false ;
gInitHUD = false ;
UserMessageBegin ( user , " ResetHUD " ) ;
WRITE_BYTE ( 0 ) ;
MessageEnd ( ) ;
if ( ! m_fGameHUDInitialized )
{
g_pGameRules - > InitHUD ( this ) ;
InitHUD ( ) ;
m_fGameHUDInitialized = true ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
variant_t value ;
g_EventQueue . AddEvent ( " game_player_manager " , " OnPlayerJoin " , value , 0 , this , this ) ;
}
}
variant_t value ;
g_EventQueue . AddEvent ( " game_player_manager " , " OnPlayerSpawn " , value , 0 , this , this ) ;
}
// HACKHACK -- send the message to display the game title
CWorld * world = GetWorldEntity ( ) ;
if ( world & & world - > GetDisplayTitle ( ) )
{
UserMessageBegin ( user , " GameTitle " ) ;
MessageEnd ( ) ;
world - > SetDisplayTitle ( false ) ;
}
if ( m_ArmorValue ! = m_iClientBattery )
{
m_iClientBattery = m_ArmorValue ;
// send "battery" update message
if ( usermessages - > LookupUserMessage ( " Battery " ) ! = - 1 )
{
UserMessageBegin ( user , " Battery " ) ;
WRITE_SHORT ( ( int ) m_ArmorValue ) ;
MessageEnd ( ) ;
}
}
#if 0 // BYE BYE!!
// Update Flashlight
if ( ( m_flFlashLightTime ) & & ( m_flFlashLightTime < = gpGlobals - > curtime ) )
{
if ( FlashlightIsOn ( ) )
{
if ( m_iFlashBattery )
{
m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals - > curtime ;
m_iFlashBattery - - ;
if ( ! m_iFlashBattery )
FlashlightTurnOff ( ) ;
}
}
else
{
if ( m_iFlashBattery < 100 )
{
m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals - > curtime ;
m_iFlashBattery + + ;
}
else
m_flFlashLightTime = 0 ;
}
}
# endif
CheckTrainUpdate ( ) ;
// Update all the items
for ( int i = 0 ; i < WeaponCount ( ) ; i + + )
{
if ( GetWeapon ( i ) ) // each item updates it's successors
GetWeapon ( i ) - > UpdateClientData ( this ) ;
}
// update the client with our poison state
m_Local . m_bPoisoned = ( m_bitsDamageType & DMG_POISON )
& & ( m_nPoisonDmg > m_nPoisonRestored )
& & ( m_iHealth < 100 ) ;
// Check if the bonus progress HUD element should be displayed
if ( m_iBonusChallenge = = 0 & & m_iBonusProgress = = 0 & & ! ( m_Local . m_iHideHUD & HIDEHUD_BONUS_PROGRESS ) )
m_Local . m_iHideHUD | = HIDEHUD_BONUS_PROGRESS ;
if ( ( m_iBonusChallenge ! = 0 ) & & ( m_Local . m_iHideHUD & HIDEHUD_BONUS_PROGRESS ) )
m_Local . m_iHideHUD & = ~ HIDEHUD_BONUS_PROGRESS ;
// Let any global rules update the HUD, too
g_pGameRules - > UpdateClientData ( this ) ;
}
void CBasePlayer : : RumbleEffect ( unsigned char index , unsigned char rumbleData , unsigned char rumbleFlags )
{
if ( ! IsAlive ( ) )
return ;
CSingleUserRecipientFilter filter ( this ) ;
filter . MakeReliable ( ) ;
UserMessageBegin ( filter , " Rumble " ) ;
WRITE_BYTE ( index ) ;
WRITE_BYTE ( rumbleData ) ;
WRITE_BYTE ( rumbleFlags ) ;
MessageEnd ( ) ;
}
void CBasePlayer : : EnableControl ( bool fControl )
{
if ( ! fControl )
AddFlag ( FL_FROZEN ) ;
else
RemoveFlag ( FL_FROZEN ) ;
}
void CBasePlayer : : CheckTrainUpdate ( void )
{
if ( ( m_iTrain & TRAIN_NEW ) )
{
CSingleUserRecipientFilter user ( this ) ;
user . MakeReliable ( ) ;
// send "Train" update message
UserMessageBegin ( user , " Train " ) ;
WRITE_BYTE ( m_iTrain & 0xF ) ;
MessageEnd ( ) ;
m_iTrain & = ~ TRAIN_NEW ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether the player should autoaim or not
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBasePlayer : : ShouldAutoaim ( void )
{
// cannot be in multiplayer
if ( gpGlobals - > maxClients > 1 )
return false ;
// autoaiming is only for easy and medium skill
return ( IsX360 ( ) | | ! g_pGameRules - > IsSkillLevel ( SKILL_HARD ) ) ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
Vector CBasePlayer : : GetAutoaimVector ( float flScale )
{
autoaim_params_t params ;
params . m_fScale = flScale ;
params . m_fMaxDist = autoaim_max_dist . GetFloat ( ) ;
GetAutoaimVector ( params ) ;
return params . m_vecAutoAimDir ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
Vector CBasePlayer : : GetAutoaimVector ( float flScale , float flMaxDist )
{
autoaim_params_t params ;
params . m_fScale = flScale ;
params . m_fMaxDist = flMaxDist ;
GetAutoaimVector ( params ) ;
return params . m_vecAutoAimDir ;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CBasePlayer : : GetAutoaimVector ( autoaim_params_t & params )
{
// Assume autoaim will not be assisting.
params . m_bAutoAimAssisting = false ;
if ( ( ShouldAutoaim ( ) = = false ) | | ( params . m_fScale = = AUTOAIM_SCALE_DIRECT_ONLY ) )
{
Vector forward ;
AngleVectors ( EyeAngles ( ) + m_Local . m_vecPunchAngle , & forward ) ;
params . m_vecAutoAimDir = forward ;
params . m_hAutoAimEntity . Set ( NULL ) ;
params . m_vecAutoAimPoint = vec3_invalid ;
params . m_bAutoAimAssisting = false ;
return ;
}
Vector vecSrc = Weapon_ShootPosition ( ) ;
m_vecAutoAim . Init ( 0.0f , 0.0f , 0.0f ) ;
QAngle angles = AutoaimDeflection ( vecSrc , params ) ;
// update ontarget if changed
if ( ! g_pGameRules - > AllowAutoTargetCrosshair ( ) )
m_fOnTarget = false ;
if ( angles . x > 180 )
angles . x - = 360 ;
if ( angles . x < - 180 )
angles . x + = 360 ;
if ( angles . y > 180 )
angles . y - = 360 ;
if ( angles . y < - 180 )
angles . y + = 360 ;
if ( angles . x > 25 )
angles . x = 25 ;
if ( angles . x < - 25 )
angles . x = - 25 ;
if ( angles . y > 12 )
angles . y = 12 ;
if ( angles . y < - 12 )
angles . y = - 12 ;
Vector forward ;
if ( IsInAVehicle ( ) & & g_pGameRules - > GetAutoAimMode ( ) = = AUTOAIM_ON_CONSOLE )
{
m_vecAutoAim = angles ;
AngleVectors ( EyeAngles ( ) + m_vecAutoAim , & forward ) ;
}
else
{
// always use non-sticky autoaim
m_vecAutoAim = angles * 0.9f ;
AngleVectors ( EyeAngles ( ) + m_Local . m_vecPunchAngle + m_vecAutoAim , & forward ) ;
}
params . m_vecAutoAimDir = forward ;
}
//-----------------------------------------------------------------------------
// Targets represent themselves to autoaim as a viewplane-parallel disc with
// a radius specified by the target. The player then modifies this radius
// to achieve more or less aggressive aiming assistance
//-----------------------------------------------------------------------------
float CBasePlayer : : GetAutoaimScore ( const Vector & eyePosition , const Vector & viewDir , const Vector & vecTarget , CBaseEntity * pTarget , float fScale , CBaseCombatWeapon * pActiveWeapon )
{
float radiusSqr ;
float targetRadius = pTarget - > GetAutoAimRadius ( ) * fScale ;
if ( pActiveWeapon ! = NULL )
targetRadius * = pActiveWeapon - > WeaponAutoAimScale ( ) ;
float targetRadiusSqr = Square ( targetRadius ) ;
Vector vecNearestPoint = PointOnLineNearestPoint ( eyePosition , eyePosition + viewDir * 8192 , vecTarget ) ;
Vector vecDiff = vecTarget - vecNearestPoint ;
radiusSqr = vecDiff . LengthSqr ( ) ;
if ( radiusSqr < = targetRadiusSqr )
{
float score ;
score = 1.0f - ( radiusSqr / targetRadiusSqr ) ;
Assert ( score > = 0.0f & & score < = 1.0f ) ;
return score ;
}
// 0 means no score- doesn't qualify for autoaim.
return 0.0f ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &vecSrc -
// flDist -
// flDelta -
// Output : Vector
//-----------------------------------------------------------------------------
QAngle CBasePlayer : : AutoaimDeflection ( Vector & vecSrc , autoaim_params_t & params )
{
float bestscore ;
float score ;
QAngle eyeAngles ;
Vector bestdir ;
CBaseEntity * bestent ;
trace_t tr ;
Vector v_forward , v_right , v_up ;
if ( ShouldAutoaim ( ) = = false )
{
m_fOnTarget = false ;
return vec3_angle ;
}
eyeAngles = EyeAngles ( ) ;
AngleVectors ( eyeAngles + m_Local . m_vecPunchAngle + m_vecAutoAim , & v_forward , & v_right , & v_up ) ;
// try all possible entities
bestdir = v_forward ;
bestscore = 0.0f ;
bestent = NULL ;
//Reset this data
m_fOnTarget = false ;
params . m_bOnTargetNatural = false ;
CBaseEntity * pIgnore = NULL ;
if ( IsInAVehicle ( ) )
{
pIgnore = GetVehicleEntity ( ) ;
}
CTraceFilterSkipTwoEntities traceFilter ( this , pIgnore , COLLISION_GROUP_NONE ) ;
UTIL_TraceLine ( vecSrc , vecSrc + bestdir * MAX_COORD_FLOAT , MASK_SHOT , & traceFilter , & tr ) ;
CBaseEntity * pEntHit = tr . m_pEnt ;
if ( pEntHit & & pEntHit - > m_takedamage ! = DAMAGE_NO & & pEntHit - > GetHealth ( ) > 0 )
{
// don't look through water
if ( ! ( ( GetWaterLevel ( ) ! = 3 & & pEntHit - > GetWaterLevel ( ) = = 3 ) | | ( GetWaterLevel ( ) = = 3 & & pEntHit - > GetWaterLevel ( ) = = 0 ) ) )
{
if ( pEntHit - > ShouldAttractAutoAim ( this ) )
{
bool bAimAtThis = true ;
if ( pEntHit - > IsNPC ( ) & & g_pGameRules - > GetAutoAimMode ( ) > AUTOAIM_NONE )
{
int iRelationType = GetDefaultRelationshipDisposition ( pEntHit - > Classify ( ) ) ;
if ( iRelationType ! = D_HT )
{
bAimAtThis = false ;
}
}
if ( bAimAtThis )
{
if ( pEntHit - > GetFlags ( ) & FL_AIMTARGET )
{
m_fOnTarget = true ;
}
// Player is already on target naturally, don't autoaim.
// Fill out the autoaim_params_t struct, though.
params . m_hAutoAimEntity . Set ( pEntHit ) ;
params . m_vecAutoAimDir = bestdir ;
params . m_vecAutoAimPoint = tr . endpos ;
params . m_bAutoAimAssisting = false ;
params . m_bOnTargetNatural = true ;
return vec3_angle ;
}
}
//Fall through and look for an autoaim ent.
}
}
int count = AimTarget_ListCount ( ) ;
if ( count )
{
CBaseEntity * * pList = ( CBaseEntity * * ) stackalloc ( sizeof ( CBaseEntity * ) * count ) ;
AimTarget_ListCopy ( pList , count ) ;
for ( int i = 0 ; i < count ; i + + )
{
Vector center ;
Vector dir ;
CBaseEntity * pEntity = pList [ i ] ;
// Don't autoaim at anything that doesn't want to be.
if ( ! pEntity - > ShouldAttractAutoAim ( this ) )
continue ;
// Don't shoot yourself
if ( pEntity = = this )
continue ;
if ( ( pEntity - > IsNPC ( ) & & ! pEntity - > IsAlive ( ) ) | | ! pEntity - > edict ( ) )
continue ;
if ( ! g_pGameRules - > ShouldAutoAim ( this , pEntity - > edict ( ) ) )
continue ;
// don't look through water
if ( ( GetWaterLevel ( ) ! = 3 & & pEntity - > GetWaterLevel ( ) = = 3 ) | | ( GetWaterLevel ( ) = = 3 & & pEntity - > GetWaterLevel ( ) = = 0 ) )
continue ;
if ( pEntity - > MyNPCPointer ( ) )
{
// If this entity is an NPC, only aim if it is an enemy.
if ( IRelationType ( pEntity ) ! = D_HT )
{
if ( ! pEntity - > IsPlayer ( ) & & ! g_pGameRules - > IsDeathmatch ( ) )
// Msg( "friend\n");
continue ;
}
}
// Don't autoaim at the noisy bodytarget, this makes the autoaim crosshair wobble.
//center = pEntity->BodyTarget( vecSrc, false );
center = pEntity - > WorldSpaceCenter ( ) ;
dir = ( center - vecSrc ) ;
float dist = dir . Length2D ( ) ;
VectorNormalize ( dir ) ;
// Skip if out of range.
if ( dist > params . m_fMaxDist )
continue ;
float dot = DotProduct ( dir , v_forward ) ;
// make sure it's in front of the player
if ( dot < 0 )
continue ;
if ( ! ( pEntity - > GetFlags ( ) & FL_FLY ) )
{
// Refuse to take wild shots at targets far from reticle.
if ( GetActiveWeapon ( ) ! = NULL & & dot < GetActiveWeapon ( ) - > GetMaxAutoAimDeflection ( ) )
{
// Be lenient if the player is looking down, though. 30 degrees through 90 degrees of pitch.
// (90 degrees is looking down at player's own 'feet'. Looking straight ahead is 0 degrees pitch.
// This was done for XBox to make it easier to fight headcrabs around the player's feet.
if ( eyeAngles . x < 30.0f | | eyeAngles . x > 90.0f | | g_pGameRules - > GetAutoAimMode ( ) ! = AUTOAIM_ON_CONSOLE )
{
continue ;
}
}
}
score = GetAutoaimScore ( vecSrc , v_forward , pEntity - > GetAutoAimCenter ( ) , pEntity , params . m_fScale , GetActiveWeapon ( ) ) ;
if ( score < = bestscore )
{
continue ;
}
UTIL_TraceLine ( vecSrc , center , MASK_SHOT , & traceFilter , & tr ) ;
if ( tr . fraction ! = 1.0 & & tr . m_pEnt ! = pEntity )
{
// Msg( "hit %s, can't see %s\n", STRING( tr.u.ent->classname ), STRING( pEdict->classname ) );
continue ;
}
// This is the best candidate so far.
bestscore = score ;
bestent = pEntity ;
bestdir = dir ;
}
if ( bestent )
{
QAngle bestang ;
VectorAngles ( bestdir , bestang ) ;
if ( IsInAVehicle ( ) )
{
bestang - = EyeAngles ( ) ;
}
else
{
bestang - = EyeAngles ( ) - m_Local . m_vecPunchAngle ;
}
m_fOnTarget = true ;
// Autoaim detected a target for us. Aim automatically at its bodytarget.
params . m_hAutoAimEntity . Set ( bestent ) ;
params . m_vecAutoAimDir = bestdir ;
params . m_vecAutoAimPoint = bestent - > BodyTarget ( vecSrc , false ) ;
params . m_bAutoAimAssisting = true ;
return bestang ;
}
}
return vec3_angle ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : ResetAutoaim ( void )
{
if ( m_vecAutoAim . x ! = 0 | | m_vecAutoAim . y ! = 0 )
{
m_vecAutoAim = QAngle ( 0 , 0 , 0 ) ;
engine - > CrosshairAngle ( edict ( ) , 0 , 0 ) ;
}
m_fOnTarget = false ;
}
// ==========================================================================
// > Weapon stuff
// ==========================================================================
//-----------------------------------------------------------------------------
// Purpose: Override base class, player can always use weapon
// Input : A weapon
// Output : true or false
//-----------------------------------------------------------------------------
bool CBasePlayer : : Weapon_CanUse ( CBaseCombatWeapon * pWeapon )
{
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Override to clear dropped weapon from the hud
//-----------------------------------------------------------------------------
void CBasePlayer : : Weapon_Drop ( CBaseCombatWeapon * pWeapon , const Vector * pvecTarget /* = NULL */ , const Vector * pVelocity /* = NULL */ )
{
bool bWasActiveWeapon = false ;
if ( pWeapon = = GetActiveWeapon ( ) )
{
bWasActiveWeapon = true ;
}
if ( pWeapon )
{
if ( bWasActiveWeapon )
{
pWeapon - > SendWeaponAnim ( ACT_VM_IDLE ) ;
}
}
BaseClass : : Weapon_Drop ( pWeapon , pvecTarget , pVelocity ) ;
if ( bWasActiveWeapon )
{
if ( ! SwitchToNextBestWeapon ( NULL ) )
{
CBaseViewModel * vm = GetViewModel ( ) ;
if ( vm )
{
vm - > AddEffects ( EF_NODRAW ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : weaponSlot -
//-----------------------------------------------------------------------------
void CBasePlayer : : Weapon_DropSlot ( int weaponSlot )
{
CBaseCombatWeapon * pWeapon ;
// Check for that slot being occupied already
for ( int i = 0 ; i < MAX_WEAPONS ; i + + )
{
pWeapon = GetWeapon ( i ) ;
if ( pWeapon ! = NULL )
{
// If the slots match, it's already occupied
if ( pWeapon - > GetSlot ( ) = = weaponSlot )
{
Weapon_Drop ( pWeapon , NULL , NULL ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Override to add weapon to the hud
//-----------------------------------------------------------------------------
void CBasePlayer : : Weapon_Equip ( CBaseCombatWeapon * pWeapon )
{
BaseClass : : Weapon_Equip ( pWeapon ) ;
bool bShouldSwitch = g_pGameRules - > FShouldSwitchWeapon ( this , pWeapon ) ;
# ifdef HL2_DLL
if ( bShouldSwitch = = false & & PhysCannonGetHeldEntity ( GetActiveWeapon ( ) ) = = pWeapon & &
Weapon_OwnsThisType ( pWeapon - > GetClassname ( ) , pWeapon - > GetSubType ( ) ) )
{
bShouldSwitch = true ;
}
# endif //HL2_DLL
// should we switch to this item?
if ( bShouldSwitch )
{
Weapon_Switch ( pWeapon ) ;
}
}
//=========================================================
// HasNamedPlayerItem Does the player already have this item?
//=========================================================
CBaseEntity * CBasePlayer : : HasNamedPlayerItem ( const char * pszItemName )
{
for ( int i = 0 ; i < WeaponCount ( ) ; i + + )
{
if ( ! GetWeapon ( i ) )
continue ;
if ( FStrEq ( pszItemName , GetWeapon ( i ) - > GetClassname ( ) ) )
{
return GetWeapon ( i ) ;
}
}
return NULL ;
}
# if defined USES_ECON_ITEMS
//-----------------------------------------------------------------------------
// Purpose: Add this wearable to the players' equipment list.
//-----------------------------------------------------------------------------
void CBasePlayer : : EquipWearable ( CEconWearable * pItem )
{
Assert ( pItem ) ;
if ( pItem )
{
m_hMyWearables . AddToHead ( pItem ) ;
pItem - > Equip ( this ) ;
}
# ifdef DEBUG
// Double check list integrity.
for ( int i = m_hMyWearables . Count ( ) - 1 ; i > = 0 ; - - i )
{
Assert ( m_hMyWearables [ i ] ! = NULL ) ;
}
// Networked Vector has a max size of MAX_WEARABLES_SENT_FROM_SERVER, should never have more then 7 wearables
// in public
// Search for : RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ),
Assert ( m_hMyWearables . Count ( ) < = MAX_WEARABLES_SENT_FROM_SERVER ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Remove this wearable from the player's equipment list.
//-----------------------------------------------------------------------------
void CBasePlayer : : RemoveWearable ( CEconWearable * pItem )
{
Assert ( pItem ) ;
for ( int i = m_hMyWearables . Count ( ) - 1 ; i > = 0 ; - - i )
{
CEconWearable * pWearable = m_hMyWearables [ i ] ;
if ( pWearable = = pItem )
{
pItem - > UnEquip ( this ) ;
UTIL_Remove ( pWearable ) ;
m_hMyWearables . Remove ( i ) ;
break ;
}
// Integrety is failing, remove NULLs
if ( ! pWearable )
{
m_hMyWearables . Remove ( i ) ;
break ;
}
}
# ifdef DEBUG
// Double check list integrity.
for ( int i = m_hMyWearables . Count ( ) - 1 ; i > = 0 ; - - i )
{
Assert ( m_hMyWearables [ i ] ! = NULL ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : PlayWearableAnimsForPlaybackEvent ( wearableanimplayback_t iPlayback )
{
// Tell all our wearables to play their animations
FOR_EACH_VEC ( m_hMyWearables , i )
{
if ( m_hMyWearables [ i ] )
{
m_hMyWearables [ i ] - > PlayAnimForPlaybackEvent ( iPlayback ) ;
}
}
}
# endif // USES_ECON_ITEMS
//================================================================================
// TEAM HANDLING
//================================================================================
//-----------------------------------------------------------------------------
// Purpose: Put the player in the specified team
//-----------------------------------------------------------------------------
void CBasePlayer : : ChangeTeam ( int iTeamNum , bool bAutoTeam , bool bSilent )
{
if ( ! GetGlobalTeam ( iTeamNum ) )
{
Warning ( " CBasePlayer::ChangeTeam( %d ) - invalid team index. \n " , iTeamNum ) ;
return ;
}
// if this is our current team, just abort
if ( iTeamNum = = GetTeamNumber ( ) )
{
return ;
}
// Immediately tell all clients that he's changing team. This has to be done
// first, so that all user messages that follow as a result of the team change
// come after this one, allowing the client to be prepared for them.
IGameEvent * event = gameeventmanager - > CreateEvent ( " player_team " ) ;
if ( event )
{
event - > SetInt ( " userid " , GetUserID ( ) ) ;
event - > SetInt ( " team " , iTeamNum ) ;
event - > SetInt ( " oldteam " , GetTeamNumber ( ) ) ;
event - > SetInt ( " disconnect " , IsDisconnecting ( ) ) ;
event - > SetInt ( " autoteam " , bAutoTeam ) ;
event - > SetInt ( " silent " , bSilent ) ;
event - > SetString ( " name " , GetPlayerName ( ) ) ;
gameeventmanager - > FireEvent ( event ) ;
}
// Remove him from his current team
if ( GetTeam ( ) )
{
GetTeam ( ) - > RemovePlayer ( this ) ;
}
// Are we being added to a team?
if ( iTeamNum )
{
GetGlobalTeam ( iTeamNum ) - > AddPlayer ( this ) ;
}
BaseClass : : ChangeTeam ( iTeamNum ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Locks a player to the spot; they can't move, shoot, or be hurt
//-----------------------------------------------------------------------------
void CBasePlayer : : LockPlayerInPlace ( void )
{
if ( m_iPlayerLocked )
return ;
AddFlag ( FL_GODMODE | FL_FROZEN ) ;
SetMoveType ( MOVETYPE_NONE ) ;
m_iPlayerLocked = true ;
// force a client data update, so that anything that has been done to
// this player previously this frame won't get delayed in being sent
UpdateClientData ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Unlocks a previously locked player
//-----------------------------------------------------------------------------
void CBasePlayer : : UnlockPlayer ( void )
{
if ( ! m_iPlayerLocked )
return ;
RemoveFlag ( FL_GODMODE | FL_FROZEN ) ;
SetMoveType ( MOVETYPE_WALK ) ;
m_iPlayerLocked = false ;
}
bool CBasePlayer : : ClearUseEntity ( )
{
if ( m_hUseEntity ! = NULL )
{
// Stop controlling the train/object
// TODO: Send HUD Update
m_hUseEntity - > Use ( this , this , USE_OFF , 0 ) ;
m_hUseEntity = NULL ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : HideViewModels ( void )
{
for ( int i = 0 ; i < MAX_VIEWMODELS ; i + + )
{
CBaseViewModel * vm = GetViewModel ( i ) ;
if ( ! vm )
continue ;
vm - > SetWeaponModel ( NULL , NULL ) ;
}
}
class CStripWeapons : public CPointEntity
{
DECLARE_CLASS ( CStripWeapons , CPointEntity ) ;
public :
void InputStripWeapons ( inputdata_t & data ) ;
void InputStripWeaponsAndSuit ( inputdata_t & data ) ;
void StripWeapons ( inputdata_t & data , bool stripSuit ) ;
DECLARE_DATADESC ( ) ;
} ;
LINK_ENTITY_TO_CLASS ( player_weaponstrip , CStripWeapons ) ;
BEGIN_DATADESC ( CStripWeapons )
DEFINE_INPUTFUNC ( FIELD_VOID , " Strip " , InputStripWeapons ) ,
DEFINE_INPUTFUNC ( FIELD_VOID , " StripWeaponsAndSuit " , InputStripWeaponsAndSuit ) ,
END_DATADESC ( )
void CStripWeapons : : InputStripWeapons ( inputdata_t & data )
{
StripWeapons ( data , false ) ;
}
void CStripWeapons : : InputStripWeaponsAndSuit ( inputdata_t & data )
{
StripWeapons ( data , true ) ;
}
void CStripWeapons : : StripWeapons ( inputdata_t & data , bool stripSuit )
{
CBasePlayer * pPlayer = NULL ;
if ( data . pActivator & & data . pActivator - > IsPlayer ( ) )
{
pPlayer = ( CBasePlayer * ) data . pActivator ;
}
else if ( ! g_pGameRules - > IsDeathmatch ( ) )
{
pPlayer = UTIL_GetLocalPlayer ( ) ;
}
if ( pPlayer )
{
pPlayer - > RemoveAllItems ( stripSuit ) ;
}
}
class CRevertSaved : public CPointEntity
{
DECLARE_CLASS ( CRevertSaved , CPointEntity ) ;
public :
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void LoadThink ( void ) ;
DECLARE_DATADESC ( ) ;
inline float Duration ( void ) { return m_Duration ; }
inline float HoldTime ( void ) { return m_HoldTime ; }
inline float LoadTime ( void ) { return m_loadTime ; }
inline void SetDuration ( float duration ) { m_Duration = duration ; }
inline void SetHoldTime ( float hold ) { m_HoldTime = hold ; }
inline void SetLoadTime ( float time ) { m_loadTime = time ; }
//Inputs
void InputReload ( inputdata_t & data ) ;
# ifdef HL1_DLL
void MessageThink ( void ) ;
inline float MessageTime ( void ) { return m_messageTime ; }
inline void SetMessageTime ( float time ) { m_messageTime = time ; }
# endif
private :
float m_loadTime ;
float m_Duration ;
float m_HoldTime ;
# ifdef HL1_DLL
string_t m_iszMessage ;
float m_messageTime ;
# endif
} ;
LINK_ENTITY_TO_CLASS ( player_loadsaved , CRevertSaved ) ;
BEGIN_DATADESC ( CRevertSaved )
# ifdef HL1_DLL
DEFINE_KEYFIELD ( m_iszMessage , FIELD_STRING , " message " ) ,
DEFINE_KEYFIELD ( m_messageTime , FIELD_FLOAT , " messagetime " ) , // These are not actual times, but durations, so save as floats
DEFINE_FUNCTION ( MessageThink ) ,
# endif
DEFINE_KEYFIELD ( m_loadTime , FIELD_FLOAT , " loadtime " ) ,
DEFINE_KEYFIELD ( m_Duration , FIELD_FLOAT , " duration " ) ,
DEFINE_KEYFIELD ( m_HoldTime , FIELD_FLOAT , " holdtime " ) ,
DEFINE_INPUTFUNC ( FIELD_VOID , " Reload " , InputReload ) ,
// Function Pointers
DEFINE_FUNCTION ( LoadThink ) ,
END_DATADESC ( )
CBaseEntity * CreatePlayerLoadSave ( Vector vOrigin , float flDuration , float flHoldTime , float flLoadTime )
{
CRevertSaved * pRevertSaved = ( CRevertSaved * ) CreateEntityByName ( " player_loadsaved " ) ;
if ( pRevertSaved = = NULL )
return NULL ;
UTIL_SetOrigin ( pRevertSaved , vOrigin ) ;
pRevertSaved - > Spawn ( ) ;
pRevertSaved - > SetDuration ( flDuration ) ;
pRevertSaved - > SetHoldTime ( flHoldTime ) ;
pRevertSaved - > SetLoadTime ( flLoadTime ) ;
return pRevertSaved ;
}
void CRevertSaved : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
UTIL_ScreenFadeAll ( m_clrRender , Duration ( ) , HoldTime ( ) , FFADE_OUT ) ;
SetNextThink ( gpGlobals - > curtime + LoadTime ( ) ) ;
SetThink ( & CRevertSaved : : LoadThink ) ;
CBasePlayer * pPlayer = UTIL_GetLocalPlayer ( ) ;
if ( pPlayer )
{
//Adrian: Setting this flag so we can't move or save a game.
pPlayer - > pl . deadflag = true ;
pPlayer - > AddFlag ( ( FL_NOTARGET | FL_FROZEN ) ) ;
// clear any pending autosavedangerous
g_ServerGameDLL . m_fAutoSaveDangerousTime = 0.0f ;
g_ServerGameDLL . m_fAutoSaveDangerousMinHealthToCommit = 0.0f ;
}
}
void CRevertSaved : : InputReload ( inputdata_t & inputdata )
{
UTIL_ScreenFadeAll ( m_clrRender , Duration ( ) , HoldTime ( ) , FFADE_OUT ) ;
# ifdef HL1_DLL
SetNextThink ( gpGlobals - > curtime + MessageTime ( ) ) ;
SetThink ( & CRevertSaved : : MessageThink ) ;
# else
SetNextThink ( gpGlobals - > curtime + LoadTime ( ) ) ;
SetThink ( & CRevertSaved : : LoadThink ) ;
# endif
CBasePlayer * pPlayer = UTIL_GetLocalPlayer ( ) ;
if ( pPlayer )
{
//Adrian: Setting this flag so we can't move or save a game.
pPlayer - > pl . deadflag = true ;
pPlayer - > AddFlag ( ( FL_NOTARGET | FL_FROZEN ) ) ;
// clear any pending autosavedangerous
g_ServerGameDLL . m_fAutoSaveDangerousTime = 0.0f ;
g_ServerGameDLL . m_fAutoSaveDangerousMinHealthToCommit = 0.0f ;
}
}
# ifdef HL1_DLL
void CRevertSaved : : MessageThink ( void )
{
UTIL_ShowMessageAll ( STRING ( m_iszMessage ) ) ;
float nextThink = LoadTime ( ) - MessageTime ( ) ;
if ( nextThink > 0 )
{
SetNextThink ( gpGlobals - > curtime + nextThink ) ;
SetThink ( & CRevertSaved : : LoadThink ) ;
}
else
LoadThink ( ) ;
}
# endif
void CRevertSaved : : LoadThink ( void )
{
if ( ! gpGlobals - > deathmatch )
{
engine - > ServerCommand ( " reload \n " ) ;
}
}
# define SF_SPEED_MOD_SUPPRESS_WEAPONS (1<<0) // Take away weapons
# define SF_SPEED_MOD_SUPPRESS_HUD (1<<1) // Take away the HUD
# define SF_SPEED_MOD_SUPPRESS_JUMP (1<<2)
# define SF_SPEED_MOD_SUPPRESS_DUCK (1<<3)
# define SF_SPEED_MOD_SUPPRESS_USE (1<<4)
# define SF_SPEED_MOD_SUPPRESS_SPEED (1<<5)
# define SF_SPEED_MOD_SUPPRESS_ATTACK (1<<6)
# define SF_SPEED_MOD_SUPPRESS_ZOOM (1<<7)
class CMovementSpeedMod : public CPointEntity
{
DECLARE_CLASS ( CMovementSpeedMod , CPointEntity ) ;
public :
void InputSpeedMod ( inputdata_t & data ) ;
private :
int GetDisabledButtonMask ( void ) ;
DECLARE_DATADESC ( ) ;
} ;
LINK_ENTITY_TO_CLASS ( player_speedmod , CMovementSpeedMod ) ;
BEGIN_DATADESC ( CMovementSpeedMod )
DEFINE_INPUTFUNC ( FIELD_FLOAT , " ModifySpeed " , InputSpeedMod ) ,
END_DATADESC ( )
int CMovementSpeedMod : : GetDisabledButtonMask ( void )
{
int nMask = 0 ;
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_JUMP ) )
{
nMask | = IN_JUMP ;
}
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_DUCK ) )
{
nMask | = IN_DUCK ;
}
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_USE ) )
{
nMask | = IN_USE ;
}
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_SPEED ) )
{
nMask | = IN_SPEED ;
}
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_ATTACK ) )
{
nMask | = ( IN_ATTACK | IN_ATTACK2 ) ;
}
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_ZOOM ) )
{
nMask | = IN_ZOOM ;
}
return nMask ;
}
void CMovementSpeedMod : : InputSpeedMod ( inputdata_t & data )
{
CBasePlayer * pPlayer = NULL ;
if ( data . pActivator & & data . pActivator - > IsPlayer ( ) )
{
pPlayer = ( CBasePlayer * ) data . pActivator ;
}
else if ( ! g_pGameRules - > IsDeathmatch ( ) )
{
pPlayer = UTIL_GetLocalPlayer ( ) ;
}
if ( pPlayer )
{
if ( data . value . Float ( ) ! = 1.0f )
{
// Holster weapon immediately, to allow it to cleanup
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_WEAPONS ) )
{
if ( pPlayer - > GetActiveWeapon ( ) )
{
pPlayer - > Weapon_SetLast ( pPlayer - > GetActiveWeapon ( ) ) ;
pPlayer - > GetActiveWeapon ( ) - > Holster ( ) ;
pPlayer - > ClearActiveWeapon ( ) ;
}
pPlayer - > HideViewModels ( ) ;
}
// Turn off the flashlight
if ( pPlayer - > FlashlightIsOn ( ) )
{
pPlayer - > FlashlightTurnOff ( ) ;
}
// Disable the flashlight's further use
pPlayer - > SetFlashlightEnabled ( false ) ;
pPlayer - > DisableButtons ( GetDisabledButtonMask ( ) ) ;
// Hide the HUD
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_HUD ) )
{
pPlayer - > m_Local . m_iHideHUD | = HIDEHUD_ALL ;
}
}
else
{
// Bring the weapon back
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_WEAPONS ) & & pPlayer - > GetActiveWeapon ( ) = = NULL )
{
pPlayer - > SetActiveWeapon ( pPlayer - > Weapon_GetLast ( ) ) ;
if ( pPlayer - > GetActiveWeapon ( ) )
{
pPlayer - > GetActiveWeapon ( ) - > Deploy ( ) ;
}
}
// Allow the flashlight again
pPlayer - > SetFlashlightEnabled ( true ) ;
pPlayer - > EnableButtons ( GetDisabledButtonMask ( ) ) ;
// Restore the HUD
if ( HasSpawnFlags ( SF_SPEED_MOD_SUPPRESS_HUD ) )
{
pPlayer - > m_Local . m_iHideHUD & = ~ HIDEHUD_ALL ;
}
}
pPlayer - > SetLaggedMovementValue ( data . value . Float ( ) ) ;
}
}
void SendProxy_CropFlagsToPlayerFlagBitsLength ( const SendProp * pProp , const void * pStruct , const void * pVarData , DVariant * pOut , int iElement , int objectID )
{
int mask = ( 1 < < PLAYER_FLAG_BITS ) - 1 ;
int data = * ( int * ) pVarData ;
pOut - > m_Int = ( data & mask ) ;
}
// -------------------------------------------------------------------------------- //
// SendTable for CPlayerState.
// -------------------------------------------------------------------------------- //
BEGIN_SEND_TABLE_NOBASE ( CPlayerState , DT_PlayerState )
SendPropInt ( SENDINFO ( deadflag ) , 1 , SPROP_UNSIGNED ) ,
END_SEND_TABLE ( )
// -------------------------------------------------------------------------------- //
// This data only gets sent to clients that ARE this player entity.
// -------------------------------------------------------------------------------- //
BEGIN_SEND_TABLE_NOBASE ( CBasePlayer , DT_LocalPlayerExclusive )
SendPropDataTable ( SENDINFO_DT ( m_Local ) , & REFERENCE_SEND_TABLE ( DT_Local ) ) ,
// If HL2_DLL is defined, then baseflex.cpp already sends these.
# ifndef HL2_DLL
SendPropFloat ( SENDINFO_VECTORELEM ( m_vecViewOffset , 0 ) , 8 , SPROP_ROUNDDOWN , - 32.0 , 32.0f ) ,
SendPropFloat ( SENDINFO_VECTORELEM ( m_vecViewOffset , 1 ) , 8 , SPROP_ROUNDDOWN , - 32.0 , 32.0f ) ,
SendPropFloat ( SENDINFO_VECTORELEM ( m_vecViewOffset , 2 ) , 20 , SPROP_CHANGES_OFTEN , 0.0f , 256.0f ) ,
# endif
SendPropFloat ( SENDINFO ( m_flFriction ) , 8 , SPROP_ROUNDDOWN , 0.0f , 4.0f ) ,
SendPropArray3 ( SENDINFO_ARRAY3 ( m_iAmmo ) , SendPropInt ( SENDINFO_ARRAY ( m_iAmmo ) , - 1 , SPROP_VARINT | SPROP_UNSIGNED ) ) ,
SendPropInt ( SENDINFO ( m_fOnTarget ) , 2 , SPROP_UNSIGNED ) ,
SendPropInt ( SENDINFO ( m_nTickBase ) , - 1 , SPROP_CHANGES_OFTEN ) ,
SendPropInt ( SENDINFO ( m_nNextThinkTick ) ) ,
SendPropEHandle ( SENDINFO ( m_hLastWeapon ) ) ,
SendPropEHandle ( SENDINFO ( m_hGroundEntity ) , SPROP_CHANGES_OFTEN ) ,
SendPropFloat ( SENDINFO_VECTORELEM ( m_vecVelocity , 0 ) , 32 , SPROP_NOSCALE | SPROP_CHANGES_OFTEN ) ,
SendPropFloat ( SENDINFO_VECTORELEM ( m_vecVelocity , 1 ) , 32 , SPROP_NOSCALE | SPROP_CHANGES_OFTEN ) ,
SendPropFloat ( SENDINFO_VECTORELEM ( m_vecVelocity , 2 ) , 32 , SPROP_NOSCALE | SPROP_CHANGES_OFTEN ) ,
# if PREDICTION_ERROR_CHECK_LEVEL > 1
SendPropVector ( SENDINFO ( m_vecBaseVelocity ) , - 1 , SPROP_COORD ) ,
# else
SendPropVector ( SENDINFO ( m_vecBaseVelocity ) , 20 , 0 , - 1000 , 1000 ) ,
# endif
SendPropEHandle ( SENDINFO ( m_hConstraintEntity ) ) ,
SendPropVector ( SENDINFO ( m_vecConstraintCenter ) , 0 , SPROP_NOSCALE ) ,
SendPropFloat ( SENDINFO ( m_flConstraintRadius ) , 0 , SPROP_NOSCALE ) ,
SendPropFloat ( SENDINFO ( m_flConstraintWidth ) , 0 , SPROP_NOSCALE ) ,
SendPropFloat ( SENDINFO ( m_flConstraintSpeedFactor ) , 0 , SPROP_NOSCALE ) ,
SendPropFloat ( SENDINFO ( m_flDeathTime ) , 0 , SPROP_NOSCALE ) ,
SendPropInt ( SENDINFO ( m_nWaterLevel ) , 2 , SPROP_UNSIGNED ) ,
SendPropFloat ( SENDINFO ( m_flLaggedMovementValue ) , 0 , SPROP_NOSCALE ) ,
END_SEND_TABLE ( )
// -------------------------------------------------------------------------------- //
// DT_BasePlayer sendtable.
// -------------------------------------------------------------------------------- //
# if defined USES_ECON_ITEMS
EXTERN_SEND_TABLE ( DT_AttributeList ) ;
# endif
IMPLEMENT_SERVERCLASS_ST ( CBasePlayer , DT_BasePlayer )
# if defined USES_ECON_ITEMS
SendPropDataTable ( SENDINFO_DT ( m_AttributeList ) , & REFERENCE_SEND_TABLE ( DT_AttributeList ) ) ,
# endif
SendPropDataTable ( SENDINFO_DT ( pl ) , & REFERENCE_SEND_TABLE ( DT_PlayerState ) , SendProxy_DataTableToDataTable ) ,
SendPropEHandle ( SENDINFO ( m_hVehicle ) ) ,
SendPropEHandle ( SENDINFO ( m_hUseEntity ) ) ,
SendPropInt ( SENDINFO ( m_iHealth ) , - 1 , SPROP_VARINT | SPROP_CHANGES_OFTEN ) ,
SendPropInt ( SENDINFO ( m_lifeState ) , 3 , SPROP_UNSIGNED ) ,
SendPropInt ( SENDINFO ( m_iBonusProgress ) , 15 ) ,
SendPropInt ( SENDINFO ( m_iBonusChallenge ) , 4 ) ,
SendPropFloat ( SENDINFO ( m_flMaxspeed ) , 12 , SPROP_ROUNDDOWN , 0.0f , 2048.0f ) , // CL
SendPropInt ( SENDINFO ( m_fFlags ) , PLAYER_FLAG_BITS , SPROP_UNSIGNED | SPROP_CHANGES_OFTEN , SendProxy_CropFlagsToPlayerFlagBitsLength ) ,
SendPropInt ( SENDINFO ( m_iObserverMode ) , 3 , SPROP_UNSIGNED ) ,
SendPropEHandle ( SENDINFO ( m_hObserverTarget ) ) ,
SendPropInt ( SENDINFO ( m_iFOV ) , 8 , SPROP_UNSIGNED ) ,
SendPropInt ( SENDINFO ( m_iFOVStart ) , 8 , SPROP_UNSIGNED ) ,
SendPropFloat ( SENDINFO ( m_flFOVTime ) ) ,
SendPropInt ( SENDINFO ( m_iDefaultFOV ) , 8 , SPROP_UNSIGNED ) ,
SendPropEHandle ( SENDINFO ( m_hZoomOwner ) ) ,
SendPropArray ( SendPropEHandle ( SENDINFO_ARRAY ( m_hViewModel ) ) , m_hViewModel ) ,
SendPropString ( SENDINFO ( m_szLastPlaceName ) ) ,
# if defined USES_ECON_ITEMS
SendPropUtlVector ( SENDINFO_UTLVECTOR ( m_hMyWearables ) , MAX_WEARABLES_SENT_FROM_SERVER , SendPropEHandle ( NULL , 0 ) ) ,
# endif // USES_ECON_ITEMS
// Data that only gets sent to the local player.
SendPropDataTable ( " localdata " , 0 , & REFERENCE_SEND_TABLE ( DT_LocalPlayerExclusive ) , SendProxy_SendLocalDataTable ) ,
END_SEND_TABLE ( )
//=============================================================================
//
// Player Physics Shadow Code
//
void CBasePlayer : : SetupVPhysicsShadow ( const Vector & vecAbsOrigin , const Vector & vecAbsVelocity , CPhysCollide * pStandModel , const char * pStandHullName , CPhysCollide * pCrouchModel , const char * pCrouchHullName )
{
solid_t solid ;
Q_strncpy ( solid . surfaceprop , " player " , sizeof ( solid . surfaceprop ) ) ;
solid . params = g_PhysDefaultObjectParams ;
solid . params . mass = 85.0f ;
solid . params . inertia = 1e24 f ;
solid . params . enableCollisions = false ;
//disable drag
solid . params . dragCoefficient = 0 ;
// create standing hull
m_pShadowStand = PhysModelCreateCustom ( this , pStandModel , GetLocalOrigin ( ) , GetLocalAngles ( ) , pStandHullName , false , & solid ) ;
m_pShadowStand - > SetCallbackFlags ( CALLBACK_GLOBAL_COLLISION | CALLBACK_SHADOW_COLLISION ) ;
// create crouchig hull
m_pShadowCrouch = PhysModelCreateCustom ( this , pCrouchModel , GetLocalOrigin ( ) , GetLocalAngles ( ) , pCrouchHullName , false , & solid ) ;
m_pShadowCrouch - > SetCallbackFlags ( CALLBACK_GLOBAL_COLLISION | CALLBACK_SHADOW_COLLISION ) ;
// default to stand
VPhysicsSetObject ( m_pShadowStand ) ;
// tell physics lists I'm a shadow controller object
PhysAddShadow ( this ) ;
m_pPhysicsController = physenv - > CreatePlayerController ( m_pShadowStand ) ;
m_pPhysicsController - > SetPushMassLimit ( 350.0f ) ;
m_pPhysicsController - > SetPushSpeedLimit ( 50.0f ) ;
// Give the controller a valid position so it doesn't do anything rash.
UpdatePhysicsShadowToPosition ( vecAbsOrigin ) ;
// init state
if ( GetFlags ( ) & FL_DUCKING )
{
SetVCollisionState ( vecAbsOrigin , vecAbsVelocity , VPHYS_CROUCH ) ;
}
else
{
SetVCollisionState ( vecAbsOrigin , vecAbsVelocity , VPHYS_WALK ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Empty, just want to keep the baseentity version from being called
// current so we don't kick up dust, etc.
//-----------------------------------------------------------------------------
void CBasePlayer : : VPhysicsCollision ( int index , gamevcollisionevent_t * pEvent )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : VPhysicsUpdate ( IPhysicsObject * pPhysics )
{
float savedImpact = m_impactEnergyScale ;
// HACKHACK: Reduce player's stress by 1/8th
m_impactEnergyScale * = 0.125f ;
ApplyStressDamage ( pPhysics , true ) ;
m_impactEnergyScale = savedImpact ;
}
//-----------------------------------------------------------------------------
// Purpose: Allow bots etc to use slightly different solid masks
//-----------------------------------------------------------------------------
unsigned int CBasePlayer : : PlayerSolidMask ( bool brushOnly ) const
{
if ( brushOnly )
{
return MASK_PLAYERSOLID_BRUSHONLY ;
}
return MASK_PLAYERSOLID ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : VPhysicsShadowUpdate ( IPhysicsObject * pPhysics )
{
if ( sv_turbophysics . GetBool ( ) )
return ;
Vector newPosition ;
bool physicsUpdated = m_pPhysicsController - > GetShadowPosition ( & newPosition , NULL ) > 0 ? true : false ;
// UNDONE: If the player is penetrating, but the player's game collisions are not stuck, teleport the physics shadow to the game position
if ( pPhysics - > GetGameFlags ( ) & FVPHYSICS_PENETRATING )
{
CUtlVector < CBaseEntity * > list ;
PhysGetListOfPenetratingEntities ( this , list ) ;
for ( int i = list . Count ( ) - 1 ; i > = 0 ; - - i )
{
// filter out anything that isn't simulated by vphysics
// UNDONE: Filter out motion disabled objects?
if ( list [ i ] - > GetMoveType ( ) = = MOVETYPE_VPHYSICS )
{
// I'm currently stuck inside a moving object, so allow vphysics to
// apply velocity to the player in order to separate these objects
m_touchedPhysObject = true ;
}
// if it's an NPC, tell them that the player is intersecting them
CAI_BaseNPC * pNPC = list [ i ] - > MyNPCPointer ( ) ;
if ( pNPC )
{
pNPC - > PlayerPenetratingVPhysics ( ) ;
}
}
}
bool bCheckStuck = false ;
if ( m_afPhysicsFlags & PFLAG_GAMEPHYSICS_ROTPUSH )
{
bCheckStuck = true ;
m_afPhysicsFlags & = ~ PFLAG_GAMEPHYSICS_ROTPUSH ;
}
if ( m_pPhysicsController - > IsInContact ( ) | | ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) )
{
m_touchedPhysObject = true ;
}
if ( IsFollowingPhysics ( ) )
{
m_touchedPhysObject = true ;
}
if ( GetMoveType ( ) = = MOVETYPE_NOCLIP | | pl . deadflag )
{
m_oldOrigin = GetAbsOrigin ( ) ;
return ;
}
if ( phys_timescale . GetFloat ( ) = = 0.0f )
{
physicsUpdated = false ;
}
if ( ! physicsUpdated )
return ;
IPhysicsObject * pPhysGround = GetGroundVPhysics ( ) ;
Vector newVelocity ;
pPhysics - > GetPosition ( & newPosition , 0 ) ;
m_pPhysicsController - > GetShadowVelocity ( & newVelocity ) ;
// assume vphysics gave us back a position without penetration
Vector lastValidPosition = newPosition ;
if ( physicsshadowupdate_render . GetBool ( ) )
{
NDebugOverlay : : Box ( GetAbsOrigin ( ) , WorldAlignMins ( ) , WorldAlignMaxs ( ) , 255 , 0 , 0 , 24 , 15.0f ) ;
NDebugOverlay : : Box ( newPosition , WorldAlignMins ( ) , WorldAlignMaxs ( ) , 0 , 0 , 255 , 24 , 15.0f ) ;
// NDebugOverlay::Box( newPosition, WorldAlignMins(), WorldAlignMaxs(), 0,0,255, 24, .01f);
}
Vector tmp = GetAbsOrigin ( ) - newPosition ;
if ( ! m_touchedPhysObject & & ! ( GetFlags ( ) & FL_ONGROUND ) )
{
tmp . z * = 0.5f ; // don't care about z delta as much
}
float dist = tmp . LengthSqr ( ) ;
float deltaV = ( newVelocity - GetAbsVelocity ( ) ) . LengthSqr ( ) ;
float maxDistErrorSqr = VPHYS_MAX_DISTSQR ;
float maxVelErrorSqr = VPHYS_MAX_VELSQR ;
if ( IsRideablePhysics ( pPhysGround ) )
{
maxDistErrorSqr * = 0.25 ;
maxVelErrorSqr * = 0.25 ;
}
// player's physics was frozen, try moving to the game's simulated position if possible
if ( m_pPhysicsController - > WasFrozen ( ) )
{
m_bPhysicsWasFrozen = true ;
// check my position (physics object could have simulated into my position
// physics is not very far away, check my position
trace_t trace ;
UTIL_TraceEntity ( this , GetAbsOrigin ( ) , GetAbsOrigin ( ) , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
if ( ! trace . startsolid )
return ;
// The physics shadow position is probably not in solid, try to move from there to the desired position
UTIL_TraceEntity ( this , newPosition , GetAbsOrigin ( ) , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
if ( ! trace . startsolid )
{
// found a valid position between the two? take it.
SetAbsOrigin ( trace . endpos ) ;
UpdateVPhysicsPosition ( trace . endpos , vec3_origin , 0 ) ;
return ;
}
}
if ( dist > = maxDistErrorSqr | | deltaV > = maxVelErrorSqr | | ( pPhysGround & & ! m_touchedPhysObject ) )
{
if ( m_touchedPhysObject | | pPhysGround )
{
// BUGBUG: Rewrite this code using fixed timestep
if ( deltaV > = maxVelErrorSqr & & ! m_bPhysicsWasFrozen )
{
Vector dir = GetAbsVelocity ( ) ;
float len = VectorNormalize ( dir ) ;
float dot = DotProduct ( newVelocity , dir ) ;
if ( dot > len )
{
dot = len ;
}
else if ( dot < - len )
{
dot = - len ;
}
VectorMA ( newVelocity , - dot , dir , newVelocity ) ;
if ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER )
{
float val = Lerp ( 0.1f , len , dot ) ;
VectorMA ( newVelocity , val - len , dir , newVelocity ) ;
}
if ( ! IsRideablePhysics ( pPhysGround ) )
{
if ( ! ( m_afPhysicsFlags & PFLAG_VPHYSICS_MOTIONCONTROLLER ) & & IsSimulatingOnAlternateTicks ( ) )
{
newVelocity * = 0.5f ;
}
ApplyAbsVelocityImpulse ( newVelocity ) ;
}
}
trace_t trace ;
UTIL_TraceEntity ( this , newPosition , newPosition , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
if ( ! trace . allsolid & & ! trace . startsolid )
{
SetAbsOrigin ( newPosition ) ;
}
}
else
{
bCheckStuck = true ;
}
}
else
{
if ( m_touchedPhysObject )
{
// check my position (physics object could have simulated into my position
// physics is not very far away, check my position
trace_t trace ;
UTIL_TraceEntity ( this , GetAbsOrigin ( ) , GetAbsOrigin ( ) ,
MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
// is current position ok?
if ( trace . allsolid | | trace . startsolid )
{
// no use the final stuck check to move back to old if this stuck fix didn't work
bCheckStuck = true ;
lastValidPosition = m_oldOrigin ;
SetAbsOrigin ( newPosition ) ;
}
}
}
if ( bCheckStuck )
{
trace_t trace ;
UTIL_TraceEntity ( this , GetAbsOrigin ( ) , GetAbsOrigin ( ) , MASK_PLAYERSOLID , this , COLLISION_GROUP_PLAYER_MOVEMENT , & trace ) ;
// current position is not ok, fixup
if ( trace . allsolid | | trace . startsolid )
{
// STUCK!?!?!
//Warning( "Checkstuck failed. Stuck on %s!!\n", trace.m_pEnt->GetClassname() );
SetAbsOrigin ( lastValidPosition ) ;
}
}
m_oldOrigin = GetAbsOrigin ( ) ;
m_bPhysicsWasFrozen = false ;
}
// recreate physics on save/load, don't try to save the state!
bool CBasePlayer : : ShouldSavePhysics ( )
{
return false ;
}
void CBasePlayer : : RefreshCollisionBounds ( void )
{
BaseClass : : RefreshCollisionBounds ( ) ;
InitVCollision ( GetAbsOrigin ( ) , GetAbsVelocity ( ) ) ;
SetViewOffset ( VEC_VIEW_SCALED ( this ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : InitVCollision ( const Vector & vecAbsOrigin , const Vector & vecAbsVelocity )
{
// Cleanup any old vphysics stuff.
VPhysicsDestroyObject ( ) ;
// in turbo physics players dont have a physics shadow
if ( sv_turbophysics . GetBool ( ) )
return ;
CPhysCollide * pModel = PhysCreateBbox ( VEC_HULL_MIN_SCALED ( this ) , VEC_HULL_MAX_SCALED ( this ) ) ;
CPhysCollide * pCrouchModel = PhysCreateBbox ( VEC_DUCK_HULL_MIN_SCALED ( this ) , VEC_DUCK_HULL_MAX_SCALED ( this ) ) ;
SetupVPhysicsShadow ( vecAbsOrigin , vecAbsVelocity , pModel , " player_stand " , pCrouchModel , " player_crouch " ) ;
}
void CBasePlayer : : VPhysicsDestroyObject ( )
{
// Since CBasePlayer aliases its pointer to the physics object, tell CBaseEntity to
// clear out its physics object pointer so we don't wind up deleting one of
// the aliased objects twice.
VPhysicsSetObject ( NULL ) ;
PhysRemoveShadow ( this ) ;
if ( m_pPhysicsController )
{
physenv - > DestroyPlayerController ( m_pPhysicsController ) ;
m_pPhysicsController = NULL ;
}
if ( m_pShadowStand )
{
m_pShadowStand - > EnableCollisions ( false ) ;
PhysDestroyObject ( m_pShadowStand ) ;
m_pShadowStand = NULL ;
}
if ( m_pShadowCrouch )
{
m_pShadowCrouch - > EnableCollisions ( false ) ;
PhysDestroyObject ( m_pShadowCrouch ) ;
m_pShadowCrouch = NULL ;
}
BaseClass : : VPhysicsDestroyObject ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : SetVCollisionState ( const Vector & vecAbsOrigin , const Vector & vecAbsVelocity , int collisionState )
{
m_vphysicsCollisionState = collisionState ;
switch ( collisionState )
{
case VPHYS_WALK :
m_pShadowStand - > SetPosition ( vecAbsOrigin , vec3_angle , true ) ;
m_pShadowStand - > SetVelocity ( & vecAbsVelocity , NULL ) ;
m_pShadowCrouch - > EnableCollisions ( false ) ;
m_pPhysicsController - > SetObject ( m_pShadowStand ) ;
VPhysicsSwapObject ( m_pShadowStand ) ;
m_pShadowStand - > EnableCollisions ( true ) ;
break ;
case VPHYS_CROUCH :
m_pShadowCrouch - > SetPosition ( vecAbsOrigin , vec3_angle , true ) ;
m_pShadowCrouch - > SetVelocity ( & vecAbsVelocity , NULL ) ;
m_pShadowStand - > EnableCollisions ( false ) ;
m_pPhysicsController - > SetObject ( m_pShadowCrouch ) ;
VPhysicsSwapObject ( m_pShadowCrouch ) ;
m_pShadowCrouch - > EnableCollisions ( true ) ;
break ;
case VPHYS_NOCLIP :
m_pShadowCrouch - > EnableCollisions ( false ) ;
m_pShadowStand - > EnableCollisions ( false ) ;
break ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBasePlayer : : GetFOV ( void )
{
int nDefaultFOV ;
// The vehicle's FOV wins if we're asking for a default value
if ( GetVehicle ( ) )
{
CacheVehicleView ( ) ;
nDefaultFOV = ( m_flVehicleViewFOV = = 0 ) ? GetDefaultFOV ( ) : ( int ) m_flVehicleViewFOV ;
}
else
{
nDefaultFOV = GetDefaultFOV ( ) ;
}
int fFOV = ( m_iFOV = = 0 ) ? nDefaultFOV : m_iFOV ;
// If it's immediate, just do it
if ( m_Local . m_flFOVRate = = 0.0f )
return fFOV ;
float deltaTime = ( float ) ( gpGlobals - > curtime - m_flFOVTime ) / m_Local . m_flFOVRate ;
if ( deltaTime > = 1.0f )
{
//If we're past the zoom time, just take the new value and stop lerping
m_iFOVStart = fFOV ;
}
else
{
fFOV = SimpleSplineRemapValClamped ( deltaTime , 0.0f , 1.0f , m_iFOVStart , fFOV ) ;
}
return fFOV ;
}
//-----------------------------------------------------------------------------
// Get the current FOV used for network computations
// Choose the smallest FOV, as it will open the largest number of portals
//-----------------------------------------------------------------------------
int CBasePlayer : : GetFOVForNetworking ( void )
{
int nDefaultFOV ;
// The vehicle's FOV wins if we're asking for a default value
if ( GetVehicle ( ) )
{
CacheVehicleView ( ) ;
nDefaultFOV = ( m_flVehicleViewFOV = = 0 ) ? GetDefaultFOV ( ) : ( int ) m_flVehicleViewFOV ;
}
else
{
nDefaultFOV = GetDefaultFOV ( ) ;
}
int fFOV = ( m_iFOV = = 0 ) ? nDefaultFOV : m_iFOV ;
// If it's immediate, just do it
if ( m_Local . m_flFOVRate = = 0.0f )
return fFOV ;
if ( gpGlobals - > curtime - m_flFOVTime < m_Local . m_flFOVRate )
{
fFOV = MIN ( fFOV , m_iFOVStart ) ;
}
return fFOV ;
}
float CBasePlayer : : GetFOVDistanceAdjustFactorForNetworking ( )
{
float defaultFOV = ( float ) GetDefaultFOV ( ) ;
float localFOV = ( float ) GetFOVForNetworking ( ) ;
if ( localFOV = = defaultFOV | | defaultFOV < 0.001f )
return 1.0f ;
// If FOV is lower, then we're "zoomed" in and this will give a factor < 1 so apparent LOD distances can be
// shorted accordingly
return localFOV / defaultFOV ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the default FOV for the player if nothing else is going on
// Input : FOV - the new base FOV for this player
//-----------------------------------------------------------------------------
void CBasePlayer : : SetDefaultFOV ( int FOV )
{
m_iDefaultFOV = ( FOV = = 0 ) ? g_pGameRules - > DefaultFOV ( ) : FOV ;
}
//-----------------------------------------------------------------------------
// Purpose: // static func
// Input : set -
//-----------------------------------------------------------------------------
void CBasePlayer : : ModifyOrAppendPlayerCriteria ( AI_CriteriaSet & set )
{
// Append our health
set . AppendCriteria ( " playerhealth " , UTIL_VarArgs ( " %i " , GetHealth ( ) ) ) ;
float healthfrac = 0.0f ;
if ( GetMaxHealth ( ) > 0 )
{
healthfrac = ( float ) GetHealth ( ) / ( float ) GetMaxHealth ( ) ;
}
set . AppendCriteria ( " playerhealthfrac " , UTIL_VarArgs ( " %.3f " , healthfrac ) ) ;
CBaseCombatWeapon * weapon = GetActiveWeapon ( ) ;
if ( weapon )
{
set . AppendCriteria ( " playerweapon " , weapon - > GetClassname ( ) ) ;
}
else
{
set . AppendCriteria ( " playerweapon " , " none " ) ;
}
// Append current activity name
set . AppendCriteria ( " playeractivity " , CAI_BaseNPC : : GetActivityName ( GetActivity ( ) ) ) ;
set . AppendCriteria ( " playerspeed " , UTIL_VarArgs ( " %.3f " , GetAbsVelocity ( ) . Length ( ) ) ) ;
AppendContextToCriteria ( set , " player " ) ;
}
const QAngle & CBasePlayer : : GetPunchAngle ( )
{
return m_Local . m_vecPunchAngle . Get ( ) ;
}
void CBasePlayer : : SetPunchAngle ( const QAngle & punchAngle )
{
m_Local . m_vecPunchAngle = punchAngle ;
if ( IsAlive ( ) )
{
int index = entindex ( ) ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( pPlayer & & i ! = index & & pPlayer - > GetObserverTarget ( ) = = this & & pPlayer - > GetObserverMode ( ) = = OBS_MODE_IN_EYE )
{
pPlayer - > SetPunchAngle ( punchAngle ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Apply a movement constraint to the player
//-----------------------------------------------------------------------------
void CBasePlayer : : ActivateMovementConstraint ( CBaseEntity * pEntity , const Vector & vecCenter , float flRadius , float flConstraintWidth , float flSpeedFactor )
{
m_hConstraintEntity = pEntity ;
m_vecConstraintCenter = vecCenter ;
m_flConstraintRadius = flRadius ;
m_flConstraintWidth = flConstraintWidth ;
m_flConstraintSpeedFactor = flSpeedFactor ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : DeactivateMovementConstraint ( )
{
m_hConstraintEntity = NULL ;
m_flConstraintRadius = 0.0f ;
m_vecConstraintCenter = vec3_origin ;
}
//-----------------------------------------------------------------------------
// Perhaps a poorly-named function. This function traces against the supplied
// NPC's hitboxes (instead of hull). If the trace hits a different NPC, the
// new NPC is selected. Otherwise, the supplied NPC is determined to be the
// one the citizen wants. This function allows the selection of a citizen over
// another citizen's shoulder, which is impossible without tracing against
// hitboxes instead of the hull (sjb)
//-----------------------------------------------------------------------------
CBaseEntity * CBasePlayer : : DoubleCheckUseNPC ( CBaseEntity * pNPC , const Vector & vecSrc , const Vector & vecDir )
{
trace_t tr ;
UTIL_TraceLine ( vecSrc , vecSrc + vecDir * 1024 , MASK_SHOT , this , COLLISION_GROUP_NONE , & tr ) ;
if ( tr . m_pEnt ! = NULL & & tr . m_pEnt - > MyNPCPointer ( ) & & tr . m_pEnt ! = pNPC )
{
// Player is selecting a different NPC through some negative space
// in the first NPC's hitboxes (between legs, over shoulder, etc).
return tr . m_pEnt ;
}
return pNPC ;
}
bool CBasePlayer : : IsBot ( ) const
{
return ( GetFlags ( ) & FL_FAKECLIENT ) ! = 0 ;
}
bool CBasePlayer : : IsFakeClient ( ) const
{
return ( GetFlags ( ) & FL_FAKECLIENT ) ! = 0 ;
}
void CBasePlayer : : EquipSuit ( bool bPlayEffects )
{
m_Local . m_bWearingSuit = true ;
}
void CBasePlayer : : RemoveSuit ( void )
{
m_Local . m_bWearingSuit = false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &tr -
// nDamageType -
//-----------------------------------------------------------------------------
void CBasePlayer : : DoImpactEffect ( trace_t & tr , int nDamageType )
{
if ( GetActiveWeapon ( ) )
{
GetActiveWeapon ( ) - > DoImpactEffect ( tr , nDamageType ) ;
return ;
}
BaseClass : : DoImpactEffect ( tr , nDamageType ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : InputSetHealth ( inputdata_t & inputdata )
{
int iNewHealth = inputdata . value . Int ( ) ;
int iDelta = abs ( GetHealth ( ) - iNewHealth ) ;
if ( iNewHealth > GetHealth ( ) )
{
TakeHealth ( iDelta , DMG_GENERIC ) ;
}
else if ( iNewHealth < GetHealth ( ) )
{
// Strip off and restore armor so that it doesn't absorb any of this damage.
int armor = m_ArmorValue ;
m_ArmorValue = 0 ;
TakeDamage ( CTakeDamageInfo ( this , this , iDelta , DMG_GENERIC ) ) ;
m_ArmorValue = armor ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : InputHandleMapEvent ( inputdata_t & inputdata )
{
Internal_HandleMapEvent ( inputdata ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Hides or displays the HUD
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CBasePlayer : : InputSetHUDVisibility ( inputdata_t & inputdata )
{
bool bEnable = inputdata . value . Bool ( ) ;
if ( bEnable )
{
m_Local . m_iHideHUD & = ~ HIDEHUD_ALL ;
}
else
{
m_Local . m_iHideHUD | = HIDEHUD_ALL ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Set the fog controller data per player.
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CBasePlayer : : InputSetFogController ( inputdata_t & inputdata )
{
// Find the fog controller with the given name.
CFogController * pFogController = dynamic_cast < CFogController * > ( gEntList . FindEntityByName ( NULL , inputdata . value . String ( ) ) ) ;
if ( pFogController )
{
m_Local . m_PlayerFog . m_hCtrl . Set ( pFogController ) ;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CBasePlayer : : InitFogController ( void )
{
// Setup with the default master controller.
m_Local . m_PlayerFog . m_hCtrl = FogSystem ( ) - > GetMasterFogController ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pEntity -
//-----------------------------------------------------------------------------
void CBasePlayer : : SetViewEntity ( CBaseEntity * pEntity )
{
m_hViewEntity = pEntity ;
if ( m_hViewEntity )
{
engine - > SetView ( edict ( ) , m_hViewEntity - > edict ( ) ) ;
}
else
{
engine - > SetView ( edict ( ) , edict ( ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Looks at the player's reserve ammo and also all his weapons for any ammo
// of the specified type
// Input : nAmmoIndex - ammo to look for
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBasePlayer : : HasAnyAmmoOfType ( int nAmmoIndex )
{
// Must be a valid index
if ( nAmmoIndex < 0 )
return false ;
// If we have some in reserve, we're already done
if ( GetAmmoCount ( nAmmoIndex ) )
return true ;
CBaseCombatWeapon * pWeapon ;
// Check all held weapons
for ( int i = 0 ; i < MAX_WEAPONS ; i + + )
{
pWeapon = GetWeapon ( i ) ;
if ( ! pWeapon )
continue ;
// We must use clips and use this sort of ammo
if ( pWeapon - > UsesClipsForAmmo1 ( ) & & pWeapon - > GetPrimaryAmmoType ( ) = = nAmmoIndex )
{
// If we have any ammo, we're done
if ( pWeapon - > HasPrimaryAmmo ( ) )
return true ;
}
// We'll check both clips for the same ammo type, just in case
if ( pWeapon - > UsesClipsForAmmo2 ( ) & & pWeapon - > GetSecondaryAmmoType ( ) = = nAmmoIndex )
{
if ( pWeapon - > HasSecondaryAmmo ( ) )
return true ;
}
}
// We're completely without this type of ammo
return false ;
}
bool CBasePlayer : : HandleVoteCommands ( const CCommand & args )
{
if ( g_voteController = = NULL )
return false ;
if ( FStrEq ( args [ 0 ] , " Vote " ) )
{
if ( args . ArgC ( ) < 2 )
return true ;
const char * arg2 = args [ 1 ] ;
char szResultString [ MAX_COMMAND_LENGTH ] ;
CVoteController : : TryCastVoteResult nTryResult = g_voteController - > TryCastVote ( entindex ( ) , arg2 ) ;
switch ( nTryResult )
{
case CVoteController : : CAST_OK :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Voting %s. \n " , arg2 ) ;
break ;
}
case CVoteController : : CAST_FAIL_SERVER_DISABLE :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Vote failed: server disabled. \n " ) ;
break ;
}
case CVoteController : : CAST_FAIL_NO_ACTIVE_ISSUE :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " A vote has not been called. \n " ) ;
break ;
}
case CVoteController : : CAST_FAIL_TEAM_RESTRICTED :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Vote failed: team restricted. \n " ) ;
break ;
}
case CVoteController : : CAST_FAIL_NO_CHANGES :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Vote failed: no changing vote. \n " ) ;
break ;
}
case CVoteController : : CAST_FAIL_DUPLICATE :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Vote failed: already voting %s. \n " , arg2 ) ;
break ;
}
case CVoteController : : CAST_FAIL_VOTE_CLOSED :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Vote failed: voting closed. \n " ) ;
break ;
}
case CVoteController : : CAST_FAIL_SYSTEM_ERROR :
default :
{
Q_snprintf ( szResultString , MAX_COMMAND_LENGTH , " Vote failed: system error. \n " ) ;
break ;
}
}
DevMsg ( " %s " , szResultString ) ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// return a string version of the players network (i.e steam) ID.
//
//-----------------------------------------------------------------------------
const char * CBasePlayer : : GetNetworkIDString ( )
{
const char * pStr = engine - > GetPlayerNetworkIDString ( edict ( ) ) ;
Q_strncpy ( m_szNetworkIDString , pStr ? pStr : " " , sizeof ( m_szNetworkIDString ) ) ;
return m_szNetworkIDString ;
}
//-----------------------------------------------------------------------------
// Assign the player a name
//-----------------------------------------------------------------------------
void CBasePlayer : : SetPlayerName ( const char * name )
{
Assert ( name ) ;
if ( name )
{
Assert ( strlen ( name ) > 0 ) ;
Q_strncpy ( m_szNetname , name , sizeof ( m_szNetname ) ) ;
}
}
//-----------------------------------------------------------------------------
// sets the "don't autokick me" flag on a player
//-----------------------------------------------------------------------------
class DisableAutokick
{
public :
DisableAutokick ( int userID )
{
m_userID = userID ;
}
bool operator ( ) ( CBasePlayer * player )
{
if ( player - > GetUserID ( ) = = m_userID )
{
Msg ( " autokick is disabled for %s \n " , player - > GetPlayerName ( ) ) ;
player - > DisableAutoKick ( true ) ;
return false ; // don't need to check other players
}
return true ; // keep looking at other players
}
private :
int m_userID ;
} ;
//-----------------------------------------------------------------------------
// sets the "don't autokick me" flag on a player
//-----------------------------------------------------------------------------
CON_COMMAND ( mp_disable_autokick , " Prevents a userid from being auto-kicked " )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
if ( args . ArgC ( ) ! = 2 )
{
Msg ( " Usage: mp_disable_autokick <userid> \n " ) ;
return ;
}
int userID = atoi ( args [ 1 ] ) ;
DisableAutokick disable ( userID ) ;
ForEachPlayer ( disable ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Toggle between the duck being on and off
//-----------------------------------------------------------------------------
void CBasePlayer : : ToggleDuck ( void )
{
// Toggle the state
m_bDuckToggled = ! m_bDuckToggled ;
}
//-----------------------------------------------------------------------------
// Just tells us how far the stick is from the center. No directional info
//-----------------------------------------------------------------------------
float CBasePlayer : : GetStickDist ( )
{
Vector2D controlStick ;
controlStick . x = m_flForwardMove ;
controlStick . y = m_flSideMove ;
return controlStick . Length ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBasePlayer : : HandleAnimEvent ( animevent_t * pEvent )
{
if ( ( pEvent - > type & AE_TYPE_NEWEVENTSYSTEM ) & & ( pEvent - > type & AE_TYPE_SERVER ) )
{
if ( pEvent - > event = = AE_RAGDOLL )
{
// Convert to ragdoll immediately
CreateRagdollEntity ( ) ;
BecomeRagdollOnClient ( vec3_origin ) ;
// Force the player to start death thinking
SetThink ( & CBasePlayer : : PlayerDeathThink ) ;
SetNextThink ( gpGlobals - > curtime + 0.1f ) ;
return ;
}
}
BaseClass : : HandleAnimEvent ( pEvent ) ;
}
//-----------------------------------------------------------------------------
// CPlayerInfo functions (simple passthroughts to get around the CBasePlayer multiple inheritence limitation)
//-----------------------------------------------------------------------------
const char * CPlayerInfo : : GetName ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetPlayerName ( ) ;
}
int CPlayerInfo : : GetUserID ( )
{
Assert ( m_pParent ) ;
return engine - > GetPlayerUserId ( m_pParent - > edict ( ) ) ;
}
const char * CPlayerInfo : : GetNetworkIDString ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetNetworkIDString ( ) ;
}
int CPlayerInfo : : GetTeamIndex ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetTeamNumber ( ) ;
}
void CPlayerInfo : : ChangeTeam ( int iTeamNum )
{
Assert ( m_pParent ) ;
m_pParent - > ChangeTeam ( iTeamNum ) ;
}
int CPlayerInfo : : GetFragCount ( )
{
Assert ( m_pParent ) ;
return m_pParent - > FragCount ( ) ;
}
int CPlayerInfo : : GetDeathCount ( )
{
Assert ( m_pParent ) ;
return m_pParent - > DeathCount ( ) ;
}
bool CPlayerInfo : : IsConnected ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsConnected ( ) ;
}
int CPlayerInfo : : GetArmorValue ( )
{
Assert ( m_pParent ) ;
return m_pParent - > ArmorValue ( ) ;
}
bool CPlayerInfo : : IsHLTV ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsHLTV ( ) ;
}
bool CPlayerInfo : : IsReplay ( )
{
# ifdef TF_DLL // FIXME: Need run-time check for whether replay is enabled
Assert ( m_pParent ) ;
return m_pParent - > IsReplay ( ) ;
# else
return false ;
# endif
}
bool CPlayerInfo : : IsPlayer ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsPlayer ( ) ;
}
bool CPlayerInfo : : IsFakeClient ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsFakeClient ( ) ;
}
bool CPlayerInfo : : IsDead ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsDead ( ) ;
}
bool CPlayerInfo : : IsInAVehicle ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsInAVehicle ( ) ;
}
bool CPlayerInfo : : IsObserver ( )
{
Assert ( m_pParent ) ;
return m_pParent - > IsObserver ( ) ;
}
const Vector CPlayerInfo : : GetAbsOrigin ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetAbsOrigin ( ) ;
}
const QAngle CPlayerInfo : : GetAbsAngles ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetAbsAngles ( ) ;
}
const Vector CPlayerInfo : : GetPlayerMins ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetPlayerMins ( ) ;
}
const Vector CPlayerInfo : : GetPlayerMaxs ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetPlayerMaxs ( ) ;
}
const char * CPlayerInfo : : GetWeaponName ( )
{
Assert ( m_pParent ) ;
CBaseCombatWeapon * weap = m_pParent - > GetActiveWeapon ( ) ;
if ( ! weap )
{
return NULL ;
}
return weap - > GetName ( ) ;
}
const char * CPlayerInfo : : GetModelName ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetModelName ( ) . ToCStr ( ) ;
}
const int CPlayerInfo : : GetHealth ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetHealth ( ) ;
}
const int CPlayerInfo : : GetMaxHealth ( )
{
Assert ( m_pParent ) ;
return m_pParent - > GetMaxHealth ( ) ;
}
void CPlayerInfo : : SetAbsOrigin ( Vector & vec )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
m_pParent - > SetAbsOrigin ( vec ) ;
}
}
void CPlayerInfo : : SetAbsAngles ( QAngle & ang )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
m_pParent - > SetAbsAngles ( ang ) ;
}
}
void CPlayerInfo : : RemoveAllItems ( bool removeSuit )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
m_pParent - > RemoveAllItems ( removeSuit ) ;
}
}
void CPlayerInfo : : SetActiveWeapon ( const char * WeaponName )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
CBaseCombatWeapon * weap = m_pParent - > Weapon_Create ( WeaponName ) ;
if ( weap )
{
m_pParent - > Weapon_Equip ( weap ) ;
m_pParent - > Weapon_Switch ( weap ) ;
}
}
}
void CPlayerInfo : : SetLocalOrigin ( const Vector & origin )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
m_pParent - > SetLocalOrigin ( origin ) ;
}
}
const Vector CPlayerInfo : : GetLocalOrigin ( void )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
Vector origin = m_pParent - > GetLocalOrigin ( ) ;
return origin ;
}
else
{
return Vector ( 0 , 0 , 0 ) ;
}
}
void CPlayerInfo : : SetLocalAngles ( const QAngle & angles )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
m_pParent - > SetLocalAngles ( angles ) ;
}
}
const QAngle CPlayerInfo : : GetLocalAngles ( void )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
return m_pParent - > GetLocalAngles ( ) ;
}
else
{
return QAngle ( ) ;
}
}
bool CPlayerInfo : : IsEFlagSet ( int nEFlagMask )
{
Assert ( m_pParent ) ;
if ( m_pParent - > IsBot ( ) )
{
return m_pParent - > IsEFlagSet ( nEFlagMask ) ;
}
return false ;
}
void CPlayerInfo : : RunPlayerMove ( CBotCmd * ucmd )
{
if ( m_pParent - > IsBot ( ) )
{
Assert ( m_pParent ) ;
CUserCmd cmd ;
cmd . buttons = ucmd - > buttons ;
cmd . command_number = ucmd - > command_number ;
cmd . forwardmove = ucmd - > forwardmove ;
cmd . hasbeenpredicted = ucmd - > hasbeenpredicted ;
cmd . impulse = ucmd - > impulse ;
cmd . mousedx = ucmd - > mousedx ;
cmd . mousedy = ucmd - > mousedy ;
cmd . random_seed = ucmd - > random_seed ;
cmd . sidemove = ucmd - > sidemove ;
cmd . tick_count = ucmd - > tick_count ;
cmd . upmove = ucmd - > upmove ;
cmd . viewangles = ucmd - > viewangles ;
cmd . weaponselect = ucmd - > weaponselect ;
cmd . weaponsubtype = ucmd - > weaponsubtype ;
// Store off the globals.. they're gonna get whacked
float flOldFrametime = gpGlobals - > frametime ;
float flOldCurtime = gpGlobals - > curtime ;
m_pParent - > SetTimeBase ( gpGlobals - > curtime ) ;
MoveHelperServer ( ) - > SetHost ( m_pParent ) ;
m_pParent - > PlayerRunCommand ( & cmd , MoveHelperServer ( ) ) ;
// save off the last good usercmd
m_pParent - > SetLastUserCommand ( cmd ) ;
// Clear out any fixangle that has been set
m_pParent - > pl . fixangle = FIXANGLE_NONE ;
// Restore the globals..
gpGlobals - > frametime = flOldFrametime ;
gpGlobals - > curtime = flOldCurtime ;
MoveHelperServer ( ) - > SetHost ( NULL ) ;
}
}
void CPlayerInfo : : SetLastUserCommand ( const CBotCmd & ucmd )
{
if ( m_pParent - > IsBot ( ) )
{
Assert ( m_pParent ) ;
CUserCmd cmd ;
cmd . buttons = ucmd . buttons ;
cmd . command_number = ucmd . command_number ;
cmd . forwardmove = ucmd . forwardmove ;
cmd . hasbeenpredicted = ucmd . hasbeenpredicted ;
cmd . impulse = ucmd . impulse ;
cmd . mousedx = ucmd . mousedx ;
cmd . mousedy = ucmd . mousedy ;
cmd . random_seed = ucmd . random_seed ;
cmd . sidemove = ucmd . sidemove ;
cmd . tick_count = ucmd . tick_count ;
cmd . upmove = ucmd . upmove ;
cmd . viewangles = ucmd . viewangles ;
cmd . weaponselect = ucmd . weaponselect ;
cmd . weaponsubtype = ucmd . weaponsubtype ;
m_pParent - > SetLastUserCommand ( cmd ) ;
}
}
CBotCmd CPlayerInfo : : GetLastUserCommand ( )
{
CBotCmd cmd ;
const CUserCmd * ucmd = m_pParent - > GetLastUserCommand ( ) ;
if ( ucmd )
{
cmd . buttons = ucmd - > buttons ;
cmd . command_number = ucmd - > command_number ;
cmd . forwardmove = ucmd - > forwardmove ;
cmd . hasbeenpredicted = ucmd - > hasbeenpredicted ;
cmd . impulse = ucmd - > impulse ;
cmd . mousedx = ucmd - > mousedx ;
cmd . mousedy = ucmd - > mousedy ;
cmd . random_seed = ucmd - > random_seed ;
cmd . sidemove = ucmd - > sidemove ;
cmd . tick_count = ucmd - > tick_count ;
cmd . upmove = ucmd - > upmove ;
cmd . viewangles = ucmd - > viewangles ;
cmd . weaponselect = ucmd - > weaponselect ;
cmd . weaponsubtype = ucmd - > weaponsubtype ;
}
return cmd ;
}
// Notify that I've killed some other entity. (called from Victim's Event_Killed).
void CBasePlayer : : Event_KilledOther ( CBaseEntity * pVictim , const CTakeDamageInfo & info )
{
BaseClass : : Event_KilledOther ( pVictim , info ) ;
if ( pVictim ! = this )
{
gamestats - > Event_PlayerKilledOther ( this , pVictim , info ) ;
}
else
{
gamestats - > Event_PlayerSuicide ( this ) ;
}
}
void CBasePlayer : : SetModel ( const char * szModelName )
{
BaseClass : : SetModel ( szModelName ) ;
m_nBodyPitchPoseParam = LookupPoseParameter ( " body_pitch " ) ;
}
void CBasePlayer : : SetBodyPitch ( float flPitch )
{
if ( m_nBodyPitchPoseParam > = 0 )
{
SetPoseParameter ( m_nBodyPitchPoseParam , flPitch ) ;
}
}
void CBasePlayer : : AdjustDrownDmg ( int nAmount )
{
m_idrowndmg + = nAmount ;
if ( m_idrowndmg < m_idrownrestored )
{
m_idrowndmg = m_idrownrestored ;
}
}
# if !defined(NO_STEAM)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBasePlayer : : GetSteamID ( CSteamID * pID )
{
const CSteamID * pClientID = engine - > GetClientSteamID ( edict ( ) ) ;
if ( pClientID )
{
* pID = * pClientID ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
uint64 CBasePlayer : : GetSteamIDAsUInt64 ( void )
{
CSteamID steamIDForPlayer ;
if ( GetSteamID ( & steamIDForPlayer ) )
return steamIDForPlayer . ConvertToUint64 ( ) ;
return 0 ;
}
# endif // NO_STEAM