diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/player.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/player.cpp')
| -rw-r--r-- | mp/src/game/server/player.cpp | 9347 |
1 files changed, 9347 insertions, 0 deletions
diff --git a/mp/src/game/server/player.cpp b/mp/src/game/server/player.cpp new file mode 100644 index 00000000..2738e40a --- /dev/null +++ b/mp/src/game/server/player.cpp @@ -0,0 +1,9347 @@ +//========= 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_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() ) )
+ 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() ) )
+ {
+ // 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)
+{
+ SetAbsOrigin( origin );
+ SetAbsVelocity( vec3_origin ); // stop movement
+ SetLocalAngles( angles );
+ SnapEyeAngles( angles );
+}
+
+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
+
+ if ( int numUsrCmdProcessTicksMax = sv_maxusrcmdprocessticks.GetInt() )
+ {
+ // 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 8bit 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 (1st 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 ¶ms )
+{
+ // 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 ¶ms )
+{
+ 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;
+ }
+ }
+
+#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 = 1e24f;
+ 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: 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
\ No newline at end of file |