diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /sp/src/game/shared/basecombatweapon_shared.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'sp/src/game/shared/basecombatweapon_shared.cpp')
| -rw-r--r-- | sp/src/game/shared/basecombatweapon_shared.cpp | 5610 |
1 files changed, 2805 insertions, 2805 deletions
diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index 28c98bf5..754919d3 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -1,2805 +1,2805 @@ -//========= 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()
+//========= 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() |