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 /mp/src/game/server/hl2/weapon_shotgun.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 'mp/src/game/server/hl2/weapon_shotgun.cpp')
| -rw-r--r-- | mp/src/game/server/hl2/weapon_shotgun.cpp | 1586 |
1 files changed, 793 insertions, 793 deletions
diff --git a/mp/src/game/server/hl2/weapon_shotgun.cpp b/mp/src/game/server/hl2/weapon_shotgun.cpp index 310939d8..a832a3d6 100644 --- a/mp/src/game/server/hl2/weapon_shotgun.cpp +++ b/mp/src/game/server/hl2/weapon_shotgun.cpp @@ -1,793 +1,793 @@ -//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: A shotgun.
-//
-// Primary attack: single barrel shot.
-// Secondary attack: double barrel shot.
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "basehlcombatweapon_shared.h"
-#include "basecombatcharacter.h"
-#include "ai_basenpc.h"
-#include "player.h"
-#include "gamerules.h" // For g_pGameRules
-#include "in_buttons.h"
-#include "soundent.h"
-#include "vstdlib/random.h"
-#include "gamestats.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-extern ConVar sk_auto_reload_time;
-extern ConVar sk_plr_num_shotgun_pellets;
-
-class CWeaponShotgun : public CBaseHLCombatWeapon
-{
- DECLARE_DATADESC();
-public:
- DECLARE_CLASS( CWeaponShotgun, CBaseHLCombatWeapon );
-
- DECLARE_SERVERCLASS();
-
-private:
- bool m_bNeedPump; // When emptied completely
- bool m_bDelayedFire1; // Fire primary when finished reloading
- bool m_bDelayedFire2; // Fire secondary when finished reloading
-
-public:
- void Precache( void );
-
- int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
-
- virtual const Vector& GetBulletSpread( void )
- {
- static Vector vitalAllyCone = VECTOR_CONE_3DEGREES;
- static Vector cone = VECTOR_CONE_10DEGREES;
-
- if( GetOwner() && (GetOwner()->Classify() == CLASS_PLAYER_ALLY_VITAL) )
- {
- // Give Alyx's shotgun blasts more a more directed punch. She needs
- // to be at least as deadly as she would be with her pistol to stay interesting (sjb)
- return vitalAllyCone;
- }
-
- return cone;
- }
-
- virtual int GetMinBurst() { return 1; }
- virtual int GetMaxBurst() { return 3; }
-
- virtual float GetMinRestTime();
- virtual float GetMaxRestTime();
-
- virtual float GetFireRate( void );
-
- bool StartReload( void );
- bool Reload( void );
- void FillClip( void );
- void FinishReload( void );
- void CheckHolsterReload( void );
- void Pump( void );
-// void WeaponIdle( void );
- void ItemHolsterFrame( void );
- void ItemPostFrame( void );
- void PrimaryAttack( void );
- void SecondaryAttack( void );
- void DryFire( void );
-
- void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles );
- void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary );
- void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
-
- DECLARE_ACTTABLE();
-
- CWeaponShotgun(void);
-};
-
-IMPLEMENT_SERVERCLASS_ST(CWeaponShotgun, DT_WeaponShotgun)
-END_SEND_TABLE()
-
-LINK_ENTITY_TO_CLASS( weapon_shotgun, CWeaponShotgun );
-PRECACHE_WEAPON_REGISTER(weapon_shotgun);
-
-BEGIN_DATADESC( CWeaponShotgun )
-
- DEFINE_FIELD( m_bNeedPump, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bDelayedFire1, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bDelayedFire2, FIELD_BOOLEAN ),
-
-END_DATADESC()
-
-acttable_t CWeaponShotgun::m_acttable[] =
-{
- { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to shotgun unique
-
- { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true },
- { ACT_RELOAD, ACT_RELOAD_SHOTGUN, false },
- { ACT_WALK, ACT_WALK_RIFLE, true },
- { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true },
-
-// Readiness activities (not aiming)
- { ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims
- { ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false },
- { ACT_IDLE_AGITATED, ACT_IDLE_SHOTGUN_AGITATED, false },//always aims
-
- { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims
- { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false },
- { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims
-
- { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims
- { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false },
- { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
-
-// Readiness activities (aiming)
- { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims
- { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false },
- { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims
-
- { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims
- { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false },
- { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims
-
- { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims
- { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false },
- { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims
-//End readiness activities
-
- { ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true },
- { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true },
- { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true },
- { ACT_RUN, ACT_RUN_RIFLE, true },
- { ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true },
- { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true },
- { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true },
- { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true },
- { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true },
- { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false },
- { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponShotgun);
-
-void CWeaponShotgun::Precache( void )
-{
- CBaseCombatWeapon::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOperator -
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles )
-{
- Vector vecShootOrigin, vecShootDir;
- CAI_BaseNPC *npc = pOperator->MyNPCPointer();
- ASSERT( npc != NULL );
- WeaponSound( SINGLE_NPC );
- pOperator->DoMuzzleFlash();
- m_iClip1 = m_iClip1 - 1;
-
- if ( bUseWeaponAngles )
- {
- QAngle angShootDir;
- GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir );
- AngleVectors( angShootDir, &vecShootDir );
- }
- else
- {
- vecShootOrigin = pOperator->Weapon_ShootPosition();
- vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin );
- }
-
- pOperator->FireBullets( 8, vecShootOrigin, vecShootDir, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary )
-{
- // Ensure we have enough rounds in the clip
- m_iClip1++;
-
- FireNPCPrimaryAttack( pOperator, true );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
-{
- switch( pEvent->event )
- {
- case EVENT_WEAPON_SHOTGUN_FIRE:
- {
- FireNPCPrimaryAttack( pOperator, false );
- }
- break;
-
- default:
- CBaseCombatWeapon::Operator_HandleAnimEvent( pEvent, pOperator );
- break;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: When we shipped HL2, the shotgun weapon did not override the
-// BaseCombatWeapon default rest time of 0.3 to 0.6 seconds. When
-// NPC's fight from a stationary position, their animation events
-// govern when they fire so the rate of fire is specified by the
-// animation. When NPC's move-and-shoot, the rate of fire is
-// specifically controlled by the shot regulator, so it's imporant
-// that GetMinRestTime and GetMaxRestTime are implemented and provide
-// reasonable defaults for the weapon. To address difficulty concerns,
-// we are going to fix the combine's rate of shotgun fire in episodic.
-// This change will not affect Alyx using a shotgun in EP1. (sjb)
-//-----------------------------------------------------------------------------
-float CWeaponShotgun::GetMinRestTime()
-{
- if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE )
- {
- return 1.2f;
- }
-
- return BaseClass::GetMinRestTime();
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-float CWeaponShotgun::GetMaxRestTime()
-{
- if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE )
- {
- return 1.5f;
- }
-
- return BaseClass::GetMaxRestTime();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Time between successive shots in a burst. Also returned for EP2
-// with an eye to not messing up Alyx in EP1.
-//-----------------------------------------------------------------------------
-float CWeaponShotgun::GetFireRate()
-{
- if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE )
- {
- return 0.8f;
- }
-
- return 0.7;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so only reload one shell at a time
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeaponShotgun::StartReload( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return false;
-
- if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- return false;
-
- if (m_iClip1 >= GetMaxClip1())
- return false;
-
- // If shotgun totally emptied then a pump animation is needed
-
- //NOTENOTE: This is kinda lame because the player doesn't get strong feedback on when the reload has finished,
- // without the pump. Technically, it's incorrect, but it's good for feedback...
-
- if (m_iClip1 <= 0)
- {
- m_bNeedPump = true;
- }
-
- int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
-
- if (j <= 0)
- return false;
-
- SendWeaponAnim( ACT_SHOTGUN_RELOAD_START );
-
- // Make shotgun shell visible
- SetBodygroup(1,0);
-
- pOwner->m_flNextAttack = gpGlobals->curtime;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-
- m_bInReload = true;
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so only reload one shell at a time
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeaponShotgun::Reload( void )
-{
- // Check that StartReload was called first
- if (!m_bInReload)
- {
- Warning("ERROR: Shotgun Reload called incorrectly!\n");
- }
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return false;
-
- if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- return false;
-
- if (m_iClip1 >= GetMaxClip1())
- return false;
-
- int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
-
- if (j <= 0)
- return false;
-
- FillClip();
- // Play reload on different channel as otherwise steals channel away from fire sound
- WeaponSound(RELOAD);
- SendWeaponAnim( ACT_VM_RELOAD );
-
- pOwner->m_flNextAttack = gpGlobals->curtime;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play finish reload anim and fill clip
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::FinishReload( void )
-{
- // Make shotgun shell invisible
- SetBodygroup(1,1);
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- m_bInReload = false;
-
- // Finish reload animation
- SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH );
-
- pOwner->m_flNextAttack = gpGlobals->curtime;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play finish reload anim and fill clip
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::FillClip( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- // Add them to the clip
- if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
- {
- if ( Clip1() < GetMaxClip1() )
- {
- m_iClip1++;
- pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play weapon pump anim
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::Pump( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- m_bNeedPump = false;
-
- WeaponSound( SPECIAL1 );
-
- // Finish reload animation
- SendWeaponAnim( ACT_SHOTGUN_PUMP );
-
- pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::DryFire( void )
-{
- WeaponSound(EMPTY);
- SendWeaponAnim( ACT_VM_DRYFIRE );
-
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::PrimaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if (!pPlayer)
- {
- return;
- }
-
- // MUST call sound before removing a round from the clip of a CMachineGun
- WeaponSound(SINGLE);
-
- pPlayer->DoMuzzleFlash();
-
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- // Don't fire again until fire animation has completed
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_iClip1 -= 1;
-
- Vector vecSrc = pPlayer->Weapon_ShootPosition( );
- Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
-
- pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 );
-
- // Fire the bullets, and force the first shot to be perfectly accuracy
- pPlayer->FireBullets( sk_plr_num_shotgun_pellets.GetInt(), vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, true, true );
-
- pPlayer->ViewPunch( QAngle( random->RandomFloat( -2, -1 ), random->RandomFloat( -2, 2 ), 0 ) );
-
- CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_SHOTGUN, 0.2, GetOwner() );
-
- if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- {
- // HEV suit - indicate out of ammo condition
- pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
- }
-
- if( m_iClip1 )
- {
- // pump so long as some rounds are left.
- m_bNeedPump = true;
- }
-
- m_iPrimaryAttacks++;
- gamestats->Event_WeaponFired( pPlayer, true, GetClassname() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::SecondaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if (!pPlayer)
- {
- return;
- }
-
- pPlayer->m_nButtons &= ~IN_ATTACK2;
- // MUST call sound before removing a round from the clip of a CMachineGun
- WeaponSound(WPN_DOUBLE);
-
- pPlayer->DoMuzzleFlash();
-
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- // Don't fire again until fire animation has completed
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks
-
- Vector vecSrc = pPlayer->Weapon_ShootPosition();
- Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT );
-
- // Fire the bullets
- pPlayer->FireBullets( 12, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, false, false );
- pPlayer->ViewPunch( QAngle(random->RandomFloat( -5, 5 ),0,0) );
-
- pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 );
-
- CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_SHOTGUN, 0.2 );
-
- if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- {
- // HEV suit - indicate out of ammo condition
- pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
- }
-
- if( m_iClip1 )
- {
- // pump so long as some rounds are left.
- m_bNeedPump = true;
- }
-
- m_iSecondaryAttacks++;
- gamestats->Event_WeaponFired( pPlayer, false, GetClassname() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so shotgun can do mulitple reloads in a row
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::ItemPostFrame( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if (!pOwner)
- {
- return;
- }
-
- if (m_bInReload)
- {
- // If I'm primary firing and have one round stop reloading and fire
- if ((pOwner->m_nButtons & IN_ATTACK ) && (m_iClip1 >=1))
- {
- m_bInReload = false;
- m_bNeedPump = false;
- m_bDelayedFire1 = true;
- }
- // If I'm secondary firing and have one round stop reloading and fire
- else if ((pOwner->m_nButtons & IN_ATTACK2 ) && (m_iClip1 >=2))
- {
- m_bInReload = false;
- m_bNeedPump = false;
- m_bDelayedFire2 = true;
- }
- else if (m_flNextPrimaryAttack <= gpGlobals->curtime)
- {
- // If out of ammo end reload
- if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <=0)
- {
- FinishReload();
- return;
- }
- // If clip not full reload again
- if (m_iClip1 < GetMaxClip1())
- {
- Reload();
- return;
- }
- // Clip full, stop reloading
- else
- {
- FinishReload();
- return;
- }
- }
- }
- else
- {
- // Make shotgun shell invisible
- SetBodygroup(1,1);
- }
-
- if ((m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
- {
- Pump();
- return;
- }
-
- // Shotgun uses same timing and ammo for secondary attack
- if ((m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2)&&(m_flNextPrimaryAttack <= gpGlobals->curtime))
- {
- m_bDelayedFire2 = false;
-
- if ( (m_iClip1 <= 1 && UsesClipsForAmmo1()))
- {
- // If only one shell is left, do a single shot instead
- if ( m_iClip1 == 1 )
- {
- PrimaryAttack();
- }
- else if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
- {
- DryFire();
- }
- else
- {
- StartReload();
- }
- }
-
- // Fire underwater?
- else if (GetOwner()->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
- {
- WeaponSound(EMPTY);
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
- return;
- }
- else
- {
- // If the firing button was just pressed, reset the firing time
- if ( pOwner->m_afButtonPressed & IN_ATTACK )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime;
- }
- SecondaryAttack();
- }
- }
- else if ( (m_bDelayedFire1 || pOwner->m_nButtons & IN_ATTACK) && m_flNextPrimaryAttack <= gpGlobals->curtime)
- {
- m_bDelayedFire1 = false;
- if ( (m_iClip1 <= 0 && UsesClipsForAmmo1()) || ( !UsesClipsForAmmo1() && !pOwner->GetAmmoCount(m_iPrimaryAmmoType) ) )
- {
- if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
- {
- DryFire();
- }
- else
- {
- StartReload();
- }
- }
- // Fire underwater?
- else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
- {
- WeaponSound(EMPTY);
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
- return;
- }
- else
- {
- // If the firing button was just pressed, reset the firing time
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- if ( pPlayer && pPlayer->m_afButtonPressed & IN_ATTACK )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime;
- }
- PrimaryAttack();
- }
- }
-
- if ( pOwner->m_nButtons & IN_RELOAD && UsesClipsForAmmo1() && !m_bInReload )
- {
- // reload when reload is pressed, or if no buttons are down and weapon is empty.
- StartReload();
- }
- else
- {
- // no fire buttons down
- m_bFireOnEmpty = false;
-
- if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime )
- {
- // weapon isn't useable, switch.
- if ( !(GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && pOwner->SwitchToNextBestWeapon( this ) )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
- return;
- }
- }
- else
- {
- // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
- if ( m_iClip1 <= 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
- {
- if (StartReload())
- {
- // if we've successfully started to reload, we're done
- return;
- }
- }
- }
-
- WeaponIdle( );
- return;
- }
-
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CWeaponShotgun::CWeaponShotgun( void )
-{
- m_bReloadsSingly = true;
-
- m_bNeedPump = false;
- m_bDelayedFire1 = false;
- m_bDelayedFire2 = false;
-
- m_fMinRange1 = 0.0;
- m_fMaxRange1 = 500;
- m_fMinRange2 = 0.0;
- m_fMaxRange2 = 200;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::ItemHolsterFrame( void )
-{
- // Must be player held
- if ( GetOwner() && GetOwner()->IsPlayer() == false )
- return;
-
- // We can't be active
- if ( GetOwner()->GetActiveWeapon() == this )
- return;
-
- // If it's been longer than three seconds, reload
- if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() )
- {
- // Reset the timer
- m_flHolsterTime = gpGlobals->curtime;
-
- if ( GetOwner() == NULL )
- return;
-
- if ( m_iClip1 == GetMaxClip1() )
- return;
-
- // Just load the clip with no animations
- int ammoFill = MIN( (GetMaxClip1() - m_iClip1), GetOwner()->GetAmmoCount( GetPrimaryAmmoType() ) );
-
- GetOwner()->RemoveAmmo( ammoFill, GetPrimaryAmmoType() );
- m_iClip1 += ammoFill;
- }
-}
-
-//==================================================
-// Purpose:
-//==================================================
-/*
-void CWeaponShotgun::WeaponIdle( void )
-{
- //Only the player fires this way so we can cast
- CBasePlayer *pPlayer = GetOwner()
-
- if ( pPlayer == NULL )
- return;
-
- //If we're on a target, play the new anim
- if ( pPlayer->IsOnTarget() )
- {
- SendWeaponAnim( ACT_VM_IDLE_ACTIVE );
- }
-}
-*/
+//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: A shotgun. +// +// Primary attack: single barrel shot. +// Secondary attack: double barrel shot. +// +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "basehlcombatweapon_shared.h" +#include "basecombatcharacter.h" +#include "ai_basenpc.h" +#include "player.h" +#include "gamerules.h" // For g_pGameRules +#include "in_buttons.h" +#include "soundent.h" +#include "vstdlib/random.h" +#include "gamestats.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern ConVar sk_auto_reload_time; +extern ConVar sk_plr_num_shotgun_pellets; + +class CWeaponShotgun : public CBaseHLCombatWeapon +{ + DECLARE_DATADESC(); +public: + DECLARE_CLASS( CWeaponShotgun, CBaseHLCombatWeapon ); + + DECLARE_SERVERCLASS(); + +private: + bool m_bNeedPump; // When emptied completely + bool m_bDelayedFire1; // Fire primary when finished reloading + bool m_bDelayedFire2; // Fire secondary when finished reloading + +public: + void Precache( void ); + + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + + virtual const Vector& GetBulletSpread( void ) + { + static Vector vitalAllyCone = VECTOR_CONE_3DEGREES; + static Vector cone = VECTOR_CONE_10DEGREES; + + if( GetOwner() && (GetOwner()->Classify() == CLASS_PLAYER_ALLY_VITAL) ) + { + // Give Alyx's shotgun blasts more a more directed punch. She needs + // to be at least as deadly as she would be with her pistol to stay interesting (sjb) + return vitalAllyCone; + } + + return cone; + } + + virtual int GetMinBurst() { return 1; } + virtual int GetMaxBurst() { return 3; } + + virtual float GetMinRestTime(); + virtual float GetMaxRestTime(); + + virtual float GetFireRate( void ); + + bool StartReload( void ); + bool Reload( void ); + void FillClip( void ); + void FinishReload( void ); + void CheckHolsterReload( void ); + void Pump( void ); +// void WeaponIdle( void ); + void ItemHolsterFrame( void ); + void ItemPostFrame( void ); + void PrimaryAttack( void ); + void SecondaryAttack( void ); + void DryFire( void ); + + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + + DECLARE_ACTTABLE(); + + CWeaponShotgun(void); +}; + +IMPLEMENT_SERVERCLASS_ST(CWeaponShotgun, DT_WeaponShotgun) +END_SEND_TABLE() + +LINK_ENTITY_TO_CLASS( weapon_shotgun, CWeaponShotgun ); +PRECACHE_WEAPON_REGISTER(weapon_shotgun); + +BEGIN_DATADESC( CWeaponShotgun ) + + DEFINE_FIELD( m_bNeedPump, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bDelayedFire1, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bDelayedFire2, FIELD_BOOLEAN ), + +END_DATADESC() + +acttable_t CWeaponShotgun::m_acttable[] = +{ + { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to shotgun unique + + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RELOAD, ACT_RELOAD_SHOTGUN, false }, + { ACT_WALK, ACT_WALK_RIFLE, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_SHOTGUN_AGITATED, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, +}; + +IMPLEMENT_ACTTABLE(CWeaponShotgun); + +void CWeaponShotgun::Precache( void ) +{ + CBaseCombatWeapon::Precache(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOperator - +//----------------------------------------------------------------------------- +void CWeaponShotgun::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ) +{ + Vector vecShootOrigin, vecShootDir; + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + ASSERT( npc != NULL ); + WeaponSound( SINGLE_NPC ); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; + + if ( bUseWeaponAngles ) + { + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + } + else + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + } + + pOperator->FireBullets( 8, vecShootOrigin, vecShootDir, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponShotgun::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + FireNPCPrimaryAttack( pOperator, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponShotgun::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_SHOTGUN_FIRE: + { + FireNPCPrimaryAttack( pOperator, false ); + } + break; + + default: + CBaseCombatWeapon::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: When we shipped HL2, the shotgun weapon did not override the +// BaseCombatWeapon default rest time of 0.3 to 0.6 seconds. When +// NPC's fight from a stationary position, their animation events +// govern when they fire so the rate of fire is specified by the +// animation. When NPC's move-and-shoot, the rate of fire is +// specifically controlled by the shot regulator, so it's imporant +// that GetMinRestTime and GetMaxRestTime are implemented and provide +// reasonable defaults for the weapon. To address difficulty concerns, +// we are going to fix the combine's rate of shotgun fire in episodic. +// This change will not affect Alyx using a shotgun in EP1. (sjb) +//----------------------------------------------------------------------------- +float CWeaponShotgun::GetMinRestTime() +{ + if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE ) + { + return 1.2f; + } + + return BaseClass::GetMinRestTime(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CWeaponShotgun::GetMaxRestTime() +{ + if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE ) + { + return 1.5f; + } + + return BaseClass::GetMaxRestTime(); +} + +//----------------------------------------------------------------------------- +// Purpose: Time between successive shots in a burst. Also returned for EP2 +// with an eye to not messing up Alyx in EP1. +//----------------------------------------------------------------------------- +float CWeaponShotgun::GetFireRate() +{ + if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE ) + { + return 0.8f; + } + + return 0.7; +} + +//----------------------------------------------------------------------------- +// Purpose: Override so only reload one shell at a time +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CWeaponShotgun::StartReload( void ) +{ + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return false; + + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + return false; + + if (m_iClip1 >= GetMaxClip1()) + return false; + + // If shotgun totally emptied then a pump animation is needed + + //NOTENOTE: This is kinda lame because the player doesn't get strong feedback on when the reload has finished, + // without the pump. Technically, it's incorrect, but it's good for feedback... + + if (m_iClip1 <= 0) + { + m_bNeedPump = true; + } + + int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); + + if (j <= 0) + return false; + + SendWeaponAnim( ACT_SHOTGUN_RELOAD_START ); + + // Make shotgun shell visible + SetBodygroup(1,0); + + pOwner->m_flNextAttack = gpGlobals->curtime; + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + + m_bInReload = true; + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Override so only reload one shell at a time +// Input : +// Output : +//----------------------------------------------------------------------------- +bool CWeaponShotgun::Reload( void ) +{ + // Check that StartReload was called first + if (!m_bInReload) + { + Warning("ERROR: Shotgun Reload called incorrectly!\n"); + } + + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return false; + + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + return false; + + if (m_iClip1 >= GetMaxClip1()) + return false; + + int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType)); + + if (j <= 0) + return false; + + FillClip(); + // Play reload on different channel as otherwise steals channel away from fire sound + WeaponSound(RELOAD); + SendWeaponAnim( ACT_VM_RELOAD ); + + pOwner->m_flNextAttack = gpGlobals->curtime; + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Play finish reload anim and fill clip +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponShotgun::FinishReload( void ) +{ + // Make shotgun shell invisible + SetBodygroup(1,1); + + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return; + + m_bInReload = false; + + // Finish reload animation + SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH ); + + pOwner->m_flNextAttack = gpGlobals->curtime; + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +} + +//----------------------------------------------------------------------------- +// Purpose: Play finish reload anim and fill clip +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponShotgun::FillClip( void ) +{ + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return; + + // Add them to the clip + if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 ) + { + if ( Clip1() < GetMaxClip1() ) + { + m_iClip1++; + pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Play weapon pump anim +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponShotgun::Pump( void ) +{ + CBaseCombatCharacter *pOwner = GetOwner(); + + if ( pOwner == NULL ) + return; + + m_bNeedPump = false; + + WeaponSound( SPECIAL1 ); + + // Finish reload animation + SendWeaponAnim( ACT_SHOTGUN_PUMP ); + + pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration(); + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CWeaponShotgun::DryFire( void ) +{ + WeaponSound(EMPTY); + SendWeaponAnim( ACT_VM_DRYFIRE ); + + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CWeaponShotgun::PrimaryAttack( void ) +{ + // Only the player fires this way so we can cast + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if (!pPlayer) + { + return; + } + + // MUST call sound before removing a round from the clip of a CMachineGun + WeaponSound(SINGLE); + + pPlayer->DoMuzzleFlash(); + + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + // Don't fire again until fire animation has completed + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_iClip1 -= 1; + + Vector vecSrc = pPlayer->Weapon_ShootPosition( ); + Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT ); + + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 ); + + // Fire the bullets, and force the first shot to be perfectly accuracy + pPlayer->FireBullets( sk_plr_num_shotgun_pellets.GetInt(), vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, true, true ); + + pPlayer->ViewPunch( QAngle( random->RandomFloat( -2, -1 ), random->RandomFloat( -2, 2 ), 0 ) ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_SHOTGUN, 0.2, GetOwner() ); + + if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + { + // HEV suit - indicate out of ammo condition + pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + } + + if( m_iClip1 ) + { + // pump so long as some rounds are left. + m_bNeedPump = true; + } + + m_iPrimaryAttacks++; + gamestats->Event_WeaponFired( pPlayer, true, GetClassname() ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// +// +//----------------------------------------------------------------------------- +void CWeaponShotgun::SecondaryAttack( void ) +{ + // Only the player fires this way so we can cast + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + + if (!pPlayer) + { + return; + } + + pPlayer->m_nButtons &= ~IN_ATTACK2; + // MUST call sound before removing a round from the clip of a CMachineGun + WeaponSound(WPN_DOUBLE); + + pPlayer->DoMuzzleFlash(); + + SendWeaponAnim( ACT_VM_SECONDARYATTACK ); + + // player "shoot" animation + pPlayer->SetAnimation( PLAYER_ATTACK1 ); + + // Don't fire again until fire animation has completed + m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); + m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks + + Vector vecSrc = pPlayer->Weapon_ShootPosition(); + Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_SCALE_DEFAULT ); + + // Fire the bullets + pPlayer->FireBullets( 12, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, false, false ); + pPlayer->ViewPunch( QAngle(random->RandomFloat( -5, 5 ),0,0) ); + + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_SHOTGUN, 0.2 ); + + if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) + { + // HEV suit - indicate out of ammo condition + pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0); + } + + if( m_iClip1 ) + { + // pump so long as some rounds are left. + m_bNeedPump = true; + } + + m_iSecondaryAttacks++; + gamestats->Event_WeaponFired( pPlayer, false, GetClassname() ); +} + +//----------------------------------------------------------------------------- +// Purpose: Override so shotgun can do mulitple reloads in a row +//----------------------------------------------------------------------------- +void CWeaponShotgun::ItemPostFrame( void ) +{ + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + if (!pOwner) + { + return; + } + + if (m_bInReload) + { + // If I'm primary firing and have one round stop reloading and fire + if ((pOwner->m_nButtons & IN_ATTACK ) && (m_iClip1 >=1)) + { + m_bInReload = false; + m_bNeedPump = false; + m_bDelayedFire1 = true; + } + // If I'm secondary firing and have one round stop reloading and fire + else if ((pOwner->m_nButtons & IN_ATTACK2 ) && (m_iClip1 >=2)) + { + m_bInReload = false; + m_bNeedPump = false; + m_bDelayedFire2 = true; + } + else if (m_flNextPrimaryAttack <= gpGlobals->curtime) + { + // If out of ammo end reload + if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <=0) + { + FinishReload(); + return; + } + // If clip not full reload again + if (m_iClip1 < GetMaxClip1()) + { + Reload(); + return; + } + // Clip full, stop reloading + else + { + FinishReload(); + return; + } + } + } + else + { + // Make shotgun shell invisible + SetBodygroup(1,1); + } + + if ((m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime)) + { + Pump(); + return; + } + + // Shotgun uses same timing and ammo for secondary attack + if ((m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2)&&(m_flNextPrimaryAttack <= gpGlobals->curtime)) + { + m_bDelayedFire2 = false; + + if ( (m_iClip1 <= 1 && UsesClipsForAmmo1())) + { + // If only one shell is left, do a single shot instead + if ( m_iClip1 == 1 ) + { + PrimaryAttack(); + } + else if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType)) + { + DryFire(); + } + else + { + StartReload(); + } + } + + // Fire underwater? + else if (GetOwner()->GetWaterLevel() == 3 && m_bFiresUnderwater == false) + { + WeaponSound(EMPTY); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + return; + } + else + { + // If the firing button was just pressed, reset the firing time + if ( pOwner->m_afButtonPressed & IN_ATTACK ) + { + m_flNextPrimaryAttack = gpGlobals->curtime; + } + SecondaryAttack(); + } + } + else if ( (m_bDelayedFire1 || pOwner->m_nButtons & IN_ATTACK) && m_flNextPrimaryAttack <= gpGlobals->curtime) + { + m_bDelayedFire1 = false; + if ( (m_iClip1 <= 0 && UsesClipsForAmmo1()) || ( !UsesClipsForAmmo1() && !pOwner->GetAmmoCount(m_iPrimaryAmmoType) ) ) + { + if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType)) + { + DryFire(); + } + else + { + StartReload(); + } + } + // Fire underwater? + else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false) + { + WeaponSound(EMPTY); + m_flNextPrimaryAttack = gpGlobals->curtime + 0.2; + return; + } + else + { + // If the firing button was just pressed, reset the firing time + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if ( pPlayer && pPlayer->m_afButtonPressed & IN_ATTACK ) + { + m_flNextPrimaryAttack = gpGlobals->curtime; + } + PrimaryAttack(); + } + } + + if ( pOwner->m_nButtons & IN_RELOAD && UsesClipsForAmmo1() && !m_bInReload ) + { + // reload when reload is pressed, or if no buttons are down and weapon is empty. + StartReload(); + } + else + { + // no fire buttons down + m_bFireOnEmpty = false; + + if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + // weapon isn't useable, switch. + if ( !(GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && pOwner->SwitchToNextBestWeapon( this ) ) + { + m_flNextPrimaryAttack = gpGlobals->curtime + 0.3; + return; + } + } + else + { + // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing + if ( m_iClip1 <= 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime ) + { + if (StartReload()) + { + // if we've successfully started to reload, we're done + return; + } + } + } + + WeaponIdle( ); + return; + } + +} + + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CWeaponShotgun::CWeaponShotgun( void ) +{ + m_bReloadsSingly = true; + + m_bNeedPump = false; + m_bDelayedFire1 = false; + m_bDelayedFire2 = false; + + m_fMinRange1 = 0.0; + m_fMaxRange1 = 500; + m_fMinRange2 = 0.0; + m_fMaxRange2 = 200; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponShotgun::ItemHolsterFrame( void ) +{ + // Must be player held + if ( GetOwner() && GetOwner()->IsPlayer() == false ) + return; + + // We can't be active + if ( GetOwner()->GetActiveWeapon() == this ) + return; + + // If it's been longer than three seconds, reload + if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() ) + { + // Reset the timer + m_flHolsterTime = gpGlobals->curtime; + + if ( GetOwner() == NULL ) + return; + + if ( m_iClip1 == GetMaxClip1() ) + return; + + // Just load the clip with no animations + int ammoFill = MIN( (GetMaxClip1() - m_iClip1), GetOwner()->GetAmmoCount( GetPrimaryAmmoType() ) ); + + GetOwner()->RemoveAmmo( ammoFill, GetPrimaryAmmoType() ); + m_iClip1 += ammoFill; + } +} + +//================================================== +// Purpose: +//================================================== +/* +void CWeaponShotgun::WeaponIdle( void ) +{ + //Only the player fires this way so we can cast + CBasePlayer *pPlayer = GetOwner() + + if ( pPlayer == NULL ) + return; + + //If we're on a target, play the new anim + if ( pPlayer->IsOnTarget() ) + { + SendWeaponAnim( ACT_VM_IDLE_ACTIVE ); + } +} +*/ |