From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/server/player.cpp | 18692 ++++++++++++++++++++-------------------- 1 file changed, 9346 insertions(+), 9346 deletions(-) (limited to 'mp/src/game/server/player.cpp') diff --git a/mp/src/game/server/player.cpp b/mp/src/game/server/player.cpp index 2738e40a..9ae85cc0 100644 --- a/mp/src/game/server/player.cpp +++ b/mp/src/game/server/player.cpp @@ -1,9347 +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( 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 *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 );//<>//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(pObject); - if( pBomb ) - return true; - - // Allow pickup of phys props that are motion enabled on player pickup - CPhysicsProp *pProp = dynamic_cast(pObject); - CPhysBox *pBox = dynamic_cast(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( (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( 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<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 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( 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 \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; -} +//========= 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( 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 *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 );//<>//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(pObject); + if( pBomb ) + return true; + + // Allow pickup of phys props that are motion enabled on player pickup + CPhysicsProp *pProp = dynamic_cast(pObject); + CPhysBox *pBox = dynamic_cast(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( (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( 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<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 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( 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 \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 -- cgit v1.2.3