* 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
3931 lines
107 KiB
C++
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 ¶ms )
|
|
{
|
|
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 ¢er,
|
|
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 ¢er, 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
|