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