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.
2805 lines
83 KiB
2805 lines
83 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "in_buttons.h" |
|
#include "engine/IEngineSound.h" |
|
#include "ammodef.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "physics_saverestore.h" |
|
#include "datacache/imdlcache.h" |
|
#include "activitylist.h" |
|
|
|
// NVNT start extra includes |
|
#include "haptics/haptic_utils.h" |
|
#ifdef CLIENT_DLL |
|
#include "prediction.h" |
|
#endif |
|
// NVNT end extra includes |
|
|
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) |
|
#include "tf_shareddefs.h" |
|
#endif |
|
|
|
#if !defined( CLIENT_DLL ) |
|
|
|
// Game DLL Headers |
|
#include "soundent.h" |
|
#include "eventqueue.h" |
|
#include "fmtstr.h" |
|
#include "gameweaponmanager.h" |
|
|
|
#ifdef HL2MP |
|
#include "hl2mp_gamerules.h" |
|
#endif |
|
|
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// The minimum time a hud hint for a weapon should be on screen. If we switch away before |
|
// this, then teh hud hint counter will be deremented so the hint will be shown again, as |
|
// if it had never been seen. The total display time for a hud hint is specified in client |
|
// script HudAnimations.txt (which I can't read here). |
|
#define MIN_HUDHINT_DISPLAY_TIME 7.0f |
|
|
|
#define HIDEWEAPON_THINK_CONTEXT "BaseCombatWeapon_HideThink" |
|
|
|
extern bool UTIL_ItemCanBeTouchedByPlayer( CBaseEntity *pItem, CBasePlayer *pPlayer ); |
|
|
|
#if defined ( TF_CLIENT_DLL ) || defined ( TF_DLL ) |
|
#ifdef _DEBUG |
|
ConVar tf_weapon_criticals_force_random( "tf_weapon_criticals_force_random", "0", FCVAR_REPLICATED | FCVAR_CHEAT ); |
|
#endif // _DEBUG |
|
ConVar tf_weapon_criticals_bucket_cap( "tf_weapon_criticals_bucket_cap", "1000.0", FCVAR_REPLICATED | FCVAR_CHEAT ); |
|
ConVar tf_weapon_criticals_bucket_bottom( "tf_weapon_criticals_bucket_bottom", "-250.0", FCVAR_REPLICATED | FCVAR_CHEAT ); |
|
ConVar tf_weapon_criticals_bucket_default( "tf_weapon_criticals_bucket_default", "300.0", FCVAR_REPLICATED | FCVAR_CHEAT ); |
|
#endif // TF |
|
|
|
CBaseCombatWeapon::CBaseCombatWeapon() |
|
{ |
|
// Constructor must call this |
|
// CONSTRUCT_PREDICTABLE( CBaseCombatWeapon ); |
|
|
|
// Some default values. There should be set in the particular weapon classes |
|
m_fMinRange1 = 65; |
|
m_fMinRange2 = 65; |
|
m_fMaxRange1 = 1024; |
|
m_fMaxRange2 = 1024; |
|
|
|
m_bReloadsSingly = false; |
|
|
|
// Defaults to zero |
|
m_nViewModelIndex = 0; |
|
|
|
m_bFlipViewModel = false; |
|
|
|
#if defined( CLIENT_DLL ) |
|
m_iState = m_iOldState = WEAPON_NOT_CARRIED; |
|
m_iClip1 = -1; |
|
m_iClip2 = -1; |
|
m_iPrimaryAmmoType = -1; |
|
m_iSecondaryAmmoType = -1; |
|
#endif |
|
|
|
#if !defined( CLIENT_DLL ) |
|
m_pConstraint = NULL; |
|
OnBaseCombatWeaponCreated( this ); |
|
#endif |
|
|
|
m_hWeaponFileInfo = GetInvalidWeaponInfoHandle(); |
|
|
|
#if defined( TF_DLL ) |
|
UseClientSideAnimation(); |
|
#endif |
|
|
|
#if defined ( TF_CLIENT_DLL ) || defined ( TF_DLL ) |
|
m_flCritTokenBucket = tf_weapon_criticals_bucket_default.GetFloat(); |
|
m_nCritChecks = 1; |
|
m_nCritSeedRequests = 0; |
|
#endif // TF |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor |
|
//----------------------------------------------------------------------------- |
|
CBaseCombatWeapon::~CBaseCombatWeapon( void ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
//Remove our constraint, if we have one |
|
if ( m_pConstraint != NULL ) |
|
{ |
|
physenv->DestroyConstraint( m_pConstraint ); |
|
m_pConstraint = NULL; |
|
} |
|
OnBaseCombatWeaponDestroyed( this ); |
|
#endif |
|
} |
|
|
|
void CBaseCombatWeapon::Activate( void ) |
|
{ |
|
BaseClass::Activate(); |
|
|
|
#ifndef CLIENT_DLL |
|
if ( GetOwnerEntity() ) |
|
return; |
|
|
|
if ( g_pGameRules->IsAllowedToSpawn( this ) == false ) |
|
{ |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
#endif |
|
|
|
} |
|
void CBaseCombatWeapon::GiveDefaultAmmo( void ) |
|
{ |
|
// If I use clips, set my clips to the default |
|
if ( UsesClipsForAmmo1() ) |
|
{ |
|
m_iClip1 = AutoFiresFullClip() ? 0 : GetDefaultClip1(); |
|
} |
|
else |
|
{ |
|
SetPrimaryAmmoCount( GetDefaultClip1() ); |
|
m_iClip1 = WEAPON_NOCLIP; |
|
} |
|
if ( UsesClipsForAmmo2() ) |
|
{ |
|
m_iClip2 = GetDefaultClip2(); |
|
} |
|
else |
|
{ |
|
SetSecondaryAmmoCount( GetDefaultClip2() ); |
|
m_iClip2 = WEAPON_NOCLIP; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set mode to world model and start falling to the ground |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::Spawn( void ) |
|
{ |
|
Precache(); |
|
|
|
BaseClass::Spawn(); |
|
|
|
SetSolid( SOLID_BBOX ); |
|
m_flNextEmptySoundTime = 0.0f; |
|
|
|
// Weapons won't show up in trace calls if they are being carried... |
|
RemoveEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID ); |
|
|
|
m_iState = WEAPON_NOT_CARRIED; |
|
// Assume |
|
m_nViewModelIndex = 0; |
|
|
|
GiveDefaultAmmo(); |
|
|
|
if ( GetWorldModel() ) |
|
{ |
|
SetModel( GetWorldModel() ); |
|
} |
|
|
|
#if !defined( CLIENT_DLL ) |
|
if( IsX360() ) |
|
{ |
|
AddEffects( EF_ITEM_BLINK ); |
|
} |
|
|
|
FallInit(); |
|
SetCollisionGroup( COLLISION_GROUP_WEAPON ); |
|
m_takedamage = DAMAGE_EVENTS_ONLY; |
|
|
|
SetBlocksLOS( false ); |
|
|
|
// Default to non-removeable, because we don't want the |
|
// game_weapon_manager entity to remove weapons that have |
|
// been hand-placed by level designers. We only want to remove |
|
// weapons that have been dropped by NPC's. |
|
SetRemoveable( false ); |
|
#endif |
|
|
|
// Bloat the box for player pickup |
|
CollisionProp()->UseTriggerBounds( true, 36 ); |
|
|
|
// Use more efficient bbox culling on the client. Otherwise, it'll setup bones for most |
|
// characters even when they're not in the frustum. |
|
AddEffects( EF_BONEMERGE_FASTCULL ); |
|
|
|
m_iReloadHudHintCount = 0; |
|
m_iAltFireHudHintCount = 0; |
|
m_flHudHintMinDisplayTime = 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: get this game's encryption key for decoding weapon kv files |
|
// Output : virtual const unsigned char |
|
//----------------------------------------------------------------------------- |
|
const unsigned char *CBaseCombatWeapon::GetEncryptionKey( void ) |
|
{ |
|
return g_pGameRules->GetEncryptionKey(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::Precache( void ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
Assert( Q_strlen( GetClassname() ) > 0 ); |
|
// Msg( "Client got %s\n", GetClassname() ); |
|
#endif |
|
m_iPrimaryAmmoType = m_iSecondaryAmmoType = -1; |
|
|
|
// Add this weapon to the weapon registry, and get our index into it |
|
// Get weapon data from script file |
|
if ( ReadWeaponDataFromFileForSlot( filesystem, GetClassname(), &m_hWeaponFileInfo, GetEncryptionKey() ) ) |
|
{ |
|
// Get the ammo indexes for the ammo's specified in the data file |
|
if ( GetWpnData().szAmmo1[0] ) |
|
{ |
|
m_iPrimaryAmmoType = GetAmmoDef()->Index( GetWpnData().szAmmo1 ); |
|
if (m_iPrimaryAmmoType == -1) |
|
{ |
|
Msg("ERROR: Weapon (%s) using undefined primary ammo type (%s)\n",GetClassname(), GetWpnData().szAmmo1); |
|
} |
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) |
|
// Ammo override |
|
int iModUseMetalOverride = 0; |
|
CALL_ATTRIB_HOOK_INT( iModUseMetalOverride, mod_use_metal_ammo_type ); |
|
if ( iModUseMetalOverride ) |
|
{ |
|
m_iPrimaryAmmoType = (int)TF_AMMO_METAL; |
|
} |
|
#endif |
|
} |
|
if ( GetWpnData().szAmmo2[0] ) |
|
{ |
|
m_iSecondaryAmmoType = GetAmmoDef()->Index( GetWpnData().szAmmo2 ); |
|
if (m_iSecondaryAmmoType == -1) |
|
{ |
|
Msg("ERROR: Weapon (%s) using undefined secondary ammo type (%s)\n",GetClassname(),GetWpnData().szAmmo2); |
|
} |
|
|
|
} |
|
#if defined( CLIENT_DLL ) |
|
gWR.LoadWeaponSprites( GetWeaponFileInfoHandle() ); |
|
#endif |
|
// Precache models (preload to avoid hitch) |
|
m_iViewModelIndex = 0; |
|
m_iWorldModelIndex = 0; |
|
if ( GetViewModel() && GetViewModel()[0] ) |
|
{ |
|
m_iViewModelIndex = CBaseEntity::PrecacheModel( GetViewModel() ); |
|
} |
|
if ( GetWorldModel() && GetWorldModel()[0] ) |
|
{ |
|
m_iWorldModelIndex = CBaseEntity::PrecacheModel( GetWorldModel() ); |
|
} |
|
|
|
// Precache sounds, too |
|
for ( int i = 0; i < NUM_SHOOT_SOUND_TYPES; ++i ) |
|
{ |
|
const char *shootsound = GetShootSound( i ); |
|
if ( shootsound && shootsound[0] ) |
|
{ |
|
CBaseEntity::PrecacheScriptSound( shootsound ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// Couldn't read data file, remove myself |
|
Warning( "Error reading weapon data file for: %s\n", GetClassname() ); |
|
// Remove( ); //don't remove, this gets released soon! |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get my data in the file weapon info array |
|
//----------------------------------------------------------------------------- |
|
const FileWeaponInfo_t &CBaseCombatWeapon::GetWpnData( void ) const |
|
{ |
|
return *GetFileWeaponInfoFromHandle( m_hWeaponFileInfo ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CBaseCombatWeapon::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const |
|
{ |
|
return GetWpnData().szViewModel; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CBaseCombatWeapon::GetWorldModel( void ) const |
|
{ |
|
return GetWpnData().szWorldModel; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CBaseCombatWeapon::GetAnimPrefix( void ) const |
|
{ |
|
return GetWpnData().szAnimationPrefix; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : char const |
|
//----------------------------------------------------------------------------- |
|
const char *CBaseCombatWeapon::GetPrintName( void ) const |
|
{ |
|
return GetWpnData().szPrintName; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetMaxClip1( void ) const |
|
{ |
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) |
|
int iModMaxClipOverride = 0; |
|
CALL_ATTRIB_HOOK_INT( iModMaxClipOverride, mod_max_primary_clip_override ); |
|
if ( iModMaxClipOverride != 0 ) |
|
return iModMaxClipOverride; |
|
#endif |
|
|
|
return GetWpnData().iMaxClip1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetMaxClip2( void ) const |
|
{ |
|
return GetWpnData().iMaxClip2; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetDefaultClip1( void ) const |
|
{ |
|
return GetWpnData().iDefaultClip1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetDefaultClip2( void ) const |
|
{ |
|
return GetWpnData().iDefaultClip2; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::UsesClipsForAmmo1( void ) const |
|
{ |
|
return ( GetMaxClip1() != WEAPON_NOCLIP ); |
|
} |
|
|
|
bool CBaseCombatWeapon::IsMeleeWeapon() const |
|
{ |
|
return GetWpnData().m_bMeleeWeapon; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::UsesClipsForAmmo2( void ) const |
|
{ |
|
return ( GetMaxClip2() != WEAPON_NOCLIP ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetWeight( void ) const |
|
{ |
|
return GetWpnData().iWeight; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Whether this weapon can be autoswitched to when the player runs out |
|
// of ammo in their current weapon or they pick this weapon up. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::AllowsAutoSwitchTo( void ) const |
|
{ |
|
return GetWpnData().bAutoSwitchTo; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Whether this weapon can be autoswitched away from when the player |
|
// runs out of ammo in this weapon or picks up another weapon or ammo. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::AllowsAutoSwitchFrom( void ) const |
|
{ |
|
return GetWpnData().bAutoSwitchFrom; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetWeaponFlags( void ) const |
|
{ |
|
return GetWpnData().iFlags; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetSlot( void ) const |
|
{ |
|
return GetWpnData().iSlot; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetPosition( void ) const |
|
{ |
|
return GetWpnData().iPosition; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CBaseCombatWeapon::GetName( void ) const |
|
{ |
|
return GetWpnData().szClassName; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteActive( void ) const |
|
{ |
|
return GetWpnData().iconActive; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteInactive( void ) const |
|
{ |
|
return GetWpnData().iconInactive; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteAmmo( void ) const |
|
{ |
|
return GetWpnData().iconAmmo; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteAmmo2( void ) const |
|
{ |
|
return GetWpnData().iconAmmo2; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteCrosshair( void ) const |
|
{ |
|
return GetWpnData().iconCrosshair; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteAutoaim( void ) const |
|
{ |
|
return GetWpnData().iconAutoaim; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteZoomedCrosshair( void ) const |
|
{ |
|
return GetWpnData().iconZoomedCrosshair; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CHudTexture const *CBaseCombatWeapon::GetSpriteZoomedAutoaim( void ) const |
|
{ |
|
return GetWpnData().iconZoomedAutoaim; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CBaseCombatWeapon::GetShootSound( int iIndex ) const |
|
{ |
|
return GetWpnData().aShootSounds[ iIndex ]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetRumbleEffect() const |
|
{ |
|
return GetWpnData().iRumbleEffect; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CBaseCombatCharacter *CBaseCombatWeapon::GetOwner() const |
|
{ |
|
return ToBaseCombatCharacter( m_hOwner.Get() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : BaseCombatCharacter - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::SetOwner( CBaseCombatCharacter *owner ) |
|
{ |
|
if ( !owner ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
// Make sure the weapon updates its state when it's removed from the player |
|
// We have to force an active state change, because it's being dropped and won't call UpdateClientData() |
|
int iOldState = m_iState; |
|
m_iState = WEAPON_NOT_CARRIED; |
|
OnActiveStateChanged( iOldState ); |
|
#endif |
|
|
|
// make sure we clear out our HideThink if we have one pending |
|
SetContextThink( NULL, 0, HIDEWEAPON_THINK_CONTEXT ); |
|
} |
|
|
|
m_hOwner = owner; |
|
|
|
#ifndef CLIENT_DLL |
|
DispatchUpdateTransmitState(); |
|
#else |
|
UpdateVisibility(); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return false if this weapon won't let the player switch away from it |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::IsAllowedToSwitch( void ) |
|
{ |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return true if this weapon can be selected via the weapon selection |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::CanBeSelected( void ) |
|
{ |
|
if ( !VisibleInWeaponSelection() ) |
|
return false; |
|
|
|
return HasAmmo(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return true if this weapon has some ammo |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::HasAmmo( void ) |
|
{ |
|
// Weapons with no ammo types can always be selected |
|
if ( m_iPrimaryAmmoType == -1 && m_iSecondaryAmmoType == -1 ) |
|
return true; |
|
if ( GetWeaponFlags() & ITEM_FLAG_SELECTONEMPTY ) |
|
return true; |
|
|
|
CBasePlayer *player = ToBasePlayer( GetOwner() ); |
|
if ( !player ) |
|
return false; |
|
return ( m_iClip1 > 0 || player->GetAmmoCount( m_iPrimaryAmmoType ) || m_iClip2 > 0 || player->GetAmmoCount( m_iSecondaryAmmoType ) ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return true if this weapon should be seen, and hence be selectable, in the weapon selection |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::VisibleInWeaponSelection( void ) |
|
{ |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::HasWeaponIdleTimeElapsed( void ) |
|
{ |
|
if ( gpGlobals->curtime > m_flTimeWeaponIdle ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : time - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::SetWeaponIdleTime( float time ) |
|
{ |
|
m_flTimeWeaponIdle = time; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CBaseCombatWeapon::GetWeaponIdleTime( void ) |
|
{ |
|
return m_flTimeWeaponIdle; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Drop/throw the weapon with the given velocity. |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::Drop( const Vector &vecVelocity ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
|
|
// Once somebody drops a gun, it's fair game for removal when/if |
|
// a game_weapon_manager does a cleanup on surplus weapons in the |
|
// world. |
|
SetRemoveable( true ); |
|
WeaponManager_AmmoMod( this ); |
|
|
|
//If it was dropped then there's no need to respawn it. |
|
AddSpawnFlags( SF_NORESPAWN ); |
|
|
|
StopAnimation(); |
|
StopFollowingEntity( ); |
|
SetMoveType( MOVETYPE_FLYGRAVITY ); |
|
// clear follow stuff, setup for collision |
|
SetGravity(1.0); |
|
m_iState = WEAPON_NOT_CARRIED; |
|
RemoveEffects( EF_NODRAW ); |
|
FallInit(); |
|
SetGroundEntity( NULL ); |
|
SetThink( &CBaseCombatWeapon::SetPickupTouch ); |
|
SetTouch(NULL); |
|
|
|
if( hl2_episodic.GetBool() ) |
|
{ |
|
RemoveSpawnFlags( SF_WEAPON_NO_PLAYER_PICKUP ); |
|
} |
|
|
|
IPhysicsObject *pObj = VPhysicsGetObject(); |
|
if ( pObj != NULL ) |
|
{ |
|
AngularImpulse angImp( 200, 200, 200 ); |
|
pObj->AddVelocity( &vecVelocity, &angImp ); |
|
} |
|
else |
|
{ |
|
SetAbsVelocity( vecVelocity ); |
|
} |
|
|
|
CBaseEntity *pOwner = GetOwnerEntity(); |
|
|
|
SetNextThink( gpGlobals->curtime + 1.0f ); |
|
SetOwnerEntity( NULL ); |
|
SetOwner( NULL ); |
|
|
|
// If we're not allowing to spawn due to the gamerules, |
|
// remove myself when I'm dropped by an NPC. |
|
if ( pOwner && pOwner->IsNPC() ) |
|
{ |
|
if ( g_pGameRules->IsAllowedToSpawn( this ) == false ) |
|
{ |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pPicker - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::OnPickedUp( CBaseCombatCharacter *pNewOwner ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
RemoveEffects( EF_ITEM_BLINK ); |
|
|
|
if( pNewOwner->IsPlayer() ) |
|
{ |
|
m_OnPlayerPickup.FireOutput(pNewOwner, this); |
|
|
|
// Play the pickup sound for 1st-person observers |
|
CRecipientFilter filter; |
|
for ( int i=1; i <= gpGlobals->maxClients; ++i ) |
|
{ |
|
CBasePlayer *player = UTIL_PlayerByIndex(i); |
|
if ( player && !player->IsAlive() && player->GetObserverMode() == OBS_MODE_IN_EYE ) |
|
{ |
|
filter.AddRecipient( player ); |
|
} |
|
} |
|
if ( filter.GetRecipientCount() ) |
|
{ |
|
CBaseEntity::EmitSound( filter, pNewOwner->entindex(), "Player.PickupWeapon" ); |
|
} |
|
|
|
// Robin: We don't want to delete weapons the player has picked up, so |
|
// clear the name of the weapon. This prevents wildcards that are meant |
|
// to find NPCs finding weapons dropped by the NPCs as well. |
|
SetName( NULL_STRING ); |
|
} |
|
else |
|
{ |
|
m_OnNPCPickup.FireOutput(pNewOwner, this); |
|
} |
|
|
|
#ifdef HL2MP |
|
HL2MPRules()->RemoveLevelDesignerPlacedObject( this ); |
|
#endif |
|
|
|
// Someone picked me up, so make it so that I can't be removed. |
|
SetRemoveable( false ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : &vecTracerSrc - |
|
// &tr - |
|
// iTracerType - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ) |
|
{ |
|
CBaseEntity *pOwner = GetOwner(); |
|
|
|
if ( pOwner == NULL ) |
|
{ |
|
BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType ); |
|
return; |
|
} |
|
|
|
const char *pszTracerName = GetTracerType(); |
|
|
|
Vector vNewSrc = vecTracerSrc; |
|
int iEntIndex = pOwner->entindex(); |
|
|
|
if ( g_pGameRules->IsMultiplayer() ) |
|
{ |
|
iEntIndex = entindex(); |
|
} |
|
|
|
int iAttachment = GetTracerAttachment(); |
|
|
|
switch ( iTracerType ) |
|
{ |
|
case TRACER_LINE: |
|
UTIL_Tracer( vNewSrc, tr.endpos, iEntIndex, iAttachment, 0.0f, true, pszTracerName ); |
|
break; |
|
|
|
case TRACER_LINE_AND_WHIZ: |
|
UTIL_Tracer( vNewSrc, tr.endpos, iEntIndex, iAttachment, 0.0f, true, pszTracerName ); |
|
break; |
|
} |
|
} |
|
|
|
void CBaseCombatWeapon::GiveTo( CBaseEntity *pOther ) |
|
{ |
|
DefaultTouch( pOther ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Default Touch function for player picking up a weapon (not AI) |
|
// Input : pOther - the entity that touched me |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::DefaultTouch( CBaseEntity *pOther ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
// Can't pick up dissolving weapons |
|
if ( IsDissolving() ) |
|
return; |
|
|
|
// if it's not a player, ignore |
|
CBasePlayer *pPlayer = ToBasePlayer(pOther); |
|
if ( !pPlayer ) |
|
return; |
|
|
|
if( UTIL_ItemCanBeTouchedByPlayer(this, pPlayer) ) |
|
{ |
|
// This makes sure the player could potentially take the object |
|
// before firing the cache interaction output. That doesn't mean |
|
// the player WILL end up taking the object, but cache interactions |
|
// are fired as soon as you prove you have found the object, not |
|
// when you finally acquire it. |
|
m_OnCacheInteraction.FireOutput( pOther, this ); |
|
} |
|
|
|
if( HasSpawnFlags(SF_WEAPON_NO_PLAYER_PICKUP) ) |
|
return; |
|
|
|
if (pPlayer->BumpWeapon(this)) |
|
{ |
|
OnPickedUp( pPlayer ); |
|
} |
|
#endif |
|
} |
|
|
|
//--------------------------------------------------------- |
|
// It's OK for base classes to override this completely |
|
// without calling up. (sjb) |
|
//--------------------------------------------------------- |
|
bool CBaseCombatWeapon::ShouldDisplayAltFireHUDHint() |
|
{ |
|
if( m_iAltFireHudHintCount >= WEAPON_RELOAD_HUD_HINT_COUNT ) |
|
return false; |
|
|
|
if( UsesSecondaryAmmo() && HasSecondaryAmmo() ) |
|
{ |
|
return true; |
|
} |
|
|
|
if( !UsesSecondaryAmmo() && HasPrimaryAmmo() ) |
|
{ |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::DisplayAltFireHudHint() |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
CFmtStr hint; |
|
hint.sprintf( "#valve_hint_alt_%s", GetClassname() ); |
|
UTIL_HudHintText( GetOwner(), hint.Access() ); |
|
m_iAltFireHudHintCount++; |
|
m_bAltFireHudHintDisplayed = true; |
|
m_flHudHintMinDisplayTime = gpGlobals->curtime + MIN_HUDHINT_DISPLAY_TIME; |
|
#endif//CLIENT_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::RescindAltFireHudHint() |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
Assert(m_bAltFireHudHintDisplayed); |
|
|
|
UTIL_HudHintText( GetOwner(), "" ); |
|
--m_iAltFireHudHintCount; |
|
m_bAltFireHudHintDisplayed = false; |
|
#endif//CLIENT_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::ShouldDisplayReloadHUDHint() |
|
{ |
|
if( m_iReloadHudHintCount >= WEAPON_RELOAD_HUD_HINT_COUNT ) |
|
return false; |
|
|
|
CBaseCombatCharacter *pOwner = GetOwner(); |
|
|
|
if( pOwner != NULL && pOwner->IsPlayer() && UsesClipsForAmmo1() && m_iClip1 < (GetMaxClip1() / 2) ) |
|
{ |
|
// I'm owned by a player, I use clips, I have less then half a clip loaded. Now, does the player have more ammo? |
|
if ( pOwner ) |
|
{ |
|
if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::DisplayReloadHudHint() |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
UTIL_HudHintText( GetOwner(), "valve_hint_reload" ); |
|
m_iReloadHudHintCount++; |
|
m_bReloadHudHintDisplayed = true; |
|
m_flHudHintMinDisplayTime = gpGlobals->curtime + MIN_HUDHINT_DISPLAY_TIME; |
|
#endif//CLIENT_DLL |
|
} |
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::RescindReloadHudHint() |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
Assert(m_bReloadHudHintDisplayed); |
|
|
|
UTIL_HudHintText( GetOwner(), "" ); |
|
--m_iReloadHudHintCount; |
|
m_bReloadHudHintDisplayed = false; |
|
#endif//CLIENT_DLL |
|
} |
|
|
|
|
|
void CBaseCombatWeapon::SetPickupTouch( void ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
SetTouch(&CBaseCombatWeapon::DefaultTouch); |
|
|
|
if ( gpGlobals->maxClients > 1 ) |
|
{ |
|
if ( GetSpawnFlags() & SF_NORESPAWN ) |
|
{ |
|
SetThink( &CBaseEntity::SUB_Remove ); |
|
SetNextThink( gpGlobals->curtime + 30.0f ); |
|
} |
|
} |
|
|
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Become a child of the owner (MOVETYPE_FOLLOW) |
|
// disables collisions, touch functions, thinking |
|
// Input : *pOwner - new owner/operator |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::Equip( CBaseCombatCharacter *pOwner ) |
|
{ |
|
// Attach the weapon to an owner |
|
SetAbsVelocity( vec3_origin ); |
|
RemoveSolidFlags( FSOLID_TRIGGER ); |
|
FollowEntity( pOwner ); |
|
SetOwner( pOwner ); |
|
SetOwnerEntity( pOwner ); |
|
|
|
// Break any constraint I might have to the world. |
|
RemoveEffects( EF_ITEM_BLINK ); |
|
|
|
#if !defined( CLIENT_DLL ) |
|
if ( m_pConstraint != NULL ) |
|
{ |
|
RemoveSpawnFlags( SF_WEAPON_START_CONSTRAINED ); |
|
physenv->DestroyConstraint( m_pConstraint ); |
|
m_pConstraint = NULL; |
|
} |
|
#endif |
|
|
|
|
|
m_flNextPrimaryAttack = gpGlobals->curtime; |
|
m_flNextSecondaryAttack = gpGlobals->curtime; |
|
SetTouch( NULL ); |
|
SetThink( NULL ); |
|
#if !defined( CLIENT_DLL ) |
|
VPhysicsDestroyObject(); |
|
#endif |
|
|
|
if ( pOwner->IsPlayer() ) |
|
{ |
|
SetModel( GetViewModel() ); |
|
} |
|
else |
|
{ |
|
// Make the weapon ready as soon as any NPC picks it up. |
|
m_flNextPrimaryAttack = gpGlobals->curtime; |
|
m_flNextSecondaryAttack = gpGlobals->curtime; |
|
SetModel( GetWorldModel() ); |
|
} |
|
} |
|
|
|
void CBaseCombatWeapon::SetActivity( Activity act, float duration ) |
|
{ |
|
//Adrian: Oh man... |
|
#if !defined( CLIENT_DLL ) && (defined( HL2MP ) || defined( PORTAL )) |
|
SetModel( GetWorldModel() ); |
|
#endif |
|
|
|
int sequence = SelectWeightedSequence( act ); |
|
|
|
// FORCE IDLE on sequences we don't have (which should be many) |
|
if ( sequence == ACTIVITY_NOT_AVAILABLE ) |
|
sequence = SelectWeightedSequence( ACT_VM_IDLE ); |
|
|
|
//Adrian: Oh man again... |
|
#if !defined( CLIENT_DLL ) && (defined( HL2MP ) || defined( PORTAL )) |
|
SetModel( GetViewModel() ); |
|
#endif |
|
|
|
if ( sequence != ACTIVITY_NOT_AVAILABLE ) |
|
{ |
|
SetSequence( sequence ); |
|
SetActivity( act ); |
|
SetCycle( 0 ); |
|
ResetSequenceInfo( ); |
|
|
|
if ( duration > 0 ) |
|
{ |
|
// FIXME: does this even make sense in non-shoot animations? |
|
m_flPlaybackRate = SequenceDuration( sequence ) / duration; |
|
m_flPlaybackRate = MIN( m_flPlaybackRate, 12.0); // FIXME; magic number!, network encoding range |
|
} |
|
else |
|
{ |
|
m_flPlaybackRate = 1.0; |
|
} |
|
} |
|
} |
|
|
|
//==================================================================================== |
|
// WEAPON CLIENT HANDLING |
|
//==================================================================================== |
|
int CBaseCombatWeapon::UpdateClientData( CBasePlayer *pPlayer ) |
|
{ |
|
int iNewState = WEAPON_IS_CARRIED_BY_PLAYER; |
|
|
|
if ( pPlayer->GetActiveWeapon() == this ) |
|
{ |
|
if ( pPlayer->m_fOnTarget ) |
|
{ |
|
iNewState = WEAPON_IS_ONTARGET; |
|
} |
|
else |
|
{ |
|
iNewState = WEAPON_IS_ACTIVE; |
|
} |
|
} |
|
else |
|
{ |
|
iNewState = WEAPON_IS_CARRIED_BY_PLAYER; |
|
} |
|
|
|
if ( m_iState != iNewState ) |
|
{ |
|
int iOldState = m_iState; |
|
m_iState = iNewState; |
|
OnActiveStateChanged( iOldState ); |
|
} |
|
return 1; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : index - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::SetViewModelIndex( int index ) |
|
{ |
|
Assert( index >= 0 && index < MAX_VIEWMODELS ); |
|
m_nViewModelIndex = index; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : iActivity - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::SendViewModelAnim( int nSequence ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
if ( !IsPredicted() ) |
|
return; |
|
#endif |
|
|
|
if ( nSequence < 0 ) |
|
return; |
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
|
|
if ( pOwner == NULL ) |
|
return; |
|
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex, false ); |
|
|
|
if ( vm == NULL ) |
|
return; |
|
|
|
SetViewModel(); |
|
Assert( vm->ViewModelIndex() == m_nViewModelIndex ); |
|
vm->SendViewModelMatchingSequence( nSequence ); |
|
} |
|
|
|
float CBaseCombatWeapon::GetViewModelSequenceDuration() |
|
{ |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( pOwner == NULL ) |
|
{ |
|
Assert( false ); |
|
return 0; |
|
} |
|
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); |
|
if ( vm == NULL ) |
|
{ |
|
Assert( false ); |
|
return 0; |
|
} |
|
|
|
SetViewModel(); |
|
Assert( vm->ViewModelIndex() == m_nViewModelIndex ); |
|
return vm->SequenceDuration(); |
|
} |
|
|
|
bool CBaseCombatWeapon::IsViewModelSequenceFinished( void ) |
|
{ |
|
// These are not valid activities and always complete immediately |
|
if ( GetActivity() == ACT_RESET || GetActivity() == ACT_INVALID ) |
|
return true; |
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( pOwner == NULL ) |
|
{ |
|
Assert( false ); |
|
return false; |
|
} |
|
|
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex ); |
|
if ( vm == NULL ) |
|
{ |
|
Assert( false ); |
|
return false; |
|
} |
|
|
|
return vm->IsSequenceFinished(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::SetViewModel() |
|
{ |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( pOwner == NULL ) |
|
return; |
|
CBaseViewModel *vm = pOwner->GetViewModel( m_nViewModelIndex, false ); |
|
if ( vm == NULL ) |
|
return; |
|
Assert( vm->ViewModelIndex() == m_nViewModelIndex ); |
|
vm->SetWeaponModel( GetViewModel( m_nViewModelIndex ), this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the desired activity for the weapon and its viewmodel counterpart |
|
// Input : iActivity - activity to play |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::SendWeaponAnim( int iActivity ) |
|
{ |
|
#ifdef USES_ECON_ITEMS |
|
iActivity = TranslateViewmodelHandActivity( (Activity)iActivity ); |
|
#endif |
|
// NVNT notify the haptics system of this weapons new activity |
|
#ifdef WIN32 |
|
#ifdef CLIENT_DLL |
|
if ( prediction->InPrediction() && prediction->IsFirstTimePredicted() ) |
|
#endif |
|
#ifndef _X360 |
|
HapticSendWeaponAnim(this,iActivity); |
|
#endif |
|
#endif |
|
//For now, just set the ideal activity and be done with it |
|
return SetIdealActivity( (Activity) iActivity ); |
|
} |
|
|
|
//==================================================================================== |
|
// WEAPON SELECTION |
|
//==================================================================================== |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the weapon currently has ammo or doesn't need ammo |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::HasAnyAmmo( void ) |
|
{ |
|
// If I don't use ammo of any kind, I can always fire |
|
if ( !UsesPrimaryAmmo() && !UsesSecondaryAmmo() ) |
|
return true; |
|
|
|
// Otherwise, I need ammo of either type |
|
return ( HasPrimaryAmmo() || HasSecondaryAmmo() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the weapon currently has ammo or doesn't need ammo |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::HasPrimaryAmmo( void ) |
|
{ |
|
// If I use a clip, and have some ammo in it, then I have ammo |
|
if ( UsesClipsForAmmo1() ) |
|
{ |
|
if ( m_iClip1 > 0 ) |
|
return true; |
|
} |
|
|
|
// Otherwise, I have ammo if I have some in my ammo counts |
|
CBaseCombatCharacter *pOwner = GetOwner(); |
|
if ( pOwner ) |
|
{ |
|
if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) |
|
return true; |
|
} |
|
else |
|
{ |
|
// No owner, so return how much primary ammo I have along with me. |
|
if( GetPrimaryAmmoCount() > 0 ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns true if the weapon currently has ammo or doesn't need ammo |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::HasSecondaryAmmo( void ) |
|
{ |
|
// If I use a clip, and have some ammo in it, then I have ammo |
|
if ( UsesClipsForAmmo2() ) |
|
{ |
|
if ( m_iClip2 > 0 ) |
|
return true; |
|
} |
|
|
|
// Otherwise, I have ammo if I have some in my ammo counts |
|
CBaseCombatCharacter *pOwner = GetOwner(); |
|
if ( pOwner ) |
|
{ |
|
if ( pOwner->GetAmmoCount( m_iSecondaryAmmoType ) > 0 ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns true if the weapon actually uses primary ammo |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::UsesPrimaryAmmo( void ) |
|
{ |
|
if ( m_iPrimaryAmmoType < 0 ) |
|
return false; |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns true if the weapon actually uses secondary ammo |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::UsesSecondaryAmmo( void ) |
|
{ |
|
if ( m_iSecondaryAmmoType < 0 ) |
|
return false; |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Show/hide weapon and corresponding view model if any |
|
// Input : visible - |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::SetWeaponVisible( bool visible ) |
|
{ |
|
CBaseViewModel *vm = NULL; |
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( pOwner ) |
|
{ |
|
vm = pOwner->GetViewModel( m_nViewModelIndex ); |
|
} |
|
|
|
if ( visible ) |
|
{ |
|
RemoveEffects( EF_NODRAW ); |
|
if ( vm ) |
|
{ |
|
vm->RemoveEffects( EF_NODRAW ); |
|
} |
|
} |
|
else |
|
{ |
|
AddEffects( EF_NODRAW ); |
|
if ( vm ) |
|
{ |
|
vm->AddEffects( EF_NODRAW ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::IsWeaponVisible( void ) |
|
{ |
|
CBaseViewModel *vm = NULL; |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( pOwner ) |
|
{ |
|
vm = pOwner->GetViewModel( m_nViewModelIndex ); |
|
if ( vm ) |
|
return ( !vm->IsEffectActive(EF_NODRAW) ); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: If the current weapon has more ammo, reload it. Otherwise, switch |
|
// to the next best weapon we've got. Returns true if it took any action. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::ReloadOrSwitchWeapons( void ) |
|
{ |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
Assert( pOwner ); |
|
|
|
m_bFireOnEmpty = false; |
|
|
|
// If we don't have any ammo, switch to the next best weapon |
|
if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime && m_flNextSecondaryAttack < gpGlobals->curtime ) |
|
{ |
|
// weapon isn't useable, switch. |
|
if ( ( (GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) == false ) && ( g_pGameRules->SwitchToNextBestWeapon( pOwner, this ) ) ) |
|
{ |
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.3; |
|
return true; |
|
} |
|
} |
|
else |
|
{ |
|
// Weapon is useable. Reload if empty and weapon has waited as long as it has to after firing |
|
if ( UsesClipsForAmmo1() && !AutoFiresFullClip() && |
|
(m_iClip1 == 0) && |
|
(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) == false && |
|
m_flNextPrimaryAttack < gpGlobals->curtime && |
|
m_flNextSecondaryAttack < gpGlobals->curtime ) |
|
{ |
|
// if we're successfully reloading, we're done |
|
if ( Reload() ) |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *szViewModel - |
|
// *szWeaponModel - |
|
// iActivity - |
|
// *szAnimExt - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt ) |
|
{ |
|
// Msg( "deploy %s at %f\n", GetClassname(), gpGlobals->curtime ); |
|
|
|
// Weapons that don't autoswitch away when they run out of ammo |
|
// can still be deployed when they have no ammo. |
|
if ( !HasAnyAmmo() && AllowsAutoSwitchFrom() ) |
|
return false; |
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( pOwner ) |
|
{ |
|
// Dead men deploy no weapons |
|
if ( pOwner->IsAlive() == false ) |
|
return false; |
|
|
|
pOwner->SetAnimationExtension( szAnimExt ); |
|
|
|
SetViewModel(); |
|
SendWeaponAnim( iActivity ); |
|
|
|
pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() ); |
|
} |
|
|
|
// Can't shoot again until we've finished deploying |
|
m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); |
|
m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); |
|
m_flHudHintMinDisplayTime = 0; |
|
|
|
m_bAltFireHudHintDisplayed = false; |
|
m_bReloadHudHintDisplayed = false; |
|
m_flHudHintPollTime = gpGlobals->curtime + 5.0f; |
|
|
|
WeaponSound( DEPLOY ); |
|
|
|
SetWeaponVisible( true ); |
|
|
|
/* |
|
|
|
This code is disabled for now, because moving through the weapons in the carousel |
|
selects and deploys each weapon as you pass it. (sjb) |
|
|
|
*/ |
|
|
|
SetContextThink( NULL, 0, HIDEWEAPON_THINK_CONTEXT ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::Deploy( ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION(); |
|
return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), GetDrawActivity(), (char*)GetAnimPrefix() ); |
|
} |
|
|
|
Activity CBaseCombatWeapon::GetDrawActivity( void ) |
|
{ |
|
return ACT_VM_DRAW; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION(); |
|
|
|
// cancel any reload in progress. |
|
m_bInReload = false; |
|
m_bFiringWholeClip = false; |
|
|
|
// kill any think functions |
|
SetThink(NULL); |
|
|
|
// Send holster animation |
|
SendWeaponAnim( ACT_VM_HOLSTER ); |
|
|
|
// Some weapon's don't have holster anims yet, so detect that |
|
float flSequenceDuration = 0; |
|
if ( GetActivity() == ACT_VM_HOLSTER ) |
|
{ |
|
flSequenceDuration = SequenceDuration(); |
|
} |
|
|
|
CBaseCombatCharacter *pOwner = GetOwner(); |
|
if (pOwner) |
|
{ |
|
pOwner->SetNextAttack( gpGlobals->curtime + flSequenceDuration ); |
|
} |
|
|
|
// If we don't have a holster anim, hide immediately to avoid timing issues |
|
if ( !flSequenceDuration ) |
|
{ |
|
SetWeaponVisible( false ); |
|
} |
|
else |
|
{ |
|
// Hide the weapon when the holster animation's finished |
|
SetContextThink( &CBaseCombatWeapon::HideThink, gpGlobals->curtime + flSequenceDuration, HIDEWEAPON_THINK_CONTEXT ); |
|
} |
|
|
|
// if we were displaying a hud hint, squelch it. |
|
if (m_flHudHintMinDisplayTime && gpGlobals->curtime < m_flHudHintMinDisplayTime) |
|
{ |
|
if( m_bAltFireHudHintDisplayed ) |
|
RescindAltFireHudHint(); |
|
|
|
if( m_bReloadHudHintDisplayed ) |
|
RescindReloadHudHint(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
|
|
void CBaseCombatWeapon::BoneMergeFastCullBloat( Vector &localMins, Vector &localMaxs, const Vector &thisEntityMins, const Vector &thisEntityMaxs ) const |
|
{ |
|
// The default behavior pushes it out by BONEMERGE_FASTCULL_BBOX_EXPAND in all directions, but we can do better |
|
// since we know the weapon will never point behind him. |
|
|
|
localMaxs.x += 20; // Leaves some space in front for long weapons. |
|
|
|
localMins.y -= 20; // Fatten it to his left and right since he can rotate that way. |
|
localMaxs.y += 20; |
|
|
|
localMaxs.z += 15; // Leave some space at the top. |
|
} |
|
|
|
#else |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::InputHideWeapon( inputdata_t &inputdata ) |
|
{ |
|
// Only hide if we're still the active weapon. If we're not the active weapon |
|
if ( GetOwner() && GetOwner()->GetActiveWeapon() == this ) |
|
{ |
|
SetWeaponVisible( false ); |
|
} |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::HideThink( void ) |
|
{ |
|
// Only hide if we're still the active weapon. If we're not the active weapon |
|
if ( GetOwner() && GetOwner()->GetActiveWeapon() == this ) |
|
{ |
|
SetWeaponVisible( false ); |
|
} |
|
} |
|
|
|
bool CBaseCombatWeapon::CanReload( void ) |
|
{ |
|
if ( AutoFiresFullClip() && m_bFiringWholeClip ) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
#if defined ( TF_CLIENT_DLL ) || defined ( TF_DLL ) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Anti-hack |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::AddToCritBucket( float flAmount ) |
|
{ |
|
float flCap = tf_weapon_criticals_bucket_cap.GetFloat(); |
|
|
|
// Regulate crit frequency to reduce client-side seed hacking |
|
if ( m_flCritTokenBucket < flCap ) |
|
{ |
|
// Treat raw damage as the resource by which we add or subtract from the bucket |
|
m_flCritTokenBucket += flAmount; |
|
m_flCritTokenBucket = Min( m_flCritTokenBucket, flCap ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Anti-hack |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::IsAllowedToWithdrawFromCritBucket( float flDamage ) |
|
{ |
|
// Note: If we're in this block of code, the assumption is that the |
|
// seed said we should grant a random crit. If allowed, the cost |
|
// will be deducted here. |
|
|
|
// Track each seed request - in cases where a player is hacking, we'll |
|
// see a silly ratio. |
|
m_nCritSeedRequests++; |
|
|
|
// Adjust token cost based on the ratio of requests vs granted, except |
|
// melee, which crits much more than ranged (as high as 60% chance) |
|
float flMult = ( IsMeleeWeapon() ) ? 0.5f : RemapValClamped( ( (float)m_nCritSeedRequests / (float)m_nCritChecks ), 0.1f, 1.f, 1.f, 3.f ); |
|
|
|
// Would this take us below our limit? |
|
float flCost = ( flDamage * TF_DAMAGE_CRIT_MULTIPLIER ) * flMult; |
|
if ( flCost > m_flCritTokenBucket ) |
|
return false; |
|
|
|
// Withdraw |
|
RemoveFromCritBucket( flCost ); |
|
|
|
float flBottom = tf_weapon_criticals_bucket_bottom.GetFloat(); |
|
if ( m_flCritTokenBucket < flBottom ) |
|
m_flCritTokenBucket = flBottom; |
|
|
|
return true; |
|
} |
|
#endif // TF_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::ItemPreFrame( void ) |
|
{ |
|
MaintainIdealActivity(); |
|
|
|
#ifndef CLIENT_DLL |
|
#ifndef HL2_EPISODIC |
|
if ( IsX360() ) |
|
#endif |
|
{ |
|
// If we haven't displayed the hint enough times yet, it's time to try to |
|
// display the hint, and the player is not standing still, try to show a hud hint. |
|
// If the player IS standing still, assume they could change away from this weapon at |
|
// any second. |
|
if( (!m_bAltFireHudHintDisplayed || !m_bReloadHudHintDisplayed) && gpGlobals->curtime > m_flHudHintMinDisplayTime && gpGlobals->curtime > m_flHudHintPollTime && GetOwner() && GetOwner()->IsPlayer() ) |
|
{ |
|
CBasePlayer *pPlayer = (CBasePlayer*)(GetOwner()); |
|
|
|
if( pPlayer && pPlayer->GetStickDist() > 0.0f ) |
|
{ |
|
// If the player is moving, they're unlikely to switch away from the current weapon |
|
// the moment this weapon displays its HUD hint. |
|
if( ShouldDisplayReloadHUDHint() ) |
|
{ |
|
DisplayReloadHudHint(); |
|
} |
|
else if( ShouldDisplayAltFireHUDHint() ) |
|
{ |
|
DisplayAltFireHudHint(); |
|
} |
|
} |
|
else |
|
{ |
|
m_flHudHintPollTime = gpGlobals->curtime + 2.0f; |
|
} |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
//==================================================================================== |
|
// WEAPON BEHAVIOUR |
|
//==================================================================================== |
|
void CBaseCombatWeapon::ItemPostFrame( void ) |
|
{ |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if (!pOwner) |
|
return; |
|
|
|
UpdateAutoFire(); |
|
|
|
//Track the duration of the fire |
|
//FIXME: Check for IN_ATTACK2 as well? |
|
//FIXME: What if we're calling ItemBusyFrame? |
|
m_fFireDuration = ( pOwner->m_nButtons & IN_ATTACK ) ? ( m_fFireDuration + gpGlobals->frametime ) : 0.0f; |
|
|
|
if ( UsesClipsForAmmo1() ) |
|
{ |
|
CheckReload(); |
|
} |
|
|
|
bool bFired = false; |
|
|
|
// Secondary attack has priority |
|
if ((pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime)) |
|
{ |
|
if (UsesSecondaryAmmo() && pOwner->GetAmmoCount(m_iSecondaryAmmoType)<=0 ) |
|
{ |
|
if (m_flNextEmptySoundTime < gpGlobals->curtime) |
|
{ |
|
WeaponSound(EMPTY); |
|
m_flNextSecondaryAttack = m_flNextEmptySoundTime = gpGlobals->curtime + 0.5; |
|
} |
|
} |
|
else if (pOwner->GetWaterLevel() == 3 && m_bAltFiresUnderwater == false) |
|
{ |
|
// This weapon doesn't fire underwater |
|
WeaponSound(EMPTY); |
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; |
|
return; |
|
} |
|
else |
|
{ |
|
// FIXME: This isn't necessarily true if the weapon doesn't have a secondary fire! |
|
// For instance, the crossbow doesn't have a 'real' secondary fire, but it still |
|
// stops the crossbow from firing on the 360 if the player chooses to hold down their |
|
// zoom button. (sjb) Orange Box 7/25/2007 |
|
#if !defined(CLIENT_DLL) |
|
if( !IsX360() || !ClassMatches("weapon_crossbow") ) |
|
#endif |
|
{ |
|
bFired = ShouldBlockPrimaryFire(); |
|
} |
|
|
|
SecondaryAttack(); |
|
|
|
// Secondary ammo doesn't have a reload animation |
|
if ( UsesClipsForAmmo2() ) |
|
{ |
|
// reload clip2 if empty |
|
if (m_iClip2 < 1) |
|
{ |
|
pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType ); |
|
m_iClip2 = m_iClip2 + 1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( !bFired && (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) |
|
{ |
|
// Clip empty? Or out of ammo on a no-clip weapon? |
|
if ( !IsMeleeWeapon() && |
|
(( UsesClipsForAmmo1() && m_iClip1 <= 0) || ( !UsesClipsForAmmo1() && pOwner->GetAmmoCount(m_iPrimaryAmmoType)<=0 )) ) |
|
{ |
|
HandleFireOnEmpty(); |
|
} |
|
else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false) |
|
{ |
|
// This weapon doesn't fire underwater |
|
WeaponSound(EMPTY); |
|
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; |
|
return; |
|
} |
|
else |
|
{ |
|
//NOTENOTE: There is a bug with this code with regards to the way machine guns catch the leading edge trigger |
|
// on the player hitting the attack key. It relies on the gun catching that case in the same frame. |
|
// However, because the player can also be doing a secondary attack, the edge trigger may be missed. |
|
// We really need to hold onto the edge trigger and only clear the condition when the gun has fired its |
|
// first shot. Right now that's too much of an architecture change -- jdw |
|
|
|
// If the firing button was just pressed, or the alt-fire just released, reset the firing time |
|
if ( ( pOwner->m_afButtonPressed & IN_ATTACK ) || ( pOwner->m_afButtonReleased & IN_ATTACK2 ) ) |
|
{ |
|
m_flNextPrimaryAttack = gpGlobals->curtime; |
|
} |
|
|
|
PrimaryAttack(); |
|
|
|
if ( AutoFiresFullClip() ) |
|
{ |
|
m_bFiringWholeClip = true; |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
pOwner->SetFiredWeapon( true ); |
|
#endif |
|
} |
|
} |
|
|
|
// ----------------------- |
|
// Reload pressed / Clip Empty |
|
// ----------------------- |
|
if ( ( pOwner->m_nButtons & IN_RELOAD ) && UsesClipsForAmmo1() && !m_bInReload ) |
|
{ |
|
// reload when reload is pressed, or if no buttons are down and weapon is empty. |
|
Reload(); |
|
m_fFireDuration = 0.0f; |
|
} |
|
|
|
// ----------------------- |
|
// No buttons down |
|
// ----------------------- |
|
if (!((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (CanReload() && pOwner->m_nButtons & IN_RELOAD))) |
|
{ |
|
// no fire buttons down or reloading |
|
if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) ) |
|
{ |
|
WeaponIdle(); |
|
} |
|
} |
|
} |
|
|
|
void CBaseCombatWeapon::HandleFireOnEmpty() |
|
{ |
|
// If we're already firing on empty, reload if we can |
|
if ( m_bFireOnEmpty ) |
|
{ |
|
ReloadOrSwitchWeapons(); |
|
m_fFireDuration = 0.0f; |
|
} |
|
else |
|
{ |
|
if (m_flNextEmptySoundTime < gpGlobals->curtime) |
|
{ |
|
WeaponSound(EMPTY); |
|
m_flNextEmptySoundTime = gpGlobals->curtime + 0.5; |
|
} |
|
m_bFireOnEmpty = true; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called each frame by the player PostThink, if the player's not ready to attack yet |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::ItemBusyFrame( void ) |
|
{ |
|
UpdateAutoFire(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Base class default for getting bullet type |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
int CBaseCombatWeapon::GetBulletType( void ) |
|
{ |
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Base class default for getting spread |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
const Vector& CBaseCombatWeapon::GetBulletSpread( void ) |
|
{ |
|
static Vector cone = VECTOR_CONE_15DEGREES; |
|
return cone; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
const WeaponProficiencyInfo_t *CBaseCombatWeapon::GetProficiencyValues() |
|
{ |
|
static WeaponProficiencyInfo_t defaultWeaponProficiencyTable[] = |
|
{ |
|
{ 1.0, 1.0 }, |
|
{ 1.0, 1.0 }, |
|
{ 1.0, 1.0 }, |
|
{ 1.0, 1.0 }, |
|
{ 1.0, 1.0 }, |
|
}; |
|
|
|
COMPILE_TIME_ASSERT( ARRAYSIZE(defaultWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1); |
|
return defaultWeaponProficiencyTable; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Base class default for getting firerate |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
float CBaseCombatWeapon::GetFireRate( void ) |
|
{ |
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Base class default for playing shoot sound |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ ) |
|
{ |
|
// If we have some sounds from the weapon classname.txt file, play a random one of them |
|
const char *shootsound = GetShootSound( sound_type ); |
|
if ( !shootsound || !shootsound[0] ) |
|
return; |
|
|
|
CSoundParameters params; |
|
|
|
if ( !GetParametersForSound( shootsound, params, NULL ) ) |
|
return; |
|
|
|
if ( params.play_to_owner_only ) |
|
{ |
|
// Am I only to play to my owner? |
|
if ( GetOwner() && GetOwner()->IsPlayer() ) |
|
{ |
|
CSingleUserRecipientFilter filter( ToBasePlayer( GetOwner() ) ); |
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() ) |
|
{ |
|
filter.UsePredictionRules(); |
|
} |
|
EmitSound( filter, GetOwner()->entindex(), shootsound, NULL, soundtime ); |
|
} |
|
} |
|
else |
|
{ |
|
// Play weapon sound from the owner |
|
if ( GetOwner() ) |
|
{ |
|
CPASAttenuationFilter filter( GetOwner(), params.soundlevel ); |
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() ) |
|
{ |
|
filter.UsePredictionRules(); |
|
} |
|
EmitSound( filter, GetOwner()->entindex(), shootsound, NULL, soundtime ); |
|
|
|
#if !defined( CLIENT_DLL ) |
|
if( sound_type == EMPTY ) |
|
{ |
|
CSoundEnt::InsertSound( SOUND_COMBAT, GetOwner()->GetAbsOrigin(), SOUNDENT_VOLUME_EMPTY, 0.2, GetOwner() ); |
|
} |
|
#endif |
|
} |
|
// If no owner play from the weapon (this is used for thrown items) |
|
else |
|
{ |
|
CPASAttenuationFilter filter( this, params.soundlevel ); |
|
if ( IsPredicted() && CBaseEntity::GetPredictionPlayer() ) |
|
{ |
|
filter.UsePredictionRules(); |
|
} |
|
EmitSound( filter, entindex(), shootsound, NULL, soundtime ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stop a sound played by this weapon. |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::StopWeaponSound( WeaponSound_t sound_type ) |
|
{ |
|
//if ( IsPredicted() ) |
|
// return; |
|
|
|
// If we have some sounds from the weapon classname.txt file, play a random one of them |
|
const char *shootsound = GetShootSound( sound_type ); |
|
if ( !shootsound || !shootsound[0] ) |
|
return; |
|
|
|
CSoundParameters params; |
|
if ( !GetParametersForSound( shootsound, params, NULL ) ) |
|
return; |
|
|
|
// Am I only to play to my owner? |
|
if ( params.play_to_owner_only ) |
|
{ |
|
if ( GetOwner() ) |
|
{ |
|
StopSound( GetOwner()->entindex(), shootsound ); |
|
} |
|
} |
|
else |
|
{ |
|
// Play weapon sound from the owner |
|
if ( GetOwner() ) |
|
{ |
|
StopSound( GetOwner()->entindex(), shootsound ); |
|
} |
|
// If no owner play from the weapon (this is used for thrown items) |
|
else |
|
{ |
|
StopSound( entindex(), shootsound ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::DefaultReload( int iClipSize1, int iClipSize2, int iActivity ) |
|
{ |
|
CBaseCombatCharacter *pOwner = GetOwner(); |
|
if (!pOwner) |
|
return false; |
|
|
|
// If I don't have any spare ammo, I can't reload |
|
if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) |
|
return false; |
|
|
|
bool bReload = false; |
|
|
|
// If you don't have clips, then don't try to reload them. |
|
if ( UsesClipsForAmmo1() ) |
|
{ |
|
// need to reload primary clip? |
|
int primary = MIN(iClipSize1 - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); |
|
if ( primary != 0 ) |
|
{ |
|
bReload = true; |
|
} |
|
} |
|
|
|
if ( UsesClipsForAmmo2() ) |
|
{ |
|
// need to reload secondary clip? |
|
int secondary = MIN(iClipSize2 - m_iClip2, pOwner->GetAmmoCount(m_iSecondaryAmmoType)); |
|
if ( secondary != 0 ) |
|
{ |
|
bReload = true; |
|
} |
|
} |
|
|
|
if ( !bReload ) |
|
return false; |
|
|
|
#ifdef CLIENT_DLL |
|
// Play reload |
|
WeaponSound( RELOAD ); |
|
#endif |
|
SendWeaponAnim( iActivity ); |
|
|
|
// Play the player's reload animation |
|
if ( pOwner->IsPlayer() ) |
|
{ |
|
( ( CBasePlayer * )pOwner)->SetAnimation( PLAYER_RELOAD ); |
|
} |
|
|
|
MDLCACHE_CRITICAL_SECTION(); |
|
float flSequenceEndTime = gpGlobals->curtime + SequenceDuration(); |
|
pOwner->SetNextAttack( flSequenceEndTime ); |
|
m_flNextPrimaryAttack = m_flNextSecondaryAttack = flSequenceEndTime; |
|
|
|
m_bInReload = true; |
|
|
|
return true; |
|
} |
|
|
|
bool CBaseCombatWeapon::ReloadsSingly( void ) const |
|
{ |
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) |
|
float fHasReload = 1.0f; |
|
CALL_ATTRIB_HOOK_FLOAT( fHasReload, mod_no_reload_display_only ); |
|
if ( fHasReload != 1.0f ) |
|
{ |
|
return false; |
|
} |
|
|
|
int iWeaponMod = 0; |
|
CALL_ATTRIB_HOOK_INT( iWeaponMod, set_scattergun_no_reload_single ); |
|
if ( iWeaponMod == 1 ) |
|
{ |
|
return false; |
|
} |
|
#endif // TF_DLL || TF_CLIENT_DLL |
|
|
|
return m_bReloadsSingly; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::Reload( void ) |
|
{ |
|
return DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD ); |
|
} |
|
|
|
//========================================================= |
|
void CBaseCombatWeapon::WeaponIdle( void ) |
|
{ |
|
//Idle again if we've finished |
|
if ( HasWeaponIdleTimeElapsed() ) |
|
{ |
|
SendWeaponAnim( ACT_VM_IDLE ); |
|
} |
|
} |
|
|
|
|
|
//========================================================= |
|
Activity CBaseCombatWeapon::GetPrimaryAttackActivity( void ) |
|
{ |
|
return ACT_VM_PRIMARYATTACK; |
|
} |
|
|
|
//========================================================= |
|
Activity CBaseCombatWeapon::GetSecondaryAttackActivity( void ) |
|
{ |
|
return ACT_VM_SECONDARYATTACK; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Adds in view kick and weapon accuracy degradation effect |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::AddViewKick( void ) |
|
{ |
|
//NOTENOTE: By default, weapon will not kick up (defined per weapon) |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get the string to print death notices with |
|
//----------------------------------------------------------------------------- |
|
char *CBaseCombatWeapon::GetDeathNoticeName( void ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
return (char*)STRING( m_iszName ); |
|
#else |
|
return "GetDeathNoticeName not implemented on client yet"; |
|
#endif |
|
} |
|
|
|
//==================================================================================== |
|
// WEAPON RELOAD TYPES |
|
//==================================================================================== |
|
void CBaseCombatWeapon::CheckReload( void ) |
|
{ |
|
if ( m_bReloadsSingly ) |
|
{ |
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
if ((m_bInReload) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) |
|
{ |
|
if ( pOwner->m_nButtons & (IN_ATTACK | IN_ATTACK2) && m_iClip1 > 0 ) |
|
{ |
|
m_bInReload = false; |
|
return; |
|
} |
|
|
|
// If out of ammo end reload |
|
if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <=0) |
|
{ |
|
FinishReload(); |
|
return; |
|
} |
|
// If clip not full reload again |
|
else if (m_iClip1 < GetMaxClip1()) |
|
{ |
|
// Add them to the clip |
|
m_iClip1 += 1; |
|
pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType ); |
|
|
|
Reload(); |
|
return; |
|
} |
|
// Clip full, stop reloading |
|
else |
|
{ |
|
FinishReload(); |
|
m_flNextPrimaryAttack = gpGlobals->curtime; |
|
m_flNextSecondaryAttack = gpGlobals->curtime; |
|
return; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if ( (m_bInReload) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) |
|
{ |
|
FinishReload(); |
|
m_flNextPrimaryAttack = gpGlobals->curtime; |
|
m_flNextSecondaryAttack = gpGlobals->curtime; |
|
m_bInReload = false; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Reload has finished. |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::FinishReload( void ) |
|
{ |
|
CBaseCombatCharacter *pOwner = GetOwner(); |
|
|
|
if (pOwner) |
|
{ |
|
// If I use primary clips, reload primary |
|
if ( UsesClipsForAmmo1() ) |
|
{ |
|
int primary = MIN( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); |
|
m_iClip1 += primary; |
|
pOwner->RemoveAmmo( primary, m_iPrimaryAmmoType); |
|
} |
|
|
|
// If I use secondary clips, reload secondary |
|
if ( UsesClipsForAmmo2() ) |
|
{ |
|
int secondary = MIN( GetMaxClip2() - m_iClip2, pOwner->GetAmmoCount(m_iSecondaryAmmoType)); |
|
m_iClip2 += secondary; |
|
pOwner->RemoveAmmo( secondary, m_iSecondaryAmmoType ); |
|
} |
|
|
|
if ( m_bReloadsSingly ) |
|
{ |
|
m_bInReload = false; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Abort any reload we have in progress |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::AbortReload( void ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
StopWeaponSound( RELOAD ); |
|
#endif |
|
m_bInReload = false; |
|
} |
|
|
|
void CBaseCombatWeapon::UpdateAutoFire( void ) |
|
{ |
|
if ( !AutoFiresFullClip() ) |
|
return; |
|
|
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); |
|
if ( !pOwner ) |
|
return; |
|
|
|
if ( m_iClip1 == 0 ) |
|
{ |
|
// Ready to reload again |
|
m_bFiringWholeClip = false; |
|
} |
|
|
|
if ( m_bFiringWholeClip ) |
|
{ |
|
// If it's firing the clip don't let them repress attack to reload |
|
pOwner->m_nButtons &= ~IN_ATTACK; |
|
} |
|
|
|
// Don't use the regular reload key |
|
if ( pOwner->m_nButtons & IN_RELOAD ) |
|
{ |
|
pOwner->m_nButtons &= ~IN_RELOAD; |
|
} |
|
|
|
// Try to fire if there's ammo in the clip and we're not holding the button |
|
bool bReleaseClip = m_iClip1 > 0 && !( pOwner->m_nButtons & IN_ATTACK ); |
|
|
|
if ( !bReleaseClip ) |
|
{ |
|
if ( CanReload() && ( pOwner->m_nButtons & IN_ATTACK ) ) |
|
{ |
|
// Convert the attack key into the reload key |
|
pOwner->m_nButtons |= IN_RELOAD; |
|
} |
|
|
|
// Don't allow attack button if we're not attacking |
|
pOwner->m_nButtons &= ~IN_ATTACK; |
|
} |
|
else |
|
{ |
|
// Fake the attack key |
|
pOwner->m_nButtons |= IN_ATTACK; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Primary fire button attack |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::PrimaryAttack( void ) |
|
{ |
|
// If my clip is empty (and I use clips) start reload |
|
if ( UsesClipsForAmmo1() && !m_iClip1 ) |
|
{ |
|
Reload(); |
|
return; |
|
} |
|
|
|
// Only the player fires this way so we can cast |
|
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); |
|
|
|
if (!pPlayer) |
|
{ |
|
return; |
|
} |
|
|
|
pPlayer->DoMuzzleFlash(); |
|
|
|
SendWeaponAnim( GetPrimaryAttackActivity() ); |
|
|
|
// player "shoot" animation |
|
pPlayer->SetAnimation( PLAYER_ATTACK1 ); |
|
|
|
FireBulletsInfo_t info; |
|
info.m_vecSrc = pPlayer->Weapon_ShootPosition( ); |
|
|
|
info.m_vecDirShooting = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT ); |
|
|
|
// To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems, |
|
// especially if the weapon we're firing has a really fast rate of fire. |
|
info.m_iShots = 0; |
|
float fireRate = GetFireRate(); |
|
|
|
while ( m_flNextPrimaryAttack <= gpGlobals->curtime ) |
|
{ |
|
// MUST call sound before removing a round from the clip of a CMachineGun |
|
WeaponSound(SINGLE, m_flNextPrimaryAttack); |
|
m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate; |
|
info.m_iShots++; |
|
if ( !fireRate ) |
|
break; |
|
} |
|
|
|
// Make sure we don't fire more than the amount in the clip |
|
if ( UsesClipsForAmmo1() ) |
|
{ |
|
info.m_iShots = MIN( info.m_iShots, m_iClip1 ); |
|
m_iClip1 -= info.m_iShots; |
|
} |
|
else |
|
{ |
|
info.m_iShots = MIN( info.m_iShots, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) ); |
|
pPlayer->RemoveAmmo( info.m_iShots, m_iPrimaryAmmoType ); |
|
} |
|
|
|
info.m_flDistance = MAX_TRACE_LENGTH; |
|
info.m_iAmmoType = m_iPrimaryAmmoType; |
|
info.m_iTracerFreq = 2; |
|
|
|
#if !defined( CLIENT_DLL ) |
|
// Fire the bullets |
|
info.m_vecSpread = pPlayer->GetAttackSpread( this ); |
|
#else |
|
//!!!HACKHACK - what does the client want this function for? |
|
info.m_vecSpread = GetActiveWeapon()->GetBulletSpread(); |
|
#endif // CLIENT_DLL |
|
|
|
pPlayer->FireBullets( info ); |
|
|
|
if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) |
|
{ |
|
// HEV suit - indicate out of ammo condition |
|
pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); |
|
} |
|
|
|
//Add our view kick in |
|
AddViewKick(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called every frame to check if the weapon is going through transition animations |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::MaintainIdealActivity( void ) |
|
{ |
|
// Must be transitioning |
|
if ( GetActivity() != ACT_TRANSITION ) |
|
return; |
|
|
|
// Must not be at our ideal already |
|
if ( ( GetActivity() == m_IdealActivity ) && ( GetSequence() == m_nIdealSequence ) ) |
|
return; |
|
|
|
// Must be finished with the current animation |
|
if ( IsViewModelSequenceFinished() == false ) |
|
return; |
|
|
|
// Move to the next animation towards our ideal |
|
SendWeaponAnim( m_IdealActivity ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the ideal activity for the weapon to be in, allowing for transitional animations inbetween |
|
// Input : ideal - activity to end up at, ideally |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::SetIdealActivity( Activity ideal ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION(); |
|
int idealSequence = SelectWeightedSequence( ideal ); |
|
|
|
if ( idealSequence == -1 ) |
|
return false; |
|
|
|
//Take the new activity |
|
m_IdealActivity = ideal; |
|
m_nIdealSequence = idealSequence; |
|
|
|
//Find the next sequence in the potential chain of sequences leading to our ideal one |
|
int nextSequence = FindTransitionSequence( GetSequence(), m_nIdealSequence, NULL ); |
|
|
|
// Don't use transitions when we're deploying |
|
if ( ideal != ACT_VM_DRAW && IsWeaponVisible() && nextSequence != m_nIdealSequence ) |
|
{ |
|
//Set our activity to the next transitional animation |
|
SetActivity( ACT_TRANSITION ); |
|
SetSequence( nextSequence ); |
|
SendViewModelAnim( nextSequence ); |
|
} |
|
else |
|
{ |
|
//Set our activity to the ideal |
|
SetActivity( m_IdealActivity ); |
|
SetSequence( m_nIdealSequence ); |
|
SendViewModelAnim( m_nIdealSequence ); |
|
} |
|
|
|
//Set the next time the weapon will idle |
|
SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() ); |
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns information about the various control panels |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName ) |
|
{ |
|
pPanelName = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns information about the various control panels |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName ) |
|
{ |
|
pPanelName = "vgui_screen"; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Locking a weapon is an exclusive action. If you lock a weapon, that means |
|
// you are preventing others from doing so for themselves. |
|
//----------------------------------------------------------------------------- |
|
void CBaseCombatWeapon::Lock( float lockTime, CBaseEntity *pLocker ) |
|
{ |
|
m_flUnlockTime = gpGlobals->curtime + lockTime; |
|
m_hLocker.Set( pLocker ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// If I'm still locked for a period of time, tell everyone except the person |
|
// that locked me that I'm not available. |
|
//----------------------------------------------------------------------------- |
|
bool CBaseCombatWeapon::IsLocked( CBaseEntity *pAsker ) |
|
{ |
|
return ( m_flUnlockTime > gpGlobals->curtime && m_hLocker != pAsker ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
Activity CBaseCombatWeapon::ActivityOverride( Activity baseAct, bool *pRequired ) |
|
{ |
|
acttable_t *pTable = ActivityList(); |
|
int actCount = ActivityListCount(); |
|
|
|
for ( int i = 0; i < actCount; i++, pTable++ ) |
|
{ |
|
if ( baseAct == pTable->baseAct ) |
|
{ |
|
if (pRequired) |
|
{ |
|
*pRequired = pTable->required; |
|
} |
|
return (Activity)pTable->weaponAct; |
|
} |
|
} |
|
return baseAct; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CDmgAccumulator::CDmgAccumulator( void ) |
|
{ |
|
#ifdef GAME_DLL |
|
SetDefLessFunc( m_TargetsDmgInfo ); |
|
#endif // GAME_DLL |
|
|
|
m_bActive = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CDmgAccumulator::~CDmgAccumulator() |
|
{ |
|
// Did a weapon get deleted while aggregating CTakeDamageInfo events? |
|
Assert( !m_bActive ); |
|
} |
|
|
|
#ifdef GAME_DLL |
|
//----------------------------------------------------------------------------- |
|
// Collect trace attacks for weapons that fire multiple bullets per attack that also penetrate |
|
//----------------------------------------------------------------------------- |
|
void CDmgAccumulator::AccumulateMultiDamage( const CTakeDamageInfo &info, CBaseEntity *pEntity ) |
|
{ |
|
if ( !pEntity ) |
|
return; |
|
|
|
Assert( m_bActive ); |
|
|
|
#if defined( GAME_DLL ) |
|
int iIndex = m_TargetsDmgInfo.Find( pEntity->entindex() ); |
|
if ( iIndex == m_TargetsDmgInfo.InvalidIndex() ) |
|
{ |
|
m_TargetsDmgInfo.Insert( pEntity->entindex(), info ); |
|
} |
|
else |
|
{ |
|
CTakeDamageInfo *pInfo = &m_TargetsDmgInfo[iIndex]; |
|
if ( pInfo ) |
|
{ |
|
// Update |
|
m_TargetsDmgInfo[iIndex].AddDamageType( info.GetDamageType() ); |
|
m_TargetsDmgInfo[iIndex].SetDamage( pInfo->GetDamage() + info.GetDamage() ); |
|
m_TargetsDmgInfo[iIndex].SetDamageForce( pInfo->GetDamageForce() + info.GetDamageForce() ); |
|
m_TargetsDmgInfo[iIndex].SetDamagePosition( info.GetDamagePosition() ); |
|
m_TargetsDmgInfo[iIndex].SetReportedPosition( info.GetReportedPosition() ); |
|
m_TargetsDmgInfo[iIndex].SetMaxDamage( MAX( pInfo->GetMaxDamage(), info.GetDamage() ) ); |
|
m_TargetsDmgInfo[iIndex].SetAmmoType( info.GetAmmoType() ); |
|
} |
|
|
|
} |
|
#endif // GAME_DLL |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Send aggregate info |
|
//----------------------------------------------------------------------------- |
|
void CDmgAccumulator::Process( void ) |
|
{ |
|
FOR_EACH_MAP( m_TargetsDmgInfo, i ) |
|
{ |
|
CBaseEntity *pEntity = UTIL_EntityByIndex( m_TargetsDmgInfo.Key( i ) ); |
|
if ( pEntity ) |
|
{ |
|
AddMultiDamage( m_TargetsDmgInfo[i], pEntity ); |
|
} |
|
} |
|
|
|
m_bActive = false; |
|
m_TargetsDmgInfo.Purge(); |
|
} |
|
#endif // GAME_DLL |
|
|
|
#if defined( CLIENT_DLL ) |
|
|
|
BEGIN_PREDICTION_DATA( CBaseCombatWeapon ) |
|
|
|
DEFINE_PRED_FIELD( m_nNextThinkTick, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
// Networked |
|
DEFINE_PRED_FIELD( m_hOwner, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ), |
|
// DEFINE_FIELD( m_hWeaponFileInfo, FIELD_SHORT ), |
|
DEFINE_PRED_FIELD( m_iState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
DEFINE_PRED_FIELD( m_iViewModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ), |
|
DEFINE_PRED_FIELD( m_iWorldModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE | FTYPEDESC_MODELINDEX ), |
|
DEFINE_PRED_FIELD_TOL( m_flNextPrimaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), |
|
DEFINE_PRED_FIELD_TOL( m_flNextSecondaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), |
|
DEFINE_PRED_FIELD_TOL( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ), |
|
|
|
DEFINE_PRED_FIELD( m_iPrimaryAmmoType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
DEFINE_PRED_FIELD( m_iSecondaryAmmoType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
DEFINE_PRED_FIELD( m_iClip1, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
DEFINE_PRED_FIELD( m_iClip2, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
|
|
DEFINE_PRED_FIELD( m_nViewModelIndex, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), |
|
|
|
// Not networked |
|
|
|
DEFINE_PRED_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ), |
|
DEFINE_FIELD( m_bInReload, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bFireOnEmpty, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bFiringWholeClip, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_flNextEmptySoundTime, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_Activity, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_fFireDuration, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_iszName, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bAltFiresUnderwater, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_fMinRange1, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fMinRange2, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fMaxRange1, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fMaxRange2, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_bReloadsSingly, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bRemoveable, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_iPrimaryAmmoCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iSecondaryAmmoCount, FIELD_INTEGER ), |
|
|
|
//DEFINE_PHYSPTR( m_pConstraint ), |
|
|
|
// DEFINE_FIELD( m_iOldState, FIELD_INTEGER ), |
|
// DEFINE_FIELD( m_bJustRestored, FIELD_BOOLEAN ), |
|
|
|
// DEFINE_FIELD( m_OnPlayerPickup, COutputEvent ), |
|
// DEFINE_FIELD( m_pConstraint, FIELD_INTEGER ), |
|
|
|
END_PREDICTION_DATA() |
|
|
|
#endif // ! CLIENT_DLL |
|
|
|
// Special hack since we're aliasing the name C_BaseCombatWeapon with a macro on the client |
|
IMPLEMENT_NETWORKCLASS_ALIASED( BaseCombatWeapon, DT_BaseCombatWeapon ) |
|
|
|
#if !defined( CLIENT_DLL ) |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Save Data for Base Weapon object |
|
//-----------------------------------------------------------------------------// |
|
BEGIN_DATADESC( CBaseCombatWeapon ) |
|
|
|
DEFINE_FIELD( m_flNextPrimaryAttack, FIELD_TIME ), |
|
DEFINE_FIELD( m_flNextSecondaryAttack, FIELD_TIME ), |
|
DEFINE_FIELD( m_flTimeWeaponIdle, FIELD_TIME ), |
|
|
|
DEFINE_FIELD( m_bInReload, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bFireOnEmpty, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), |
|
|
|
DEFINE_FIELD( m_iState, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iszName, FIELD_STRING ), |
|
DEFINE_FIELD( m_iPrimaryAmmoType, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iSecondaryAmmoType, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iClip1, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iClip2, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_bFiresUnderwater, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bAltFiresUnderwater, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_fMinRange1, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fMinRange2, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fMaxRange1, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_fMaxRange2, FIELD_FLOAT ), |
|
|
|
DEFINE_FIELD( m_iPrimaryAmmoCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iSecondaryAmmoCount, FIELD_INTEGER ), |
|
|
|
DEFINE_FIELD( m_nViewModelIndex, FIELD_INTEGER ), |
|
|
|
// don't save these, init to 0 and regenerate |
|
// DEFINE_FIELD( m_flNextEmptySoundTime, FIELD_TIME ), |
|
// DEFINE_FIELD( m_Activity, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_nIdealSequence, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_IdealActivity, FIELD_INTEGER ), |
|
|
|
DEFINE_FIELD( m_fFireDuration, FIELD_FLOAT ), |
|
|
|
DEFINE_FIELD( m_bReloadsSingly, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_iSubType, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_bRemoveable, FIELD_BOOLEAN ), |
|
|
|
DEFINE_FIELD( m_flUnlockTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_hLocker, FIELD_EHANDLE ), |
|
|
|
// DEFINE_FIELD( m_iViewModelIndex, FIELD_INTEGER ), |
|
// DEFINE_FIELD( m_iWorldModelIndex, FIELD_INTEGER ), |
|
// DEFINE_FIELD( m_hWeaponFileInfo, ???? ), |
|
|
|
DEFINE_PHYSPTR( m_pConstraint ), |
|
|
|
DEFINE_FIELD( m_iReloadHudHintCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_iAltFireHudHintCount, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_bReloadHudHintDisplayed, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bAltFireHudHintDisplayed, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_flHudHintPollTime, FIELD_TIME ), |
|
DEFINE_FIELD( m_flHudHintMinDisplayTime, FIELD_TIME ), |
|
|
|
// Just to quiet classcheck.. this field exists only on the client |
|
// DEFINE_FIELD( m_iOldState, FIELD_INTEGER ), |
|
// DEFINE_FIELD( m_bJustRestored, FIELD_BOOLEAN ), |
|
|
|
// Function pointers |
|
DEFINE_ENTITYFUNC( DefaultTouch ), |
|
DEFINE_THINKFUNC( FallThink ), |
|
DEFINE_THINKFUNC( Materialize ), |
|
DEFINE_THINKFUNC( AttemptToMaterialize ), |
|
DEFINE_THINKFUNC( DestroyItem ), |
|
DEFINE_THINKFUNC( SetPickupTouch ), |
|
|
|
DEFINE_THINKFUNC( HideThink ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "HideWeapon", InputHideWeapon ), |
|
|
|
// Outputs |
|
DEFINE_OUTPUT( m_OnPlayerUse, "OnPlayerUse"), |
|
DEFINE_OUTPUT( m_OnPlayerPickup, "OnPlayerPickup"), |
|
DEFINE_OUTPUT( m_OnNPCPickup, "OnNPCPickup"), |
|
DEFINE_OUTPUT( m_OnCacheInteraction, "OnCacheInteraction" ), |
|
|
|
END_DATADESC() |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Only send to local player if this weapon is the active weapon |
|
// Input : *pStruct - |
|
// *pVarData - |
|
// *pRecipients - |
|
// objectID - |
|
// Output : void* |
|
//----------------------------------------------------------------------------- |
|
void* SendProxy_SendActiveLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) |
|
{ |
|
// Get the weapon entity |
|
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData; |
|
if ( pWeapon ) |
|
{ |
|
// Only send this chunk of data to the player carrying this weapon |
|
CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() ); |
|
if ( pPlayer /*&& pPlayer->GetActiveWeapon() == pWeapon*/ ) |
|
{ |
|
pRecipients->SetOnly( pPlayer->GetClientIndex() ); |
|
return (void*)pVarData; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendActiveLocalWeaponDataTable ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Only send the LocalWeaponData to the player carrying the weapon |
|
//----------------------------------------------------------------------------- |
|
void* SendProxy_SendLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) |
|
{ |
|
// Get the weapon entity |
|
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData; |
|
if ( pWeapon ) |
|
{ |
|
// Only send this chunk of data to the player carrying this weapon |
|
CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
pRecipients->SetOnly( pPlayer->GetClientIndex() ); |
|
return (void*)pVarData; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendLocalWeaponDataTable ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Only send to non-local players |
|
//----------------------------------------------------------------------------- |
|
void* SendProxy_SendNonLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) |
|
{ |
|
pRecipients->SetAllRecipients(); |
|
|
|
CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData; |
|
if ( pWeapon ) |
|
{ |
|
CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() ); |
|
if ( pPlayer ) |
|
{ |
|
pRecipients->ClearRecipient( pPlayer->GetClientIndex() ); |
|
return ( void * )pVarData; |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendNonLocalWeaponDataTable ); |
|
|
|
#endif |
|
|
|
#if PREDICTION_ERROR_CHECK_LEVEL > 1 |
|
#define SendPropTime SendPropFloat |
|
#define RecvPropTime RecvPropFloat |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Propagation data for weapons. Only sent when a player's holding it. |
|
//----------------------------------------------------------------------------- |
|
BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalActiveWeaponData ) |
|
#if !defined( CLIENT_DLL ) |
|
SendPropTime( SENDINFO( m_flNextPrimaryAttack ) ), |
|
SendPropTime( SENDINFO( m_flNextSecondaryAttack ) ), |
|
SendPropInt( SENDINFO( m_nNextThinkTick ) ), |
|
SendPropTime( SENDINFO( m_flTimeWeaponIdle ) ), |
|
|
|
#if defined( TF_DLL ) |
|
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), |
|
#endif |
|
|
|
#else |
|
RecvPropTime( RECVINFO( m_flNextPrimaryAttack ) ), |
|
RecvPropTime( RECVINFO( m_flNextSecondaryAttack ) ), |
|
RecvPropInt( RECVINFO( m_nNextThinkTick ) ), |
|
RecvPropTime( RECVINFO( m_flTimeWeaponIdle ) ), |
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Propagation data for weapons. Only sent when a player's holding it. |
|
//----------------------------------------------------------------------------- |
|
BEGIN_NETWORK_TABLE_NOBASE( CBaseCombatWeapon, DT_LocalWeaponData ) |
|
#if !defined( CLIENT_DLL ) |
|
SendPropIntWithMinusOneFlag( SENDINFO(m_iClip1 ), 8 ), |
|
SendPropIntWithMinusOneFlag( SENDINFO(m_iClip2 ), 8 ), |
|
SendPropInt( SENDINFO(m_iPrimaryAmmoType ), 8 ), |
|
SendPropInt( SENDINFO(m_iSecondaryAmmoType ), 8 ), |
|
|
|
SendPropInt( SENDINFO( m_nViewModelIndex ), VIEWMODEL_INDEX_BITS, SPROP_UNSIGNED ), |
|
|
|
SendPropInt( SENDINFO( m_bFlipViewModel ) ), |
|
|
|
#if defined( TF_DLL ) |
|
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), |
|
#endif |
|
|
|
#else |
|
RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip1 )), |
|
RecvPropIntWithMinusOneFlag( RECVINFO(m_iClip2 )), |
|
RecvPropInt( RECVINFO(m_iPrimaryAmmoType )), |
|
RecvPropInt( RECVINFO(m_iSecondaryAmmoType )), |
|
|
|
RecvPropInt( RECVINFO( m_nViewModelIndex ) ), |
|
|
|
RecvPropBool( RECVINFO( m_bFlipViewModel ) ), |
|
|
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon) |
|
#if !defined( CLIENT_DLL ) |
|
SendPropDataTable("LocalWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalWeaponData), SendProxy_SendLocalWeaponDataTable ), |
|
SendPropDataTable("LocalActiveWeaponData", 0, &REFERENCE_SEND_TABLE(DT_LocalActiveWeaponData), SendProxy_SendActiveLocalWeaponDataTable ), |
|
SendPropModelIndex( SENDINFO(m_iViewModelIndex) ), |
|
SendPropModelIndex( SENDINFO(m_iWorldModelIndex) ), |
|
SendPropInt( SENDINFO(m_iState ), 8, SPROP_UNSIGNED ), |
|
SendPropEHandle( SENDINFO(m_hOwner) ), |
|
#else |
|
RecvPropDataTable("LocalWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalWeaponData)), |
|
RecvPropDataTable("LocalActiveWeaponData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponData)), |
|
RecvPropInt( RECVINFO(m_iViewModelIndex)), |
|
RecvPropInt( RECVINFO(m_iWorldModelIndex)), |
|
RecvPropInt( RECVINFO(m_iState )), |
|
RecvPropEHandle( RECVINFO(m_hOwner ) ), |
|
#endif |
|
END_NETWORK_TABLE()
|
|
|