diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/tf_weaponbase_gun.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/tf/tf_weaponbase_gun.cpp')
| -rw-r--r-- | game/shared/tf/tf_weaponbase_gun.cpp | 1133 |
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 |