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