summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weapon_shotgun.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_weapon_shotgun.cpp')
-rw-r--r--game/shared/tf/tf_weapon_shotgun.cpp581
1 files changed, 581 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_shotgun.cpp b/game/shared/tf/tf_weapon_shotgun.cpp
new file mode 100644
index 0000000..f233de1
--- /dev/null
+++ b/game/shared/tf/tf_weapon_shotgun.cpp
@@ -0,0 +1,581 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "tf_weapon_shotgun.h"
+#include "decals.h"
+#include "tf_fx_shared.h"
+#include "takedamageinfo.h"
+#include "tf_gamerules.h"
+
+// Client specific.
+#if defined( CLIENT_DLL )
+#include "c_tf_player.h"
+// Server specific.
+#else
+#include "tf_player.h"
+#include "ilagcompensationmanager.h"
+#include "collisionutils.h"
+#include "in_buttons.h"
+#endif
+
+//=============================================================================
+//
+// Weapon Shotgun tables.
+//
+
+CREATE_SIMPLE_WEAPON_TABLE( TFShotgun, tf_weapon_shotgun_primary )
+CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_Soldier, tf_weapon_shotgun_soldier )
+CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_HWG, tf_weapon_shotgun_hwg )
+CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_Pyro, tf_weapon_shotgun_pyro )
+CREATE_SIMPLE_WEAPON_TABLE( TFScatterGun, tf_weapon_scattergun )
+CREATE_SIMPLE_WEAPON_TABLE( TFShotgun_Revenge, tf_weapon_sentry_revenge )
+CREATE_SIMPLE_WEAPON_TABLE( TFSodaPopper, tf_weapon_soda_popper )
+CREATE_SIMPLE_WEAPON_TABLE( TFPEPBrawlerBlaster, tf_weapon_pep_brawler_blaster )
+CREATE_SIMPLE_WEAPON_TABLE( TFShotgunBuildingRescue, tf_weapon_shotgun_building_rescue )
+
+#define SCATTERGUN_KNOCKBACK_MIN_DMG 30.0f
+#define SCATTERGUN_KNOCKBACK_MIN_RANGE_SQ 160000.0f //400x400
+//=============================================================================
+//
+// Weapon Shotgun functions.
+//
+bool CanScatterGunKnockBack( CTFWeaponBase *pWeapon, float flDamage, float flDistanceSq )
+{
+ int nBulletKnockBack = 0;
+ CALL_ATTRIB_HOOK_INT_ON_OTHER( pWeapon, nBulletKnockBack, set_scattergun_has_knockback );
+ if ( nBulletKnockBack != 0 )
+ {
+ if (flDamage > SCATTERGUN_KNOCKBACK_MIN_DMG && flDistanceSq < SCATTERGUN_KNOCKBACK_MIN_RANGE_SQ )
+ return true;
+
+ float flKnockbackMult = 1.0f;
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pWeapon, flKnockbackMult, scattergun_knockback_mult );
+ if ( flKnockbackMult > 1.0f )
+ return true;
+ }
+
+ return false;
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFShotgun::CTFShotgun()
+{
+ m_bReloadsSingly = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFShotgun::PrimaryAttack()
+{
+ if ( !CanAttack() )
+ return;
+
+ // Set the weapon mode.
+ m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
+
+ BaseClass::PrimaryAttack();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFShotgun::UpdatePunchAngles( CTFPlayer *pPlayer )
+{
+ // Update the player's punch angle.
+ QAngle angle = pPlayer->GetPunchAngle();
+ float flPunchAngle = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flPunchAngle;
+ angle.x -= SharedRandomInt( "ShotgunPunchAngle", ( flPunchAngle - 1 ), ( flPunchAngle + 1 ) );
+ pPlayer->SetPunchAngle( angle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFShotgun::PlayWeaponShootSound( void )
+{
+ BaseClass::PlayWeaponShootSound();
+
+ if ( TFGameRules()->GameModeUsesUpgrades() )
+ {
+ PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// CTFShotgun_Revenge
+//-----------------------------------------------------------------------------
+CTFShotgun_Revenge::CTFShotgun_Revenge()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFShotgun_Revenge::Precache()
+{
+ int iModelIndex = PrecacheModel( TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL );
+ PrecacheGibsForModel( iModelIndex );
+ PrecacheParticleSystem( "blood_impact_backscatter" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFShotgun_Revenge::PrimaryAttack()
+{
+ if ( !CanAttack() )
+ return;
+
+ BaseClass::PrimaryAttack();
+
+ // Do this after the attack, so that we know if we are doing custom damage
+ CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
+ if ( pOwner )
+ {
+ int iRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
+ pOwner->m_Shared.SetRevengeCrits( iRevengeCrits-1 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFShotgun_Revenge::SentryKilled( int iCrits )
+{
+ int val = 0;
+ CALL_ATTRIB_HOOK_INT( val, sentry_killed_revenge );
+ if ( val == 1 )
+ {
+ CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
+ if ( pOwner )
+ {
+ pOwner->m_Shared.SetRevengeCrits( pOwner->m_Shared.GetRevengeCrits() + iCrits );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFShotgun_Revenge::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+#ifdef GAME_DLL
+ CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
+ if ( pOwner && pOwner->m_Shared.GetRevengeCrits() )
+ {
+ pOwner->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
+ }
+#endif
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFShotgun_Revenge::Deploy( void )
+{
+#ifdef GAME_DLL
+ CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
+ if ( pOwner && pOwner->m_Shared.GetRevengeCrits() )
+ {
+ pOwner->m_Shared.AddCond( TF_COND_CRITBOOSTED );
+ }
+#endif
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFShotgun_Revenge::GetCustomDamageType() const
+{
+ CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
+ if ( pOwner )
+ {
+ int iRevengeCrits = pOwner->m_Shared.GetRevengeCrits();
+ return iRevengeCrits > 0 ? TF_DMG_CUSTOM_SHOTGUN_REVENGE_CRIT : TF_DMG_CUSTOM_NONE;
+ }
+ return TF_DMG_CUSTOM_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFShotgun_Revenge::GetCount( void )
+{
+ CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
+ if ( pOwner )
+ {
+ return pOwner->m_Shared.GetRevengeCrits();
+ }
+
+ return 0;
+}
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// ----------------------------------------------------------------------------
+void CTFShotgun_Revenge::SetWeaponVisible( bool visible )
+{
+ if ( !visible )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) && pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_ENGINEER && pPlayer->m_Shared.GetTauntIndex() == TAUNT_BASE_WEAPON )
+ {
+ int nModelIndex = modelinfo->GetModelIndex( TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL );
+ CUtlVector<breakmodel_t> guitarGibs;
+ BuildGibList( guitarGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE );
+ if ( guitarGibs.Count() > 0 )
+ {
+ Vector vForward, vRight, vUp;
+ AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
+
+ Vector vecBreakVelocity = Vector(0,0,200);
+ AngularImpulse angularImpulse( RandomFloat( 0.0f, 120.0f ), RandomFloat( 0.0f, 120.0f ), 0.0 );
+ Vector vecOrigin = GetAbsOrigin() + vForward*70 + vUp*10;
+ QAngle vecAngle = GetAbsAngles();
+ breakablepropparams_t breakParams( vecOrigin, vecAngle, vecBreakVelocity, angularImpulse );
+ breakParams.impactEnergyScale = 1.0f;
+
+ CreateGibsFromList( guitarGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true );
+ }
+ }
+ }
+
+ BaseClass::SetWeaponVisible( visible );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// ----------------------------------------------------------------------------
+int CTFShotgun_Revenge::GetWorldModelIndex( void )
+{
+ // Engineer guitar support.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( pPlayer && pPlayer->GetPlayerClass() && ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_ENGINEER ) &&
+ ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ) && ( pPlayer->m_Shared.GetTauntIndex() == TAUNT_BASE_WEAPON ) )
+ {
+ // While we are taunting, replace our normal world model with the guitar.
+ m_iWorldModelIndex = modelinfo->GetModelIndex( TF_WEAPON_TAUNT_FRONTIER_JUSTICE_GUITAR_MODEL );
+ return m_iWorldModelIndex;
+ }
+
+ return BaseClass::GetWorldModelIndex();
+}
+#endif
+
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Reset revenge crits when the shotgun is changed
+//-----------------------------------------------------------------------------
+void CTFShotgun_Revenge::Detach( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( pPlayer )
+ {
+ pPlayer->m_Shared.SetRevengeCrits( 0 );
+ pPlayer->m_Shared.RemoveCond( TF_COND_CRITBOOSTED );
+ }
+
+ BaseClass::Detach();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFScatterGun::Reload( void )
+{
+ int iWeaponMod = 0;
+ CALL_ATTRIB_HOOK_INT( iWeaponMod, set_scattergun_no_reload_single );
+ if ( iWeaponMod == 1 )
+ {
+ m_bReloadsSingly = false;
+ }
+
+ return BaseClass::Reload();
+}
+
+#define JUMP_SPEED 268.3281572999747f
+extern ConVar tf_player_movement_stun_time;
+extern float AirBurstDamageForce( const Vector &size, float damage, float scale );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFScatterGun::FireBullet( CTFPlayer *pPlayer )
+{
+#ifndef CLIENT_DLL
+ if ( HasKnockback() )
+ {
+ // Perform some knock back.
+ CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
+ if ( !pOwner )
+ return;
+
+ // No knockback during pre-round freeze.
+ if ( TFGameRules() && (TFGameRules()->State_Get() == GR_STATE_PREROUND) )
+ return;
+
+ // Knock the firer back!
+ if ( !(pOwner->GetFlags() & FL_ONGROUND) && !pPlayer->m_bScattergunJump )
+ {
+ pPlayer->m_bScattergunJump = true;
+
+ pOwner->m_Shared.StunPlayer( 0.3f, 1.f, TF_STUN_MOVEMENT | TF_STUN_MOVEMENT_FORWARD_ONLY );
+
+ float flForce = AirBurstDamageForce( pOwner->WorldAlignSize(), 60, 6.f );
+
+ Vector vecForward;
+ AngleVectors( pOwner->EyeAngles(), &vecForward );
+ Vector vecForce = vecForward * -flForce;
+
+ EntityMatrix mtxPlayer;
+ mtxPlayer.InitFromEntity( pOwner );
+ Vector vecAbsVelocity = pOwner->GetAbsVelocity();
+ Vector vecAbsVelocityAsPoint = vecAbsVelocity + pOwner->GetAbsOrigin();
+ Vector vecLocalVelocity = mtxPlayer.WorldToLocal( vecAbsVelocityAsPoint );
+
+ vecLocalVelocity.x = -300;
+
+ vecAbsVelocityAsPoint = mtxPlayer.LocalToWorld( vecLocalVelocity );
+ vecAbsVelocity = vecAbsVelocityAsPoint - pOwner->GetAbsOrigin();
+ pOwner->SetAbsVelocity( vecAbsVelocity );
+
+ // Impulse an additional bit of Z push.
+ pOwner->ApplyAbsVelocityImpulse( Vector(0,0,50.f) );
+
+ // Slow player movement for a brief period of time.
+ pOwner->RemoveFlag( FL_ONGROUND );
+ }
+ }
+#endif
+
+ BaseClass::FireBullet( pPlayer );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFScatterGun::ApplyPostHitEffects( const CTakeDamageInfo &inputInfo, CTFPlayer *pPlayer )
+{
+#ifndef CLIENT_DLL
+ if ( !HasKnockback() )
+ return;
+
+ CTFPlayer *pAttacker = ToTFPlayer( inputInfo.GetAttacker() );
+ if ( !pAttacker )
+ return;
+
+ CTFPlayer *pTarget = pPlayer;
+ if ( !pTarget )
+ return;
+
+ if ( pTarget->m_Shared.GetWeaponKnockbackID() > -1 )
+ return;
+
+ float flDam = inputInfo.GetDamage();
+ Vector vecDir = pAttacker->WorldSpaceCenter() - pTarget->WorldSpaceCenter();
+ if ( !CanScatterGunKnockBack( this, flDam, vecDir.LengthSqr() ) )
+ return;
+
+ // Immune to pushback/knockback
+ if ( pTarget->m_Shared.InCond( TF_COND_MEGAHEAL ) )
+ return;
+
+ VectorNormalize( vecDir );
+
+ float flKnockbackMult = 3.0f;
+ CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( this, flKnockbackMult, scattergun_knockback_mult );
+
+ float flForce = AirBurstDamageForce( pTarget->WorldAlignSize(), flDam, flKnockbackMult );
+ Vector vecForce = vecDir * -flForce;
+ vecForce.z += JUMP_SPEED;
+
+ pTarget->ApplyAirBlastImpulse( vecForce );
+
+ pTarget->m_Shared.StunPlayer( 0.3f, 1.f, TF_STUN_MOVEMENT | TF_STUN_MOVEMENT_FORWARD_ONLY, pAttacker );
+ pTarget->m_Shared.SetWeaponKnockbackID( pAttacker->GetUserID() );
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFScatterGun::FinishReload( void )
+{
+ CTFPlayer* pOwner = ToTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ if ( UsesClipsForAmmo1() && !m_bReloadsSingly )
+ {
+ int primary = MIN( GetMaxClip1() - m_iClip1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
+ m_iClip1 += primary;
+
+ // Takes a whole clip worth of ammo to reload, causing us to lose whatever was chambered.
+ pOwner->RemoveAmmo( GetMaxClip1(), m_iPrimaryAmmoType);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFScatterGun::HasKnockback( void )
+{
+ int iWeaponMod = 0;
+ CALL_ATTRIB_HOOK_INT( iWeaponMod, set_scattergun_has_knockback );
+ if ( iWeaponMod == 1 )
+ return true;
+ else
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play animation appropriate to ball status.
+//-----------------------------------------------------------------------------
+bool CTFScatterGun::SendWeaponAnim( int iActivity )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return BaseClass::SendWeaponAnim( iActivity );
+
+ if ( HasKnockback() )
+ {
+ // Knockback version uses a different model and animation set.
+ switch ( iActivity )
+ {
+ case ACT_VM_DRAW:
+ iActivity = ACT_ITEM2_VM_DRAW;
+ break;
+ case ACT_VM_HOLSTER:
+ iActivity = ACT_ITEM2_VM_HOLSTER;
+ break;
+ case ACT_VM_IDLE:
+ iActivity = ACT_ITEM2_VM_IDLE;
+ break;
+ case ACT_VM_PULLBACK:
+ iActivity = ACT_ITEM2_VM_PULLBACK;
+ break;
+ case ACT_VM_PRIMARYATTACK:
+ iActivity = ACT_ITEM2_VM_PRIMARYATTACK;
+ break;
+ case ACT_VM_SECONDARYATTACK:
+ iActivity = ACT_ITEM2_VM_SECONDARYATTACK;
+ break;
+ case ACT_VM_RELOAD:
+ iActivity = ACT_ITEM2_VM_RELOAD;
+ break;
+ case ACT_VM_DRYFIRE:
+ iActivity = ACT_ITEM2_VM_DRYFIRE;
+ break;
+ case ACT_VM_IDLE_TO_LOWERED:
+ iActivity = ACT_ITEM2_VM_IDLE_TO_LOWERED;
+ break;
+ case ACT_VM_IDLE_LOWERED:
+ iActivity = ACT_ITEM2_VM_IDLE_LOWERED;
+ break;
+ case ACT_VM_LOWERED_TO_IDLE:
+ iActivity = ACT_ITEM2_VM_LOWERED_TO_IDLE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return BaseClass::SendWeaponAnim( iActivity );
+}
+
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+void CTFScatterGun::Equip( CBaseCombatCharacter *pOwner )
+{
+ CTFPlayer *pPlayer = dynamic_cast<CTFPlayer*>( pOwner );
+ if ( pPlayer )
+ {
+ pPlayer->m_Shared.SetScoutHypeMeter( 0.0f );
+ }
+
+ BaseClass::Equip( pOwner );
+}
+#endif // GAME_DLL
+//-----------------------------------------------------------------------------
+// CTFSodaPopper
+//-----------------------------------------------------------------------------
+float CTFSodaPopper::GetProgress( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return 0.f;
+
+ return pPlayer->m_Shared.GetScoutHypeMeter() * 0.01f;
+}
+
+//-----------------------------------------------------------------------------
+void CTFSodaPopper::ItemBusyFrame( void )
+{
+#ifdef GAME_DLL
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
+ {
+ // Check here so we can always activate buff when we want (similar to stickies)
+ SecondaryAttack();
+ }
+#endif
+
+ BaseClass::ItemBusyFrame();
+}
+
+//-----------------------------------------------------------------------------
+void CTFSodaPopper::SecondaryAttack()
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner( );
+ if ( !pPlayer || pPlayer->m_Shared.IsHypeBuffed() )
+ return;
+
+ if ( pPlayer->m_Shared.GetScoutHypeMeter() >= 100.f )
+ {
+ pPlayer->m_Shared.AddCond( TF_COND_SODAPOPPER_HYPE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+float CTFPEPBrawlerBlaster::GetProgress( void )
+{
+ CTFPlayer *pPlayer = GetTFPlayerOwner();
+ if ( !pPlayer )
+ return 0.f;
+
+ return pPlayer->m_Shared.GetScoutHypeMeter() * 0.01f;
+}
+
+//-----------------------------------------------------------------------------
+float CTFShotgunBuildingRescue::GetProjectileSpeed( void )
+{
+ return RemapValClamped( 0.75f, 0.0f, 1.f, 1800, 2600 ); // Temp, if we want to ramp.
+}
+
+//-----------------------------------------------------------------------------
+float CTFShotgunBuildingRescue::GetProjectileGravity( void )
+{
+ return RemapValClamped( 0.75f, 0.0f, 1.f, 0.5f, 0.1f ); // Temp, if we want to ramp.
+}
+
+//-----------------------------------------------------------------------------
+bool CTFShotgunBuildingRescue::IsViewModelFlipped( void )
+{
+ return !BaseClass::IsViewModelFlipped(); // Invert because arrows are backwards by default.
+} \ No newline at end of file