Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

953 lines
26 KiB

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "weapon_csbasegun.h"
#include "fx_cs_shared.h"
#include "in_buttons.h"
#ifdef CLIENT_DLL
#include "c_cs_player.h"
#include "cs_client_gamestats.h"
#include "cdll_client_int.h"
#else
#include "cs_player.h"
#endif
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
#define DETACHABLE_SILENCER 1
#define SILENCER_BODYGROUP_UNSET -2
IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCSBaseGun, DT_WeaponCSBaseGun )
BEGIN_NETWORK_TABLE( CWeaponCSBaseGun, DT_WeaponCSBaseGun )
#if defined( GAME_DLL )
SendPropInt( SENDINFO( m_zoomLevel ), 2, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_iBurstShotsRemaining ) ),
#else
RecvPropInt( RECVINFO( m_zoomLevel ) ),
RecvPropInt( RECVINFO( m_iBurstShotsRemaining ) ),
#endif
END_NETWORK_TABLE()
#if defined( CLIENT_DLL )
BEGIN_PREDICTION_DATA( CWeaponCSBaseGun )
DEFINE_PRED_FIELD( m_zoomLevel, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_iBurstShotsRemaining, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
DEFINE_PRED_FIELD( m_fNextBurstShot, FIELD_FLOAT, 0 ),
END_PREDICTION_DATA()
#endif
LINK_ENTITY_TO_CLASS_ALIASED( weapon_csbase_gun, WeaponCSBaseGun );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( AK47, WeaponCSBaseGun, DT_WeaponAK47, weapon_ak47 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponAug, WeaponCSBaseGun, DT_WeaponAug, weapon_aug );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponAWP, WeaponCSBaseGun, DT_WeaponAWP, weapon_awp );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponBizon, WeaponCSBaseGun, DT_WeaponBizon, weapon_bizon );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponFamas, WeaponCSBaseGun, DT_WeaponFamas, weapon_famas );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponFiveSeven, WeaponCSBaseGun, DT_WeaponFiveSeven, weapon_fiveseven );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponG3SG1, WeaponCSBaseGun, DT_WeaponG3SG1, weapon_g3sg1 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponGalil, WeaponCSBaseGun, DT_WeaponGalil, weapon_galil );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponGalilAR, WeaponCSBaseGun, DT_WeaponGalilAR, weapon_galilar );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponGlock, WeaponCSBaseGun, DT_WeaponGlock, weapon_glock );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponHKP2000, WeaponCSBaseGun, DT_WeaponHKP2000, weapon_hkp2000 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponM4A1, WeaponCSBaseGun, DT_WeaponM4A1, weapon_m4a1 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponMAC10, WeaponCSBaseGun, DT_WeaponMAC10, weapon_mac10 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponMag7, WeaponCSBaseGun, DT_WeaponMag7, weapon_mag7 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponMP5Navy, WeaponCSBaseGun, DT_WeaponMP5Navy, weapon_mp5navy );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponMP7, WeaponCSBaseGun, DT_WeaponMP7, weapon_mp7 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponMP9, WeaponCSBaseGun, DT_WeaponMP9, weapon_mp9 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponNegev, WeaponCSBaseGun, DT_WeaponNegev, weapon_negev );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponP228, WeaponCSBaseGun, DT_WeaponP228, weapon_p228 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponP250, WeaponCSBaseGun, DT_WeaponP250, weapon_p250 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponP90, WeaponCSBaseGun, DT_WeaponP90, weapon_p90 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( SCAR17, WeaponCSBaseGun, DT_WeaponSCAR17, weapon_scar17 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponSCAR20, WeaponCSBaseGun, DT_WeaponSCAR20, weapon_scar20 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponScout, WeaponCSBaseGun, DT_WeaponScout, weapon_scout );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponSG550, WeaponCSBaseGun, DT_WeaponSG550, weapon_sg550 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponSG556, WeaponCSBaseGun, DT_WeaponSG556, weapon_sg556 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponSSG08, WeaponCSBaseGun, DT_WeaponSSG08, weapon_ssg08 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponTec9, WeaponCSBaseGun, DT_WeaponTec9, weapon_tec9 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponTMP, WeaponCSBaseGun, DT_WeaponTMP, weapon_tmp );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponUMP45, WeaponCSBaseGun, DT_WeaponUMP45, weapon_ump45 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponM249, WeaponCSBaseGun, DT_WeaponM249, weapon_m249 );
LINK_ENTITY_TO_CLASS_SIMPLE_DERIVED( WeaponUSP, WeaponCSBaseGun, DT_WeaponUSP, weapon_usp );
CWeaponCSBaseGun::CWeaponCSBaseGun()
{
m_pWeaponInfo = NULL;
m_zoomLevel = 0;
m_inPrecache = false;
#ifdef CLIENT_DLL
m_iSilencerBodygroup = SILENCER_BODYGROUP_UNSET;
#endif
}
void CWeaponCSBaseGun::Spawn( )
{
BaseClass::Spawn();
m_bBurstMode = false;
m_iBurstShotsRemaining = 0;
m_fNextBurstShot = 0.0f;
ResetPostponeFireReadyTime();
}
void CWeaponCSBaseGun::Precache()
{
m_inPrecache = true;
BaseClass::Precache();
m_inPrecache = false;
}
const char * CWeaponCSBaseGun::GetWorldModel( void ) const
{
return BaseClass::GetWorldModel();
}
Activity CWeaponCSBaseGun::GetDeployActivity( void )
{
if( IsSilenced() )
{
return ACT_VM_DRAW_SILENCED;
}
else
{
return BaseClass::GetDeployActivity();
}
}
void CWeaponCSBaseGun::Drop( const Vector &vecVelocity )
{
// re-deploying the weapon is punishment enough for canceling a silencer attach/detach before completion
if ( (GetActivity() == ACT_VM_ATTACH_SILENCER && m_bSilencerOn == false) ||
(GetActivity() == ACT_VM_DETACH_SILENCER && m_bSilencerOn == true ) )
{
m_flDoneSwitchingSilencer = gpGlobals->curtime;
m_flNextSecondaryAttack = gpGlobals->curtime;
m_flNextPrimaryAttack = gpGlobals->curtime;
}
//make sure the world-model silencer bodygroup is correct, we might have hidden/unhidden it prematurely to make the 3rd-person animation look correct
else if ( (GetActivity() == ACT_VM_ATTACH_SILENCER) || (GetActivity() == ACT_VM_DETACH_SILENCER) )
{
int iBodyGroup = FindBodygroupByName( "silencer" );
if ( iBodyGroup != -1 )
SetBodygroup( iBodyGroup, m_bSilencerOn ? 0 : 1 );
}
BaseClass::Drop( vecVelocity );
}
void CWeaponCSBaseGun::ItemBusyFrame()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
// if we're scoped during a reload, pull us out of the scope for the duration (and set resumezoom so we'll re-zoom when reloading is done)
if ( HasZoom() && (IsZoomed() || pPlayer->m_bIsScoped) && m_bInReload )
{
//m_zoomLevel = 0; //don't affect zoom level, so it'll restore when reloading is done
pPlayer->m_bIsScoped = false;
pPlayer->m_bResumeZoom = true;
pPlayer->SetFOV( pPlayer, GetZoomFOV( 0 ), GetZoomTime( 0 ) );
m_weaponMode = Primary_Mode;
}
BaseClass::ItemBusyFrame();
}
void CWeaponCSBaseGun::ItemPostFrame()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
// smoother out the accuracy a bit
//float flFOV = GetFOVForAccuracy();
//GOOSEMAN : Return zoom level back to previous zoom level before we fired a shot. This is used only for the AWP.
// And Scout.
if ( (m_flNextPrimaryAttack <= gpGlobals->curtime) && (pPlayer->m_bResumeZoom == TRUE)
&& m_zoomLevel > 0 ) // only need to re-zoom the zoom when there's a zoom to re-zoom to. who knew?
{
if ( m_iClip1 != 0 || ( GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD ) )
{
m_weaponMode = Secondary_Mode;
// the zoom amount is taking care of below
pPlayer->SetFOV( pPlayer, GetZoomFOV( m_zoomLevel ), 0.1f );
m_fScopeZoomEndTime = gpGlobals->curtime + 0.1;
pPlayer->m_bIsScoped = true;
#ifdef CLIENT_DLL
/*
ScreenFade_t fade;
fade.duration = ( unsigned short )( ( float )( 1 << SCREENFADE_FRACBITS ) * 0.175 );
fade.holdTime = ( unsigned short )( ( float )( 1 << SCREENFADE_FRACBITS ) * 0 );
fade.fadeFlags = 0;
fade.fadeFlags |= FFADE_IN;
fade.r = 0;
fade.g = 0;
fade.b = 0;
fade.a = 255;
clientdll->View_Fade( &fade );
*/
#endif
}
pPlayer->m_bResumeZoom = false;
}
/*
// do this for sniper rifles only and only when the initial zoom has finished zooming
if ( GetCSWpnData().m_iZoomLevels >= 2 && (m_fScopeZoomEndTime <= gpGlobals->curtime) && m_weaponMode == Secondary_Mode )
{
// if we're zoomed in
if ( IsZoomed() )
{
// this is the zoom we are suppoed to be at if we're standing still
float flFOVDiff = MAX( 0, flFOV - pPlayer->GetFOV() );
flFOV = ceil( flFOV );
if ( flFOV < 0 )
flFOV *= -1;
if ( flFOVDiff >= 1.0f )
{
flFOVDiff = MIN( flFOVDiff, 10.0f )/50;
if ( flFOVDiff < 0.05f )
flFOVDiff = 0;
pPlayer->SetFOV( pPlayer, flFOV, flFOVDiff );
}
}
else
{
m_fAccuracySmoothedForZoom = 0;
}
}
*/
if ( WeaponHasBurst() )
{
if ( m_iBurstShotsRemaining > 0 && gpGlobals->curtime >= m_fNextBurstShot )
{
BurstFireRemaining();
}
}
BaseClass::ItemPostFrame();
}
bool CWeaponCSBaseGun::SendWeaponAnim( int iActivity )
{
#ifndef CLIENT_DLL
// firing or reloading should interrupt weapon inspection
if ( iActivity == ACT_VM_PRIMARYATTACK || iActivity == ACT_VM_RELOAD || iActivity == ACT_SECONDARY_VM_RELOAD || iActivity == ACT_VM_ATTACH_SILENCER || iActivity == ACT_VM_DETACH_SILENCER )
{
if ( CCSPlayer *pPlayer = GetPlayerOwner() )
{
pPlayer->StopLookingAtWeapon();
}
}
#endif
return BaseClass::SendWeaponAnim( iActivity );
}
float CWeaponCSBaseGun::GetFOVForAccuracy( void )
{
const CCSWeaponInfo& weaponInfo = GetCSWpnData();
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return 0;
float flDefaultAccuracy = weaponInfo.GetInaccuracyStand( GetEconItemView(), m_weaponMode );
// if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
// {
// flDefaultAccuracy = weaponInfo.GetInaccuracyLadder( m_weaponMode, GetEconItemView() ) + weaponInfo.GetInaccuracyLadder( 0, GetEconItemView() );
// }
if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
{
flDefaultAccuracy = weaponInfo.GetInaccuracyCrouch( GetEconItemView(), m_weaponMode );
}
m_fAccuracySmoothedForZoom = Approach( GetInaccuracy(), m_fAccuracySmoothedForZoom, gpGlobals->frametime * 10.0f );
float flTargetFOVForZoom = GetZoomFOV( m_zoomLevel );
float flFOV = flTargetFOVForZoom;
// and apply it to the player's fov
if ( m_fAccuracySmoothedForZoom >= 0 )
{
flFOV = flTargetFOVForZoom - ( m_fAccuracySmoothedForZoom - flDefaultAccuracy ) * 10;//MIN( flTargetFOVForZoom * ( 1 + ( m_fAccuracySmoothedForZoom*2 ) ), flTargetFOVForZoom * ( 1 + ( m_fAccuracySmoothedForZoom ) ) );
//Msg( "flFOV = %f\n", flFOV );
}
return flFOV;
}
void CWeaponCSBaseGun::PrimaryAttack()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return;
if ( CannotShootUnderwater() )
{
PlayEmptySound();
m_flNextPrimaryAttack = gpGlobals->curtime + 0.15f;
return;
}
float flCycleTime = GetCycleTime();
// change a few things if we're in burst mode
if ( IsInBurstMode() )
{
CALL_ATTRIB_HOOK_FLOAT( flCycleTime, cycletime_when_in_burst_mode );
m_iBurstShotsRemaining = 2;
m_fNextBurstShot = gpGlobals->curtime;
CALL_ATTRIB_HOOK_FLOAT( m_fNextBurstShot, time_between_burst_shots );
}
if ( IsZoomed() )
{
CALL_ATTRIB_HOOK_FLOAT( flCycleTime, cycletime_when_zoomed );
}
if ( !CSBaseGunFire( flCycleTime, m_weaponMode ) ) // <-- 'PEW PEW' HAPPENS HERE
return;
if ( IsSilenced() )
SendWeaponAnim( ACT_VM_PRIMARYATTACK_SILENCED );
// Does this gun unzoom after a shot, as in a bolt action rifle?
if ( IsZoomed() && ( DoesUnzoomAfterShot() ) )
{
pPlayer->m_bIsScoped = false;
pPlayer->m_bResumeZoom = true;
pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.05f );
m_weaponMode = Primary_Mode;
}
}
void CWeaponCSBaseGun::SecondaryAttack()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( pPlayer == NULL )
{
Assert(pPlayer != NULL);
return;
}
if ( HasZoom() )
{
if ( ++m_zoomLevel > GetZoomLevels() )
m_zoomLevel = 0;
bool bIsSniperRifle = GetWeaponType() == WEAPONTYPE_SNIPER_RIFLE;
if ( IsZoomed() )
{
m_weaponMode = Secondary_Mode;
//float flFOV = GetFOVForAccuracy();
if ( bIsSniperRifle )
pPlayer->SetFOV( pPlayer, GetZoomFOV( m_zoomLevel ), GetZoomTime( m_zoomLevel) );
m_fAccuracyPenalty += GetCSWpnData().GetInaccuracyAltSwitch();
m_fAccuracySmoothedForZoom = 0;
pPlayer->m_bIsScoped = true;
#ifdef IRONSIGHT
if ( pPlayer->GetActiveCSWeapon() )
{
CIronSightController *pIronSightController = pPlayer->GetActiveCSWeapon()->GetIronSightController();
if (pIronSightController)
{
pPlayer->GetActiveCSWeapon()->UpdateIronSightController();
pPlayer->SetFOV(pPlayer, pIronSightController->GetIronSightIdealFOV(), pIronSightController->GetIronSightPullUpDuration());
pIronSightController->SetState( IronSight_should_approach_sighted );
//stop looking at weapon when going into ironsights
#ifndef CLIENT_DLL
pPlayer->StopLookingAtWeapon();
//force idle animation
CBaseViewModel *pViewModel = pPlayer->GetViewModel();
if (pViewModel)
{
int nSequence = pViewModel->LookupSequence("idle");
if (nSequence != ACTIVITY_NOT_AVAILABLE)
{
pViewModel->ForceCycle(0);
pViewModel->ResetSequence(nSequence);
}
}
#endif
}
}
#endif
}
else
{
m_weaponMode = Primary_Mode;
if ( bIsSniperRifle )
{
int iFOV = FBitSet( pPlayer->GetFlags(), FL_DUCKING ) ? pPlayer->GetDefaultCrouchedFOV() : pPlayer->GetDefaultFOV();
pPlayer->SetFOV( pPlayer, iFOV, GetZoomTime( 0 ));
}
m_fAccuracySmoothedForZoom = 0;
pPlayer->m_bIsScoped = false;
#ifdef IRONSIGHT
if ( pPlayer->GetActiveCSWeapon() )
{
CIronSightController *pIronSightController = pPlayer->GetActiveCSWeapon()->GetIronSightController();
if (pIronSightController)
{
pPlayer->GetActiveCSWeapon()->UpdateIronSightController();
int iFOV = FBitSet(pPlayer->GetFlags(), FL_DUCKING) ? pPlayer->GetDefaultCrouchedFOV() : pPlayer->GetDefaultFOV();
pPlayer->SetFOV(pPlayer, iFOV, pIronSightController->GetIronSightPutDownDuration());
pIronSightController->SetState(IronSight_should_approach_unsighted);
SendWeaponAnim(ACT_VM_FIDGET);
}
}
#endif
}
#ifdef CLIENT_DLL
/*
if ( GetPlayerOwner() && ( bIsSniperRifle && IsZoomed() && m_zoomLevel == 1 ) )
{
ScreenFade_t fade;
fade.duration = ( unsigned short )( ( float )( 1 << SCREENFADE_FRACBITS ) * 0.22 );
fade.holdTime = ( unsigned short )( ( float )( 1 << SCREENFADE_FRACBITS ) * 0 );
fade.fadeFlags = 0;
fade.fadeFlags |= FFADE_IN;
fade.r = 0;
fade.g = 0;
fade.b = 0;
fade.a = 255;
clientdll->View_Fade( &fade );
}
*/
#endif
#ifndef CLIENT_DLL
// If this isn't guarded, the sound will be emitted twice, once by the server and once by the client.
// Let the server play it since if only the client plays it, it's liable to get played twice cause of
// a prediction error. joy.
// [tj] Playing this from the player so that we don't try to play the sound outside the level.
if ( GetPlayerOwner() )
{
if ( IsZoomed() )
{
const char *pszZoomSound = GetZoomInSound();
if ( pszZoomSound && pszZoomSound[0] )
{
GetPlayerOwner()->EmitSound( pszZoomSound );
}
//if ( !bIsSniperRifle )
//{
// color32 clr = {0, 0, 0, 200};
// float flZoomTime = weaponInfo.m_fZoomTime[m_zoomLevel];
// float flBlackTime = MAX( flZoomTime/15, 0.02 );
// UTIL_ScreenFade( pPlayer, clr, flBlackTime, (flZoomTime - (flZoomTime/5)) - flBlackTime, FFADE_IN );
//}
}
else
{
const char *pszZoomSound = GetZoomOutSound();
if ( pszZoomSound && pszZoomSound[0] )
{
GetPlayerOwner()->EmitSound( pszZoomSound );
}
//if ( !bIsSniperRifle )
//{
// color32 clr = {0, 0, 0, 175};
// float flZoomTime = weaponInfo.m_fZoomTime[0];
// float flBlackTime = MAX( flZoomTime/15, 0.02 );
// UTIL_ScreenFade( pPlayer, clr, flBlackTime, flZoomTime - flBlackTime, FFADE_OUT );
//}
}
if ( bIsSniperRifle )
{
// let the bots hear the sniper rifle zoom
IGameEvent * event = gameeventmanager->CreateEvent( "weapon_zoom" );
if ( event )
{
event->SetInt( "userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
}
else
{
// exists for the game instructor to let it know when the player zoomed in with a regular rifle
// different from the above weapon_zoom because we don't use this event to notify bots
IGameEvent * event = gameeventmanager->CreateEvent( "weapon_zoom_rifle" );
if ( event )
{
event->SetInt( "userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
}
}
#endif
m_fScopeZoomEndTime = gpGlobals->curtime + GetZoomTime( m_zoomLevel );
}
#ifndef CLIENT_DLL
else if ( WeaponHasBurst() )
{
if ( IsInBurstMode() )
{
pPlayer->HintMessage( "#Cstrike_TitlesTXT_Switch_To_FullAuto", false );
m_bBurstMode = false;
m_weaponMode = Primary_Mode;
}
else
{
pPlayer->HintMessage( "#Cstrike_TitlesTXT_Switch_To_BurstFire", false );
m_bBurstMode = true;
m_weaponMode = Secondary_Mode;
}
pPlayer->EmitSound( "Weapon.AutoSemiAutoSwitch" );
}
#endif
#if DETACHABLE_SILENCER
else if ( HasSilencer() && m_flDoneSwitchingSilencer <= gpGlobals->curtime )
{
if ( m_bSilencerOn )
{
SendWeaponAnim( ACT_VM_DETACH_SILENCER );
#ifndef CLIENT_DLL
SendActivityEvents( ACT_VM_DETACH_SILENCER );
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_SILENCER_DETACH );
IGameEvent * event = gameeventmanager->CreateEvent( "silencer_detach" );
if ( event )
{
event->SetInt( "userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
#endif
}
else
{
SendWeaponAnim( ACT_VM_ATTACH_SILENCER );
#ifndef CLIENT_DLL
SendActivityEvents( ACT_VM_ATTACH_SILENCER );
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_SILENCER_ATTACH );
#endif
}
float nextAttackTime = gpGlobals->curtime + SequenceDuration();
m_flDoneSwitchingSilencer = nextAttackTime;
m_flNextSecondaryAttack = nextAttackTime;
m_flNextPrimaryAttack = nextAttackTime;
SetWeaponIdleTime( nextAttackTime );
}
#endif
else if ( IsRevolver() && m_flNextSecondaryAttack < gpGlobals->curtime )
{
float flCycletimeAlt = GetCycleTime( Secondary_Mode );
m_weaponMode = Secondary_Mode;
UpdateAccuracyPenalty();
#ifndef CLIENT_DLL
// Logic for weapon_fire event mimics weapon_csbase.cpp CWeaponCSBase::ItemPostFrame() primary fire implementation
IGameEvent * event = gameeventmanager->CreateEvent( ( HasAmmo() ) ? "weapon_fire" : "weapon_fire_on_empty" );
if ( event )
{
const char *weaponName = GetDefinitionName();
event->SetInt( "userid", pPlayer->GetUserID() );
event->SetString( "weapon", weaponName );
event->SetBool( "silenced", IsSilenced() );
gameeventmanager->FireEvent( event );
}
#endif
CSBaseGunFire( flCycletimeAlt, Secondary_Mode ); // <-- 'PEW PEW' HAPPENS HERE
m_flNextSecondaryAttack = gpGlobals->curtime + flCycletimeAlt;
return;
}
else
{
BaseClass::SecondaryAttack();
}
m_flNextSecondaryAttack = gpGlobals->curtime + 0.3f;
}
void CWeaponCSBaseGun::BurstFireRemaining()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer || m_iClip1 <= 0 )
{
m_iClip1 = 0;
m_iBurstShotsRemaining = 0;
m_fNextBurstShot = 0.0f;
return;
}
uint16 nItemDefIndex = 0;
FX_FireBullets(
pPlayer->entindex(),
nItemDefIndex,
pPlayer->Weapon_ShootPosition(),
pPlayer->GetFinalAimAngle(),
GetCSWeaponID(),
Secondary_Mode,
CBaseEntity::GetPredictionRandomSeed( SERVER_PLATTIME_RNG ) & 255,
GetInaccuracy(),
GetSpread(),
GetAccuracyFishtail(),
m_fNextBurstShot,
(HasSilencer() && IsSilenced()) ? SPECIAL1 : SINGLE,
m_flRecoilIndex );
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
pPlayer->DoMuzzleFlash();
pPlayer->SetAnimation( PLAYER_ATTACK1 );
--m_iBurstShotsRemaining;
if ( m_iBurstShotsRemaining > 0 )
{
CALL_ATTRIB_HOOK_FLOAT( m_fNextBurstShot, time_between_burst_shots );
}
else
{
m_fNextBurstShot = 0.0f;
}
const CCSWeaponInfo& weaponInfo = GetCSWpnData();
// update accuracy
m_fAccuracyPenalty += weaponInfo.GetInaccuracyFire( GetEconItemView(), m_weaponMode );
// table driven recoil
Recoil( Secondary_Mode );
++pPlayer->m_iShotsFired;
m_flRecoilIndex += 1.0f;
--m_iClip1;
}
bool CWeaponCSBaseGun::CSBaseGunFire( float flCycleTime, CSWeaponMode weaponMode )
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
const CCSWeaponInfo& weaponInfo = GetCSWpnData();
if ( m_iClip1 == 0 )
{
if ( m_bFireOnEmpty )
{
PlayEmptySound();
m_iNumEmptyAttacks++;
// NOTE[pmf]: we don't want to actually play the dry fire animations, as most seem to depict the weapon actually firing.
// SendWeaponAnim( ACT_VM_DRYFIRE );
//++pPlayer->m_iShotsFired; // don't play "auto" empty clicks -- make the player release the trigger before clicking again
m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f;
if ( IsRevolver() )
{
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + GetCycleTime( weaponMode );
BaseClass::SendWeaponAnim( ACT_VM_DRYFIRE ); // empty!
}
}
return false;
}
float flCurAttack = CalculateNextAttackTime( flCycleTime );
if ( (GetWeaponType() != WEAPONTYPE_SNIPER_RIFLE && IsZoomed()) || (IsRevolver() && weaponMode == Secondary_Mode) )
{
SendWeaponAnim( ACT_VM_SECONDARYATTACK );
}
else if ( IsRevolver() )
{
BaseClass::SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
else
{
SendWeaponAnim( ACT_VM_PRIMARYATTACK );
}
// player "shoot" animation
pPlayer->SetAnimation( PLAYER_ATTACK1 );
uint16 nItemDefIndex = 0;
FX_FireBullets(
pPlayer->entindex(),
nItemDefIndex,
pPlayer->Weapon_ShootPosition(),
pPlayer->GetFinalAimAngle(),
GetCSWeaponID(),
weaponMode,
CBaseEntity::GetPredictionRandomSeed( SERVER_PLATTIME_RNG ) & 255,
GetInaccuracy(),
GetSpread(),
GetAccuracyFishtail(),
flCurAttack,
(HasSilencer() && IsSilenced()) ? SPECIAL1 : SINGLE,
m_flRecoilIndex );
DoFireEffects();
#ifdef IRONSIGHT
#ifdef CLIENT_DLL
if ( GetIronSightController() )
{
GetIronSightController()->IncreaseDotBlur( RandomFloat( 0.22f, 0.28f ) );
}
#endif
#endif
SetWeaponIdleTime( gpGlobals->curtime + weaponInfo.GetTimeToIdleAfterFire( GetEconItemView() ) );
// update accuracy
m_fAccuracyPenalty += weaponInfo.GetInaccuracyFire( GetEconItemView(), weaponMode );
// table driven recoil
Recoil( weaponMode );
++pPlayer->m_iShotsFired;
m_flRecoilIndex += 1.0f;
--m_iClip1;
return true;
}
bool CWeaponCSBaseGun::IsFullAuto() const
{
if ( BaseClass::IsFullAuto() )
{
return !IsInBurstMode();
}
else
{
return false;
}
}
void CWeaponCSBaseGun::DoFireEffects()
{
if ( IsSilenced() )
return;
CCSPlayer *pPlayer = GetPlayerOwner();
if ( pPlayer )
pPlayer->DoMuzzleFlash();
}
bool CWeaponCSBaseGun::Reload()
{
CCSPlayer *pPlayer = GetPlayerOwner();
if ( !pPlayer )
return false;
if ( GetReserveAmmoCount( AMMO_POSITION_PRIMARY ) <= 0 )
return false;
int iResult = 0;
iResult = DefaultReload( GetMaxClip1(), GetMaxClip2(), m_iReloadActivityIndex );
if ( !iResult )
return false;
pPlayer->SetAnimation( PLAYER_RELOAD );
if ( HasZoom() )
{
m_zoomLevel = 0;
m_weaponMode = Primary_Mode;
}
if ( pPlayer->GetFOV() != pPlayer->GetDefaultFOV() && pPlayer->m_bIsScoped)
{
pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.0f );
pPlayer->m_bIsScoped = false;
}
pPlayer->m_iShotsFired = 0;
m_flRecoilIndex += 1.0f;
return BaseClass::Reload();
}
void CWeaponCSBaseGun::WeaponIdle()
{
if (m_flTimeWeaponIdle > gpGlobals->curtime)
return;
// only idle if the slid isn't back
if ( m_iClip1 != 0 )
{
SetWeaponIdleTime( gpGlobals->curtime + GetCSWpnData().GetIdleInterval( GetEconItemView() ) );
//silencers are bodygroups, so there is no longer a silencer-specific idle.
SendWeaponAnim( ACT_VM_IDLE );
}
}
bool CWeaponCSBaseGun::Holster( CBaseCombatWeapon *pSwitchingTo )
{
// re-deploying the weapon is punishment enough for canceling a silencer attach/detach before completion
if ( (GetActivity() == ACT_VM_ATTACH_SILENCER && m_bSilencerOn == false) ||
(GetActivity() == ACT_VM_DETACH_SILENCER && m_bSilencerOn == true ) )
{
m_flDoneSwitchingSilencer = gpGlobals->curtime;
m_flNextSecondaryAttack = gpGlobals->curtime;
m_flNextPrimaryAttack = gpGlobals->curtime;
}
if ( HasZoom() )
{
m_zoomLevel = 0;
m_weaponMode = Primary_Mode;
}
// not sure we want to fully support animation cancelling
if ( m_bInReload && !m_bReloadVisuallyComplete )
{
m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime;
}
return BaseClass::Holster(pSwitchingTo);
}
bool CWeaponCSBaseGun::Deploy()
{
// don't allow weapon switching to shortcut cycle time (quickswitch exploit)
float fOldNextPrimaryAttack = m_flNextPrimaryAttack;
float fOldNextSecondaryAttack = m_flNextSecondaryAttack;
m_flDoneSwitchingSilencer = 0.0f;
m_iBurstShotsRemaining = 0;
m_fNextBurstShot = 0.0f;
if ( !BaseClass::Deploy() )
return false;
if ( HasZoom() )
{
m_zoomLevel = 0;
m_weaponMode = Primary_Mode;
}
if ( IsRevolver() )
{
m_weaponMode = Secondary_Mode;
}
m_flNextPrimaryAttack = Max( m_flNextPrimaryAttack.Get(), fOldNextPrimaryAttack );
m_flNextSecondaryAttack = Max( m_flNextSecondaryAttack.Get(), fOldNextSecondaryAttack );
return true;
}
bool CWeaponCSBaseGun::HasZoom()
{
return GetZoomLevels() != 0;
}
#ifdef CLIENT_DLL
const char* CWeaponCSBaseGun::GetMuzzleFlashEffectName_1stPerson( void )
{
if ( HasSilencer() && IsSilenced() )
{
return GetCSWpnData().GetMuzzleFlashEffectName_1stPersonAlt( GetEconItemView() );
}
else
{
return GetCSWpnData().GetMuzzleFlashEffectName_1stPerson( GetEconItemView() );
}
}
const char* CWeaponCSBaseGun::GetMuzzleFlashEffectName_3rdPerson( void )
{
if ( HasSilencer() && IsSilenced() )
{
return GetCSWpnData().GetMuzzleFlashEffectName_3rdPersonAlt( GetEconItemView() );
}
else
{
return GetCSWpnData().GetMuzzleFlashEffectName_3rdPerson( GetEconItemView() );
}
}
#endif
CCSWeaponInfo const & CWeaponCSBaseGun::GetCSWpnData() const
{
if ( m_pWeaponInfo != NULL )
return *m_pWeaponInfo;
else
return BaseClass::GetCSWpnData();
}
bool CWeaponCSBaseGun::IsInBurstMode() const
{
return m_bBurstMode;
}
bool CWeaponCSBaseGun::IsZoomed( void ) const
{
return ( m_zoomLevel > 0 );
}