summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weaponbase_gun.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_weaponbase_gun.cpp')
-rw-r--r--game/shared/tf/tf_weaponbase_gun.cpp1133
1 files changed, 1133 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weaponbase_gun.cpp b/game/shared/tf/tf_weaponbase_gun.cpp
new file mode 100644
index 0000000..a4c1a13
--- /dev/null
+++ b/game/shared/tf/tf_weaponbase_gun.cpp
@@ -0,0 +1,1133 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Weapon Base Gun
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "tf_weaponbase_gun.h"
+#include "tf_fx_shared.h"
+#include "effect_dispatch_data.h"
+#include "takedamageinfo.h"
+#include "tf_projectile_nail.h"
+#include "tf_weapon_jar.h"
+#include "tf_weapon_flaregun.h"
+#include "tf_projectile_energy_ring.h"
+
+#if !defined( CLIENT_DLL ) // Server specific.
+
+ #include "tf_gamestats.h"
+ #include "tf_player.h"
+ #include "tf_fx.h"
+ #include "te_effect_dispatch.h"
+
+ #include "tf_projectile_flare.h"
+ #include "tf_projectile_rocket.h"
+ #include "tf_projectile_arrow.h"
+ #include "tf_projectile_energy_ball.h"
+ #include "tf_weapon_grenade_pipebomb.h"
+ #include "te.h"
+
+#else // Client specific.
+
+ #include "c_tf_player.h"
+ #include "c_te_effect_dispatch.h"
+ #include "c_tf_gamestats.h"
+
+#endif
+
+//=============================================================================
+//
+// TFWeaponBase Gun tables.
+//
+IMPLEMENT_NETWORKCLASS_ALIASED( TFWeaponBaseGun, DT_TFWeaponBaseGun )
+
+BEGIN_NETWORK_TABLE( CTFWeaponBaseGun, DT_TFWeaponBaseGun )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CTFWeaponBaseGun )
+END_PREDICTION_DATA()
+
+// Server specific.
+#if !defined( CLIENT_DLL )
+BEGIN_DATADESC( CTFWeaponBaseGun )
+DEFINE_THINKFUNC( ZoomOutIn ),
+DEFINE_THINKFUNC( ZoomOut ),
+DEFINE_THINKFUNC( ZoomIn ),
+END_DATADESC()
+#endif
+
+//=============================================================================
+//
+// TFWeaponBase Gun functions.
+//
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+CTFWeaponBaseGun::CTFWeaponBaseGun()
+{
+ m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
+ m_iAmmoToAdd = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::PrimaryAttack( void )
+{
+ float flUberChargeAmmoPerShot = UberChargeAmmoPerShot();
+ if ( flUberChargeAmmoPerShot > 0.0f )
+ {
+ if ( !HasPrimaryAmmo() )
+ return;
+ }
+
+ // Check for ammunition.
+ if ( m_iClip1 <= 0 && m_iClip1 != -1 )
+ return;
+
+ // Are we capable of firing again?
+ if ( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ // Get the player owning the weapon.
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !CanAttack() )
+ return;
+
+ float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flFireDelay, hwn_mult_postfiredelay );
+
+ // Some weapons change fire delay based on player's health
+ float flReducedHealthBonus = 1.0f;
+ CALL_ATTRIB_HOOK_FLOAT( flReducedHealthBonus, mult_postfiredelay_with_reduced_health );
+ if ( flReducedHealthBonus != 1.0f )
+ {
+ flReducedHealthBonus = RemapValClamped( pPlayer->HealthFraction(), 0.2f, 0.9f, flReducedHealthBonus, 1.0f );
+ flFireDelay *= flReducedHealthBonus;
+ }
+
+ if ( pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) )
+ {
+ CALL_ATTRIB_HOOK_FLOAT( flFireDelay, rocketjump_attackrate_bonus );
+ }
+ else
+ {
+ CALL_ATTRIB_HOOK_FLOAT( flFireDelay, mul_nonrocketjump_attackrate );
+ }
+
+ if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
+ {
+ if ( GetOwner() && GetAmmoPerShot() > GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) )
+ {
+ WeaponSound( EMPTY );
+ m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
+ return;
+ }
+ }
+
+ CalcIsAttackCritical();
+
+#ifndef CLIENT_DLL
+ if ( pPlayer->m_Shared.IsStealthed() && ShouldRemoveInvisibilityOnPrimaryAttack() )
+ {
+ pPlayer->RemoveInvisibility();
+ }
+
+ // Minigun has custom handling
+ if ( GetWeaponID() != TF_WEAPON_MINIGUN )
+ {
+ pPlayer->SpeakWeaponFire();
+ }
+ CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
+#else
+ C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
+#endif
+
+ // Minigun has custom handling
+ if ( GetWeaponID() != TF_WEAPON_MINIGUN )
+ {
+ // Set the weapon mode.
+ m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
+ }
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ CBaseEntity* pProj = FireProjectile( pPlayer );
+ ModifyProjectile( pProj );
+
+ if ( !UsesClipsForAmmo1() )
+ {
+ // Sniper rifles and such don't actually reload, so we hook reduced reload here
+ float flBaseFireDelay = flFireDelay;
+ CALL_ATTRIB_HOOK_FLOAT( flFireDelay, fast_reload );
+
+ float flPlaybackRate = flFireDelay == 0.f ? 0.f : flBaseFireDelay / flFireDelay;
+
+ if ( pPlayer->GetViewModel( 0 ) )
+ {
+ pPlayer->GetViewModel( 0 )->SetPlaybackRate( flPlaybackRate );
+ }
+ if ( pPlayer->GetViewModel( 1 ) )
+ {
+ pPlayer->GetViewModel( 1 )->SetPlaybackRate( flPlaybackRate );
+ }
+ }
+
+ // Set next attack times.
+ m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
+
+ // Don't push out secondary attack, because our secondary fire
+ // systems are all separate from primary fire (sniper zooming, demoman pipebomb detonating, etc)
+ //m_flNextSecondaryAttack = gpGlobals->curtime + m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
+
+ // Set the idle animation times based on the sequence duration, so that we play full fire animations
+ // that last longer than the refire rate may allow.
+ if ( Clip1() > 0 )
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+ }
+ else
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+ }
+
+ // Check the reload mode and behave appropriately.
+ if ( m_bReloadsSingly )
+ {
+ m_iReloadMode.Set( TF_RELOAD_START );
+ }
+
+#ifdef STAGING_ONLY
+ // Remove Cond if I attack
+ if ( pPlayer->m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
+ {
+ pPlayer->m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
+ }
+#endif
+
+ m_flLastPrimaryAttackTime = gpGlobals->curtime;
+
+ if ( ShouldRemoveDisguiseOnPrimaryAttack() )
+ {
+ pPlayer->RemoveDisguise();
+ }
+}
+
+bool CTFWeaponBaseGun::ShouldRemoveDisguiseOnPrimaryAttack() const
+{
+ int iAttr = 0;
+ CALL_ATTRIB_HOOK_INT( iAttr, keep_disguise_on_attack );
+ if ( iAttr )
+ return false;
+
+ return true;
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::SecondaryAttack( void )
+{
+ // semi-auto behaviour
+ if ( m_bInAttack2 )
+ return;
+
+ // Get the player owning the weapon.
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ pPlayer->DoClassSpecialSkill();
+
+ m_bInAttack2 = true;
+
+#ifdef STAGING_ONLY
+ // Remove Cond if I attack
+ if ( pPlayer->m_Shared.InCond( TF_COND_NO_COMBAT_SPEED_BOOST ) )
+ {
+ pPlayer->m_Shared.RemoveCond( TF_COND_NO_COMBAT_SPEED_BOOST );
+ }
+#endif
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+}
+
+CBaseEntity *CTFWeaponBaseGun::FireProjectile( CTFPlayer *pPlayer )
+{
+ // New behavior: allow weapons to have attributes to specify what sort of
+ // projectile they fire.
+ int iProjectile = 0;
+ CALL_ATTRIB_HOOK_INT( iProjectile, override_projectile_type );
+
+ // Previous default behavior: ask the weapon type for what sort of projectile
+ // to launch.
+ if ( iProjectile == 0 )
+ {
+ iProjectile = GetWeaponProjectileType();
+ }
+
+ CBaseEntity *pProjectile = NULL;
+
+ // Anyone ever hear of a factory? This is a disgrace.
+ switch( iProjectile )
+ {
+ case TF_PROJECTILE_BULLET:
+ FireBullet( pPlayer );
+ //pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+
+ case TF_PROJECTILE_ROCKET:
+ pProjectile = FireRocket( pPlayer, iProjectile );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+
+ case TF_PROJECTILE_SYRINGE:
+#ifdef STAGING_ONLY
+ case TF_PROJECTILE_TRANQ:
+#endif // STAGING_ONLY
+ pProjectile = FireNail( pPlayer, iProjectile );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+
+ case TF_PROJECTILE_FLARE:
+ pProjectile = FireFlare( pPlayer );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+
+ case TF_PROJECTILE_PIPEBOMB:
+ case TF_PROJECTILE_PIPEBOMB_REMOTE:
+ case TF_PROJECTILE_PIPEBOMB_PRACTICE:
+ case TF_PROJECTILE_CANNONBALL:
+ pProjectile = FirePipeBomb( pPlayer, iProjectile );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+
+ case TF_PROJECTILE_JAR:
+ case TF_PROJECTILE_JAR_MILK:
+ case TF_PROJECTILE_CLEAVER:
+ case TF_PROJECTILE_THROWABLE:
+ case TF_PROJECTILE_FESTIVE_JAR:
+ case TF_PROJECTILE_BREADMONSTER_JARATE:
+ case TF_PROJECTILE_BREADMONSTER_MADMILK:
+ pProjectile = FireJar( pPlayer );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+ case TF_PROJECTILE_ARROW:
+ case TF_PROJECTILE_HEALING_BOLT:
+ case TF_PROJECTILE_BUILDING_REPAIR_BOLT:
+ case TF_PROJECTILE_FESTIVE_ARROW:
+ case TF_PROJECTILE_FESTIVE_HEALING_BOLT:
+#ifdef STAGING_ONLY
+ case TF_PROJECTILE_SNIPERBULLET:
+ case TF_PROJECTILE_MILK_BOLT:
+#endif
+ case TF_PROJECTILE_GRAPPLINGHOOK:
+ pProjectile = FireArrow( pPlayer, ProjectileType_t( iProjectile ) );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ break;
+
+ case TF_PROJECTILE_FLAME_ROCKET:
+ pProjectile = FireFlameRocket( pPlayer );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_SECONDARY );
+ break;
+
+ case TF_PROJECTILE_ENERGY_BALL:
+ pProjectile = FireEnergyBall( pPlayer );
+ if ( ShouldPlayFireAnim() )
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ }
+ break;
+
+ case TF_PROJECTILE_ENERGY_RING:
+ pProjectile = FireEnergyBall( pPlayer, true );
+ if ( ShouldPlayFireAnim() )
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+ }
+ break;
+
+ case TF_PROJECTILE_NONE:
+ default:
+ // do nothing!
+ DevMsg( "Weapon does not have a projectile type set\n" );
+ break;
+ }
+
+ RemoveProjectileAmmo( pPlayer );
+
+ m_flLastFireTime = gpGlobals->curtime;
+
+ DoFireEffects();
+
+ UpdatePunchAngles( pPlayer );
+
+#ifdef GAME_DLL
+ // Some game modes may allow any class to have stealth. Continuous-fire weapons like the
+ // minigun might be firing when stealth is applied, so we try removing it from here, too.
+ if ( pPlayer->m_Shared.IsStealthed() && ShouldRemoveInvisibilityOnPrimaryAttack() )
+ {
+ pPlayer->RemoveInvisibility();
+ }
+#endif // GAME_DLL
+
+ return pProjectile;
+}
+
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::RemoveProjectileAmmo( CTFPlayer *pPlayer )
+{
+
+ if ( m_iClip1 != -1 )
+ {
+ m_iClip1 -= GetAmmoPerShot();
+ }
+ else
+ {
+ if ( m_iWeaponMode == TF_WEAPON_PRIMARY_MODE )
+ {
+ pPlayer->RemoveAmmo( GetAmmoPerShot(), m_iPrimaryAmmoType );
+
+#ifndef CLIENT_DLL
+ // delayed ammo adding for the onhit attribute
+ if ( m_iAmmoToAdd > 0 )
+ {
+ pPlayer->GiveAmmo( m_iAmmoToAdd, m_iPrimaryAmmoType );
+ m_iAmmoToAdd = 0;
+ }
+#endif
+ }
+ else
+ {
+ pPlayer->RemoveAmmo( GetAmmoPerShot(), m_iSecondaryAmmoType );
+
+#ifndef CLIENT_DLL
+ // delayed ammo adding for the onhit attribute
+ if ( m_iAmmoToAdd > 0 )
+ {
+ pPlayer->GiveAmmo( m_iAmmoToAdd, m_iSecondaryAmmoType );
+ m_iAmmoToAdd = 0;
+ }
+#endif
+ }
+ }
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFWeaponBaseGun::HasPrimaryAmmo( void )
+{
+ if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
+ {
+ if ( GetOwner() && ( GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) < GetAmmoPerShot() ) )
+ return false;
+ }
+
+ return BaseClass::HasPrimaryAmmo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFWeaponBaseGun::CanDeploy( void )
+{
+ if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
+ {
+ if ( GetOwner() && ( GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) < GetAmmoPerShot() ) )
+ return false;
+ }
+
+ return BaseClass::CanDeploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFWeaponBaseGun::CanBeSelected( void )
+{
+ if ( m_iPrimaryAmmoType == TF_AMMO_METAL )
+ {
+ if ( GetOwner() && ( GetOwner()->GetAmmoCount( m_iPrimaryAmmoType ) < GetAmmoPerShot() ) )
+ return false;
+ }
+
+ return BaseClass::CanBeSelected();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFWeaponBaseGun::GetAmmoPerShot( void )
+{
+ if ( IsEnergyWeapon() )
+ return 0;
+ else
+ {
+ int iAmmoPerShot = 0;
+ CALL_ATTRIB_HOOK_INT( iAmmoPerShot, mod_ammo_per_shot );
+ if ( iAmmoPerShot > 0 )
+ return iAmmoPerShot;
+
+ return m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_iAmmoPerShot;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::UpdatePunchAngles( CTFPlayer *pPlayer )
+{
+ // Update the player's punch angle.
+ QAngle angle = pPlayer->GetPunchAngle();
+ float flPunchAngle = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flPunchAngle;
+
+ if ( flPunchAngle > 0 )
+ {
+ angle.x -= SharedRandomInt( "ShotgunPunchAngle", ( flPunchAngle - 1 ), ( flPunchAngle + 1 ) );
+ pPlayer->SetPunchAngle( angle );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire a bullet!
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::FireBullet( CTFPlayer *pPlayer )
+{
+ PlayWeaponShootSound();
+
+ FX_FireBullets(
+ this,
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ m_iWeaponMode,
+ CBaseEntity::GetPredictionRandomSeed( UseServerRandomSeed() ) & 255,
+ GetWeaponSpread(),
+ GetProjectileDamage(),
+ IsCurrentAttackACrit() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire a rocket
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireRocket( CTFPlayer *pPlayer, int iRocketType )
+{
+ PlayWeaponShootSound();
+
+ // Server only - create the rocket.
+#ifdef GAME_DLL
+
+ Vector vecSrc;
+ QAngle angForward;
+ Vector vecOffset( 23.5f, 12.0f, -3.0f );
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ vecOffset.z = 8.0f;
+ }
+ GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
+
+ trace_t trace;
+ Vector vecEye = pPlayer->EyePosition();
+ CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
+ UTIL_TraceLine( vecEye, vecSrc, MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
+
+ CTFProjectile_Rocket *pProjectile = CTFProjectile_Rocket::Create( this, trace.endpos, angForward, pPlayer, pPlayer );
+
+ if ( pProjectile )
+ {
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+ pProjectile->SetDamage( GetProjectileDamage() );
+ }
+
+ return pProjectile;
+
+#endif
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire an energy ball
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireEnergyBall( CTFPlayer *pPlayer, bool bRing )
+{
+ PlayWeaponShootSound();
+
+ Vector vecSrc;
+ QAngle angForward;
+ Vector vecOffset( 23.5f, -8.0f, -3.0f );
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ vecOffset.z = 8.0f;
+ }
+ GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
+
+ trace_t trace;
+ Vector vecEye = pPlayer->EyePosition();
+ CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
+ UTIL_TraceLine( vecEye, vecSrc, MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
+
+ if ( bRing )
+ {
+ CTFProjectile_EnergyRing* pProjectile = CTFProjectile_EnergyRing::Create( this, trace.endpos, angForward,
+ GetProjectileSpeed(), GetProjectileGravity(), pPlayer, pPlayer, GetParticleColor(1), GetParticleColor(2), IsCurrentAttackACrit() );
+ if ( pProjectile )
+ {
+ pProjectile->SetWeaponID( GetWeaponID() );
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+#ifdef GAME_DLL
+ pProjectile->SetDamage( GetProjectileDamage() );
+#endif
+ }
+ return pProjectile;
+ }
+ else
+ {
+#ifdef GAME_DLL
+ CTFProjectile_EnergyBall* pProjectile = CTFProjectile_EnergyBall::Create( trace.endpos, angForward, GetProjectileSpeed(), GetProjectileGravity(), pPlayer, pPlayer );
+ if ( pProjectile )
+ {
+ pProjectile->SetLauncher( this );
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+ pProjectile->SetDamage( GetProjectileDamage() );
+ }
+ return pProjectile;
+#endif
+ }
+
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire a projectile nail
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireNail( CTFPlayer *pPlayer, int iSpecificNail )
+{
+ PlayWeaponShootSound();
+
+ Vector vecSrc;
+ QAngle angForward;
+
+ // Add some spread
+ float flSpread = 1.5;
+ flSpread += GetProjectileSpread();
+
+ CTFBaseProjectile *pProjectile = NULL;
+ switch( iSpecificNail )
+ {
+ case TF_PROJECTILE_SYRINGE:
+ {
+ Vector vecOffset( 16, 6, -8 );
+ GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward );
+ angForward.x += RandomFloat( -flSpread, flSpread );
+ angForward.y += RandomFloat( -flSpread, flSpread );
+ pProjectile = CTFProjectile_Syringe::Create( vecSrc, angForward, this, pPlayer, pPlayer, IsCurrentAttackACrit() );
+ }
+ break;
+#ifdef STAGING_ONLY
+ case TF_PROJECTILE_TRANQ:
+ {
+ Vector vecOffset( 16, 6, 0 );
+ GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward );
+ pProjectile = CTFProjectile_Tranq::Create( vecSrc, angForward, this, pPlayer, pPlayer, IsCurrentAttackACrit() );
+ }
+ break;
+#endif // STAGING_ONLY
+ default:
+ Assert(0);
+ }
+
+ if ( pProjectile )
+ {
+ pProjectile->SetWeaponID( GetWeaponID() );
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+#ifdef GAME_DLL
+ pProjectile->SetLauncher( this );
+ pProjectile->SetDamage( GetProjectileDamage() );
+#endif
+ }
+
+ return pProjectile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire a pipe bomb
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FirePipeBomb( CTFPlayer *pPlayer, int iPipeBombType )
+{
+ PlayWeaponShootSound();
+
+#ifdef GAME_DLL
+ QAngle angEyes = pPlayer->EyeAngles();
+
+ float flSpreadAngle = 0.0f;
+ CALL_ATTRIB_HOOK_FLOAT( flSpreadAngle, projectile_spread_angle );
+ if ( flSpreadAngle > 0.0f )
+ {
+ QAngle angSpread = RandomAngle( -flSpreadAngle, flSpreadAngle );
+ angSpread.z = 0.0f;
+ angEyes += angSpread;
+ DevMsg( "Fire bomb at %f %f %f\n", XYZ(angEyes) );
+ }
+
+ Vector vecForward, vecRight, vecUp;
+ AngleVectors( angEyes, &vecForward, &vecRight, &vecUp );
+
+ // Create grenades here!!
+ float fRight = 8.f;
+ if ( IsViewModelFlipped() )
+ {
+ fRight *= -1;
+ }
+ Vector vecSrc = pPlayer->Weapon_ShootPosition();
+ vecSrc += vecForward * 16.0f + vecRight * fRight + vecUp * -6.0f;
+
+ trace_t trace;
+ Vector vecEye = pPlayer->EyePosition();
+ CTraceFilterSimple traceFilter( this, COLLISION_GROUP_NONE );
+ UTIL_TraceHull( vecEye, vecSrc, -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, &traceFilter, &trace );
+
+ // If we started in solid, don't let them fire at all
+ if ( trace.startsolid )
+ return NULL;
+
+ float flLaunchSpeed = GetProjectileSpeed();
+ CALL_ATTRIB_HOOK_FLOAT( flLaunchSpeed, mult_projectile_range );
+ Vector vecVelocity = ( vecForward * flLaunchSpeed ) + ( vecUp * 200.0f ) + ( random->RandomFloat( -10.0f, 10.0f ) * vecRight ) +
+ ( random->RandomFloat( -10.0f, 10.0f ) * vecUp );
+
+ float flMultDmg = 1.f;
+ CALL_ATTRIB_HOOK_FLOAT( flMultDmg, mult_dmg );
+
+ // no spin for loch-n-load
+ Vector angImpulse = AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 );
+ int iNoSpin = 0;
+ CALL_ATTRIB_HOOK_INT( iNoSpin, grenade_no_spin );
+ if ( iNoSpin )
+ {
+ angImpulse.Zero();
+ }
+
+ CTFGrenadePipebombProjectile *pProjectile = CTFGrenadePipebombProjectile::Create( trace.endpos, angEyes, vecVelocity, angImpulse, pPlayer, GetTFWpnData(), iPipeBombType, flMultDmg );
+
+ if ( pProjectile )
+ {
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+ pProjectile->SetLauncher( this );
+
+ //float flFizzle = 0;
+ //CALL_ATTRIB_HOOK_FLOAT( flFizzle, stickybomb_fizzle_time );
+ //if ( flFizzle )
+ //{
+ // pProjectile->SetDetonateTimerLength( flFizzle );
+ //}
+ CAttribute_String attrCustomModelName;
+ GetCustomProjectileModel( &attrCustomModelName );
+ if ( attrCustomModelName.has_value() )
+ {
+ pProjectile->SetModel( attrCustomModelName.value().c_str() );
+ }
+
+ }
+
+ return pProjectile;
+
+#endif
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire a flare
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireFlare( CTFPlayer *pPlayer )
+{
+ PlayWeaponShootSound();
+
+ // Server only - create the flare.
+#ifdef GAME_DLL
+
+ Vector vecSrc;
+ QAngle angForward;
+ Vector vecOffset( 23.5f, 12.0f, -3.0f );
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ vecOffset.z = 8.0f;
+ }
+ GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
+
+ CTFProjectile_Flare *pProjectile = CTFProjectile_Flare::Create( this, vecSrc, angForward, pPlayer, pPlayer );
+ if ( pProjectile )
+ {
+ pProjectile->SetLauncher( this );
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+ pProjectile->SetDamage( GetProjectileDamage() );
+ CTFFlareGun *pFlareGun = dynamic_cast<CTFFlareGun *>( this );
+ if ( pFlareGun && pFlareGun->GetFlareGunType() == FLAREGUN_DETONATE )
+ {
+ pFlareGun->AddFlare( pProjectile );
+ }
+ }
+ return pProjectile;
+
+#endif
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire an arrow
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireArrow( CTFPlayer *pPlayer, ProjectileType_t projectileType )
+{
+ PlayWeaponShootSound();
+
+ // Server only - create the rocket.
+#ifdef GAME_DLL
+
+ Vector vecSrc;
+ QAngle angForward;
+ Vector vecOffset( 23.5f, -8.0f, -3.0f );
+#ifdef STAGING_ONLY
+ if ( projectileType == TF_PROJECTILE_SNIPERBULLET )
+ {
+ // Center the bullet while zoomed, otherwise flip the arrow cause arrows are dumb
+ if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ {
+ vecOffset = Vector( 32, 0, -2.0f );
+ }
+ else
+ {
+ vecOffset.y = 8.0f;
+ }
+ }
+#endif // STAGING_ONLY
+
+ GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
+
+ CTFProjectile_Arrow *pProjectile = CTFProjectile_Arrow::Create( vecSrc, angForward, GetProjectileSpeed(), GetProjectileGravity(), projectileType, pPlayer, pPlayer );
+ if ( pProjectile )
+ {
+ pProjectile->SetLauncher( this );
+ pProjectile->SetCritical( IsCurrentAttackACrit() );
+ pProjectile->SetDamage( GetProjectileDamage() );
+
+ int iPenetrate = 0;
+ CALL_ATTRIB_HOOK_INT( iPenetrate, projectile_penetration );
+ if ( iPenetrate == 1 )
+ {
+ pProjectile->SetPenetrate( true );
+ }
+ pProjectile->SetCollisionGroup( TFCOLLISION_GROUP_ROCKET_BUT_NOT_WITH_OTHER_ROCKETS );
+ }
+ return pProjectile;
+
+#endif
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toss a Jar...of something...
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireJar( CTFPlayer *pPlayer )
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFWeaponBaseGun::FireFlameRocket( CTFPlayer *pPlayer )
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::PlayWeaponShootSound( void )
+{
+ if ( IsCurrentAttackACrit() )
+ {
+ WeaponSound( BURST );
+ }
+ else
+ {
+ WeaponSound( SINGLE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFWeaponBaseGun::GetWeaponSpread( void )
+{
+ float fSpread = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flSpread;
+ CALL_ATTRIB_HOOK_FLOAT( fSpread, mult_spread_scale );
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+
+ if ( pPlayer )
+ {
+ if ( pPlayer->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
+ {
+ if ( GetWeaponID() == TF_WEAPON_MINIGUN )
+ {
+ fSpread *= 0.4f;
+ }
+ else
+ {
+ fSpread *= 0.1f;
+ }
+ }
+
+ // Some weapons change fire delay based on player's health
+ float flReducedHealthBonus = 1.0f;
+ CALL_ATTRIB_HOOK_FLOAT( flReducedHealthBonus, panic_attack_negative );
+ if ( flReducedHealthBonus != 1.0f )
+ {
+ flReducedHealthBonus = RemapValClamped( pPlayer->HealthFraction(), 0.2f, 0.9f, flReducedHealthBonus, 1.0f );
+ fSpread *= flReducedHealthBonus;
+ }
+ }
+
+ return fSpread;
+}
+
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::GetCustomProjectileModel( CAttribute_String *attrCustomProjModel )
+{
+ // Must still add these to a precache somewhere
+ // ie CTFGrenadePipebombProjectile::Precache()
+ static CSchemaAttributeDefHandle pAttrDef_ProjectileEntityName( "custom projectile model" );
+ CEconItemView *pItem = GetAttributeContainer()->GetItem();
+ if ( pAttrDef_ProjectileEntityName && pItem )
+ {
+ pItem->FindAttribute( pAttrDef_ProjectileEntityName, attrCustomProjModel );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Accessor for damage, so sniper etc can modify damage
+//-----------------------------------------------------------------------------
+float CTFWeaponBaseGun::GetProjectileDamage( void )
+{
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+
+ float flDamage = (float)m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_nDamage;
+ CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg );
+
+ // Some weapons mod dmg when not disguised
+ bool bDisguised = pPlayer && pPlayer->m_Shared.InCond( TF_COND_DISGUISED );
+ if ( bDisguised )
+ {
+ CALL_ATTRIB_HOOK_FLOAT( flDamage, mult_dmg_disguised );
+ }
+
+ if ( pPlayer && ( pPlayer->IsPlayerClass( TF_CLASS_SOLDIER ) || pPlayer->IsPlayerClass( TF_CLASS_PYRO ) ) )
+ {
+ float flRageDamage = 1.f;
+ CALL_ATTRIB_HOOK_FLOAT( flRageDamage, rage_damage );
+
+ if ( flRageDamage > 1.f )
+ {
+ float flRageRatio = pPlayer->m_Shared.GetRageMeter() / 100.f;
+ flRageDamage = (flRageDamage - 1.f) * flRageRatio;
+ flDamage *= 1.f + flRageDamage;
+ }
+ }
+
+#ifdef GAME_DLL
+ float flMedicHealDamageBonus = 1.f;
+ CALL_ATTRIB_HOOK_FLOAT( flMedicHealDamageBonus, medic_healed_damage_bonus );
+
+ if ( flMedicHealDamageBonus > 1.f )
+ {
+ if ( pPlayer )
+ {
+ int numHealers = pPlayer->m_Shared.GetNumHealers();
+ bool bHealedByMedic = false;
+ for ( int i=0; i<numHealers; i++ )
+ {
+ if ( ToTFPlayer( pPlayer->m_Shared.GetHealerByIndex( i ) ) != NULL )
+ {
+ bHealedByMedic = true;
+ }
+ }
+
+ if ( bHealedByMedic )
+ {
+ flDamage *= flMedicHealDamageBonus;
+ }
+ }
+ }
+
+ if ( GetWeaponProjectileType() == TF_PROJECTILE_BULLET )
+ {
+ float flScaleDamage = 1.f;
+ CALL_ATTRIB_HOOK_FLOAT( flScaleDamage, accuracy_scales_damage );
+ if ( flScaleDamage > 1.f )
+ {
+ // Bullets fired vs hit ratio over last x.x second(s)
+ if ( gpGlobals->curtime < GetLastHitTime() + 0.7f )
+ {
+ float flRatio = (float)m_iHitsInTime / (float)m_iFiredInTime;
+ float flDmgMod = RemapValClamped( flRatio, 0.f, 1.f, 1.f, flScaleDamage );
+
+// DevMsg( "A: %f - D: %f\n", flRatio, flDmgMod );
+// DevMsg( "H: %d - F: %d\n", m_iHitsInTime, m_iFiredInTime );
+
+ flDamage *= flDmgMod;
+ }
+ else
+ {
+ m_iHitsInTime = 1;
+ m_iFiredInTime = 1;
+ }
+ }
+ }
+
+#endif
+
+ return flDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFWeaponBaseGun::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+// Server specific.
+#if !defined( CLIENT_DLL )
+
+ // Make sure to zoom out before we holster the weapon.
+ ZoomOut();
+ SetContextThink( NULL, 0, ZOOM_CONTEXT );
+
+#endif
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// NOTE: Should this be put into fire gun
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::DoFireEffects()
+{
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( ShouldDoMuzzleFlash() )
+ {
+ pPlayer->DoMuzzleFlash();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::ToggleZoom( void )
+{
+ // Toggle the zoom.
+ CBasePlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer )
+ {
+ if( pPlayer->GetFOV() >= 75 )
+ {
+ ZoomIn();
+ }
+ else
+ {
+ ZoomOut();
+ }
+ }
+
+ // Get the zoom animation time.
+ m_flNextSecondaryAttack = gpGlobals->curtime + 1.2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::ZoomIn( void )
+{
+ // The the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Set the weapon zoom.
+ // TODO: The weapon fov should be gotten from the script file.
+ float fBaseZoom = TF_WEAPON_ZOOM_FOV;
+
+ // Disabled this for now, because we have no attributes using it
+ //CALL_ATTRIB_HOOK_FLOAT( fBaseZoom, mult_zoom_fov );
+
+ pPlayer->SetFOV( pPlayer, fBaseZoom, 0.1f );
+ pPlayer->m_Shared.AddCond( TF_COND_ZOOMED );
+
+#if defined( CLIENT_DLL )
+ // Doing this allows us to show/hide the player viewmodel/localmodel
+ pPlayer->UpdateVisibility();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::ZoomOut( void )
+{
+ // The the owning player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->m_Shared.InCond( TF_COND_ZOOMED ) )
+ {
+ // Set the FOV to 0 set the default FOV.
+ pPlayer->SetFOV( pPlayer, 0, 0.1f );
+ pPlayer->m_Shared.RemoveCond( TF_COND_ZOOMED );
+ }
+
+#if defined( CLIENT_DLL )
+ // Doing this allows us to show/hide the player viewmodel/localmodel
+ pPlayer->UpdateVisibility();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFWeaponBaseGun::ZoomOutIn( void )
+{
+ //Zoom out, set think to zoom back in.
+ ZoomOut();
+ SetContextThink( &CTFWeaponBaseGun::ZoomIn, gpGlobals->curtime + ZOOM_REZOOM_TIME, ZOOM_CONTEXT );
+}
+
+//-----------------------------------------------------------------------------
+bool CTFWeaponBaseGun::HasLastShotCritical( void )
+{
+ if ( m_iClip1 == 1 )
+ {
+ int iAttr = 0;
+ CALL_ATTRIB_HOOK_INT( iAttr, last_shot_crits );
+ if ( iAttr )
+ {
+ return true;
+ }
+ }
+ return false;
+} \ No newline at end of file