summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weapon_sniperrifle.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_weapon_sniperrifle.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/tf_weapon_sniperrifle.cpp')
-rw-r--r--game/shared/tf/tf_weapon_sniperrifle.cpp2271
1 files changed, 2271 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_sniperrifle.cpp b/game/shared/tf/tf_weapon_sniperrifle.cpp
new file mode 100644
index 0000000..40f095f
--- /dev/null
+++ b/game/shared/tf/tf_weapon_sniperrifle.cpp
@@ -0,0 +1,2271 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: TF Sniper Rifle
+//
+//=============================================================================//
+#include "cbase.h"
+#include "tf_fx_shared.h"
+#include "tf_weapon_sniperrifle.h"
+#include "in_buttons.h"
+#include "tf_gamerules.h"
+
+// Client specific.
+#ifdef CLIENT_DLL
+#include "view.h"
+#include "beamdraw.h"
+#include "vgui/ISurface.h"
+#include <vgui/ILocalize.h>
+#include "vgui_controls/Controls.h"
+#include "hud_crosshair.h"
+#include "functionproxy.h"
+#include "materialsystem/imaterialvar.h"
+#include "toolframework_client.h"
+#include "input.h"
+#include "client_virtualreality.h"
+#include "sourcevr/isourcevirtualreality.h"
+
+// forward declarations
+void ToolFramework_RecordMaterialParams( IMaterial *pMaterial );
+#else
+#include "tf_gamerules.h"
+#include "tf_fx.h"
+#endif
+
+#define TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC 50.0
+#define TF_WEAPON_SNIPERRIFLE_UNCHARGE_PER_SEC 75.0
+#define TF_WEAPON_SNIPERRIFLE_DAMAGE_MIN 50
+#define TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX 150
+#define TF_WEAPON_SNIPERRIFLE_RELOAD_TIME 1.5f
+#define TF_WEAPON_SNIPERRIFLE_ZOOM_TIME 0.3f
+
+#define TF_WEAPON_SNIPERRIFLE_NO_CRIT_AFTER_ZOOM_TIME 0.2f
+
+#define SNIPER_DOT_SPRITE_RED "effects/sniperdot_red.vmt"
+#define SNIPER_DOT_SPRITE_BLUE "effects/sniperdot_blue.vmt"
+#define SNIPER_CHARGE_BEAM_RED "tfc_sniper_charge_red"
+#define SNIPER_CHARGE_BEAM_BLUE "tfc_sniper_charge_blue"
+
+#ifdef CLIENT_DLL
+ConVar tf_sniper_fullcharge_bell( "tf_sniper_fullcharge_bell", "0", FCVAR_ARCHIVE );
+#endif
+
+//=============================================================================
+//
+// Weapon Sniper Rifles tables.
+//
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifle, DT_TFSniperRifle )
+
+BEGIN_NETWORK_TABLE_NOBASE( CTFSniperRifle, DT_SniperRifleLocalData )
+#if !defined( CLIENT_DLL )
+ SendPropFloat( SENDINFO(m_flChargedDamage), 0, SPROP_NOSCALE | SPROP_CHANGES_OFTEN ),
+#else
+ RecvPropFloat( RECVINFO(m_flChargedDamage) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_NETWORK_TABLE( CTFSniperRifle, DT_TFSniperRifle )
+#if !defined( CLIENT_DLL )
+ SendPropDataTable( "SniperRifleLocalData", 0, &REFERENCE_SEND_TABLE( DT_SniperRifleLocalData ), SendProxy_SendLocalWeaponDataTable ),
+#else
+ RecvPropDataTable( "SniperRifleLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_SniperRifleLocalData ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFSniperRifle )
+#ifdef CLIENT_DLL
+ DEFINE_PRED_FIELD( m_flUnzoomTime, FIELD_FLOAT, 0 ),
+ DEFINE_PRED_FIELD( m_flRezoomTime, FIELD_FLOAT, 0 ),
+ DEFINE_PRED_FIELD( m_bRezoomAfterShot, FIELD_BOOLEAN, 0 ),
+ DEFINE_PRED_FIELD( m_flChargedDamage, FIELD_FLOAT, 0 ),
+#endif
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle, CTFSniperRifle );
+PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle );
+
+BEGIN_DATADESC( CTFSniperRifle )
+DEFINE_FIELD( m_flUnzoomTime, FIELD_FLOAT ),
+DEFINE_FIELD( m_flRezoomTime, FIELD_FLOAT ),
+DEFINE_FIELD( m_bRezoomAfterShot, FIELD_BOOLEAN ),
+DEFINE_FIELD( m_flChargedDamage, FIELD_FLOAT ),
+END_DATADESC()
+
+//=============================================================================
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifleDecap, DT_TFSniperRifleDecap )
+
+BEGIN_NETWORK_TABLE( CTFSniperRifleDecap, DT_TFSniperRifleDecap )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFSniperRifleDecap )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle_decap, CTFSniperRifleDecap );
+PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle_decap );
+
+//=============================================================================
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifleClassic, DT_TFSniperRifleClassic )
+
+BEGIN_NETWORK_TABLE( CTFSniperRifleClassic, DT_TFSniperRifleClassic )
+#if !defined( CLIENT_DLL )
+ SendPropBool( SENDINFO(m_bCharging) ),
+#else
+ RecvPropBool( RECVINFO(m_bCharging) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFSniperRifleClassic )
+#ifdef CLIENT_DLL
+ DEFINE_PRED_FIELD( m_bCharging, FIELD_BOOLEAN, 0 ),
+#endif
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle_classic, CTFSniperRifleClassic );
+PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle_classic );
+//=============================================================================
+
+#ifdef STAGING_ONLY
+
+IMPLEMENT_NETWORKCLASS_ALIASED( TFSniperRifleRevolver, DT_TFSniperRifleRevolver )
+
+BEGIN_NETWORK_TABLE( CTFSniperRifleRevolver, DT_TFSniperRifleRevolver )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFSniperRifleRevolver )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( tf_weapon_sniperrifle_revolver, CTFSniperRifleRevolver );
+PRECACHE_WEAPON_REGISTER( tf_weapon_sniperrifle_revolver );
+
+#endif // STAGING_ONLY
+
+//=============================================================================
+//
+// Weapon Sniper Rifles functions.
+//
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+CTFSniperRifle::CTFSniperRifle()
+{
+// Server specific.
+#ifdef GAME_DLL
+ m_hSniperDot = NULL;
+#else
+ m_bPlayedBell = false;
+#endif
+
+ m_bCurrentShotIsHeadshot = false;
+ m_flChargedDamage = 0.0f;
+ m_flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
+
+ m_bWasAimedAtEnemy = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+CTFSniperRifle::~CTFSniperRifle()
+{
+// Server specific.
+#ifdef GAME_DLL
+ DestroySniperDot();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::Spawn()
+{
+ m_iAltFireHint = HINT_ALTFIRE_SNIPERRIFLE;
+ BaseClass::Spawn();
+
+ ResetTimers();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::Precache()
+{
+ BaseClass::Precache();
+ PrecacheModel( SNIPER_DOT_SPRITE_RED );
+ PrecacheModel( SNIPER_DOT_SPRITE_BLUE );
+
+ PrecacheScriptSound( "doomsday.warhead" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ResetTimers( void )
+{
+ m_flUnzoomTime = -1;
+ m_flRezoomTime = -1;
+ m_bRezoomAfterShot = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::Reload( void )
+{
+ // We currently don't reload.
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::CanHolster( void ) const
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer )
+ {
+ // TF_COND_MELEE_ONLY need to be able to immediately holster and switch to melee weapon
+ if ( pPlayer->m_Shared.InCond( TF_COND_MELEE_ONLY ) )
+ return true;
+
+ // don't allow us to holster this weapon if we're in the process of zooming and
+ // we've just fired the weapon (next primary attack is only 1.5 seconds after firing)
+ if ( ( pPlayer->GetFOV() < pPlayer->GetDefaultFOV() ) && ( m_flNextPrimaryAttack > gpGlobals->curtime ) )
+ return false;
+ }
+
+ return BaseClass::CanHolster();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+// Server specific.
+#ifdef GAME_DLL
+ // Destroy the sniper dot.
+ DestroySniperDot();
+#endif
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ {
+ ZoomOut();
+ }
+
+ m_flChargedDamage = 0.0f;
+#ifdef CLIENT_DLL
+ m_bPlayedBell = false;
+#endif
+ ResetTimers();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+void CTFSniperRifle::WeaponReset( void )
+{
+ BaseClass::WeaponReset();
+
+ ZoomOut();
+
+ m_bCurrentShotIsHeadshot = false;
+ m_flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::OwnerCanJump( void )
+{
+ return gpGlobals->curtime > m_flUnzoomTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::HandleZooms( void )
+{
+ // Get the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Handle the zoom when taunting.
+ if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) )
+ {
+ ToggleZoom();
+ }
+
+ //Don't rezoom in the middle of a taunt.
+ ResetTimers();
+ }
+
+ if ( m_flUnzoomTime > 0 && gpGlobals->curtime > m_flUnzoomTime )
+ {
+ if ( m_bRezoomAfterShot )
+ {
+ ZoomOutIn();
+ m_bRezoomAfterShot = false;
+ }
+ else
+ {
+ ZoomOut();
+ }
+
+ m_flUnzoomTime = -1;
+ }
+
+ if ( m_flRezoomTime > 0 )
+ {
+ if ( gpGlobals->curtime > m_flRezoomTime )
+ {
+ ZoomIn();
+ m_flRezoomTime = -1;
+ }
+ }
+
+ if ( ( pPlayer->m_nButtons & IN_ATTACK2 ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
+ {
+ // If we're in the process of rezooming, just cancel it
+ if ( m_flRezoomTime > 0 || m_flUnzoomTime > 0 )
+ {
+ // Prevent them from rezooming in less time than they would have
+ m_flNextSecondaryAttack = m_flRezoomTime + TF_WEAPON_SNIPERRIFLE_ZOOM_TIME;
+ m_flRezoomTime = -1;
+ }
+ else
+ {
+ Zoom();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ItemPostFrame( void )
+{
+ // If we're lowered, we're not allowed to fire
+ if ( m_bLowered )
+ return;
+
+ // Get the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !CanAttack() )
+ {
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ }
+ return;
+ }
+
+ HandleZooms();
+
+#ifdef GAME_DLL
+ // Update the sniper dot position if we have one
+ if ( m_hSniperDot )
+ {
+ UpdateSniperDot();
+ }
+#endif
+
+ // Start charging when we're zoomed in, and allowed to fire
+ if ( pPlayer->m_Shared.IsJumping() )
+ {
+ // Unzoom if we're jumping
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ }
+
+ m_flChargedDamage = 0.0f;
+ m_bRezoomAfterShot = false;
+ m_flRezoomTime = -1.f;
+ }
+ else if ( m_flNextSecondaryAttack <= gpGlobals->curtime )
+ {
+ // Don't start charging in the time just after a shot before we unzoom to play rack anim.
+ if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) && !m_bRezoomAfterShot )
+ {
+ float fSniperRifleChargePerSec = m_flChargePerSec;
+ ApplyChargeSpeedModifications( fSniperRifleChargePerSec );
+ fSniperRifleChargePerSec += SniperRifleChargeRateMod();
+
+ // we don't want sniper charge rate to go too high.
+ fSniperRifleChargePerSec = clamp( fSniperRifleChargePerSec, 0, 2.f * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC );
+
+ m_flChargedDamage = MIN( m_flChargedDamage + gpGlobals->frametime * fSniperRifleChargePerSec, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
+
+#ifdef CLIENT_DLL
+ // play the recharged bell if we're fully charged
+ if ( IsFullyCharged() && !m_bPlayedBell )
+ {
+ m_bPlayedBell = true;
+ if ( tf_sniper_fullcharge_bell.GetBool() )
+ {
+ C_TFPlayer::GetLocalTFPlayer()->EmitSound( "TFPlayer.ReCharged" );
+ }
+ }
+#endif
+ }
+ else
+ {
+ m_flChargedDamage = MAX( 0, m_flChargedDamage - gpGlobals->frametime * TF_WEAPON_SNIPERRIFLE_UNCHARGE_PER_SEC );
+ }
+ }
+
+ // Fire.
+ if ( pPlayer->m_nButtons & IN_ATTACK )
+ {
+ Fire( pPlayer );
+ }
+
+ // Idle.
+ if ( !( ( pPlayer->m_nButtons & IN_ATTACK) || ( pPlayer->m_nButtons & IN_ATTACK2 ) ) )
+ {
+ // No fire buttons down or reloading
+ if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) )
+ {
+ WeaponIdle();
+ }
+ }
+
+ // Sniper Rage (Hitman's heatmaker)
+ // Activate on 'R'
+ // no longer need full charge
+ if ( (pPlayer->m_nButtons & IN_RELOAD ) && pPlayer->m_Shared.GetRageMeter() > 1.0f )
+ {
+ int iBuffType = 0;
+ CALL_ATTRIB_HOOK_INT( iBuffType, set_buff_type );
+ if ( iBuffType > 0 )
+ {
+ pPlayer->m_Shared.ActivateRageBuff( pPlayer, iBuffType );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::PlayWeaponShootSound( void )
+{
+ if ( TFGameRules()->GameModeUsesUpgrades() )
+ {
+ PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
+ }
+
+ if ( !IsFullyCharged() )
+ {
+ float flDamageBonus = 1.0f;
+ CALL_ATTRIB_HOOK_FLOAT( flDamageBonus, sniper_full_charge_damage_bonus );
+ if ( flDamageBonus > 1.0f )
+ {
+ WeaponSound( SPECIAL3 );
+ return;
+ }
+ }
+
+ BaseClass::PlayWeaponShootSound();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::Lower( void )
+{
+ if ( BaseClass::Lower() )
+ {
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Secondary attack.
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::Zoom( void )
+{
+ // Don't allow the player to zoom in while jumping
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer && pPlayer->m_Shared.IsJumping() )
+ {
+ if ( pPlayer->GetFOV() >= 75 )
+ return;
+ }
+
+ ToggleZoom();
+
+ // at least 0.1 seconds from now, but don't stomp a previous value
+ m_flNextPrimaryAttack = MAX( m_flNextPrimaryAttack, gpGlobals->curtime + 0.1 );
+ m_flNextSecondaryAttack = gpGlobals->curtime + TF_WEAPON_SNIPERRIFLE_ZOOM_TIME;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ZoomOutIn( void )
+{
+ ZoomOut();
+
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer && pPlayer->ShouldAutoRezoom() )
+ {
+ float flRezoomDelay = 0.9f;
+ if ( !UsesClipsForAmmo1() )
+ {
+ // Since sniper rifles don't actually use clips the fast reload hook also affects unzoom and zoom delays
+ ApplyScopeSpeedModifications( flRezoomDelay );
+ }
+ m_flRezoomTime = gpGlobals->curtime + flRezoomDelay;
+ }
+ else
+ {
+ m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ZoomIn( void )
+{
+ // Start aiming.
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
+ return;
+
+ BaseClass::ZoomIn();
+
+ pPlayer->m_Shared.AddCond( TF_COND_AIMING );
+ pPlayer->TeamFortress_SetSpeed();
+
+#ifdef GAME_DLL
+ // Create the sniper dot.
+ CreateSniperDot();
+ pPlayer->ClearExpression();
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::IsZoomed( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+
+ if ( pPlayer )
+ {
+ return pPlayer->m_Shared.InCond( TF_COND_ZOOMED );
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Have we been zoomed in long enough for our shot to do max damage
+//
+bool CTFSniperRifle::IsFullyCharged( void ) const
+{
+ return m_flChargedDamage >= TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ZoomOut( void )
+{
+ BaseClass::ZoomOut();
+
+ // Stop aiming
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
+ pPlayer->TeamFortress_SetSpeed();
+
+#ifdef GAME_DLL
+ // Destroy the sniper dot.
+ DestroySniperDot();
+ pPlayer->ClearExpression();
+#endif
+
+ // if we are thinking about zooming, cancel it
+ m_flUnzoomTime = -1;
+ m_flRezoomTime = -1;
+ m_bRezoomAfterShot = false;
+ m_flChargedDamage = 0.0f;
+
+#ifdef CLIENT_DLL
+ m_bPlayedBell = false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ApplyScopeSpeedModifications( float &flBaseRef )
+{
+ CALL_ATTRIB_HOOK_FLOAT( flBaseRef, fast_reload );
+
+ // Prototype hack
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE || pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
+ {
+ flBaseRef *= 0.5f;
+ }
+ else if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_KING || pPlayer->m_Shared.InCond( TF_COND_KING_BUFFED ) )
+ {
+ flBaseRef *= 0.75f;
+ }
+
+ int iMaster = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iMaster, ability_master_sniper );
+ if ( iMaster )
+ {
+ flBaseRef *= RemapValClamped( iMaster, 1, 2, 0.6f, 0.3f );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ApplyChargeSpeedModifications( float &flBaseRef )
+{
+ CALL_ATTRIB_HOOK_FLOAT( flBaseRef, mult_sniper_charge_per_sec );
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ Vector vForward;
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vForward );
+
+ Vector vShootPos = pPlayer->Weapon_ShootPosition();
+ trace_t tr;
+ UTIL_TraceLine( vShootPos, vShootPos + vForward * m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flRange, MASK_BLOCKLOS_AND_NPCS, pPlayer, COLLISION_GROUP_NONE, &tr );
+
+ CTFPlayer *pTarget = ToTFPlayer( tr.m_pEnt );
+ if ( pTarget && pTarget->IsAlive() && pTarget->GetTeamNumber() != pPlayer->GetTeamNumber() &&
+ !( pTarget->m_Shared.IsStealthed() && !pTarget->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) ) )
+ {
+ CALL_ATTRIB_HOOK_FLOAT( flBaseRef, mult_sniper_charge_per_sec_with_enemy_under_crosshair );
+
+ int nBeep = 0;
+ CALL_ATTRIB_HOOK_FLOAT( nBeep, sniper_beep_with_enemy_under_crosshair );
+
+ if ( nBeep > 0 && !m_bWasAimedAtEnemy )
+ {
+ pPlayer->EmitSound( "doomsday.warhead" );
+ }
+
+ m_bWasAimedAtEnemy = true;
+ }
+ else
+ {
+ m_bWasAimedAtEnemy = false;
+ }
+
+ if ( pPlayer && ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION || pPlayer->m_Shared.GetCarryingRuneType() == RUNE_HASTE ) )
+ {
+ flBaseRef *= 3.0f;
+ }
+ else if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_KING || pPlayer->m_Shared.InCond( TF_COND_KING_BUFFED ) )
+ {
+ flBaseRef *= 1.5f;
+ }
+
+
+ // Prototype hack
+ int iMaster = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iMaster, ability_master_sniper );
+ if ( iMaster )
+ {
+ flBaseRef *= RemapValClamped( iMaster, 1, 2, 1.5f, 3.f );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::MustBeZoomedToFire( void )
+{
+ int iModOnlyFireZoomed = 0;
+ CALL_ATTRIB_HOOK_INT( iModOnlyFireZoomed, sniper_only_fire_zoomed );
+ return ( iModOnlyFireZoomed != 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::HandleNoScopeFireDeny( void )
+{
+ if ( m_flNextEmptySoundTime < gpGlobals->curtime )
+ {
+ WeaponSound( SPECIAL2 );
+
+#ifdef CLIENT_DLL
+ ParticleProp()->Init( this );
+ ParticleProp()->Create( "dxhr_sniper_fizzle", PATTACH_POINT_FOLLOW, "muzzle" );
+#endif
+
+ m_flNextEmptySoundTime = gpGlobals->curtime + 0.5;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ETFDmgCustom CTFSniperRifle::GetPenetrateType() const
+{
+ if ( IsFullyCharged() )
+ {
+ int iPenetrate = 0;
+ CALL_ATTRIB_HOOK_INT( iPenetrate, sniper_penetrate_players_when_charged );
+ if ( iPenetrate > 0 )
+ return TF_DMG_CUSTOM_PENETRATE_ALL_PLAYERS;
+ }
+
+ return BaseClass::GetPenetrateType();
+}
+
+#ifdef SIXENSE
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFSniperRifle::GetRezoomTime() const
+{
+ return m_flRezoomTime;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::Fire( CTFPlayer *pPlayer )
+{
+ // Check the ammo. We don't use clip ammo, check the primary ammo type.
+ if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
+ {
+ HandleFireOnEmpty();
+ return;
+ }
+
+ // Some weapons can only fire while zoomed
+ if ( MustBeZoomedToFire() )
+ {
+ if ( !IsZoomed() )
+ {
+ HandleNoScopeFireDeny();
+ return;
+ }
+ }
+
+ if ( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ // Fire the sniper shot.
+ PrimaryAttack();
+
+ if ( IsZoomed() )
+ {
+ // If we have more bullets, zoom out, play the bolt animation and zoom back in
+ if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
+ {
+ // do not zoom out if we're under rage or about to enter it
+ if ( !( pPlayer->m_Shared.InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) ) )
+ {
+ float flUnzoomDelay = 0.5f;
+ if ( !UsesClipsForAmmo1() )
+ {
+ // Since sniper rifles don't actually use clips the fast reload hook also affects unzoom and zoom delays
+ ApplyScopeSpeedModifications( flUnzoomDelay );
+ }
+ SetRezoom( true, flUnzoomDelay ); // zoom out in 0.5 seconds, then rezoom
+ }
+ }
+ else
+ {
+ //just zoom out
+ SetRezoom( false, 0.5f ); // just zoom out in 0.5 seconds
+ }
+ }
+ else
+ {
+ float flZoomDelay = SequenceDuration();
+
+ // Since sniper rifles don't actually use clips the fast reload hook also affects zoom delays
+ ApplyScopeSpeedModifications( flZoomDelay );
+
+ // Prevent primary fire preventing zooms
+ m_flNextSecondaryAttack = gpGlobals->curtime + flZoomDelay;
+ }
+
+ m_flChargedDamage = 0.0f;
+
+#ifdef GAME_DLL
+ if ( m_hSniperDot )
+ {
+ m_hSniperDot->ResetChargeTime();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::SetRezoom( bool bRezoom, float flDelay )
+{
+ m_flUnzoomTime = gpGlobals->curtime + flDelay;
+
+ m_bRezoomAfterShot = bRezoom;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CTFSniperRifle::GetProjectileDamage( void )
+{
+ float flDamage = MAX( m_flChargedDamage, TF_WEAPON_SNIPERRIFLE_DAMAGE_MIN );
+
+ float flDamageMod = 1.f;
+ CALL_ATTRIB_HOOK_FLOAT( flDamageMod, mult_dmg );
+ flDamage *= flDamageMod;
+
+
+ if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+
+ if ( pPlayer && pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
+ {
+ flDamage *= 2.f;
+ }
+ }
+
+ if ( IsFullyCharged() )
+ {
+ CALL_ATTRIB_HOOK_FLOAT( flDamage, sniper_full_charge_damage_bonus );
+ }
+
+ return flDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFSniperRifle::GetDamageType( void ) const
+{
+ // Only do hit location damage if we're zoomed
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ return BaseClass::GetDamageType();
+
+ int nDamageType = BaseClass::GetDamageType() & ~DMG_USE_HITLOCATIONS;
+
+ return nDamageType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::CreateSniperDot( void )
+{
+// Server specific.
+#ifdef GAME_DLL
+
+ // Check to see if we have already been created?
+ if ( m_hSniperDot )
+ return;
+
+ // Get the owning player (make sure we have one).
+ CBaseCombatCharacter *pPlayer = GetOwner();
+ if ( !pPlayer )
+ return;
+
+ // Create the sniper dot, but do not make it visible yet.
+ m_hSniperDot = CSniperDot::Create( GetAbsOrigin(), pPlayer, true );
+ m_hSniperDot->ChangeTeam( pPlayer->GetTeamNumber() );
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::DestroySniperDot( void )
+{
+// Server specific.
+#ifdef GAME_DLL
+
+ // Destroy the sniper dot.
+ if ( m_hSniperDot )
+ {
+ UTIL_Remove( m_hSniperDot );
+ m_hSniperDot = NULL;
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::UpdateSniperDot( void )
+{
+// Server specific.
+#ifdef GAME_DLL
+
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Get the start and endpoints.
+ Vector vecMuzzlePos = pPlayer->Weapon_ShootPosition();
+ Vector forward;
+ pPlayer->EyeVectors( &forward );
+ Vector vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
+
+ trace_t trace;
+ UTIL_TraceLine( vecMuzzlePos, vecEndPos, ( MASK_SHOT & ~CONTENTS_WINDOW ), GetOwner(), COLLISION_GROUP_NONE, &trace );
+
+ // Update the sniper dot.
+ if ( m_hSniperDot )
+ {
+ CBaseEntity *pEntity = NULL;
+ if ( trace.DidHitNonWorldEntity() )
+ {
+ pEntity = trace.m_pEnt;
+ if ( !pEntity || !pEntity->m_takedamage )
+ {
+ pEntity = NULL;
+ }
+ }
+
+ m_hSniperDot->Update( pEntity, trace.endpos, trace.plane.normal );
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::CanFireCriticalShot( bool bIsHeadshot )
+{
+ m_bCurrentAttackIsCrit = false;
+ m_bCurrentShotIsHeadshot = false;
+
+ if ( !BaseClass::CanFireCriticalShot( bIsHeadshot ) )
+ return false;
+
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer && pPlayer->m_Shared.IsCritBoosted() )
+ {
+ m_bCurrentShotIsHeadshot = bIsHeadshot;
+ return true;
+ }
+
+ // If we don't auto crit on a headshot, use standard criteria to determine other crits.
+ if ( GetRifleType() == RIFLE_JARATE )
+ {
+ return false; // Never auto crit on headshot.
+ }
+
+ // can only fire a crit shot if this is a headshot, unless we're critboosted
+ if ( !bIsHeadshot )
+ return false;
+
+ int iFullChargeHeadShotPenalty = 0;
+ CALL_ATTRIB_HOOK_INT( iFullChargeHeadShotPenalty, sniper_no_headshot_without_full_charge );
+ if ( iFullChargeHeadShotPenalty != 0 )
+ {
+ if ( !IsFullyCharged() )
+ return false;
+ }
+
+ int iCanCritNoScope = 0;
+ CALL_ATTRIB_HOOK_INT( iCanCritNoScope, sniper_crit_no_scope );
+ if ( iCanCritNoScope == 0 )
+ {
+ if ( pPlayer )
+ {
+ // no crits if they're not zoomed
+ if ( pPlayer->GetFOV() >= pPlayer->GetDefaultFOV() )
+ {
+ return false;
+ }
+
+ // no crits for 0.2 seconds after starting to zoom
+ if ( ( gpGlobals->curtime - pPlayer->GetFOVTime() ) < TF_WEAPON_SNIPERRIFLE_NO_CRIT_AFTER_ZOOM_TIME )
+ {
+ return false;
+ }
+ }
+ }
+
+ m_bCurrentAttackIsCrit = true;
+ m_bCurrentShotIsHeadshot = bIsHeadshot;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Our owner was stunned.
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::OnControlStunned( void )
+{
+ BaseClass::OnControlStunned();
+
+ ZoomOut();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFSniperRifle::GetCustomDamageType() const
+{
+ if ( IsJarateRifle() )
+ {
+ return TF_DMG_CUSTOM_PENETRATE_NONBURNING_TEAMMATE;
+ }
+
+ return TF_DMG_CUSTOM_PENETRATE_MY_TEAM;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::Detach( void )
+{
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ }
+
+ BaseClass::Detach();
+}
+
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
+{
+ BaseClass::OnPlayerKill( pVictim, info );
+
+ if ( m_iConsecutiveKills == 3 )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ pPlayer->AwardAchievement( ACHIEVEMENT_TF_SNIPER_RIFLE_NO_MISSING );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::OnBulletFire( int iEnemyPlayersHit )
+{
+ BaseClass::OnBulletFire( iEnemyPlayersHit );
+
+ // Did we completely miss?
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if( iEnemyPlayersHit == 0 && pPlayer && pPlayer->m_Shared.InCond( TF_COND_AIMING ) )
+ {
+ EconEntity_OnOwnerKillEaterEventNoPartner( assert_cast<CEconEntity *>( this ), pPlayer, kKillEaterEvent_NEGATIVE_SniperShotsMissed );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifle::ExplosiveHeadShot( CTFPlayer *pAttacker, CTFPlayer *pVictim )
+{
+ if ( !pAttacker )
+ return;
+
+ if ( !pVictim )
+ return;
+
+ int iExplosiveShot = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER ( pAttacker, iExplosiveShot, explosive_sniper_shot );
+
+ // Stun the source
+ float flStunDuration = 1.f + ( ( (float)iExplosiveShot - 1.f ) * 0.5f );
+ float flStunAmt = pVictim->IsMiniBoss() ? 0.5f : RemapValClamped( iExplosiveShot, 1, 3, 0.5f, 0.8f );
+ pVictim->m_Shared.StunPlayer( flStunDuration, flStunAmt, TF_STUN_MOVEMENT, pAttacker );
+
+ // Generate an explosion and look for nearby bots
+ float flDmgRange = 125.f + iExplosiveShot * 25.f;
+ float flDmg = 130.f + iExplosiveShot * 20.f;
+
+ CBaseEntity *pObjects[ 32 ];
+ int nCount = UTIL_EntitiesInSphere( pObjects, ARRAYSIZE( pObjects ), pVictim->GetAbsOrigin(), flDmgRange, FL_CLIENT );
+ for ( int i = 0; i < nCount; i++ )
+ {
+ if ( !pObjects[i] )
+ continue;
+
+ if ( !pObjects[i]->IsAlive() )
+ continue;
+
+ if ( pObjects[i] == pVictim )
+ continue;
+
+ if ( pAttacker->InSameTeam( pObjects[i] ) )
+ continue;
+
+ if ( !pVictim->FVisible( pObjects[i], MASK_OPAQUE ) )
+ continue;
+
+ CTFPlayer *pTFPlayer = static_cast<CTFPlayer *>( pObjects[i] );
+ if ( !pTFPlayer )
+ continue;
+
+ if ( pTFPlayer->m_Shared.InCond( TF_COND_PHASE ) || pTFPlayer->m_Shared.InCond( TF_COND_PASSTIME_INTERCEPTION ) )
+ continue;
+
+ if ( pTFPlayer->m_Shared.IsInvulnerable() )
+ continue;
+
+ // Stun
+ flStunAmt = pTFPlayer->IsMiniBoss() ? 0.5f : RemapValClamped( iExplosiveShot, 1, 3, 0.5f, 0.8f );
+ pTFPlayer->m_Shared.StunPlayer( flStunDuration, flStunAmt, TF_STUN_MOVEMENT, pAttacker );
+
+ // DoT
+ pTFPlayer->m_Shared.MakeBleed( pAttacker, this, 0.1f, flDmg );
+
+ // Shoot a beam at them
+ CPVSFilter filter( pTFPlayer->WorldSpaceCenter() );
+ Vector vStart = pVictim->EyePosition();
+ Vector vEnd = pTFPlayer->EyePosition();
+ te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
+ TE_TFParticleEffectComplex( filter, 0.0f, "dxhr_arm_muzzleflash", vStart, QAngle( 0, 0, 0 ), NULL, &controlPoint, pTFPlayer, PATTACH_CUSTOMORIGIN );
+
+ pTFPlayer->EmitSound( "Weapon_Upgrade.ExplosiveHeadshot" );
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::IsJarateRifle( void ) const
+{
+ return GetJarateTimeInternal() > 0.f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFSniperRifle::GetJarateTime( void ) const
+{
+ if ( m_flChargedDamage > 0.f )
+ {
+ return GetJarateTimeInternal();
+ }
+ else
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFSniperRifle::GetJarateTimeInternal( void ) const
+{
+ float flMaxJarateTime = 0.0f;
+ CALL_ATTRIB_HOOK_FLOAT( flMaxJarateTime, jarate_duration );
+ if ( flMaxJarateTime > 0 )
+ {
+ const float flMinJarateTime = 2.f;
+ float flDuration = RemapValClamped( m_flChargedDamage, TF_WEAPON_SNIPERRIFLE_DAMAGE_MIN, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX, flMinJarateTime, flMaxJarateTime );
+ return flDuration;
+ }
+
+ return 0.f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: UI Progress (same as GetProgress() without the division by 100.0f)
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::IsRageFull( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ {
+ return false;
+ }
+
+ return ( pPlayer->m_Shared.GetRageMeter() >= 100.0f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::EffectMeterShouldFlash( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ {
+ return false;
+ }
+
+ if ( pPlayer && ( IsRageFull() || pPlayer->m_Shared.IsRageDraining() ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: UI Progress
+//-----------------------------------------------------------------------------
+float CTFSniperRifle::GetProgress( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ {
+ return 0.f;
+ }
+
+ return pPlayer->m_Shared.GetRageMeter() / 100.0f;
+}
+
+//=============================================================================
+//
+// Client specific functions.
+//
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifle::ShouldEjectBrass()
+{
+ if ( GetJarateTimeInternal() > 0.f )
+ return false;
+ else
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFSniperRifle::GetHUDDamagePerc( void )
+{
+ return (m_flChargedDamage / TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX);
+}
+
+//-----------------------------------------------------------------------------
+// Returns the sniper chargeup from 0 to 1
+//-----------------------------------------------------------------------------
+class CProxySniperRifleCharge : public CResultProxy
+{
+public:
+ void OnBind( void *pC_BaseEntity );
+};
+
+void CProxySniperRifleCharge::OnBind( void *pC_BaseEntity )
+{
+ Assert( m_pResult );
+
+ C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
+
+ if ( GetSpectatorTarget() != 0 && GetSpectatorMode() == OBS_MODE_IN_EYE )
+ {
+ pPlayer = (C_TFPlayer *)UTIL_PlayerByIndex( GetSpectatorTarget() );
+ }
+
+ if ( pPlayer )
+ {
+ CTFSniperRifle *pWeapon = assert_cast<CTFSniperRifle*>(pPlayer->GetActiveTFWeapon());
+ if ( pWeapon )
+ {
+ float flChargeValue = ( ( 1.0 - pWeapon->GetHUDDamagePerc() ) * 0.8 ) + 0.6;
+
+ VMatrix mat, temp;
+
+ Vector2D center( 0.5, 0.5 );
+ MatrixBuildTranslation( mat, -center.x, -center.y, 0.0f );
+
+ // scale
+ {
+ Vector2D scale( 1.0f, 0.25f );
+ MatrixBuildScale( temp, scale.x, scale.y, 1.0f );
+ MatrixMultiply( temp, mat, mat );
+ }
+
+ MatrixBuildTranslation( temp, center.x, center.y, 0.0f );
+ MatrixMultiply( temp, mat, mat );
+
+ // translation
+ {
+ Vector2D translation( 0.0f, flChargeValue );
+ MatrixBuildTranslation( temp, translation.x, translation.y, 0.0f );
+ MatrixMultiply( temp, mat, mat );
+ }
+
+ m_pResult->SetMatrixValue( mat );
+ }
+ }
+
+ if ( ToolsEnabled() )
+ {
+ ToolFramework_RecordMaterialParams( GetMaterial() );
+ }
+}
+
+EXPOSE_INTERFACE( CProxySniperRifleCharge, IMaterialProxy, "SniperRifleCharge" IMATERIAL_PROXY_INTERFACE_VERSION );
+#endif
+
+//=============================================================================
+//
+// Laser Dot functions.
+//
+
+IMPLEMENT_NETWORKCLASS_ALIASED( SniperDot, DT_SniperDot )
+
+BEGIN_NETWORK_TABLE( CSniperDot, DT_SniperDot )
+#ifdef CLIENT_DLL
+ RecvPropFloat( RECVINFO( m_flChargeStartTime ) ),
+#else
+ SendPropTime( SENDINFO( m_flChargeStartTime ) ),
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( env_sniperdot, CSniperDot );
+
+BEGIN_DATADESC( CSniperDot )
+DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ),
+DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+CSniperDot::CSniperDot( void )
+{
+ m_vecSurfaceNormal.Init();
+ m_hTargetEnt = NULL;
+
+#ifdef CLIENT_DLL
+ m_hSpriteMaterial = NULL;
+ m_laserBeamEffect = NULL;
+#endif
+
+
+ ResetChargeTime();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+CSniperDot::~CSniperDot( void )
+{
+#ifdef CLIENT_DLL
+ if ( m_laserBeamEffect )
+ {
+ ParticleProp()->StopEmissionAndDestroyImmediately( m_laserBeamEffect );
+
+ m_laserBeamEffect = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &origin -
+// Output : CSniperDot
+//-----------------------------------------------------------------------------
+CSniperDot *CSniperDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
+{
+// Client specific.
+#ifdef CLIENT_DLL
+
+ return NULL;
+
+// Server specific.
+#else
+
+ // Create the sniper dot entity.
+ CSniperDot *pDot = static_cast<CSniperDot*>( CBaseEntity::Create( "env_sniperdot", origin, QAngle( 0.0f, 0.0f, 0.0f ) ) );
+ if ( !pDot )
+ return NULL;
+
+ //Create the graphic
+ pDot->SetMoveType( MOVETYPE_NONE );
+ pDot->AddSolidFlags( FSOLID_NOT_SOLID );
+ pDot->AddEffects( EF_NOSHADOW );
+ UTIL_SetSize( pDot, -Vector( 4.0f, 4.0f, 4.0f ), Vector( 4.0f, 4.0f, 4.0f ) );
+
+ // Set owner.
+ pDot->SetOwnerEntity( pOwner );
+
+ // Force updates even though we don't have a model.
+ pDot->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
+
+
+ return pDot;
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSniperDot::Update( CBaseEntity *pTarget, const Vector &vecOrigin, const Vector &vecNormal )
+{
+ SetAbsOrigin( vecOrigin );
+ m_vecSurfaceNormal = vecNormal;
+ m_hTargetEnt = pTarget;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector CSniperDot::GetChasePosition()
+{
+ return GetAbsOrigin() - m_vecSurfaceNormal * 10;
+}
+
+//=============================================================================
+//
+// Client specific functions.
+//
+#ifdef CLIENT_DLL
+
+bool CSniperDot::GetRenderingPositions( C_TFPlayer *pPlayer, Vector &vecAttachment, Vector &vecEndPos, float &flSize )
+{
+ if ( !pPlayer )
+ return false;
+
+ // Get the sprite rendering position.
+ flSize = 6.0;
+ bool bScaleSizeByDistance = false;
+
+ const float c_fMaxSizeDistVR = 384.0f;
+ const float c_flMaxSizeDistUnzoomed = 200.0f;
+
+ if ( !pPlayer->IsDormant() )
+ {
+ Vector vecDir;
+ QAngle angles;
+
+ float flDist = MAX_TRACE_LENGTH;
+
+ // Always draw the dot in front of our faces when in first-person.
+ if ( pPlayer->IsLocalPlayer() )
+ {
+ // Take our view position and orientation
+ vecAttachment = CurrentViewOrigin();
+ vecDir = CurrentViewForward();
+
+ // Clamp the forward distance for the sniper's firstperson
+ flDist = c_fMaxSizeDistVR;
+ flSize = 2.0;
+
+ // Make the dot bigger when charging and not zoomed in (The Classic)
+ if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) && !pPlayer->m_Shared.InCond( TF_COND_ZOOMED ))
+ {
+ flSize = 4.0f;
+ bScaleSizeByDistance = true;
+ }
+
+ if ( UseVR() )
+ {
+ // The view direction is not exactly the same as the weapon direction because of stereo, calibration, etc.
+ g_ClientVirtualReality.OverrideWeaponHudAimVectors ( &vecAttachment, &vecDir );
+
+ // No clamping, thanks - we need the distance to be correct so that
+ // vergence works properly, and we'll scale the size up accordingly.
+ flDist = MAX_TRACE_LENGTH;
+ bScaleSizeByDistance = true;
+ }
+ }
+ else
+ {
+ // Take the owning player eye position and direction.
+ vecAttachment = pPlayer->EyePosition();
+ QAngle anglesEye = pPlayer->EyeAngles();
+ AngleVectors( anglesEye, &vecDir );
+ }
+
+ trace_t tr;
+ CTraceFilterIgnoreFriendlyCombatItems filter( pPlayer, COLLISION_GROUP_NONE, pPlayer->GetTeamNumber() );
+ UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * flDist ), MASK_SHOT, &filter, &tr );
+
+ // Backup off the hit plane, towards the source
+ vecEndPos = tr.endpos + vecDir * -4;
+ if ( UseVR() )
+ {
+ float fDist = ( vecEndPos - vecAttachment ).Length();
+ if ( fDist > c_fMaxSizeDistVR )
+ {
+ // Scale the dot up so it's still visible in first person.
+ flSize *= ( fDist * ( 1.0f / c_fMaxSizeDistVR ) );
+ }
+ }
+ else if ( bScaleSizeByDistance )
+ {
+ float fDist = ( vecEndPos - vecAttachment ).Length();
+ if ( fDist > c_flMaxSizeDistUnzoomed )
+ {
+ // Scale the dot up so it's still visible in first person.
+ flSize *= ( fDist * ( 1.0f / c_flMaxSizeDistUnzoomed ) );
+ }
+ }
+ }
+ else
+ {
+ // Just use our position if we can't predict it otherwise.
+ vecAttachment = GetAbsOrigin();
+ vecEndPos = GetAbsOrigin();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// TFTODO: Make the sniper dot get brighter the more damage it will do.
+//-----------------------------------------------------------------------------
+int CSniperDot::DrawModel( int flags )
+{
+ // Get the owning player.
+ C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( !pPlayer )
+ return -1;
+
+ Vector vecAttachement;
+ Vector vecEndPos;
+ float flSize;
+
+ if ( !GetRenderingPositions( pPlayer, vecAttachement, vecEndPos, flSize ) )
+ {
+ return -1;
+ }
+
+ // Draw our laser dot in space.
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_hSpriteMaterial, this );
+
+ CTFWeaponBase *pBaseWeapon = pPlayer->GetActiveTFWeapon();
+ CTFSniperRifle *pWeapon = dynamic_cast< CTFSniperRifle* >( pBaseWeapon );
+
+ float flLifeTime = gpGlobals->curtime - m_flChargeStartTime;
+ float flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
+ if ( pWeapon )
+ {
+ pWeapon->ApplyChargeSpeedModifications( flChargePerSec );
+ }
+
+ // Sniper Rage
+ if ( pPlayer->m_Shared.InCond( TF_COND_SNIPERCHARGE_RAGE_BUFF ) )
+ {
+ flChargePerSec *= 1.25f;
+ }
+
+ float flStrength;
+
+ if ( pWeapon )
+ {
+ flStrength = pWeapon->GetHUDDamagePerc();
+
+ // FIXME: We should find out what's causing this and fix it.
+ AssertMsg1( flStrength >= ( 0.0f - FLT_EPSILON ) && flStrength <= ( 1.0f + FLT_EPSILON ), "GetHUDDamagePerc returned out of range value: %f", flStrength );
+ flStrength = clamp( flStrength, 0.1f, 1.0f );
+ }
+ else
+ {
+ flStrength = RemapValClamped( flLifeTime, 0.0, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX / flChargePerSec, 0.1f, 1.0f );
+ }
+
+ color32 innercolor = { 255, 255, 255, 255 };
+ color32 outercolor = { 255, 255, 255, 128 };
+
+ DrawSprite( vecEndPos, flSize, flSize, outercolor );
+ DrawSprite( vecEndPos, flSize * flStrength, flSize * flStrength, innercolor );
+
+ // Successful.
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CSniperDot::ShouldDraw( void )
+{
+ if ( IsEffectActive( EF_NODRAW ) )
+ return false;
+
+ // Don't draw the sniper dot when in thirdperson.
+ if ( ::input->CAM_IsThirdPerson() )
+ return false;
+
+ return true;
+}
+
+void CSniperDot::ClientThink( void )
+{
+ // snipers have laser sights in PvE mode
+ if ( TFGameRules()->IsPVEModeActive() && GetTeamNumber() == TF_TEAM_PVE_INVADERS )
+ {
+ C_TFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ if ( !m_laserBeamEffect )
+ {
+ m_laserBeamEffect = ParticleProp()->Create( "laser_sight_beam", PATTACH_ABSORIGIN_FOLLOW );
+ }
+
+ if ( m_laserBeamEffect )
+ {
+ m_laserBeamEffect->SetSortOrigin( m_laserBeamEffect->GetRenderOrigin() );
+ m_laserBeamEffect->SetControlPoint( 2, Vector( 0, 0, 255 ) );
+
+ Vector vecAttachment;
+ Vector vecEndPos;
+ float flSize;
+
+ if ( pPlayer->GetAttachment( "eye_1", vecAttachment ) )
+ {
+ m_laserBeamEffect->SetControlPoint( 1, vecAttachment );
+ }
+ else if ( GetRenderingPositions( pPlayer, vecAttachment, vecEndPos, flSize ) )
+ {
+ m_laserBeamEffect->SetControlPoint( 1, vecAttachment );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CSniperDot::OnDataChanged( DataUpdateType_t updateType )
+{
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ if ( GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ m_hSpriteMaterial.Init( SNIPER_DOT_SPRITE_BLUE, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+ else
+ {
+ m_hSpriteMaterial.Init( SNIPER_DOT_SPRITE_RED, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+#endif // CLIENT_DLL
+
+#ifdef GAME_DLL
+
+void CTFSniperRifleDecap::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
+{
+ BaseClass::OnPlayerKill( pVictim, info );
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer && IsHeadshot( info.GetDamageCustom() ) )
+ {
+ // If we got a headshot kill, increment our number of decapitations.
+ int iDecaps = pPlayer->m_Shared.GetDecapitations() + 1;
+ pPlayer->m_Shared.SetDecapitations( iDecaps );
+ }
+}
+
+#endif // GAME_DLL
+
+static const int MAX_HEAD_BONUS = 6;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFSniperRifleDecap::SniperRifleChargeRateMod()
+{
+ return ( .25f * ( MIN( GetCount(), MAX_HEAD_BONUS ) - 2 ) ) * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFSniperRifleDecap::GetCount( void )
+{
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwnerEntity() );
+ if ( pPlayer )
+ {
+ return pPlayer->m_Shared.GetDecapitations();
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+CTFSniperRifleClassic::CTFSniperRifleClassic()
+{
+ m_bCharging = false;
+#ifdef CLIENT_DLL
+ m_pChargedEffect = NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+CTFSniperRifleClassic::~CTFSniperRifleClassic()
+{
+#ifdef CLIENT_DLL
+ if ( m_pChargedEffect )
+ {
+ ParticleProp()->StopEmissionAndDestroyImmediately( m_pChargedEffect );
+ m_pChargedEffect = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::Precache()
+{
+ BaseClass::Precache();
+ PrecacheParticleSystem( SNIPER_CHARGE_BEAM_RED );
+ PrecacheParticleSystem( SNIPER_CHARGE_BEAM_BLUE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::ZoomOut( void )
+{
+ CTFWeaponBaseGun::ZoomOut(); // intentionally skipping CTFSniperRifle::ZoomOut()
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::ZoomIn( void )
+{
+ // Start aiming.
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
+ return;
+
+ CTFWeaponBaseGun::ZoomIn(); // intentionally skipping CTFSniperRifle::ZoomIn()
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Secondary attack.
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::Zoom( void )
+{
+ ToggleZoom();
+
+ // at least 0.1 seconds from now, but don't stomp a previous value
+ m_flNextSecondaryAttack = gpGlobals->curtime + TF_WEAPON_SNIPERRIFLE_ZOOM_TIME;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::HandleZooms( void )
+{
+ // Get the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Handle the zoom when taunting.
+ if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ return;
+ }
+ }
+
+ if ( ( pPlayer->m_nButtons & IN_ATTACK2 ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
+ {
+ Zoom();
+ }
+}
+
+#ifdef CLIENT_DLL
+void CTFSniperRifleClassic::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ ManageChargeBeam();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::ItemPostFrame( void )
+{
+ // If we're lowered, we're not allowed to fire
+ if ( m_bLowered )
+ return;
+
+ // Get the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !CanAttack() )
+ {
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ }
+ WeaponReset();
+ return;
+ }
+
+ HandleZooms();
+
+#ifdef GAME_DLL
+ // Update the sniper dot position if we have one
+ if ( m_hSniperDot )
+ {
+ UpdateSniperDot();
+ }
+#endif
+
+ if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ WeaponReset();
+ return;
+ }
+
+ if ( ( pPlayer->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
+ {
+ if ( !m_bCharging )
+ {
+ pPlayer->m_Shared.AddCond( TF_COND_AIMING );
+ pPlayer->TeamFortress_SetSpeed();
+
+ m_bCharging = true;
+#ifdef GAME_DLL
+ // Create the sniper dot.
+ CreateSniperDot();
+ pPlayer->ClearExpression();
+#endif
+ }
+
+ float fSniperRifleChargePerSec = m_flChargePerSec;
+ ApplyChargeSpeedModifications( fSniperRifleChargePerSec );
+ fSniperRifleChargePerSec += SniperRifleChargeRateMod();
+
+ // we don't want sniper charge rate to go too high.
+ fSniperRifleChargePerSec = clamp( fSniperRifleChargePerSec, 0, 2.f * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC );
+
+ m_flChargedDamage = MIN( m_flChargedDamage + gpGlobals->frametime * fSniperRifleChargePerSec, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
+
+#ifdef CLIENT_DLL
+ // play the recharged bell if we're fully charged
+ if ( IsFullyCharged() && !m_bPlayedBell )
+ {
+ m_bPlayedBell = true;
+ if ( tf_sniper_fullcharge_bell.GetBool() )
+ {
+ C_TFPlayer::GetLocalTFPlayer()->EmitSound( "TFPlayer.ReCharged" );
+ }
+ }
+#endif
+ }
+ else if ( m_bCharging )
+ {
+ if ( pPlayer->GetGroundEntity() )
+ {
+ Fire( pPlayer );
+ }
+ else
+ {
+ pPlayer->EmitSound( "Player.DenyWeaponSelection" );
+ }
+
+ WeaponReset();
+ }
+ else
+ {
+ // Idle.
+ // No fire buttons down or reloading
+ if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) )
+ {
+ WeaponIdle();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFSniperRifleClassic::GetDamageType( void ) const
+{
+ return CTFWeaponBaseGun::GetDamageType(); // intentionally skipping CTFSniperRifle::GetDamageType()
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifleClassic::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ WeaponReset();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifleClassic::Deploy( void )
+{
+ WeaponReset();
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::WeaponReset( void )
+{
+ m_flChargedDamage = 0.0f;
+ m_bCharging = false;
+#ifdef CLIENT_DLL
+ ManageChargeBeam();
+#endif
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
+ pPlayer->TeamFortress_SetSpeed();
+ }
+#ifdef GAME_DLL
+ // Destroy the sniper dot.
+ DestroySniperDot();
+ if ( pPlayer )
+ {
+ pPlayer->ClearExpression();
+ }
+#else
+ m_bPlayedBell = false;
+#endif
+
+ m_bCurrentShotIsHeadshot = false;
+ m_flChargePerSec = TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC;
+
+ CTFWeaponBase::WeaponReset(); // intentionally skipping CTFSniperRifle::WeaponReset()
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFSniperRifleClassic::Lower( void )
+{
+ if ( BaseClass::Lower() )
+ {
+ WeaponReset();
+ return true;
+ }
+
+ return false;
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::ManageChargeBeam( void )
+{
+ if ( m_bCharging )
+ {
+ if ( !m_pChargedEffect )
+ {
+ m_pChargedEffect = ParticleProp()->Create( ( GetTeamNumber() == TF_TEAM_RED ) ? SNIPER_CHARGE_BEAM_RED : SNIPER_CHARGE_BEAM_BLUE, PATTACH_POINT_FOLLOW, "laser" );
+ }
+ }
+ else
+ {
+ if ( m_pChargedEffect )
+ {
+ ParticleProp()->StopEmissionAndDestroyImmediately( m_pChargedEffect );
+ m_pChargedEffect = NULL;
+ }
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFSniperRifleClassic::Detach( void )
+{
+ WeaponReset();
+ BaseClass::Detach();
+}
+
+#ifdef STAGING_ONLY
+
+// ********************************************************************************************************
+// CTFSniperRifleRevolver
+// ********************************************************************************************************
+void CTFSniperRifleRevolver::PrimaryAttack()
+{
+ BaseClass::PrimaryAttack();
+#ifdef GAME_DLL
+ // Head bob
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer /*&& pPlayer->m_Shared.InCond( TF_COND_ZOOMED )*/ )
+ {
+ float impulse = RandomFloat( -2.0f, -1.0f );
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ impulse = RandomFloat( -0.5f, -0.2f );
+ }
+ pPlayer->ApplyPunchImpulseX( impulse );
+ }
+
+ float flCharge = (m_flChargedDamage / TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX);
+
+ if ( flCharge > 0.99 )
+ {
+ // Only at full charge do you get fast attack speed
+ // reduce the time between attacks
+ float flCurrTime = gpGlobals->curtime;
+ //float flTimeBetweenShots = m_flNextPrimaryAttack - flCurrTime;
+ //float flTime = RemapVal( flCharge, 0.0, 1, flTimeBetweenShots, 0.2 );
+
+ m_flNextPrimaryAttack = flCurrTime + 0.3;
+ }
+#endif
+}
+//-----------------------------------------------------------------------------
+float CTFSniperRifleRevolver::GetProjectileDamage()
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ {
+ float flCharge = ( m_flChargedDamage / TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
+ if ( flCharge > 0.99 )
+ {
+ return 75.0f; // Full Charge dmg bonus is less then the normal one (150)
+ }
+ return 50.0f;
+ }
+ return 40.0;
+}
+//-----------------------------------------------------------------------------
+bool CTFSniperRifleRevolver::Reload( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ // do not reload if zoomed unless you are empty
+ if ( m_iClip1 > 0 && pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ return false;
+
+ bool bReload = CTFWeaponBaseGun::Reload(); // intentionally skipping CTFSniperRifle::Reload().
+ if ( bReload && m_iClip1 <= 0 && m_iClip1 != -1 )
+ {
+ if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ {
+ ZoomOut();
+ m_bRezoomAfterShot = pPlayer->ShouldAutoRezoom();
+ }
+ }
+ return bReload;
+}
+//-----------------------------------------------------------------------------
+void CTFSniperRifleRevolver::ZoomIn( void )
+{
+ // Start aiming.
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ if ( m_iClip1 <= 0 && m_iClip1 != -1 )
+ return;
+
+ pPlayer->m_Shared.AddCond( TF_COND_AIMING );
+ pPlayer->TeamFortress_SetSpeed();
+ m_flChargedDamage = 0;
+
+#ifdef GAME_DLL
+ // Create the sniper dot.
+ CreateSniperDot();
+ pPlayer->ClearExpression();
+#endif
+
+ CTFWeaponBaseGun::ZoomIn(); // intentionally skipping CTFSniperRifle::ZoomIn()
+}
+//-----------------------------------------------------------------------------
+void CTFSniperRifleRevolver::ZoomOut( void )
+{
+ // Start aiming.
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
+ pPlayer->TeamFortress_SetSpeed();
+ m_flChargedDamage = 0;
+
+#ifdef GAME_DLL
+ // Destroy the sniper dot.
+ DestroySniperDot();
+ pPlayer->ClearExpression();
+#endif
+
+ CTFWeaponBaseGun::ZoomOut(); // intentionally skipping CTFSniperRifle::ZoomOut()
+}
+//-----------------------------------------------------------------------------
+void CTFSniperRifleRevolver::ItemPostFrame( void )
+{
+ // If we're lowered, we're not allowed to fire
+ if ( m_bLowered )
+ return;
+
+ // Get the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !CanAttack() )
+ {
+ if ( IsZoomed() )
+ {
+ ToggleZoom();
+ }
+ WeaponReset();
+ }
+
+ if ( m_bRezoomAfterShot && m_iClip1 > 0 )
+ {
+ Zoom();
+ m_bRezoomAfterShot = false;
+ }
+
+ HandleZooms();
+
+#ifdef GAME_DLL
+ // Update the sniper dot position if we have one
+ if ( m_hSniperDot )
+ {
+ UpdateSniperDot();
+ }
+#endif
+
+ if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
+ {
+ WeaponReset();
+ return;
+ }
+
+ // Handle Charge Meter
+ if ( m_flNextSecondaryAttack <= gpGlobals->curtime )
+ {
+ // Don't start charging in the time just after a shot before we unzoom to play rack anim.
+ if ( pPlayer->m_Shared.InCond( TF_COND_AIMING ) && !m_bRezoomAfterShot )
+ {
+ float fSniperRifleChargePerSec = m_flChargePerSec;
+ ApplyChargeSpeedModifications( fSniperRifleChargePerSec );
+ fSniperRifleChargePerSec += SniperRifleChargeRateMod();
+
+ // we don't want sniper charge rate to go too high.
+ fSniperRifleChargePerSec = clamp( fSniperRifleChargePerSec, 0, 2.f * TF_WEAPON_SNIPERRIFLE_CHARGE_PER_SEC );
+
+ m_flChargedDamage = MIN( m_flChargedDamage + gpGlobals->frametime * fSniperRifleChargePerSec, TF_WEAPON_SNIPERRIFLE_DAMAGE_MAX );
+
+#ifdef CLIENT_DLL
+ // play the recharged bell if we're fully charged
+ if ( IsFullyCharged() && !m_bPlayedBell )
+ {
+ m_bPlayedBell = true;
+ if ( tf_sniper_fullcharge_bell.GetBool() )
+ {
+ C_TFPlayer::GetLocalTFPlayer()->EmitSound( "TFPlayer.ReCharged" );
+ }
+ }
+#endif
+ }
+ else
+ {
+ m_flChargedDamage = MAX( 0, m_flChargedDamage - gpGlobals->frametime * TF_WEAPON_SNIPERRIFLE_UNCHARGE_PER_SEC );
+ }
+ }
+
+ return CTFWeaponBaseGun::ItemPostFrame(); // intentionally skipping CTFSniperRifle::ItemPostFrame(). This should just fire the gun
+}
+
+//-----------------------------------------------------------------------------
+bool CTFSniperRifleRevolver::CanFireCriticalShot( bool bIsHeadshot )
+{
+ return CTFSniperRifle::CanFireCriticalShot( bIsHeadshot ); // Skip TFC Sniper Rifle
+}
+
+//-----------------------------------------------------------------------------
+//
+ConVar tf_sniper_bolt_speed( "tf_sniper_bolt_speed", "3000", FCVAR_REPLICATED, "Dev Convar - Speed of projectile for Revolver Sniper");
+ConVar tf_sniper_bolt_gravity( "tf_sniper_bolt_gravity", "0.1", FCVAR_REPLICATED, "Dev Convar - Gravity of projectile for Revolver Sniper");
+float CTFSniperRifleRevolver::GetProjectileSpeed( void )
+{
+ //return 4900.0;
+ return tf_sniper_bolt_speed.GetFloat();
+}
+//-----------------------------------------------------------------------------
+float CTFSniperRifleRevolver::GetProjectileGravity( void )
+{
+ //return 0.1;
+ return tf_sniper_bolt_gravity.GetFloat();
+}
+
+
+#endif // STAGING_ONLY