You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3930 lines
107 KiB
3930 lines
107 KiB
//========= 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
|
|
|