|
|
//========= 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
|