sourcearena/mp/src/game/server/hl2/hl2_player.cpp
Joe Ludwig beaae8ac45 Updated the SDK with the latest code from the TF and HL2 branches
* Adds support for Visual Studio 2012 and 2013
* VR Mode:
. Switches from headtrack.dll to sourcevr.dll
. Improved readability of the UI in VR
. Removed the IPD calibration tool. TF2 will now obey the Oculus
configuration file. Use the Oculus calibration tool in your SDK or
install and run "OpenVR" under Tools in Steam to calibrate your IPD.
. Added dropdown to enable VR mode in the Video options. Removed the -vr
command line option.
. Added the ability to switch in and out of VR mode without quitting the
game
. By default VR mode will run full screen. To switch back to a
borderless window set the vr_force_windowed convar.
. Added support for VR mode on Linux
* Many assorted bug fixes and other changes from Team Fortress in
various shared files
2013-12-03 08:54:16 -08:00

3931 lines
107 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Player for HL2.
//
//=============================================================================//
#include "cbase.h"
#include "hl2_player.h"
#include "globalstate.h"
#include "game.h"
#include "gamerules.h"
#include "trains.h"
#include "basehlcombatweapon_shared.h"
#include "vcollide_parse.h"
#include "in_buttons.h"
#include "ai_interactions.h"
#include "ai_squad.h"
#include "igamemovement.h"
#include "ai_hull.h"
#include "hl2_shareddefs.h"
#include "info_camera_link.h"
#include "point_camera.h"
#include "engine/IEngineSound.h"
#include "ndebugoverlay.h"
#include "iservervehicle.h"
#include "IVehicle.h"
#include "globals.h"
#include "collisionutils.h"
#include "coordsize.h"
#include "effect_color_tables.h"
#include "vphysics/player_controller.h"
#include "player_pickup.h"
#include "weapon_physcannon.h"
#include "script_intro.h"
#include "effect_dispatch_data.h"
#include "te_effect_dispatch.h"
#include "ai_basenpc.h"
#include "AI_Criteria.h"
#include "npc_barnacle.h"
#include "entitylist.h"
#include "env_zoom.h"
#include "hl2_gamerules.h"
#include "prop_combine_ball.h"
#include "datacache/imdlcache.h"
#include "eventqueue.h"
#include "gamestats.h"
#include "filters.h"
#include "tier0/icommandline.h"
#ifdef HL2_EPISODIC
#include "npc_alyx_episodic.h"
#endif
#ifdef PORTAL
#include "portal_player.h"
#endif // PORTAL
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern ConVar weapon_showproficiency;
extern ConVar autoaim_max_dist;
// Do not touch with without seeing me, please! (sjb)
// For consistency's sake, enemy gunfire is traced against a scaled down
// version of the player's hull, not the hitboxes for the player's model
// because the player isn't aware of his model, and can't do anything about
// preventing headshots and other such things. Also, game difficulty will
// not change if the model changes. This is the value by which to scale
// the X/Y of the player's hull to get the volume to trace bullets against.
#define PLAYER_HULL_REDUCTION 0.70
// This switches between the single primary weapon, and multiple weapons with buckets approach (jdw)
#define HL2_SINGLE_PRIMARY_WEAPON_MODE 0
#define TIME_IGNORE_FALL_DAMAGE 10.0
extern int gEvilImpulse101;
ConVar sv_autojump( "sv_autojump", "0" );
ConVar hl2_walkspeed( "hl2_walkspeed", "150" );
ConVar hl2_normspeed( "hl2_normspeed", "190" );
ConVar hl2_sprintspeed( "hl2_sprintspeed", "320" );
ConVar hl2_darkness_flashlight_factor ( "hl2_darkness_flashlight_factor", "1" );
#ifdef HL2MP
#define HL2_WALK_SPEED 150
#define HL2_NORM_SPEED 190
#define HL2_SPRINT_SPEED 320
#else
#define HL2_WALK_SPEED hl2_walkspeed.GetFloat()
#define HL2_NORM_SPEED hl2_normspeed.GetFloat()
#define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
#endif
ConVar player_showpredictedposition( "player_showpredictedposition", "0" );
ConVar player_showpredictedposition_timestep( "player_showpredictedposition_timestep", "1.0" );
ConVar player_squad_transient_commands( "player_squad_transient_commands", "1", FCVAR_REPLICATED );
ConVar player_squad_double_tap_time( "player_squad_double_tap_time", "0.25" );
ConVar sv_infinite_aux_power( "sv_infinite_aux_power", "0", FCVAR_CHEAT );
ConVar autoaim_unlock_target( "autoaim_unlock_target", "0.8666" );
ConVar sv_stickysprint("sv_stickysprint", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX);
#define FLASH_DRAIN_TIME 1.1111 // 100 units / 90 secs
#define FLASH_CHARGE_TIME 50.0f // 100 units / 2 secs
//==============================================================================================
// CAPPED PLAYER PHYSICS DAMAGE TABLE
//==============================================================================================
static impactentry_t cappedPlayerLinearTable[] =
{
{ 150*150, 5 },
{ 250*250, 10 },
{ 450*450, 20 },
{ 550*550, 30 },
//{ 700*700, 100 },
//{ 1000*1000, 500 },
};
static impactentry_t cappedPlayerAngularTable[] =
{
{ 100*100, 10 },
{ 150*150, 20 },
{ 200*200, 30 },
//{ 300*300, 500 },
};
static impactdamagetable_t gCappedPlayerImpactDamageTable =
{
cappedPlayerLinearTable,
cappedPlayerAngularTable,
ARRAYSIZE(cappedPlayerLinearTable),
ARRAYSIZE(cappedPlayerAngularTable),
24*24.0f, // minimum linear speed
360*360.0f, // minimum angular speed
2.0f, // can't take damage from anything under 2kg
5.0f, // anything less than 5kg is "small"
5.0f, // never take more than 5 pts of damage from anything under 5kg
36*36.0f, // <5kg objects must go faster than 36 in/s to do damage
0.0f, // large mass in kg (no large mass effects)
1.0f, // large mass scale
2.0f, // large mass falling scale
320.0f, // min velocity for player speed to cause damage
};
// Flashlight utility
bool g_bCacheLegacyFlashlightStatus = true;
bool g_bUseLegacyFlashlight;
bool Flashlight_UseLegacyVersion( void )
{
// If this is the first run through, cache off what the answer should be (cannot change during a session)
if ( g_bCacheLegacyFlashlightStatus )
{
char modDir[MAX_PATH];
if ( UTIL_GetModDir( modDir, sizeof(modDir) ) == false )
return false;
g_bUseLegacyFlashlight = ( !Q_strcmp( modDir, "hl2" ) ||
!Q_strcmp( modDir, "episodic" ) ||
!Q_strcmp( modDir, "lostcoast" ) || !Q_strcmp( modDir, "hl1" ));
g_bCacheLegacyFlashlightStatus = false;
}
// Return the results
return g_bUseLegacyFlashlight;
}
//-----------------------------------------------------------------------------
// Purpose: Used to relay outputs/inputs from the player to the world and viceversa
//-----------------------------------------------------------------------------
class CLogicPlayerProxy : public CLogicalEntity
{
DECLARE_CLASS( CLogicPlayerProxy, CLogicalEntity );
private:
DECLARE_DATADESC();
public:
COutputEvent m_OnFlashlightOn;
COutputEvent m_OnFlashlightOff;
COutputEvent m_PlayerHasAmmo;
COutputEvent m_PlayerHasNoAmmo;
COutputEvent m_PlayerDied;
COutputEvent m_PlayerMissedAR2AltFire; // Player fired a combine ball which did not dissolve any enemies.
COutputInt m_RequestedPlayerHealth;
void InputRequestPlayerHealth( inputdata_t &inputdata );
void InputSetFlashlightSlowDrain( inputdata_t &inputdata );
void InputSetFlashlightNormalDrain( inputdata_t &inputdata );
void InputSetPlayerHealth( inputdata_t &inputdata );
void InputRequestAmmoState( inputdata_t &inputdata );
void InputLowerWeapon( inputdata_t &inputdata );
void InputEnableCappedPhysicsDamage( inputdata_t &inputdata );
void InputDisableCappedPhysicsDamage( inputdata_t &inputdata );
void InputSetLocatorTargetEntity( inputdata_t &inputdata );
#ifdef PORTAL
void InputSuppressCrosshair( inputdata_t &inputdata );
#endif // PORTAL2
void Activate ( void );
bool PassesDamageFilter( const CTakeDamageInfo &info );
EHANDLE m_hPlayer;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CC_ToggleZoom( void )
{
CBasePlayer* pPlayer = UTIL_GetCommandClient();
if( pPlayer )
{
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>(pPlayer);
if( pHL2Player && pHL2Player->IsSuitEquipped() )
{
pHL2Player->ToggleZoom();
}
}
}
static ConCommand toggle_zoom("toggle_zoom", CC_ToggleZoom, "Toggles zoom display" );
// ConVar cl_forwardspeed( "cl_forwardspeed", "400", FCVAR_CHEAT ); // Links us to the client's version
ConVar xc_crouch_range( "xc_crouch_range", "0.85", FCVAR_ARCHIVE, "Percentarge [1..0] of joystick range to allow ducking within" ); // Only 1/2 of the range is used
ConVar xc_use_crouch_limiter( "xc_use_crouch_limiter", "0", FCVAR_ARCHIVE, "Use the crouch limiting logic on the controller" );
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CC_ToggleDuck( void )
{
CBasePlayer* pPlayer = UTIL_GetCommandClient();
if ( pPlayer == NULL )
return;
// Cannot be frozen
if ( pPlayer->GetFlags() & FL_FROZEN )
return;
static bool bChecked = false;
static ConVar *pCVcl_forwardspeed = NULL;
if ( !bChecked )
{
bChecked = true;
pCVcl_forwardspeed = ( ConVar * )cvar->FindVar( "cl_forwardspeed" );
}
// If we're not ducked, do extra checking
if ( xc_use_crouch_limiter.GetBool() )
{
if ( pPlayer->GetToggledDuckState() == false )
{
float flForwardSpeed = 400.0f;
if ( pCVcl_forwardspeed )
{
flForwardSpeed = pCVcl_forwardspeed->GetFloat();
}
flForwardSpeed = MAX( 1.0f, flForwardSpeed );
// Make sure we're not in the blindspot on the crouch detection
float flStickDistPerc = ( pPlayer->GetStickDist() / flForwardSpeed ); // Speed is the magnitude
if ( flStickDistPerc > xc_crouch_range.GetFloat() )
return;
}
}
// Toggle the duck
pPlayer->ToggleDuck();
}
static ConCommand toggle_duck("toggle_duck", CC_ToggleDuck, "Toggles duck" );
#ifndef HL2MP
#ifndef PORTAL
LINK_ENTITY_TO_CLASS( player, CHL2_Player );
#endif
#endif
PRECACHE_REGISTER(player);
CBaseEntity *FindEntityForward( CBasePlayer *pMe, bool fHull );
BEGIN_SIMPLE_DATADESC( LadderMove_t )
DEFINE_FIELD( m_bForceLadderMove, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bForceMount, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_FIELD( m_flArrivalTime, FIELD_TIME ),
DEFINE_FIELD( m_vecGoalPosition, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_vecStartPosition, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_hForceLadder, FIELD_EHANDLE ),
DEFINE_FIELD( m_hReservedSpot, FIELD_EHANDLE ),
END_DATADESC()
// Global Savedata for HL2 player
BEGIN_DATADESC( CHL2_Player )
DEFINE_FIELD( m_nControlClass, FIELD_INTEGER ),
DEFINE_EMBEDDED( m_HL2Local ),
DEFINE_FIELD( m_bSprintEnabled, FIELD_BOOLEAN ),
DEFINE_FIELD( m_flTimeAllSuitDevicesOff, FIELD_TIME ),
DEFINE_FIELD( m_fIsSprinting, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fIsWalking, FIELD_BOOLEAN ),
/*
// These are initialized every time the player calls Activate()
DEFINE_FIELD( m_bIsAutoSprinting, FIELD_BOOLEAN ),
DEFINE_FIELD( m_fAutoSprintMinTime, FIELD_TIME ),
*/
// Field is used within a single tick, no need to save restore
// DEFINE_FIELD( m_bPlayUseDenySound, FIELD_BOOLEAN ),
// m_pPlayerAISquad reacquired on load
DEFINE_AUTO_ARRAY( m_vecMissPositions, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( m_nNumMissPositions, FIELD_INTEGER ),
// m_pPlayerAISquad
DEFINE_EMBEDDED( m_CommanderUpdateTimer ),
// m_RealTimeLastSquadCommand
DEFINE_FIELD( m_QueuedCommand, FIELD_INTEGER ),
DEFINE_FIELD( m_flTimeIgnoreFallDamage, FIELD_TIME ),
DEFINE_FIELD( m_bIgnoreFallDamageResetAfterImpact, FIELD_BOOLEAN ),
// Suit power fields
DEFINE_FIELD( m_flSuitPowerLoad, FIELD_FLOAT ),
DEFINE_FIELD( m_flIdleTime, FIELD_TIME ),
DEFINE_FIELD( m_flMoveTime, FIELD_TIME ),
DEFINE_FIELD( m_flLastDamageTime, FIELD_TIME ),
DEFINE_FIELD( m_flTargetFindTime, FIELD_TIME ),
DEFINE_FIELD( m_flAdmireGlovesAnimTime, FIELD_TIME ),
DEFINE_FIELD( m_flNextFlashlightCheckTime, FIELD_TIME ),
DEFINE_FIELD( m_flFlashlightPowerDrainScale, FIELD_FLOAT ),
DEFINE_FIELD( m_bFlashlightDisabled, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bUseCappedPhysicsDamageTable, FIELD_BOOLEAN ),
DEFINE_FIELD( m_hLockedAutoAimEntity, FIELD_EHANDLE ),
DEFINE_EMBEDDED( m_LowerWeaponTimer ),
DEFINE_EMBEDDED( m_AutoaimTimer ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreFallDamage", InputIgnoreFallDamage ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "IgnoreFallDamageWithoutReset", InputIgnoreFallDamageWithoutReset ),
DEFINE_INPUTFUNC( FIELD_VOID, "OnSquadMemberKilled", OnSquadMemberKilled ),
DEFINE_INPUTFUNC( FIELD_VOID, "DisableFlashlight", InputDisableFlashlight ),
DEFINE_INPUTFUNC( FIELD_VOID, "EnableFlashlight", InputEnableFlashlight ),
DEFINE_INPUTFUNC( FIELD_VOID, "ForceDropPhysObjects", InputForceDropPhysObjects ),
DEFINE_SOUNDPATCH( m_sndLeeches ),
DEFINE_SOUNDPATCH( m_sndWaterSplashes ),
DEFINE_FIELD( m_flArmorReductionTime, FIELD_TIME ),
DEFINE_FIELD( m_iArmorReductionFrom, FIELD_INTEGER ),
DEFINE_FIELD( m_flTimeUseSuspended, FIELD_TIME ),
DEFINE_FIELD( m_hLocatorTargetEntity, FIELD_EHANDLE ),
DEFINE_FIELD( m_flTimeNextLadderHint, FIELD_TIME ),
//DEFINE_FIELD( m_hPlayerProxy, FIELD_EHANDLE ), //Shut up class check!
END_DATADESC()
CHL2_Player::CHL2_Player()
{
m_nNumMissPositions = 0;
m_pPlayerAISquad = 0;
m_bSprintEnabled = true;
m_flArmorReductionTime = 0.0f;
m_iArmorReductionFrom = 0;
}
//
// SUIT POWER DEVICES
//
#define SUITPOWER_CHARGE_RATE 12.5 // 100 units in 8 seconds
#ifdef HL2MP
CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 25.0f ); // 100 units in 4 seconds
#else
CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 12.5f ); // 100 units in 8 seconds
#endif
#ifdef HL2_EPISODIC
CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 1.111 ); // 100 units in 90 second
#else
CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 2.222 ); // 100 units in 45 second
#endif
CSuitPowerDevice SuitDeviceBreather( bits_SUIT_DEVICE_BREATHER, 6.7f ); // 100 units in 15 seconds (plus three padded seconds)
IMPLEMENT_SERVERCLASS_ST(CHL2_Player, DT_HL2_Player)
SendPropDataTable(SENDINFO_DT(m_HL2Local), &REFERENCE_SEND_TABLE(DT_HL2Local), SendProxy_SendLocalDataTable),
SendPropBool( SENDINFO(m_fIsSprinting) ),
END_SEND_TABLE()
void CHL2_Player::Precache( void )
{
BaseClass::Precache();
PrecacheScriptSound( "HL2Player.SprintNoPower" );
PrecacheScriptSound( "HL2Player.SprintStart" );
PrecacheScriptSound( "HL2Player.UseDeny" );
PrecacheScriptSound( "HL2Player.FlashLightOn" );
PrecacheScriptSound( "HL2Player.FlashLightOff" );
PrecacheScriptSound( "HL2Player.PickupWeapon" );
PrecacheScriptSound( "HL2Player.TrainUse" );
PrecacheScriptSound( "HL2Player.Use" );
PrecacheScriptSound( "HL2Player.BurnPain" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::CheckSuitZoom( void )
{
//#ifndef _XBOX
//Adrian - No zooming without a suit!
if ( IsSuitEquipped() )
{
if ( m_afButtonReleased & IN_ZOOM )
{
StopZooming();
}
else if ( m_afButtonPressed & IN_ZOOM )
{
StartZooming();
}
}
//#endif//_XBOX
}
void CHL2_Player::EquipSuit( bool bPlayEffects )
{
MDLCACHE_CRITICAL_SECTION();
BaseClass::EquipSuit();
m_HL2Local.m_bDisplayReticle = true;
if ( bPlayEffects == true )
{
StartAdmireGlovesAnimation();
}
}
void CHL2_Player::RemoveSuit( void )
{
BaseClass::RemoveSuit();
m_HL2Local.m_bDisplayReticle = false;
}
void CHL2_Player::HandleSpeedChanges( void )
{
int buttonsChanged = m_afButtonPressed | m_afButtonReleased;
bool bCanSprint = CanSprint();
bool bIsSprinting = IsSprinting();
bool bWantSprint = ( bCanSprint && IsSuitEquipped() && (m_nButtons & IN_SPEED) );
if ( bIsSprinting != bWantSprint && (buttonsChanged & IN_SPEED) )
{
// If someone wants to sprint, make sure they've pressed the button to do so. We want to prevent the
// case where a player can hold down the sprint key and burn tiny bursts of sprint as the suit recharges
// We want a full debounce of the key to resume sprinting after the suit is completely drained
if ( bWantSprint )
{
if ( sv_stickysprint.GetBool() )
{
StartAutoSprint();
}
else
{
StartSprinting();
}
}
else
{
if ( !sv_stickysprint.GetBool() )
{
StopSprinting();
}
// Reset key, so it will be activated post whatever is suppressing it.
m_nButtons &= ~IN_SPEED;
}
}
bool bIsWalking = IsWalking();
// have suit, pressing button, not sprinting or ducking
bool bWantWalking;
if( IsSuitEquipped() )
{
bWantWalking = (m_nButtons & IN_WALK) && !IsSprinting() && !(m_nButtons & IN_DUCK);
}
else
{
bWantWalking = true;
}
if( bIsWalking != bWantWalking )
{
if ( bWantWalking )
{
StartWalking();
}
else
{
StopWalking();
}
}
}
//-----------------------------------------------------------------------------
// This happens when we powerdown from the mega physcannon to the regular one
//-----------------------------------------------------------------------------
void CHL2_Player::HandleArmorReduction( void )
{
if ( m_flArmorReductionTime < gpGlobals->curtime )
return;
if ( ArmorValue() <= 0 )
return;
float flPercent = 1.0f - (( m_flArmorReductionTime - gpGlobals->curtime ) / ARMOR_DECAY_TIME );
int iArmor = Lerp( flPercent, m_iArmorReductionFrom, 0 );
SetArmorValue( iArmor );
}
//-----------------------------------------------------------------------------
// Purpose: Allow pre-frame adjustments on the player
//-----------------------------------------------------------------------------
void CHL2_Player::PreThink(void)
{
if ( player_showpredictedposition.GetBool() )
{
Vector predPos;
UTIL_PredictedPosition( this, player_showpredictedposition_timestep.GetFloat(), &predPos );
NDebugOverlay::Box( predPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), 0, 255, 0, 0, 0.01f );
NDebugOverlay::Line( GetAbsOrigin(), predPos, 0, 255, 0, 0, 0.01f );
}
#ifdef HL2_EPISODIC
if( m_hLocatorTargetEntity != NULL )
{
// Keep track of the entity here, the client will pick up the rest of the work
m_HL2Local.m_vecLocatorOrigin = m_hLocatorTargetEntity->WorldSpaceCenter();
}
else
{
m_HL2Local.m_vecLocatorOrigin = vec3_invalid; // This tells the client we have no locator target.
}
#endif//HL2_EPISODIC
// Riding a vehicle?
if ( IsInAVehicle() )
{
VPROF( "CHL2_Player::PreThink-Vehicle" );
// make sure we update the client, check for timed damage and update suit even if we are in a vehicle
UpdateClientData();
CheckTimeBasedDamage();
// Allow the suit to recharge when in the vehicle.
SuitPower_Update();
CheckSuitUpdate();
CheckSuitZoom();
WaterMove();
return;
}
// This is an experiment of mine- autojumping!
// only affects you if sv_autojump is nonzero.
if( (GetFlags() & FL_ONGROUND) && sv_autojump.GetFloat() != 0 )
{
VPROF( "CHL2_Player::PreThink-Autojump" );
// check autojump
Vector vecCheckDir;
vecCheckDir = GetAbsVelocity();
float flVelocity = VectorNormalize( vecCheckDir );
if( flVelocity > 200 )
{
// Going fast enough to autojump
vecCheckDir = WorldSpaceCenter() + vecCheckDir * 34 - Vector( 0, 0, 16 );
trace_t tr;
UTIL_TraceHull( WorldSpaceCenter() - Vector( 0, 0, 16 ), vecCheckDir, NAI_Hull::Mins(HULL_TINY_CENTERED),NAI_Hull::Maxs(HULL_TINY_CENTERED), MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER, &tr );
//NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 10 );
if( tr.fraction == 1.0 && !tr.startsolid )
{
// Now trace down!
UTIL_TraceLine( vecCheckDir, vecCheckDir - Vector( 0, 0, 64 ), MASK_PLAYERSOLID, this, COLLISION_GROUP_NONE, &tr );
//NDebugOverlay::Line( tr.startpos, tr.endpos, 0,255,0, true, 10 );
if( tr.fraction == 1.0 && !tr.startsolid )
{
// !!!HACKHACK
// I KNOW, I KNOW, this is definitely not the right way to do this,
// but I'm prototyping! (sjb)
Vector vecNewVelocity = GetAbsVelocity();
vecNewVelocity.z += 250;
SetAbsVelocity( vecNewVelocity );
}
}
}
}
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-Speed" );
HandleSpeedChanges();
#ifdef HL2_EPISODIC
HandleArmorReduction();
#endif
if( sv_stickysprint.GetBool() && m_bIsAutoSprinting )
{
// If we're ducked and not in the air
if( IsDucked() && GetGroundEntity() != NULL )
{
StopSprinting();
}
// Stop sprinting if the player lets off the stick for a moment.
else if( GetStickDist() == 0.0f )
{
if( gpGlobals->curtime > m_fAutoSprintMinTime )
{
StopSprinting();
}
}
else
{
// Stop sprinting one half second after the player stops inputting with the move stick.
m_fAutoSprintMinTime = gpGlobals->curtime + 0.5f;
}
}
else if ( IsSprinting() )
{
// Disable sprint while ducked unless we're in the air (jumping)
if ( IsDucked() && ( GetGroundEntity() != NULL ) )
{
StopSprinting();
}
}
VPROF_SCOPE_END();
if ( g_fGameOver || IsPlayerLockedInPlace() )
return; // finale
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-ItemPreFrame" );
ItemPreFrame( );
VPROF_SCOPE_END();
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-WaterMove" );
WaterMove();
VPROF_SCOPE_END();
if ( g_pGameRules && g_pGameRules->FAllowFlashlight() )
m_Local.m_iHideHUD &= ~HIDEHUD_FLASHLIGHT;
else
m_Local.m_iHideHUD |= HIDEHUD_FLASHLIGHT;
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CommanderUpdate" );
CommanderUpdate();
VPROF_SCOPE_END();
// Operate suit accessories and manage power consumption/charge
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-SuitPower_Update" );
SuitPower_Update();
VPROF_SCOPE_END();
// checks if new client data (for HUD and view control) needs to be sent to the client
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-UpdateClientData" );
UpdateClientData();
VPROF_SCOPE_END();
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckTimeBasedDamage" );
CheckTimeBasedDamage();
VPROF_SCOPE_END();
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckSuitUpdate" );
CheckSuitUpdate();
VPROF_SCOPE_END();
VPROF_SCOPE_BEGIN( "CHL2_Player::PreThink-CheckSuitZoom" );
CheckSuitZoom();
VPROF_SCOPE_END();
if (m_lifeState >= LIFE_DYING)
{
PlayerDeathThink();
return;
}
#ifdef HL2_EPISODIC
CheckFlashlight();
#endif // HL2_EPISODIC
// So the correct flags get sent to client asap.
//
if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
AddFlag( FL_ONTRAIN );
else
RemoveFlag( FL_ONTRAIN );
// Train speed control
if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
{
CBaseEntity *pTrain = GetGroundEntity();
float vel;
if ( pTrain )
{
if ( !(pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) )
pTrain = NULL;
}
if ( !pTrain )
{
if ( GetActiveWeapon() && (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) )
{
// Warning( "In train mode with no train!\n" );
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;
}
}
else if (m_iTrain & TRAIN_ACTIVE)
{
m_iTrain = TRAIN_NEW; // turn off train
}
//
// If we're not on the ground, we're falling. Update our falling velocity.
//
if ( !( GetFlags() & FL_ONGROUND ) )
{
m_Local.m_flFallVelocity = -GetAbsVelocity().z;
}
if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
{
bool bOnBarnacle = false;
CNPC_Barnacle *pBarnacle = NULL;
do
{
// FIXME: Not a good or fast solution, but maybe it will catch the bug!
pBarnacle = (CNPC_Barnacle*)gEntList.FindEntityByClassname( pBarnacle, "npc_barnacle" );
if ( pBarnacle )
{
if ( pBarnacle->GetEnemy() == this )
{
bOnBarnacle = true;
}
}
} while ( pBarnacle );
if ( !bOnBarnacle )
{
Warning( "Attached to barnacle?\n" );
Assert( 0 );
m_afPhysicsFlags &= ~PFLAG_ONBARNACLE;
}
else
{
SetAbsVelocity( vec3_origin );
}
}
// StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating?
// Update weapon's ready status
UpdateWeaponPosture();
// Disallow shooting while zooming
if ( IsX360() )
{
if ( IsZooming() )
{
if( GetActiveWeapon() && !GetActiveWeapon()->IsWeaponZoomed() )
{
// If not zoomed because of the weapon itself, do not attack.
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
}
}
}
else
{
if ( m_nButtons & IN_ZOOM )
{
//FIXME: Held weapons like the grenade get sad when this happens
#ifdef HL2_EPISODIC
// Episodic allows players to zoom while using a func_tank
CBaseCombatWeapon* pWep = GetActiveWeapon();
if ( !m_hUseEntity || ( pWep && pWep->IsWeaponVisible() ) )
#endif
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
}
}
}
void CHL2_Player::PostThink( void )
{
BaseClass::PostThink();
if ( !g_fGameOver && !IsPlayerLockedInPlace() && IsAlive() )
{
HandleAdmireGlovesAnimation();
}
}
void CHL2_Player::StartAdmireGlovesAnimation( void )
{
MDLCACHE_CRITICAL_SECTION();
CBaseViewModel *vm = GetViewModel( 0 );
if ( vm && !GetActiveWeapon() )
{
vm->SetWeaponModel( "models/weapons/v_hands.mdl", NULL );
ShowViewModel( true );
int idealSequence = vm->SelectWeightedSequence( ACT_VM_IDLE );
if ( idealSequence >= 0 )
{
vm->SendViewModelMatchingSequence( idealSequence );
m_flAdmireGlovesAnimTime = gpGlobals->curtime + vm->SequenceDuration( idealSequence );
}
}
}
void CHL2_Player::HandleAdmireGlovesAnimation( void )
{
CBaseViewModel *pVM = GetViewModel();
if ( pVM && pVM->GetOwningWeapon() == NULL )
{
if ( m_flAdmireGlovesAnimTime != 0.0 )
{
if ( m_flAdmireGlovesAnimTime > gpGlobals->curtime )
{
pVM->m_flPlaybackRate = 1.0f;
pVM->StudioFrameAdvance( );
}
else if ( m_flAdmireGlovesAnimTime < gpGlobals->curtime )
{
m_flAdmireGlovesAnimTime = 0.0f;
pVM->SetWeaponModel( NULL, NULL );
}
}
}
else
m_flAdmireGlovesAnimTime = 0.0f;
}
#define HL2PLAYER_RELOADGAME_ATTACK_DELAY 1.0f
void CHL2_Player::Activate( void )
{
BaseClass::Activate();
InitSprinting();
#ifdef HL2_EPISODIC
// Delay attacks by 1 second after loading a game.
if ( GetActiveWeapon() )
{
float flRemaining = GetActiveWeapon()->m_flNextPrimaryAttack - gpGlobals->curtime;
if ( flRemaining < HL2PLAYER_RELOADGAME_ATTACK_DELAY )
{
GetActiveWeapon()->m_flNextPrimaryAttack = gpGlobals->curtime + HL2PLAYER_RELOADGAME_ATTACK_DELAY;
}
flRemaining = GetActiveWeapon()->m_flNextSecondaryAttack - gpGlobals->curtime;
if ( flRemaining < HL2PLAYER_RELOADGAME_ATTACK_DELAY )
{
GetActiveWeapon()->m_flNextSecondaryAttack = gpGlobals->curtime + HL2PLAYER_RELOADGAME_ATTACK_DELAY;
}
}
#endif
GetPlayerProxy();
}
//------------------------------------------------------------------------------
// Purpose :
// Input :
// Output :
//------------------------------------------------------------------------------
Class_T CHL2_Player::Classify ( void )
{
// If player controlling another entity? If so, return this class
if (m_nControlClass != CLASS_NONE)
{
return m_nControlClass;
}
else
{
if(IsInAVehicle())
{
IServerVehicle *pVehicle = GetVehicle();
return pVehicle->ClassifyPassenger( this, CLASS_PLAYER );
}
else
{
return CLASS_PLAYER;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: This is a generic function (to be implemented by sub-classes) to
// handle specific interactions between different types of characters
// (For example the barnacle grabbing an NPC)
// Input : Constant for the type of interaction
// Output : true - if sub-class has a response for the interaction
// false - if sub-class has no response
//-----------------------------------------------------------------------------
bool CHL2_Player::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
{
if ( interactionType == g_interactionBarnacleVictimDangle )
return false;
if (interactionType == g_interactionBarnacleVictimReleased)
{
m_afPhysicsFlags &= ~PFLAG_ONBARNACLE;
SetMoveType( MOVETYPE_WALK );
return true;
}
else if (interactionType == g_interactionBarnacleVictimGrab)
{
#ifdef HL2_EPISODIC
CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
if ( pAlyx )
{
// Make Alyx totally hate this barnacle so that she saves the player.
int priority;
priority = pAlyx->IRelationPriority(sourceEnt);
pAlyx->AddEntityRelationship( sourceEnt, D_HT, priority + 5 );
}
#endif//HL2_EPISODIC
m_afPhysicsFlags |= PFLAG_ONBARNACLE;
ClearUseEntity();
return true;
}
return false;
}
void CHL2_Player::PlayerRunCommand(CUserCmd *ucmd, IMoveHelper *moveHelper)
{
// Handle FL_FROZEN.
if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
{
ucmd->forwardmove = 0;
ucmd->sidemove = 0;
ucmd->upmove = 0;
ucmd->buttons &= ~IN_USE;
}
// Can't use stuff while dead
if ( IsDead() )
{
ucmd->buttons &= ~IN_USE;
}
//Update our movement information
if ( ( ucmd->forwardmove != 0 ) || ( ucmd->sidemove != 0 ) || ( ucmd->upmove != 0 ) )
{
m_flIdleTime -= TICK_INTERVAL * 2.0f;
if ( m_flIdleTime < 0.0f )
{
m_flIdleTime = 0.0f;
}
m_flMoveTime += TICK_INTERVAL;
if ( m_flMoveTime > 4.0f )
{
m_flMoveTime = 4.0f;
}
}
else
{
m_flIdleTime += TICK_INTERVAL;
if ( m_flIdleTime > 4.0f )
{
m_flIdleTime = 4.0f;
}
m_flMoveTime -= TICK_INTERVAL * 2.0f;
if ( m_flMoveTime < 0.0f )
{
m_flMoveTime = 0.0f;
}
}
//Msg("Player time: [ACTIVE: %f]\t[IDLE: %f]\n", m_flMoveTime, m_flIdleTime );
BaseClass::PlayerRunCommand( ucmd, moveHelper );
}
//-----------------------------------------------------------------------------
// Purpose: Sets HL2 specific defaults.
//-----------------------------------------------------------------------------
void CHL2_Player::Spawn(void)
{
#ifndef HL2MP
#ifndef PORTAL
SetModel( "models/player.mdl" );
#endif
#endif
BaseClass::Spawn();
//
// Our player movement speed is set once here. This will override the cl_xxxx
// cvars unless they are set to be lower than this.
//
//m_flMaxspeed = 320;
if ( !IsSuitEquipped() )
StartWalking();
SuitPower_SetCharge( 100 );
m_Local.m_iHideHUD |= HIDEHUD_CHAT;
m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME));
InitSprinting();
// Setup our flashlight values
#ifdef HL2_EPISODIC
m_HL2Local.m_flFlashBattery = 100.0f;
#endif
GetPlayerProxy();
SetFlashlightPowerDrainScale( 1.0f );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::UpdateLocatorPosition( const Vector &vecPosition )
{
#ifdef HL2_EPISODIC
m_HL2Local.m_vecLocatorOrigin = vecPosition;
#endif//HL2_EPISODIC
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::InitSprinting( void )
{
StopSprinting();
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether or not we are allowed to sprint now.
//-----------------------------------------------------------------------------
bool CHL2_Player::CanSprint()
{
return ( m_bSprintEnabled && // Only if sprint is enabled
!IsWalking() && // Not if we're walking
!( m_Local.m_bDucked && !m_Local.m_bDucking ) && // Nor if we're ducking
(GetWaterLevel() != 3) && // Certainly not underwater
(GlobalEntity_GetState("suit_no_sprint") != GLOBAL_ON) ); // Out of the question without the sprint module
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::StartAutoSprint()
{
if( IsSprinting() )
{
StopSprinting();
}
else
{
StartSprinting();
m_bIsAutoSprinting = true;
m_fAutoSprintMinTime = gpGlobals->curtime + 1.5f;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::StartSprinting( void )
{
if( m_HL2Local.m_flSuitPower < 10 )
{
// Don't sprint unless there's a reasonable
// amount of suit power.
// debounce the button for sound playing
if ( m_afButtonPressed & IN_SPEED )
{
CPASAttenuationFilter filter( this );
filter.UsePredictionRules();
EmitSound( filter, entindex(), "HL2Player.SprintNoPower" );
}
return;
}
if( !SuitPower_AddDevice( SuitDeviceSprint ) )
return;
CPASAttenuationFilter filter( this );
filter.UsePredictionRules();
EmitSound( filter, entindex(), "HL2Player.SprintStart" );
SetMaxSpeed( HL2_SPRINT_SPEED );
m_fIsSprinting = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::StopSprinting( void )
{
if ( m_HL2Local.m_bitsActiveDevices & SuitDeviceSprint.GetDeviceID() )
{
SuitPower_RemoveDevice( SuitDeviceSprint );
}
if( IsSuitEquipped() )
{
SetMaxSpeed( HL2_NORM_SPEED );
}
else
{
SetMaxSpeed( HL2_WALK_SPEED );
}
m_fIsSprinting = false;
if ( sv_stickysprint.GetBool() )
{
m_bIsAutoSprinting = false;
m_fAutoSprintMinTime = 0.0f;
}
}
//-----------------------------------------------------------------------------
// Purpose: Called to disable and enable sprint due to temporary circumstances:
// - Carrying a heavy object with the physcannon
//-----------------------------------------------------------------------------
void CHL2_Player::EnableSprint( bool bEnable )
{
if ( !bEnable && IsSprinting() )
{
StopSprinting();
}
m_bSprintEnabled = bEnable;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::StartWalking( void )
{
SetMaxSpeed( HL2_WALK_SPEED );
m_fIsWalking = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::StopWalking( void )
{
SetMaxSpeed( HL2_NORM_SPEED );
m_fIsWalking = false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHL2_Player::CanZoom( CBaseEntity *pRequester )
{
if ( IsZooming() )
return false;
//Check our weapon
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::ToggleZoom(void)
{
if( IsZooming() )
{
StopZooming();
}
else
{
StartZooming();
}
}
//-----------------------------------------------------------------------------
// Purpose: +zoom suit zoom
//-----------------------------------------------------------------------------
void CHL2_Player::StartZooming( void )
{
int iFOV = 25;
if ( SetFOV( this, iFOV, 0.4f ) )
{
m_HL2Local.m_bZooming = true;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::StopZooming( void )
{
int iFOV = GetZoomOwnerDesiredFOV( m_hZoomOwner );
if ( SetFOV( this, iFOV, 0.2f ) )
{
m_HL2Local.m_bZooming = false;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHL2_Player::IsZooming( void )
{
if ( m_hZoomOwner != NULL )
return true;
return false;
}
class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent
{
public:
int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
{
CHL2_Player *pPlayer = (CHL2_Player *)pObject->GetGameData();
if ( pPlayer )
{
if ( pPlayer->TouchedPhysics() )
{
return 0;
}
}
return 1;
}
};
static CPhysicsPlayerCallback playerCallback;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
{
BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity );
// Setup the HL2 specific callback.
IPhysicsPlayerController *pPlayerController = GetPhysicsController();
if ( pPlayerController )
{
pPlayerController->SetEventHandler( &playerCallback );
}
}
CHL2_Player::~CHL2_Player( void )
{
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::CommanderFindGoal( commandgoal_t *pGoal )
{
CAI_BaseNPC *pAllyNpc;
trace_t tr;
Vector vecTarget;
Vector forward;
EyeVectors( &forward );
//---------------------------------
// MASK_SHOT on purpose! So that you don't hit the invisible hulls of the NPCs.
CTraceFilterSkipTwoEntities filter( this, PhysCannonGetHeldEntity( GetActiveWeapon() ), COLLISION_GROUP_INTERACTIVE_DEBRIS );
UTIL_TraceLine( EyePosition(), EyePosition() + forward * MAX_COORD_RANGE, MASK_SHOT, &filter, &tr );
if( !tr.DidHitWorld() )
{
CUtlVector<CAI_BaseNPC *> Allies;
AISquadIter_t iter;
for ( pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) )
{
if ( pAllyNpc->IsCommandable() )
Allies.AddToTail( pAllyNpc );
}
for( int i = 0 ; i < Allies.Count() ; i++ )
{
if( Allies[ i ]->IsValidCommandTarget( tr.m_pEnt ) )
{
pGoal->m_pGoalEntity = tr.m_pEnt;
return true;
}
}
}
if( tr.fraction == 1.0 || (tr.surface.flags & SURF_SKY) )
{
// Move commands invalid against skybox.
pGoal->m_vecGoalLocation = tr.endpos;
return false;
}
if ( tr.m_pEnt->IsNPC() && ((CAI_BaseNPC *)(tr.m_pEnt))->IsCommandable() )
{
pGoal->m_vecGoalLocation = tr.m_pEnt->GetAbsOrigin();
}
else
{
vecTarget = tr.endpos;
Vector mins( -16, -16, 0 );
Vector maxs( 16, 16, 0 );
// Back up from whatever we hit so that there's enough space at the
// target location for a bounding box.
// Now trace down.
//UTIL_TraceLine( vecTarget, vecTarget - Vector( 0, 0, 8192 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
UTIL_TraceHull( vecTarget + tr.plane.normal * 24,
vecTarget - Vector( 0, 0, 8192 ),
mins,
maxs,
MASK_SOLID_BRUSHONLY,
this,
COLLISION_GROUP_NONE,
&tr );
if ( !tr.startsolid )
pGoal->m_vecGoalLocation = tr.endpos;
else
pGoal->m_vecGoalLocation = vecTarget;
}
pAllyNpc = GetSquadCommandRepresentative();
if ( !pAllyNpc )
return false;
vecTarget = pGoal->m_vecGoalLocation;
if ( !pAllyNpc->FindNearestValidGoalPos( vecTarget, &pGoal->m_vecGoalLocation ) )
return false;
return ( ( vecTarget - pGoal->m_vecGoalLocation ).LengthSqr() < Square( 15*12 ) );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CAI_BaseNPC *CHL2_Player::GetSquadCommandRepresentative()
{
if ( m_pPlayerAISquad != NULL )
{
CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember();
if ( pAllyNpc )
{
return pAllyNpc->GetSquadCommandRepresentative();
}
}
return NULL;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CHL2_Player::GetNumSquadCommandables()
{
AISquadIter_t iter;
int c = 0;
for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) )
{
if ( pAllyNpc->IsCommandable() )
c++;
}
return c;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CHL2_Player::GetNumSquadCommandableMedics()
{
AISquadIter_t iter;
int c = 0;
for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) )
{
if ( pAllyNpc->IsCommandable() && pAllyNpc->IsMedic() )
c++;
}
return c;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::CommanderUpdate()
{
CAI_BaseNPC *pCommandRepresentative = GetSquadCommandRepresentative();
bool bFollowMode = false;
if ( pCommandRepresentative )
{
bFollowMode = ( pCommandRepresentative->GetCommandGoal() == vec3_invalid );
// set the variables for network transmission (to show on the hud)
m_HL2Local.m_iSquadMemberCount = GetNumSquadCommandables();
m_HL2Local.m_iSquadMedicCount = GetNumSquadCommandableMedics();
m_HL2Local.m_fSquadInFollowMode = bFollowMode;
// debugging code for displaying extra squad indicators
/*
char *pszMoving = "";
AISquadIter_t iter;
for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) )
{
if ( pAllyNpc->IsCommandMoving() )
{
pszMoving = "<-";
break;
}
}
NDebugOverlay::ScreenText(
0.932, 0.919,
CFmtStr( "%d|%c%s", GetNumSquadCommandables(), ( bFollowMode ) ? 'F' : 'S', pszMoving ),
255, 128, 0, 128,
0 );
*/
}
else
{
m_HL2Local.m_iSquadMemberCount = 0;
m_HL2Local.m_iSquadMedicCount = 0;
m_HL2Local.m_fSquadInFollowMode = true;
}
if ( m_QueuedCommand != CC_NONE && ( m_QueuedCommand == CC_FOLLOW || gpGlobals->realtime - m_RealTimeLastSquadCommand >= player_squad_double_tap_time.GetFloat() ) )
{
CommanderExecute( m_QueuedCommand );
m_QueuedCommand = CC_NONE;
}
else if ( !bFollowMode && pCommandRepresentative && m_CommanderUpdateTimer.Expired() && player_squad_transient_commands.GetBool() )
{
m_CommanderUpdateTimer.Set(2.5);
if ( pCommandRepresentative->ShouldAutoSummon() )
CommanderExecute( CC_FOLLOW );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//
// bHandled - indicates whether to continue delivering this order to
// all allies. Allows us to stop delivering certain types of orders once we find
// a suitable candidate. (like picking up a single weapon. We don't wish for
// all allies to respond and try to pick up one weapon).
//-----------------------------------------------------------------------------
bool CHL2_Player::CommanderExecuteOne( CAI_BaseNPC *pNpc, const commandgoal_t &goal, CAI_BaseNPC **Allies, int numAllies )
{
if ( goal.m_pGoalEntity )
{
return pNpc->TargetOrder( goal.m_pGoalEntity, Allies, numAllies );
}
else if ( pNpc->IsInPlayerSquad() )
{
pNpc->MoveOrder( goal.m_vecGoalLocation, Allies, numAllies );
}
return true;
}
//---------------------------------------------------------
//---------------------------------------------------------
void CHL2_Player::CommanderExecute( CommanderCommand_t command )
{
CAI_BaseNPC *pPlayerSquadLeader = GetSquadCommandRepresentative();
if ( !pPlayerSquadLeader )
{
EmitSound( "HL2Player.UseDeny" );
return;
}
int i;
CUtlVector<CAI_BaseNPC *> Allies;
commandgoal_t goal;
if ( command == CC_TOGGLE )
{
if ( pPlayerSquadLeader->GetCommandGoal() != vec3_invalid )
command = CC_FOLLOW;
else
command = CC_SEND;
}
else
{
if ( command == CC_FOLLOW && pPlayerSquadLeader->GetCommandGoal() == vec3_invalid )
return;
}
if ( command == CC_FOLLOW )
{
goal.m_pGoalEntity = this;
goal.m_vecGoalLocation = vec3_invalid;
}
else
{
goal.m_pGoalEntity = NULL;
goal.m_vecGoalLocation = vec3_invalid;
// Find a goal for ourselves.
if( !CommanderFindGoal( &goal ) )
{
EmitSound( "HL2Player.UseDeny" );
return; // just keep following
}
}
#ifdef _DEBUG
if( goal.m_pGoalEntity == NULL && goal.m_vecGoalLocation == vec3_invalid )
{
DevMsg( 1, "**ERROR: Someone sent an invalid goal to CommanderExecute!\n" );
}
#endif // _DEBUG
AISquadIter_t iter;
for ( CAI_BaseNPC *pAllyNpc = m_pPlayerAISquad->GetFirstMember(&iter); pAllyNpc; pAllyNpc = m_pPlayerAISquad->GetNextMember(&iter) )
{
if ( pAllyNpc->IsCommandable() )
Allies.AddToTail( pAllyNpc );
}
//---------------------------------
// If the trace hits an NPC, send all ally NPCs a "target" order. Always
// goes to targeted one first
#ifdef DBGFLAG_ASSERT
int nAIs = g_AI_Manager.NumAIs();
#endif
CAI_BaseNPC * pTargetNpc = (goal.m_pGoalEntity) ? goal.m_pGoalEntity->MyNPCPointer() : NULL;
bool bHandled = false;
if( pTargetNpc )
{
bHandled = !CommanderExecuteOne( pTargetNpc, goal, Allies.Base(), Allies.Count() );
}
for ( i = 0; !bHandled && i < Allies.Count(); i++ )
{
if ( Allies[i] != pTargetNpc && Allies[i]->IsPlayerAlly() )
{
bHandled = !CommanderExecuteOne( Allies[i], goal, Allies.Base(), Allies.Count() );
}
Assert( nAIs == g_AI_Manager.NumAIs() ); // not coded to support mutating set of NPCs
}
}
//-----------------------------------------------------------------------------
// Enter/exit commander mode, manage ally selection.
//-----------------------------------------------------------------------------
void CHL2_Player::CommanderMode()
{
float commandInterval = gpGlobals->realtime - m_RealTimeLastSquadCommand;
m_RealTimeLastSquadCommand = gpGlobals->realtime;
if ( commandInterval < player_squad_double_tap_time.GetFloat() )
{
m_QueuedCommand = CC_FOLLOW;
}
else
{
m_QueuedCommand = (player_squad_transient_commands.GetBool()) ? CC_SEND : CC_TOGGLE;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iImpulse -
//-----------------------------------------------------------------------------
void CHL2_Player::CheatImpulseCommands( int iImpulse )
{
switch( iImpulse )
{
case 50:
{
CommanderMode();
break;
}
case 51:
{
// Cheat to create a dynamic resupply item
Vector vecForward;
AngleVectors( EyeAngles(), &vecForward );
CBaseEntity *pItem = (CBaseEntity *)CreateEntityByName( "item_dynamic_resupply" );
if ( pItem )
{
Vector vecOrigin = GetAbsOrigin() + vecForward * 256 + Vector(0,0,64);
QAngle vecAngles( 0, GetAbsAngles().y - 90, 0 );
pItem->SetAbsOrigin( vecOrigin );
pItem->SetAbsAngles( vecAngles );
pItem->KeyValue( "targetname", "resupply" );
pItem->Spawn();
pItem->Activate();
}
break;
}
case 52:
{
// Rangefinder
trace_t tr;
UTIL_TraceLine( EyePosition(), EyePosition() + EyeDirection3D() * MAX_COORD_RANGE, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
if( tr.fraction != 1.0 )
{
float flDist = (tr.startpos - tr.endpos).Length();
float flDist2D = (tr.startpos - tr.endpos).Length2D();
DevMsg( 1,"\nStartPos: %.4f %.4f %.4f --- EndPos: %.4f %.4f %.4f\n", tr.startpos.x,tr.startpos.y,tr.startpos.z,tr.endpos.x,tr.endpos.y,tr.endpos.z );
DevMsg( 1,"3D Distance: %.4f units (%.2f feet) --- 2D Distance: %.4f units (%.2f feet)\n", flDist, flDist / 12.0, flDist2D, flDist2D / 12.0 );
}
break;
}
default:
BaseClass::CheatImpulseCommands( iImpulse );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
{
BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
PointCameraSetupVisibility( this, area, pvs, pvssize );
// If the intro script is playing, we want to get it's visibility points
if ( g_hIntroScript )
{
Vector vecOrigin;
CBaseEntity *pCamera;
if ( g_hIntroScript->GetIncludedPVSOrigin( &vecOrigin, &pCamera ) )
{
// If it's a point camera, turn it on
CPointCamera *pPointCamera = dynamic_cast< CPointCamera* >(pCamera);
if ( pPointCamera )
{
pPointCamera->SetActive( true );
}
engine->AddOriginToPVS( vecOrigin );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::SuitPower_Update( void )
{
if( SuitPower_ShouldRecharge() )
{
SuitPower_Charge( SUITPOWER_CHARGE_RATE * gpGlobals->frametime );
}
else if( m_HL2Local.m_bitsActiveDevices )
{
float flPowerLoad = m_flSuitPowerLoad;
//Since stickysprint quickly shuts off sprint if it isn't being used, this isn't an issue.
if ( !sv_stickysprint.GetBool() )
{
if( SuitPower_IsDeviceActive(SuitDeviceSprint) )
{
if( !fabs(GetAbsVelocity().x) && !fabs(GetAbsVelocity().y) )
{
// If player's not moving, don't drain sprint juice.
flPowerLoad -= SuitDeviceSprint.GetDeviceDrainRate();
}
}
}
if( SuitPower_IsDeviceActive(SuitDeviceFlashlight) )
{
float factor;
factor = 1.0f / m_flFlashlightPowerDrainScale;
flPowerLoad -= ( SuitDeviceFlashlight.GetDeviceDrainRate() * (1.0f - factor) );
}
if( !SuitPower_Drain( flPowerLoad * gpGlobals->frametime ) )
{
// TURN OFF ALL DEVICES!!
if( IsSprinting() )
{
StopSprinting();
}
if ( Flashlight_UseLegacyVersion() )
{
if( FlashlightIsOn() )
{
#ifndef HL2MP
FlashlightTurnOff();
#endif
}
}
}
if ( Flashlight_UseLegacyVersion() )
{
// turn off flashlight a little bit after it hits below one aux power notch (5%)
if( m_HL2Local.m_flSuitPower < 4.8f && FlashlightIsOn() )
{
#ifndef HL2MP
FlashlightTurnOff();
#endif
}
}
}
}
//-----------------------------------------------------------------------------
// Charge battery fully, turn off all devices.
//-----------------------------------------------------------------------------
void CHL2_Player::SuitPower_Initialize( void )
{
m_HL2Local.m_bitsActiveDevices = 0x00000000;
m_HL2Local.m_flSuitPower = 100.0;
m_flSuitPowerLoad = 0.0;
}
//-----------------------------------------------------------------------------
// Purpose: Interface to drain power from the suit's power supply.
// Input: Amount of charge to remove (expressed as percentage of full charge)
// Output: Returns TRUE if successful, FALSE if not enough power available.
//-----------------------------------------------------------------------------
bool CHL2_Player::SuitPower_Drain( float flPower )
{
// Suitpower cheat on?
if ( sv_infinite_aux_power.GetBool() )
return true;
m_HL2Local.m_flSuitPower -= flPower;
if( m_HL2Local.m_flSuitPower < 0.0 )
{
// Power is depleted!
// Clamp and fail
m_HL2Local.m_flSuitPower = 0.0;
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Interface to add power to the suit's power supply
// Input: Amount of charge to add
//-----------------------------------------------------------------------------
void CHL2_Player::SuitPower_Charge( float flPower )
{
m_HL2Local.m_flSuitPower += flPower;
if( m_HL2Local.m_flSuitPower > 100.0 )
{
// Full charge, clamp.
m_HL2Local.m_flSuitPower = 100.0;
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::SuitPower_IsDeviceActive( const CSuitPowerDevice &device )
{
return (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) != 0;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::SuitPower_AddDevice( const CSuitPowerDevice &device )
{
// Make sure this device is NOT active!!
if( m_HL2Local.m_bitsActiveDevices & device.GetDeviceID() )
return false;
if( !IsSuitEquipped() )
return false;
m_HL2Local.m_bitsActiveDevices |= device.GetDeviceID();
m_flSuitPowerLoad += device.GetDeviceDrainRate();
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::SuitPower_RemoveDevice( const CSuitPowerDevice &device )
{
// Make sure this device is active!!
if( ! (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) )
return false;
if( !IsSuitEquipped() )
return false;
// Take a little bit of suit power when you disable a device. If the device is shutting off
// because the battery is drained, no harm done, the battery charge cannot go below 0.
// This code in combination with the delay before the suit can start recharging are a defense
// against exploits where the player could rapidly tap sprint and never run out of power.
SuitPower_Drain( device.GetDeviceDrainRate() * 0.1f );
m_HL2Local.m_bitsActiveDevices &= ~device.GetDeviceID();
m_flSuitPowerLoad -= device.GetDeviceDrainRate();
if( m_HL2Local.m_bitsActiveDevices == 0x00000000 )
{
// With this device turned off, we can set this timer which tells us when the
// suit power system entered a no-load state.
m_flTimeAllSuitDevicesOff = gpGlobals->curtime;
}
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#define SUITPOWER_BEGIN_RECHARGE_DELAY 0.5f
bool CHL2_Player::SuitPower_ShouldRecharge( void )
{
// Make sure all devices are off.
if( m_HL2Local.m_bitsActiveDevices != 0x00000000 )
return false;
// Is the system fully charged?
if( m_HL2Local.m_flSuitPower >= 100.0f )
return false;
// Has the system been in a no-load state for long enough
// to begin recharging?
if( gpGlobals->curtime < m_flTimeAllSuitDevicesOff + SUITPOWER_BEGIN_RECHARGE_DELAY )
return false;
return true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ConVar sk_battery( "sk_battery","0" );
bool CHL2_Player::ApplyBattery( float powerMultiplier )
{
const float MAX_NORMAL_BATTERY = 100;
if ((ArmorValue() < MAX_NORMAL_BATTERY) && IsSuitEquipped())
{
int pct;
char szcharge[64];
IncrementArmorValue( sk_battery.GetFloat() * powerMultiplier, MAX_NORMAL_BATTERY );
CPASAttenuationFilter filter( this, "ItemBattery.Touch" );
EmitSound( filter, entindex(), "ItemBattery.Touch" );
CSingleUserRecipientFilter user( this );
user.MakeReliable();
UserMessageBegin( user, "ItemPickup" );
WRITE_STRING( "item_battery" );
MessageEnd();
// Suit reports new power level
// For some reason this wasn't working in release build -- round it.
pct = (int)( (float)(ArmorValue() * 100.0) * (1.0/MAX_NORMAL_BATTERY) + 0.5);
pct = (pct / 5);
if (pct > 0)
pct--;
Q_snprintf( szcharge,sizeof(szcharge),"!HEV_%1dP", pct );
//UTIL_EmitSoundSuit(edict(), szcharge);
//SetSuitUpdate(szcharge, FALSE, SUIT_NEXT_IN_30SEC);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CHL2_Player::FlashlightIsOn( void )
{
return IsEffectActive( EF_DIMLIGHT );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::FlashlightTurnOn( void )
{
if( m_bFlashlightDisabled )
return;
if ( Flashlight_UseLegacyVersion() )
{
if( !SuitPower_AddDevice( SuitDeviceFlashlight ) )
return;
}
#ifdef HL2_DLL
if( !IsSuitEquipped() )
return;
#endif
AddEffects( EF_DIMLIGHT );
EmitSound( "HL2Player.FlashLightOn" );
variant_t flashlighton;
flashlighton.SetFloat( m_HL2Local.m_flSuitPower / 100.0f );
FirePlayerProxyOutput( "OnFlashlightOn", flashlighton, this, this );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::FlashlightTurnOff( void )
{
if ( Flashlight_UseLegacyVersion() )
{
if( !SuitPower_RemoveDevice( SuitDeviceFlashlight ) )
return;
}
RemoveEffects( EF_DIMLIGHT );
EmitSound( "HL2Player.FlashLightOff" );
variant_t flashlightoff;
flashlightoff.SetFloat( m_HL2Local.m_flSuitPower / 100.0f );
FirePlayerProxyOutput( "OnFlashlightOff", flashlightoff, this, this );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#define FLASHLIGHT_RANGE Square(600)
bool CHL2_Player::IsIlluminatedByFlashlight( CBaseEntity *pEntity, float *flReturnDot )
{
if( !FlashlightIsOn() )
return false;
if( pEntity->Classify() == CLASS_BARNACLE && pEntity->GetEnemy() == this )
{
// As long as my flashlight is on, the barnacle that's pulling me in is considered illuminated.
// This is because players often shine their flashlights at Alyx when they are in a barnacle's
// grasp, and wonder why Alyx isn't helping. Alyx isn't helping because the light isn't pointed
// at the barnacle. This will allow Alyx to see the barnacle no matter which way the light is pointed.
return true;
}
// Within 50 feet?
float flDistSqr = GetAbsOrigin().DistToSqr(pEntity->GetAbsOrigin());
if( flDistSqr > FLASHLIGHT_RANGE )
return false;
// Within 45 degrees?
Vector vecSpot = pEntity->WorldSpaceCenter();
Vector los;
// If the eyeposition is too close, move it back. Solves problems
// caused by the player being too close the target.
if ( flDistSqr < (128 * 128) )
{
Vector vecForward;
EyeVectors( &vecForward );
Vector vecMovedEyePos = EyePosition() - (vecForward * 128);
los = ( vecSpot - vecMovedEyePos );
}
else
{
los = ( vecSpot - EyePosition() );
}
VectorNormalize( los );
Vector facingDir = EyeDirection3D( );
float flDot = DotProduct( los, facingDir );
if ( flReturnDot )
{
*flReturnDot = flDot;
}
if ( flDot < 0.92387f )
return false;
if( !FVisible(pEntity) )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Let NPCs know when the flashlight is trained on them
//-----------------------------------------------------------------------------
void CHL2_Player::CheckFlashlight( void )
{
if ( !FlashlightIsOn() )
return;
if ( m_flNextFlashlightCheckTime > gpGlobals->curtime )
return;
m_flNextFlashlightCheckTime = gpGlobals->curtime + FLASHLIGHT_NPC_CHECK_INTERVAL;
// Loop through NPCs looking for illuminated ones
for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
{
CAI_BaseNPC *pNPC = g_AI_Manager.AccessAIs()[i];
float flDot;
if ( IsIlluminatedByFlashlight( pNPC, &flDot ) )
{
pNPC->PlayerHasIlluminatedNPC( this, flDot );
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::SetPlayerUnderwater( bool state )
{
if ( state )
{
SuitPower_AddDevice( SuitDeviceBreather );
}
else
{
SuitPower_RemoveDevice( SuitDeviceBreather );
}
BaseClass::SetPlayerUnderwater( state );
}
//-----------------------------------------------------------------------------
bool CHL2_Player::PassesDamageFilter( const CTakeDamageInfo &info )
{
CBaseEntity *pAttacker = info.GetAttacker();
if( pAttacker && pAttacker->MyNPCPointer() && pAttacker->MyNPCPointer()->IsPlayerAlly() )
{
return false;
}
if( m_hPlayerProxy && !m_hPlayerProxy->PassesDamageFilter( info ) )
{
return false;
}
return BaseClass::PassesDamageFilter( info );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::SetFlashlightEnabled( bool bState )
{
m_bFlashlightDisabled = !bState;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::InputDisableFlashlight( inputdata_t &inputdata )
{
if( FlashlightIsOn() )
FlashlightTurnOff();
SetFlashlightEnabled( false );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::InputEnableFlashlight( inputdata_t &inputdata )
{
SetFlashlightEnabled( true );
}
//-----------------------------------------------------------------------------
// Purpose: Prevent the player from taking fall damage for [n] seconds, but
// reset back to taking fall damage after the first impact (so players will be
// hurt if they bounce off what they hit). This is the original behavior.
//-----------------------------------------------------------------------------
void CHL2_Player::InputIgnoreFallDamage( inputdata_t &inputdata )
{
float timeToIgnore = inputdata.value.Float();
if ( timeToIgnore <= 0.0 )
timeToIgnore = TIME_IGNORE_FALL_DAMAGE;
m_flTimeIgnoreFallDamage = gpGlobals->curtime + timeToIgnore;
m_bIgnoreFallDamageResetAfterImpact = true;
}
//-----------------------------------------------------------------------------
// Purpose: Absolutely prevent the player from taking fall damage for [n] seconds.
//-----------------------------------------------------------------------------
void CHL2_Player::InputIgnoreFallDamageWithoutReset( inputdata_t &inputdata )
{
float timeToIgnore = inputdata.value.Float();
if ( timeToIgnore <= 0.0 )
timeToIgnore = TIME_IGNORE_FALL_DAMAGE;
m_flTimeIgnoreFallDamage = gpGlobals->curtime + timeToIgnore;
m_bIgnoreFallDamageResetAfterImpact = false;
}
//-----------------------------------------------------------------------------
// Purpose: Notification of a player's npc ally in the players squad being killed
//-----------------------------------------------------------------------------
void CHL2_Player::OnSquadMemberKilled( inputdata_t &data )
{
// send a message to the client, to notify the hud of the loss
CSingleUserRecipientFilter user( this );
user.MakeReliable();
UserMessageBegin( user, "SquadMemberDied" );
MessageEnd();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::NotifyFriendsOfDamage( CBaseEntity *pAttackerEntity )
{
CAI_BaseNPC *pAttacker = pAttackerEntity->MyNPCPointer();
if ( pAttacker )
{
const Vector &origin = GetAbsOrigin();
for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
{
const float NEAR_Z = 12*12;
const float NEAR_XY_SQ = Square( 50*12 );
CAI_BaseNPC *pNpc = g_AI_Manager.AccessAIs()[i];
if ( pNpc->IsPlayerAlly() )
{
const Vector &originNpc = pNpc->GetAbsOrigin();
if ( fabsf( originNpc.z - origin.z ) < NEAR_Z )
{
if ( (originNpc.AsVector2D() - origin.AsVector2D()).LengthSqr() < NEAR_XY_SQ )
{
pNpc->OnFriendDamaged( this, pAttacker );
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ConVar test_massive_dmg("test_massive_dmg", "30" );
ConVar test_massive_dmg_clip("test_massive_dmg_clip", "0.5" );
int CHL2_Player::OnTakeDamage( const CTakeDamageInfo &info )
{
if ( GlobalEntity_GetState( "gordon_invulnerable" ) == GLOBAL_ON )
return 0;
// ignore fall damage if instructed to do so by input
if ( ( info.GetDamageType() & DMG_FALL ) && m_flTimeIgnoreFallDamage > gpGlobals->curtime )
{
// usually, we will reset the input flag after the first impact. However there is another input that
// prevents this behavior.
if ( m_bIgnoreFallDamageResetAfterImpact )
{
m_flTimeIgnoreFallDamage = 0;
}
return 0;
}
if( info.GetDamageType() & DMG_BLAST_SURFACE )
{
if( GetWaterLevel() > 2 )
{
// Don't take blast damage from anything above the surface.
if( info.GetInflictor()->GetWaterLevel() == 0 )
{
return 0;
}
}
}
if ( info.GetDamage() > 0.0f )
{
m_flLastDamageTime = gpGlobals->curtime;
if ( info.GetAttacker() )
NotifyFriendsOfDamage( info.GetAttacker() );
}
// Modify the amount of damage the player takes, based on skill.
CTakeDamageInfo playerDamage = info;
// Should we run this damage through the skill level adjustment?
bool bAdjustForSkillLevel = true;
if( info.GetDamageType() == DMG_GENERIC && info.GetAttacker() == this && info.GetInflictor() == this )
{
// Only do a skill level adjustment if the player isn't his own attacker AND inflictor.
// This prevents damage from SetHealth() inputs from being adjusted for skill level.
bAdjustForSkillLevel = false;
}
if ( GetVehicleEntity() != NULL && GlobalEntity_GetState("gordon_protect_driver") == GLOBAL_ON )
{
if( playerDamage.GetDamage() > test_massive_dmg.GetFloat() && playerDamage.GetInflictor() == GetVehicleEntity() && (playerDamage.GetDamageType() & DMG_CRUSH) )
{
playerDamage.ScaleDamage( test_massive_dmg_clip.GetFloat() / playerDamage.GetDamage() );
}
}
if( bAdjustForSkillLevel )
{
playerDamage.AdjustPlayerDamageTakenForSkillLevel();
}
gamestats->Event_PlayerDamage( this, info );
return BaseClass::OnTakeDamage( playerDamage );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &info -
//-----------------------------------------------------------------------------
int CHL2_Player::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
// Drown
if( info.GetDamageType() & DMG_DROWN )
{
if( m_idrowndmg == m_idrownrestored )
{
EmitSound( "Player.DrownStart" );
}
else
{
EmitSound( "Player.DrownContinue" );
}
}
// Burnt
if ( info.GetDamageType() & DMG_BURN )
{
EmitSound( "HL2Player.BurnPain" );
}
if( (info.GetDamageType() & DMG_SLASH) && hl2_episodic.GetBool() )
{
if( m_afPhysicsFlags & PFLAG_USING )
{
// Stop the player using a rotating button for a short time if hit by a creature's melee attack.
// This is for the antlion burrow-corking training in EP1 (sjb).
SuspendUse( 0.5f );
}
}
// Call the base class implementation
return BaseClass::OnTakeDamage_Alive( info );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::OnDamagedByExplosion( const CTakeDamageInfo &info )
{
if ( info.GetInflictor() && info.GetInflictor()->ClassMatches( "mortarshell" ) )
{
// No ear ringing for mortar
UTIL_ScreenShake( info.GetInflictor()->GetAbsOrigin(), 4.0, 1.0, 0.5, 1000, SHAKE_START, false );
return;
}
BaseClass::OnDamagedByExplosion( info );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::ShouldShootMissTarget( CBaseCombatCharacter *pAttacker )
{
if( gpGlobals->curtime > m_flTargetFindTime )
{
// Put this off into the future again.
m_flTargetFindTime = gpGlobals->curtime + random->RandomFloat( 3, 5 );
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Notifies Alyx that player has put a combine ball into a socket so she can comment on it.
// Input : pCombineBall - ball the was socketed
//-----------------------------------------------------------------------------
void CHL2_Player::CombineBallSocketed( CPropCombineBall *pCombineBall )
{
#ifdef HL2_EPISODIC
CNPC_Alyx *pAlyx = CNPC_Alyx::GetAlyx();
if ( pAlyx )
{
pAlyx->CombineBallSocketed( pCombineBall->NumBounces() );
}
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info )
{
BaseClass::Event_KilledOther( pVictim, info );
#ifdef HL2_EPISODIC
CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
{
if ( ppAIs[i] && ppAIs[i]->IRelationType(this) == D_LI )
{
ppAIs[i]->OnPlayerKilledOther( pVictim, info );
}
}
#endif
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::Event_Killed( const CTakeDamageInfo &info )
{
BaseClass::Event_Killed( info );
FirePlayerProxyOutput( "PlayerDied", variant_t(), this, this );
NotifyScriptsOfDeath();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::NotifyScriptsOfDeath( void )
{
CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "scripted_sequence" );
while( pEnt )
{
variant_t emptyVariant;
pEnt->AcceptInput( "ScriptPlayerDeath", NULL, NULL, emptyVariant, 0 );
pEnt = gEntList.FindEntityByClassname( pEnt, "scripted_sequence" );
}
pEnt = gEntList.FindEntityByClassname( NULL, "logic_choreographed_scene" );
while( pEnt )
{
variant_t emptyVariant;
pEnt->AcceptInput( "ScriptPlayerDeath", NULL, NULL, emptyVariant, 0 );
pEnt = gEntList.FindEntityByClassname( pEnt, "logic_choreographed_scene" );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CHL2_Player::GetAutoaimVector( autoaim_params_t &params )
{
BaseClass::GetAutoaimVector( params );
if ( IsX360() )
{
if( IsInAVehicle() )
{
if( m_hLockedAutoAimEntity && m_hLockedAutoAimEntity->IsAlive() && ShouldKeepLockedAutoaimTarget(m_hLockedAutoAimEntity) )
{
if( params.m_hAutoAimEntity && params.m_hAutoAimEntity != m_hLockedAutoAimEntity )
{
// Autoaim has picked a new target. Switch.
m_hLockedAutoAimEntity = params.m_hAutoAimEntity;
}
// Ignore autoaim and just keep aiming at this target.
params.m_hAutoAimEntity = m_hLockedAutoAimEntity;
Vector vecTarget = m_hLockedAutoAimEntity->BodyTarget( EyePosition(), false );
Vector vecDir = vecTarget - EyePosition();
VectorNormalize( vecDir );
params.m_vecAutoAimDir = vecDir;
params.m_vecAutoAimPoint = vecTarget;
return;
}
else
{
m_hLockedAutoAimEntity = NULL;
}
}
// If the player manually gets his crosshair onto a target, make that target sticky
if( params.m_fScale != AUTOAIM_SCALE_DIRECT_ONLY )
{
// Only affect this for 'real' queries
//if( params.m_hAutoAimEntity && params.m_bOnTargetNatural )
if( params.m_hAutoAimEntity )
{
// Turn on sticky.
m_HL2Local.m_bStickyAutoAim = true;
if( IsInAVehicle() )
{
m_hLockedAutoAimEntity = params.m_hAutoAimEntity;
}
}
else if( !params.m_hAutoAimEntity )
{
// Turn off sticky only if there's no target at all.
m_HL2Local.m_bStickyAutoAim = false;
m_hLockedAutoAimEntity = NULL;
}
}
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::ShouldKeepLockedAutoaimTarget( EHANDLE hLockedTarget )
{
Vector vecLooking;
Vector vecToTarget;
vecToTarget = hLockedTarget->WorldSpaceCenter() - EyePosition();
float flDist = vecToTarget.Length2D();
VectorNormalize( vecToTarget );
if( flDist > autoaim_max_dist.GetFloat() )
return false;
float flDot;
vecLooking = EyeDirection3D();
flDot = DotProduct( vecLooking, vecToTarget );
if( flDot < autoaim_unlock_target.GetFloat() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : iCount -
// iAmmoIndex -
// bSuppressSound -
// Output : int
//-----------------------------------------------------------------------------
int CHL2_Player::GiveAmmo( int nCount, int nAmmoIndex, bool bSuppressSound)
{
// Don't try to give the player invalid ammo indices.
if (nAmmoIndex < 0)
return 0;
bool bCheckAutoSwitch = false;
if (!HasAnyAmmoOfType(nAmmoIndex))
{
bCheckAutoSwitch = true;
}
int nAdd = BaseClass::GiveAmmo(nCount, nAmmoIndex, bSuppressSound);
if ( nCount > 0 && nAdd == 0 )
{
// we've been denied the pickup, display a hud icon to show that
CSingleUserRecipientFilter user( this );
user.MakeReliable();
UserMessageBegin( user, "AmmoDenied" );
WRITE_SHORT( nAmmoIndex );
MessageEnd();
}
//
// If I was dry on ammo for my best weapon and justed picked up ammo for it,
// autoswitch to my best weapon now.
//
if (bCheckAutoSwitch)
{
CBaseCombatWeapon *pWeapon = g_pGameRules->GetNextBestWeapon(this, GetActiveWeapon());
if ( pWeapon && pWeapon->GetPrimaryAmmoType() == nAmmoIndex )
{
SwitchToNextBestWeapon(GetActiveWeapon());
}
}
return nAdd;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CHL2_Player::Weapon_CanUse( CBaseCombatWeapon *pWeapon )
{
#ifndef HL2MP
if ( pWeapon->ClassMatches( "weapon_stunstick" ) )
{
if ( ApplyBattery( 0.5 ) )
UTIL_Remove( pWeapon );
return false;
}
#endif
return BaseClass::Weapon_CanUse( pWeapon );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pWeapon -
//-----------------------------------------------------------------------------
void CHL2_Player::Weapon_Equip( CBaseCombatWeapon *pWeapon )
{
#if HL2_SINGLE_PRIMARY_WEAPON_MODE
if ( pWeapon->GetSlot() == WEAPON_PRIMARY_SLOT )
{
Weapon_DropSlot( WEAPON_PRIMARY_SLOT );
}
#endif
if( GetActiveWeapon() == NULL )
{
m_HL2Local.m_bWeaponLowered = false;
}
BaseClass::Weapon_Equip( pWeapon );
}
//-----------------------------------------------------------------------------
// 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 CHL2_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
{
#if HL2_SINGLE_PRIMARY_WEAPON_MODE
CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
// Can I have this weapon type?
if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
{
if ( gEvilImpulse101 )
{
UTIL_Remove( pWeapon );
}
return false;
}
// ----------------------------------------
// If I already have it just take the ammo
// ----------------------------------------
if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()))
{
//Only remove the weapon if we attained ammo from it
if ( Weapon_EquipAmmoOnly( pWeapon ) == false )
return false;
// Only remove me if I have no ammo left
// Can't just check HasAnyAmmo because if I don't use clips, I want to be removed,
if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() )
return false;
UTIL_Remove( pWeapon );
return false;
}
// -------------------------
// Otherwise take the weapon
// -------------------------
else
{
//Make sure we're not trying to take a new weapon type we already have
if ( Weapon_SlotOccupied( pWeapon ) )
{
CBaseCombatWeapon *pActiveWeapon = Weapon_GetSlot( WEAPON_PRIMARY_SLOT );
if ( pActiveWeapon != NULL && pActiveWeapon->HasAnyAmmo() == false && Weapon_CanSwitchTo( pWeapon ) )
{
Weapon_Equip( pWeapon );
return true;
}
//Attempt to take ammo if this is the gun we're holding already
if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) )
{
Weapon_EquipAmmoOnly( pWeapon );
}
return false;
}
pWeapon->CheckRespawn();
pWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
pWeapon->AddEffects( EF_NODRAW );
Weapon_Equip( pWeapon );
EmitSound( "HL2Player.PickupWeapon" );
return true;
}
#else
return BaseClass::BumpWeapon( pWeapon );
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *cmd -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHL2_Player::ClientCommand( const CCommand &args )
{
#if HL2_SINGLE_PRIMARY_WEAPON_MODE
//Drop primary weapon
if ( !Q_stricmp( args[0], "DropPrimary" ) )
{
Weapon_DropSlot( WEAPON_PRIMARY_SLOT );
return true;
}
#endif
if ( !Q_stricmp( args[0], "emit" ) )
{
CSingleUserRecipientFilter filter( this );
if ( args.ArgC() > 1 )
{
EmitSound( filter, entindex(), args[ 1 ] );
}
else
{
EmitSound( filter, entindex(), "Test.Sound" );
}
return true;
}
return BaseClass::ClientCommand( args );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : void CBasePlayer::PlayerUse
//-----------------------------------------------------------------------------
void CHL2_Player::PlayerUse ( void )
{
// Was use pressed or released?
if ( ! ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) )
return;
if ( m_afButtonPressed & IN_USE )
{
// Currently using a latched entity?
if ( ClearUseEntity() )
{
return;
}
else
{
if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE )
{
m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE;
m_iTrain = TRAIN_NEW|TRAIN_OFF;
return;
}
else
{ // Start controlling the train!
CBaseEntity *pTrain = GetGroundEntity();
if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) )
{
m_afPhysicsFlags |= PFLAG_DIROVERRIDE;
m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed());
m_iTrain |= TRAIN_NEW;
EmitSound( "HL2Player.TrainUse" );
return;
}
}
}
// Tracker 3926: We can't +USE something if we're climbing a ladder
if ( GetMoveType() == MOVETYPE_LADDER )
{
return;
}
}
if( m_flTimeUseSuspended > gpGlobals->curtime )
{
// Something has temporarily stopped us being able to USE things.
// Obviously, this should be used very carefully.(sjb)
return;
}
CBaseEntity *pUseEntity = FindUseEntity();
bool usedSomething = false;
// Found an object
if ( pUseEntity )
{
//!!!UNDONE: traceline here to prevent +USEing buttons through walls
int caps = pUseEntity->ObjectCaps();
variant_t emptyVariant;
if ( m_afButtonPressed & IN_USE )
{
// Robin: Don't play sounds for NPCs, because NPCs will allow respond with speech.
if ( !pUseEntity->MyNPCPointer() )
{
EmitSound( "HL2Player.Use" );
}
}
if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) ||
( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) )
{
if ( caps & FCAP_CONTINUOUS_USE )
m_afPhysicsFlags |= PFLAG_USING;
pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
usedSomething = true;
}
// UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away
else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use
{
pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE );
usedSomething = true;
}
#if HL2_SINGLE_PRIMARY_WEAPON_MODE
//Check for weapon pick-up
if ( m_afButtonPressed & IN_USE )
{
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(pUseEntity);
if ( ( pWeapon != NULL ) && ( Weapon_CanSwitchTo( pWeapon ) ) )
{
//Try to take ammo or swap the weapon
if ( Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType() ) )
{
Weapon_EquipAmmoOnly( pWeapon );
}
else
{
Weapon_DropSlot( pWeapon->GetSlot() );
Weapon_Equip( pWeapon );
}
usedSomething = true;
}
}
#endif
}
else if ( m_afButtonPressed & IN_USE )
{
// Signal that we want to play the deny sound, unless the user is +USEing on a ladder!
// The sound is emitted in ItemPostFrame, since that occurs after GameMovement::ProcessMove which
// lets the ladder code unset this flag.
m_bPlayUseDenySound = true;
}
// Debounce the use key
if ( usedSomething && pUseEntity )
{
m_Local.m_nOldButtons |= IN_USE;
m_afButtonPressed &= ~IN_USE;
}
}
ConVar sv_show_crosshair_target( "sv_show_crosshair_target", "0" );
//-----------------------------------------------------------------------------
// Purpose: Updates the posture of the weapon from lowered to ready
//-----------------------------------------------------------------------------
void CHL2_Player::UpdateWeaponPosture( void )
{
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(GetActiveWeapon());
if ( pWeapon && m_LowerWeaponTimer.Expired() && pWeapon->CanLower() )
{
m_LowerWeaponTimer.Set( .3 );
VPROF( "CHL2_Player::UpdateWeaponPosture-CheckLower" );
Vector vecAim = BaseClass::GetAutoaimVector( AUTOAIM_SCALE_DIRECT_ONLY );
const float CHECK_FRIENDLY_RANGE = 50 * 12;
trace_t tr;
UTIL_TraceLine( EyePosition(), EyePosition() + vecAim * CHECK_FRIENDLY_RANGE, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
CBaseEntity *aimTarget = tr.m_pEnt;
//If we're over something
if ( aimTarget && !tr.DidHitWorld() )
{
if ( !aimTarget->IsNPC() || aimTarget->MyNPCPointer()->GetState() != NPC_STATE_COMBAT )
{
Disposition_t dis = IRelationType( aimTarget );
//Debug info for seeing what an object "cons" as
if ( sv_show_crosshair_target.GetBool() )
{
int text_offset = BaseClass::DrawDebugTextOverlays();
char tempstr[255];
switch ( dis )
{
case D_LI:
Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Like" );
break;
case D_HT:
Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Hate" );
break;
case D_FR:
Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Fear" );
break;
case D_NU:
Q_snprintf( tempstr, sizeof(tempstr), "Disposition: Neutral" );
break;
default:
case D_ER:
Q_snprintf( tempstr, sizeof(tempstr), "Disposition: !!!ERROR!!!" );
break;
}
//Draw the text
NDebugOverlay::EntityText( aimTarget->entindex(), text_offset, tempstr, 0 );
}
//See if we hates it
if ( dis == D_LI )
{
//We're over a friendly, drop our weapon
if ( Weapon_Lower() == false )
{
//FIXME: We couldn't lower our weapon!
}
return;
}
}
}
if ( Weapon_Ready() == false )
{
//FIXME: We couldn't raise our weapon!
}
}
if( g_pGameRules->GetAutoAimMode() != AUTOAIM_NONE )
{
if( !pWeapon )
{
// This tells the client to draw no crosshair
m_HL2Local.m_bWeaponLowered = true;
return;
}
else
{
if( !pWeapon->CanLower() && m_HL2Local.m_bWeaponLowered )
m_HL2Local.m_bWeaponLowered = false;
}
if( !m_AutoaimTimer.Expired() )
return;
m_AutoaimTimer.Set( .1 );
VPROF( "hl2_x360_aiming" );
// Call the autoaim code to update the local player data, which allows the client to update.
autoaim_params_t params;
params.m_vecAutoAimPoint.Init();
params.m_vecAutoAimDir.Init();
params.m_fScale = AUTOAIM_SCALE_DEFAULT;
params.m_fMaxDist = autoaim_max_dist.GetFloat();
GetAutoaimVector( params );
m_HL2Local.m_hAutoAimTarget.Set( params.m_hAutoAimEntity );
m_HL2Local.m_vecAutoAimPoint.Set( params.m_vecAutoAimPoint );
m_HL2Local.m_bAutoAimTarget = ( params.m_bAutoAimAssisting || params.m_bOnTargetNatural );
return;
}
else
{
// Make sure there's no residual autoaim target if the user changes the xbox_aiming convar on the fly.
m_HL2Local.m_hAutoAimTarget.Set(NULL);
}
}
//-----------------------------------------------------------------------------
// Purpose: Lowers the weapon posture (for hovering over friendlies)
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHL2_Player::Weapon_Lower( void )
{
VPROF( "CHL2_Player::Weapon_Lower" );
// Already lowered?
if ( m_HL2Local.m_bWeaponLowered )
return true;
m_HL2Local.m_bWeaponLowered = true;
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(GetActiveWeapon());
if ( pWeapon == NULL )
return false;
return pWeapon->Lower();
}
//-----------------------------------------------------------------------------
// Purpose: Returns the weapon posture to normal
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CHL2_Player::Weapon_Ready( void )
{
VPROF( "CHL2_Player::Weapon_Ready" );
// Already ready?
if ( m_HL2Local.m_bWeaponLowered == false )
return true;
m_HL2Local.m_bWeaponLowered = false;
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(GetActiveWeapon());
if ( pWeapon == NULL )
return false;
return pWeapon->Ready();
}
//-----------------------------------------------------------------------------
// Purpose: Returns whether or not we can switch to the given weapon.
// Input : pWeapon -
//-----------------------------------------------------------------------------
bool CHL2_Player::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
{
CBasePlayer *pPlayer = (CBasePlayer *)this;
#if !defined( CLIENT_DLL )
IServerVehicle *pVehicle = pPlayer->GetVehicle();
#else
IClientVehicle *pVehicle = pPlayer->GetVehicle();
#endif
if (pVehicle && !pPlayer->UsingStandardWeaponsInVehicle())
return false;
if ( !pWeapon->HasAnyAmmo() && !GetAmmoCount( pWeapon->m_iPrimaryAmmoType ) )
return false;
if ( !pWeapon->CanDeploy() )
return false;
if ( GetActiveWeapon() )
{
if ( PhysCannonGetHeldEntity( GetActiveWeapon() ) == pWeapon &&
Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()) )
{
return true;
}
if ( !GetActiveWeapon()->CanHolster() )
return false;
}
return true;
}
void CHL2_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize )
{
// can't pick up what you're standing on
if ( GetGroundEntity() == pObject )
return;
if ( bLimitMassAndSize == true )
{
if ( CBasePlayer::CanPickupObject( pObject, 35, 128 ) == false )
return;
}
// Can't be picked up if NPCs are on me
if ( pObject->HasNPCsOnIt() )
return;
PlayerPickupObject( this, pObject );
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : CBaseEntity
//-----------------------------------------------------------------------------
bool CHL2_Player::IsHoldingEntity( CBaseEntity *pEnt )
{
return PlayerPickupControllerIsHoldingEntity( m_hUseEntity, pEnt );
}
float CHL2_Player::GetHeldObjectMass( IPhysicsObject *pHeldObject )
{
float mass = PlayerPickupGetHeldObjectMass( m_hUseEntity, pHeldObject );
if ( mass == 0.0f )
{
mass = PhysCannonGetHeldObjectMass( GetActiveWeapon(), pHeldObject );
}
return mass;
}
//-----------------------------------------------------------------------------
// Purpose: Force the player to drop any physics objects he's carrying
//-----------------------------------------------------------------------------
void CHL2_Player::ForceDropOfCarriedPhysObjects( CBaseEntity *pOnlyIfHoldingThis )
{
if ( PhysIsInCallback() )
{
variant_t value;
g_EventQueue.AddEvent( this, "ForceDropPhysObjects", value, 0.01f, pOnlyIfHoldingThis, this );
return;
}
#ifdef HL2_EPISODIC
if ( hl2_episodic.GetBool() )
{
CBaseEntity *pHeldEntity = PhysCannonGetHeldEntity( GetActiveWeapon() );
if( pHeldEntity && pHeldEntity->ClassMatches( "grenade_helicopter" ) )
{
return;
}
}
#endif
// Drop any objects being handheld.
ClearUseEntity();
// Then force the physcannon to drop anything it's holding, if it's our active weapon
PhysCannonForceDrop( GetActiveWeapon(), NULL );
}
void CHL2_Player::InputForceDropPhysObjects( inputdata_t &data )
{
ForceDropOfCarriedPhysObjects( data.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHL2_Player::UpdateClientData( void )
{
if (m_DmgTake || m_DmgSave || m_bitsHUDDamage != m_bitsDamageType)
{
// Comes from inside me if not set
Vector damageOrigin = GetLocalOrigin();
// send "damage" message
// causes screen to flash, and pain compass to show direction of damage
damageOrigin = m_DmgOrigin;
// only send down damage type that have hud art
int iShowHudDamage = g_pGameRules->Damage_GetShowOnHud();
int visibleDamageBits = m_bitsDamageType & iShowHudDamage;
m_DmgTake = clamp( m_DmgTake, 0, 255 );
m_DmgSave = clamp( m_DmgSave, 0, 255 );
// If we're poisoned, but it wasn't this frame, don't send the indicator
// Without this check, any damage that occured to the player while they were
// recovering from a poison bite would register as poisonous as well and flash
// the whole screen! -- jdw
if ( visibleDamageBits & DMG_POISON )
{
float flLastPoisonedDelta = gpGlobals->curtime - m_tbdPrev;
if ( flLastPoisonedDelta > 0.1f )
{
visibleDamageBits &= ~DMG_POISON;
}
}
CSingleUserRecipientFilter user( this );
user.MakeReliable();
UserMessageBegin( user, "Damage" );
WRITE_BYTE( m_DmgSave );
WRITE_BYTE( m_DmgTake );
WRITE_LONG( visibleDamageBits );
WRITE_FLOAT( damageOrigin.x ); //BUG: Should be fixed point (to hud) not floats
WRITE_FLOAT( damageOrigin.y ); //BUG: However, the HUD does _not_ implement bitfield messages (yet)
WRITE_FLOAT( damageOrigin.z ); //BUG: We use WRITE_VEC3COORD for everything else
MessageEnd();
m_DmgTake = 0;
m_DmgSave = 0;
m_bitsHUDDamage = m_bitsDamageType;
// Clear off non-time-based damage indicators
int iTimeBasedDamage = g_pGameRules->Damage_GetTimeBased();
m_bitsDamageType &= iTimeBasedDamage;
}
// Update Flashlight
#ifdef HL2_EPISODIC
if ( Flashlight_UseLegacyVersion() == false )
{
if ( FlashlightIsOn() && sv_infinite_aux_power.GetBool() == false )
{
m_HL2Local.m_flFlashBattery -= FLASH_DRAIN_TIME * gpGlobals->frametime;
if ( m_HL2Local.m_flFlashBattery < 0.0f )
{
FlashlightTurnOff();
m_HL2Local.m_flFlashBattery = 0.0f;
}
}
else
{
m_HL2Local.m_flFlashBattery += FLASH_CHARGE_TIME * gpGlobals->frametime;
if ( m_HL2Local.m_flFlashBattery > 100.0f )
{
m_HL2Local.m_flFlashBattery = 100.0f;
}
}
}
else
{
m_HL2Local.m_flFlashBattery = -1.0f;
}
#endif // HL2_EPISODIC
BaseClass::UpdateClientData();
}
//---------------------------------------------------------
//---------------------------------------------------------
void CHL2_Player::OnRestore()
{
BaseClass::OnRestore();
m_pPlayerAISquad = g_AI_SquadManager.FindCreateSquad(AllocPooledString(PLAYER_SQUADNAME));
}
//---------------------------------------------------------
//---------------------------------------------------------
Vector CHL2_Player::EyeDirection2D( void )
{
Vector vecReturn = EyeDirection3D();
vecReturn.z = 0;
vecReturn.AsVector2D().NormalizeInPlace();
return vecReturn;
}
//---------------------------------------------------------
//---------------------------------------------------------
Vector CHL2_Player::EyeDirection3D( void )
{
Vector vecForward;
// Return the vehicle angles if we request them
if ( GetVehicle() != NULL )
{
CacheVehicleView();
EyeVectors( &vecForward );
return vecForward;
}
AngleVectors( EyeAngles(), &vecForward );
return vecForward;
}
//---------------------------------------------------------
//---------------------------------------------------------
bool CHL2_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex )
{
MDLCACHE_CRITICAL_SECTION();
// Recalculate proficiency!
SetCurrentWeaponProficiency( CalcWeaponProficiency( pWeapon ) );
// Come out of suit zoom mode
if ( IsZooming() )
{
StopZooming();
}
return BaseClass::Weapon_Switch( pWeapon, viewmodelindex );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
WeaponProficiency_t CHL2_Player::CalcWeaponProficiency( CBaseCombatWeapon *pWeapon )
{
WeaponProficiency_t proficiency;
proficiency = WEAPON_PROFICIENCY_PERFECT;
if( weapon_showproficiency.GetBool() != 0 )
{
Msg("Player switched to %s, proficiency is %s\n", pWeapon->GetClassname(), GetWeaponProficiencyName( proficiency ) );
}
return proficiency;
}
//-----------------------------------------------------------------------------
// Purpose: override how single player rays hit the player
//-----------------------------------------------------------------------------
bool LineCircleIntersection(
const Vector2D &center,
const float radius,
const Vector2D &vLinePt,
const Vector2D &vLineDir,
float *fIntersection1,
float *fIntersection2)
{
// Line = P + Vt
// Sphere = r (assume we've translated to origin)
// (P + Vt)^2 = r^2
// VVt^2 + 2PVt + (PP - r^2)
// Solve as quadratic: (-b +/- sqrt(b^2 - 4ac)) / 2a
// If (b^2 - 4ac) is < 0 there is no solution.
// If (b^2 - 4ac) is = 0 there is one solution (a case this function doesn't support).
// If (b^2 - 4ac) is > 0 there are two solutions.
Vector2D P;
float a, b, c, sqr, insideSqr;
// Translate circle to origin.
P[0] = vLinePt[0] - center[0];
P[1] = vLinePt[1] - center[1];
a = vLineDir.Dot(vLineDir);
b = 2.0f * P.Dot(vLineDir);
c = P.Dot(P) - (radius * radius);
insideSqr = b*b - 4*a*c;
if(insideSqr <= 0.000001f)
return false;
// Ok, two solutions.
sqr = (float)FastSqrt(insideSqr);
float denom = 1.0 / (2.0f * a);
*fIntersection1 = (-b - sqr) * denom;
*fIntersection2 = (-b + sqr) * denom;
return true;
}
static void Collision_ClearTrace( const Vector &vecRayStart, const Vector &vecRayDelta, CBaseTrace *pTrace )
{
pTrace->startpos = vecRayStart;
pTrace->endpos = vecRayStart;
pTrace->endpos += vecRayDelta;
pTrace->startsolid = false;
pTrace->allsolid = false;
pTrace->fraction = 1.0f;
pTrace->contents = 0;
}
bool IntersectRayWithAACylinder( const Ray_t &ray,
const Vector &center, float radius, float height, CBaseTrace *pTrace )
{
Assert( ray.m_IsRay );
Collision_ClearTrace( ray.m_Start, ray.m_Delta, pTrace );
// First intersect the ray with the top + bottom planes
float halfHeight = height * 0.5;
// Handle parallel case
Vector vStart = ray.m_Start - center;
Vector vEnd = vStart + ray.m_Delta;
float flEnterFrac, flLeaveFrac;
if (FloatMakePositive(ray.m_Delta.z) < 1e-8)
{
if ( (vStart.z < -halfHeight) || (vStart.z > halfHeight) )
{
return false; // no hit
}
flEnterFrac = 0.0f; flLeaveFrac = 1.0f;
}
else
{
// Clip the ray to the top and bottom of box
flEnterFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, halfHeight);
flLeaveFrac = IntersectRayWithAAPlane( vStart, vEnd, 2, 1, -halfHeight);
if ( flLeaveFrac < flEnterFrac )
{
float temp = flLeaveFrac;
flLeaveFrac = flEnterFrac;
flEnterFrac = temp;
}
if ( flLeaveFrac < 0 || flEnterFrac > 1)
{
return false;
}
}
// Intersect with circle
float flCircleEnterFrac, flCircleLeaveFrac;
if ( !LineCircleIntersection( vec3_origin.AsVector2D(), radius,
vStart.AsVector2D(), ray.m_Delta.AsVector2D(), &flCircleEnterFrac, &flCircleLeaveFrac ) )
{
return false; // no hit
}
Assert( flCircleEnterFrac <= flCircleLeaveFrac );
if ( flCircleLeaveFrac < 0 || flCircleEnterFrac > 1)
{
return false;
}
if ( flEnterFrac < flCircleEnterFrac )
flEnterFrac = flCircleEnterFrac;
if ( flLeaveFrac > flCircleLeaveFrac )
flLeaveFrac = flCircleLeaveFrac;
if ( flLeaveFrac < flEnterFrac )
return false;
VectorMA( ray.m_Start, flEnterFrac , ray.m_Delta, pTrace->endpos );
pTrace->fraction = flEnterFrac;
pTrace->contents = CONTENTS_SOLID;
// Calculate the point on our center line where we're nearest the intersection point
Vector collisionCenter;
CalcClosestPointOnLineSegment( pTrace->endpos, center + Vector( 0, 0, halfHeight ), center - Vector( 0, 0, halfHeight ), collisionCenter );
// Our normal is the direction from that center point to the intersection point
pTrace->plane.normal = pTrace->endpos - collisionCenter;
VectorNormalize( pTrace->plane.normal );
return true;
}
bool CHL2_Player::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
{
if( g_pGameRules->IsMultiplayer() )
{
return BaseClass::TestHitboxes( ray, fContentsMask, tr );
}
else
{
Assert( ray.m_IsRay );
Vector mins, maxs;
mins = WorldAlignMins();
maxs = WorldAlignMaxs();
if ( IntersectRayWithAACylinder( ray, WorldSpaceCenter(), maxs.x * PLAYER_HULL_REDUCTION, maxs.z - mins.z, &tr ) )
{
tr.hitbox = 0;
CStudioHdr *pStudioHdr = GetModelPtr( );
if (!pStudioHdr)
return false;
mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( m_nHitboxSet );
if ( !set || !set->numhitboxes )
return false;
mstudiobbox_t *pbox = set->pHitbox( tr.hitbox );
mstudiobone_t *pBone = pStudioHdr->pBone(pbox->bone);
tr.surface.name = "**studio**";
tr.surface.flags = SURF_HITBOX;
tr.surface.surfaceProps = physprops->GetSurfaceIndex( pBone->pszSurfaceProp() );
}
return true;
}
}
//---------------------------------------------------------
// Show the player's scaled down bbox that we use for
// bullet impacts.
//---------------------------------------------------------
void CHL2_Player::DrawDebugGeometryOverlays(void)
{
BaseClass::DrawDebugGeometryOverlays();
if (m_debugOverlays & OVERLAY_BBOX_BIT)
{
Vector mins, maxs;
mins = WorldAlignMins();
maxs = WorldAlignMaxs();
mins.x *= PLAYER_HULL_REDUCTION;
mins.y *= PLAYER_HULL_REDUCTION;
maxs.x *= PLAYER_HULL_REDUCTION;
maxs.y *= PLAYER_HULL_REDUCTION;
NDebugOverlay::Box( GetAbsOrigin(), mins, maxs, 255, 0, 0, 100, 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose: Helper to remove from ladder
//-----------------------------------------------------------------------------
void CHL2_Player::ExitLadder()
{
if ( MOVETYPE_LADDER != GetMoveType() )
return;
SetMoveType( MOVETYPE_WALK );
SetMoveCollide( MOVECOLLIDE_DEFAULT );
// Remove from ladder
m_HL2Local.m_hLadder.Set( NULL );
}
surfacedata_t *CHL2_Player::GetLadderSurface( const Vector &origin )
{
extern const char *FuncLadder_GetSurfaceprops(CBaseEntity *pLadderEntity);
CBaseEntity *pLadder = m_HL2Local.m_hLadder.Get();
if ( pLadder )
{
const char *pSurfaceprops = FuncLadder_GetSurfaceprops(pLadder);
// get ladder material from func_ladder
return physprops->GetSurfaceData( physprops->GetSurfaceIndex( pSurfaceprops ) );
}
return BaseClass::GetLadderSurface(origin);
}
//-----------------------------------------------------------------------------
// Purpose: Queues up a use deny sound, played in ItemPostFrame.
//-----------------------------------------------------------------------------
void CHL2_Player::PlayUseDenySound()
{
m_bPlayUseDenySound = true;
}
void CHL2_Player::ItemPostFrame()
{
BaseClass::ItemPostFrame();
if ( m_bPlayUseDenySound )
{
m_bPlayUseDenySound = false;
EmitSound( "HL2Player.UseDeny" );
}
}
void CHL2_Player::StartWaterDeathSounds( void )
{
CPASAttenuationFilter filter( this );
if ( m_sndLeeches == NULL )
{
m_sndLeeches = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.leech_bites_loop" , ATTN_NORM );
}
if ( m_sndLeeches )
{
(CSoundEnvelopeController::GetController()).Play( m_sndLeeches, 1.0f, 100 );
}
if ( m_sndWaterSplashes == NULL )
{
m_sndWaterSplashes = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "coast.leech_water_churn_loop" , ATTN_NORM );
}
if ( m_sndWaterSplashes )
{
(CSoundEnvelopeController::GetController()).Play( m_sndWaterSplashes, 1.0f, 100 );
}
}
void CHL2_Player::StopWaterDeathSounds( void )
{
if ( m_sndLeeches )
{
(CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndLeeches, 0.5f, true );
m_sndLeeches = NULL;
}
if ( m_sndWaterSplashes )
{
(CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndWaterSplashes, 0.5f, true );
m_sndWaterSplashes = NULL;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHL2_Player::MissedAR2AltFire()
{
if( GetPlayerProxy() != NULL )
{
GetPlayerProxy()->m_PlayerMissedAR2AltFire.FireOutput( this, this );
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CHL2_Player::DisplayLadderHudHint()
{
#if !defined( CLIENT_DLL )
if( gpGlobals->curtime > m_flTimeNextLadderHint )
{
m_flTimeNextLadderHint = gpGlobals->curtime + 60.0f;
CFmtStr hint;
hint.sprintf( "#Valve_Hint_Ladder" );
UTIL_HudHintText( this, hint.Access() );
}
#endif//CLIENT_DLL
}
//-----------------------------------------------------------------------------
// Shuts down sounds
//-----------------------------------------------------------------------------
void CHL2_Player::StopLoopingSounds( void )
{
if ( m_sndLeeches != NULL )
{
(CSoundEnvelopeController::GetController()).SoundDestroy( m_sndLeeches );
m_sndLeeches = NULL;
}
if ( m_sndWaterSplashes != NULL )
{
(CSoundEnvelopeController::GetController()).SoundDestroy( m_sndWaterSplashes );
m_sndWaterSplashes = NULL;
}
BaseClass::StopLoopingSounds();
}
//-----------------------------------------------------------------------------
void CHL2_Player::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set )
{
BaseClass::ModifyOrAppendPlayerCriteria( set );
if ( GlobalEntity_GetIndex( "gordon_precriminal" ) == -1 )
{
set.AppendCriteria( "gordon_precriminal", "0" );
}
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
const impactdamagetable_t &CHL2_Player::GetPhysicsImpactDamageTable()
{
if ( m_bUseCappedPhysicsDamageTable )
return gCappedPlayerImpactDamageTable;
return BaseClass::GetPhysicsImpactDamageTable();
}
//-----------------------------------------------------------------------------
// Purpose: Makes a splash when the player transitions between water states
//-----------------------------------------------------------------------------
void CHL2_Player::Splash( void )
{
CEffectData data;
data.m_fFlags = 0;
data.m_vOrigin = GetAbsOrigin();
data.m_vNormal = Vector(0,0,1);
data.m_vAngles = QAngle( 0, 0, 0 );
if ( GetWaterType() & CONTENTS_SLIME )
{
data.m_fFlags |= FX_WATER_IN_SLIME;
}
float flSpeed = GetAbsVelocity().Length();
if ( flSpeed < 300 )
{
data.m_flScale = random->RandomFloat( 10, 12 );
DispatchEffect( "waterripple", data );
}
else
{
data.m_flScale = random->RandomFloat( 6, 8 );
DispatchEffect( "watersplash", data );
}
}
CLogicPlayerProxy *CHL2_Player::GetPlayerProxy( void )
{
CLogicPlayerProxy *pProxy = dynamic_cast< CLogicPlayerProxy* > ( m_hPlayerProxy.Get() );
if ( pProxy == NULL )
{
pProxy = (CLogicPlayerProxy*)gEntList.FindEntityByClassname(NULL, "logic_playerproxy" );
if ( pProxy == NULL )
return NULL;
pProxy->m_hPlayer = this;
m_hPlayerProxy = pProxy;
}
return pProxy;
}
void CHL2_Player::FirePlayerProxyOutput( const char *pszOutputName, variant_t variant, CBaseEntity *pActivator, CBaseEntity *pCaller )
{
if ( GetPlayerProxy() == NULL )
return;
GetPlayerProxy()->FireNamedOutput( pszOutputName, variant, pActivator, pCaller );
}
LINK_ENTITY_TO_CLASS( logic_playerproxy, CLogicPlayerProxy);
BEGIN_DATADESC( CLogicPlayerProxy )
DEFINE_OUTPUT( m_OnFlashlightOn, "OnFlashlightOn" ),
DEFINE_OUTPUT( m_OnFlashlightOff, "OnFlashlightOff" ),
DEFINE_OUTPUT( m_RequestedPlayerHealth, "PlayerHealth" ),
DEFINE_OUTPUT( m_PlayerHasAmmo, "PlayerHasAmmo" ),
DEFINE_OUTPUT( m_PlayerHasNoAmmo, "PlayerHasNoAmmo" ),
DEFINE_OUTPUT( m_PlayerDied, "PlayerDied" ),
DEFINE_OUTPUT( m_PlayerMissedAR2AltFire, "PlayerMissedAR2AltFire" ),
DEFINE_INPUTFUNC( FIELD_VOID, "RequestPlayerHealth", InputRequestPlayerHealth ),
DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightSlowDrain", InputSetFlashlightSlowDrain ),
DEFINE_INPUTFUNC( FIELD_VOID, "SetFlashlightNormalDrain", InputSetFlashlightNormalDrain ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetPlayerHealth", InputSetPlayerHealth ),
DEFINE_INPUTFUNC( FIELD_VOID, "RequestAmmoState", InputRequestAmmoState ),
DEFINE_INPUTFUNC( FIELD_VOID, "LowerWeapon", InputLowerWeapon ),
DEFINE_INPUTFUNC( FIELD_VOID, "EnableCappedPhysicsDamage", InputEnableCappedPhysicsDamage ),
DEFINE_INPUTFUNC( FIELD_VOID, "DisableCappedPhysicsDamage", InputDisableCappedPhysicsDamage ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetLocatorTargetEntity", InputSetLocatorTargetEntity ),
#ifdef PORTAL
DEFINE_INPUTFUNC( FIELD_VOID, "SuppressCrosshair", InputSuppressCrosshair ),
#endif // PORTAL
DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
END_DATADESC()
void CLogicPlayerProxy::Activate( void )
{
BaseClass::Activate();
if ( m_hPlayer == NULL )
{
m_hPlayer = AI_GetSinglePlayer();
}
}
bool CLogicPlayerProxy::PassesDamageFilter( const CTakeDamageInfo &info )
{
if (m_hDamageFilter)
{
CBaseFilter *pFilter = (CBaseFilter *)(m_hDamageFilter.Get());
return pFilter->PassesDamageFilter(info);
}
return true;
}
void CLogicPlayerProxy::InputSetPlayerHealth( inputdata_t &inputdata )
{
if ( m_hPlayer == NULL )
return;
m_hPlayer->SetHealth( inputdata.value.Int() );
}
void CLogicPlayerProxy::InputRequestPlayerHealth( inputdata_t &inputdata )
{
if ( m_hPlayer == NULL )
return;
m_RequestedPlayerHealth.Set( m_hPlayer->GetHealth(), inputdata.pActivator, inputdata.pCaller );
}
void CLogicPlayerProxy::InputSetFlashlightSlowDrain( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
if( pPlayer )
pPlayer->SetFlashlightPowerDrainScale( hl2_darkness_flashlight_factor.GetFloat() );
}
void CLogicPlayerProxy::InputSetFlashlightNormalDrain( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
if( pPlayer )
pPlayer->SetFlashlightPowerDrainScale( 1.0f );
}
void CLogicPlayerProxy::InputRequestAmmoState( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i )
{
CBaseCombatWeapon* pCheck = pPlayer->GetWeapon( i );
if ( pCheck )
{
if ( pCheck->HasAnyAmmo() && (pCheck->UsesPrimaryAmmo() || pCheck->UsesSecondaryAmmo()))
{
m_PlayerHasAmmo.FireOutput( this, this, 0 );
return;
}
}
}
m_PlayerHasNoAmmo.FireOutput( this, this, 0 );
}
void CLogicPlayerProxy::InputLowerWeapon( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
pPlayer->Weapon_Lower();
}
void CLogicPlayerProxy::InputEnableCappedPhysicsDamage( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
pPlayer->EnableCappedPhysicsDamage();
}
void CLogicPlayerProxy::InputDisableCappedPhysicsDamage( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
pPlayer->DisableCappedPhysicsDamage();
}
void CLogicPlayerProxy::InputSetLocatorTargetEntity( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CBaseEntity *pTarget = NULL; // assume no target
string_t iszTarget = MAKE_STRING( inputdata.value.String() );
if( iszTarget != NULL_STRING )
{
pTarget = gEntList.FindEntityByName( NULL, iszTarget );
}
CHL2_Player *pPlayer = dynamic_cast<CHL2_Player*>(m_hPlayer.Get());
pPlayer->SetLocatorTargetEntity(pTarget);
}
#ifdef PORTAL
void CLogicPlayerProxy::InputSuppressCrosshair( inputdata_t &inputdata )
{
if( m_hPlayer == NULL )
return;
CPortal_Player *pPlayer = ToPortalPlayer(m_hPlayer.Get());
pPlayer->SuppressCrosshair( true );
}
#endif // PORTAL