//========= 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 ); }