summaryrefslogtreecommitdiff
path: root/game/shared/tf/tf_weapon_pipebomblauncher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf/tf_weapon_pipebomblauncher.cpp')
-rw-r--r--game/shared/tf/tf_weapon_pipebomblauncher.cpp643
1 files changed, 643 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_pipebomblauncher.cpp b/game/shared/tf/tf_weapon_pipebomblauncher.cpp
new file mode 100644
index 0000000..1e36470
--- /dev/null
+++ b/game/shared/tf/tf_weapon_pipebomblauncher.cpp
@@ -0,0 +1,643 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include "tf_weapon_pipebomblauncher.h"
+#include "tf_fx_shared.h"
+#include "tf_weapon_grenade_pipebomb.h"
+#include "in_buttons.h"
+#include "datacache/imdlcache.h"
+#include "tf_gamerules.h"
+
+// Client specific.
+#ifdef CLIENT_DLL
+#include "c_tf_player.h"
+#include <vgui_controls/Panel.h>
+#include <vgui/ISurface.h>
+#include "prediction.h"
+#include "c_tf_gamestats.h"
+// Server specific.
+#else
+#include "tf_player.h"
+#include "tf_gamestats.h"
+#endif
+
+#define TF_PIPEBOMB_HIGHLIGHT 1
+#define TF_PIPEBOMB_DETONATE 2
+
+#define TF_WEAPON_PIPEBOMBD_MODEL "models/weapons/w_models/w_stickybomb_d.mdl"
+
+#define TF_WEAPON_PIPEBOMB_LAUNCHER_CHARGE_SOUND "Weapon_StickyBombLauncher.ChargeUp"
+
+//=============================================================================
+//
+// Weapon Pipebomb Launcher tables.
+//
+IMPLEMENT_NETWORKCLASS_ALIASED( TFPipebombLauncher, DT_WeaponPipebombLauncher )
+
+BEGIN_NETWORK_TABLE_NOBASE( CTFPipebombLauncher, DT_PipebombLauncherLocalData )
+#ifdef CLIENT_DLL
+ RecvPropInt( RECVINFO( m_iPipebombCount ) ),
+ RecvPropFloat( RECVINFO( m_flChargeBeginTime ) ),
+#else
+ SendPropInt( SENDINFO( m_iPipebombCount ), 5, SPROP_UNSIGNED ),
+ SendPropFloat( SENDINFO( m_flChargeBeginTime ) ),
+#endif
+END_NETWORK_TABLE()
+
+
+BEGIN_NETWORK_TABLE( CTFPipebombLauncher, DT_WeaponPipebombLauncher )
+#ifdef CLIENT_DLL
+ RecvPropDataTable( "PipebombLauncherLocalData", 0, 0, &REFERENCE_RECV_TABLE( DT_PipebombLauncherLocalData ) ),
+#else
+ SendPropDataTable( "PipebombLauncherLocalData", 0, &REFERENCE_SEND_TABLE( DT_PipebombLauncherLocalData ), SendProxy_SendLocalWeaponDataTable ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CTFPipebombLauncher )
+ DEFINE_FIELD( m_flChargeBeginTime, FIELD_FLOAT )
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( tf_weapon_pipebomblauncher, CTFPipebombLauncher );
+PRECACHE_WEAPON_REGISTER( tf_weapon_pipebomblauncher );
+
+// Server specific.
+#ifndef CLIENT_DLL
+BEGIN_DATADESC( CTFPipebombLauncher )
+END_DATADESC()
+#endif
+
+//=============================================================================
+//
+// Weapon Pipebomb Launcher functions.
+//
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+CTFPipebombLauncher::CTFPipebombLauncher()
+{
+ m_bReloadsSingly = true;
+ m_flLastDenySoundTime = 0.0f;
+ m_bNoAutoRelease = false;
+ m_bWantsToShoot = false;
+#ifdef CLIENT_DLL
+ m_flNextBombCheckTime = 0;
+ m_bBombThinking = false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+CTFPipebombLauncher::~CTFPipebombLauncher()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::Spawn( void )
+{
+ m_iAltFireHint = HINT_ALTFIRE_PIPEBOMBLAUNCHER;
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset the charge when we holster
+//-----------------------------------------------------------------------------
+bool CTFPipebombLauncher::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ m_flChargeBeginTime = 0;
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset the charge when we deploy
+//-----------------------------------------------------------------------------
+bool CTFPipebombLauncher::Deploy( void )
+{
+ m_flChargeBeginTime = 0;
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::WeaponReset( void )
+{
+ BaseClass::WeaponReset();
+
+#ifndef CLIENT_DLL
+ DetonateRemotePipebombs( true );
+#endif
+
+ m_flChargeBeginTime = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::ItemPostFrame( void )
+{
+ BaseClass::ItemPostFrame();
+
+ if ( m_flChargeBeginTime > 0 )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ // If we're not holding down the attack button, launch our grenade
+ if ( m_iClip1 > 0 && !(pPlayer->m_nButtons & IN_ATTACK) && (pPlayer->m_afButtonReleased & IN_ATTACK) )
+ {
+ LaunchGrenade();
+ }
+ else if ( !m_bNoAutoRelease )
+ {
+ float flTotalChargeTime = gpGlobals->curtime - m_flChargeBeginTime;
+ if ( flTotalChargeTime >= GetChargeForceReleaseTime() )
+ {
+ ForceLaunchGrenade();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::PrimaryAttack( void )
+{
+ // Check for ammunition.
+ if ( m_iClip1 <= 0 && m_iClip1 != -1 )
+ return;
+
+ // Are we capable of firing again?
+ if ( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ if ( !CanAttack() )
+ {
+ m_flChargeBeginTime = 0;
+ return;
+ }
+
+ if ( m_flChargeBeginTime <= 0 )
+ {
+ // Set the weapon mode.
+ m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
+
+ // save that we had the attack button down
+ m_flChargeBeginTime = gpGlobals->curtime;
+
+ SendWeaponAnim( ACT_VM_PULLBACK );
+
+#ifdef CLIENT_DLL
+ EmitSound( TF_WEAPON_PIPEBOMB_LAUNCHER_CHARGE_SOUND );
+#endif // CLIENT_DLL
+ }
+ else
+ {
+ float flTotalChargeTime = gpGlobals->curtime - m_flChargeBeginTime;
+
+ if ( flTotalChargeTime >= GetChargeMaxTime() )
+ {
+ LaunchGrenade();
+ }
+ }
+
+#ifdef CLIENT_DLL
+ if ( GetDetonateMode() == TF_DETONATE_MODE_DOT && !m_bBombThinking )
+ {
+ m_bBombThinking = true;
+ SetContextThink( &CTFPipebombLauncher::BombHighlightThink, gpGlobals->curtime + 0.1f, "BOMB_HIGHLIGHT_THINK" );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+void CTFPipebombLauncher::BombHighlightThink( void )
+{
+ ModifyPipebombsInView( TF_PIPEBOMB_HIGHLIGHT );
+ if ( GetOwner() )
+ {
+ SetContextThink( &CTFPipebombLauncher::BombHighlightThink, gpGlobals->curtime + 0.1f, "BOMB_HIGHLIGHT_THINK" );
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::WeaponIdle( void )
+{
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( m_flChargeBeginTime > 0 && m_iClip1 > 0 && (pPlayer->m_afButtonReleased & IN_ATTACK) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ m_bWantsToShoot = true;
+ }
+ }
+
+ if ( m_bWantsToShoot )
+ {
+ LaunchGrenade();
+ }
+ else
+ {
+ BaseClass::WeaponIdle();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::LaunchGrenade( void )
+{
+ // Get the player owning the weapon.
+ CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
+ if ( !pPlayer )
+ return;
+
+ m_bWantsToShoot = false;
+
+ CalcIsAttackCritical();
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
+
+ CTFGrenadePipebombProjectile *pProjectile = static_cast<CTFGrenadePipebombProjectile*>( FireProjectile( pPlayer ) );
+ if ( pProjectile )
+ {
+ // Save the charge time to scale the detonation timer.
+ pProjectile->SetChargeTime( gpGlobals->curtime - m_flChargeBeginTime );
+
+#ifdef GAME_DLL
+ if ( GetDetonateMode() == TF_DETONATE_MODE_AIR )
+ {
+ pProjectile->m_bWallShatter = true;
+ }
+ else if ( GetDetonateMode() == TF_DETONATE_MODE_DOT )
+ {
+ pProjectile->m_bDefensiveBomb = true;
+ pProjectile->SetModel( TF_WEAPON_PIPEBOMBD_MODEL );
+ }
+
+ float flChargeDmg = 1.0f;
+ CALL_ATTRIB_HOOK_FLOAT( flChargeDmg, stickybomb_charge_damage_increase );
+ if ( flChargeDmg != 1.0f )
+ {
+ float flDamage = pProjectile->GetDamage();
+ flDamage += flDamage * ( flChargeDmg - 1.0f ) * GetCurrentCharge();
+ pProjectile->SetDamage( flDamage );
+ }
+#endif // GAME_DLL
+ }
+
+#ifdef CLIENT_DLL
+ C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
+ StopSound( TF_WEAPON_PIPEBOMB_LAUNCHER_CHARGE_SOUND );
+#else
+ pPlayer->SpeakWeaponFire();
+ CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
+#endif
+
+ // Set next attack times.
+
+ float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
+ m_flLastDenySoundTime = gpGlobals->curtime;
+
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+
+ // Check the reload mode and behave appropriately.
+ if ( m_bReloadsSingly )
+ {
+ m_iReloadMode.Set( TF_RELOAD_START );
+ }
+
+ m_flChargeBeginTime = 0;
+
+ if ( TFGameRules()->GameModeUsesUpgrades() )
+ {
+ PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
+ }
+}
+
+float CTFPipebombLauncher::GetProjectileSpeed( void )
+{
+ float flForwardSpeed = RemapValClamped( ( gpGlobals->curtime - m_flChargeBeginTime ),
+ 0.0f,
+ GetChargeMaxTime(),
+ TF_PIPEBOMB_MIN_CHARGE_VEL,
+ TF_PIPEBOMB_MAX_CHARGE_VEL );
+
+ return flForwardSpeed;
+}
+
+void CTFPipebombLauncher::AddPipeBomb( CTFGrenadePipebombProjectile *pBomb )
+{
+ PipebombHandle hHandle;
+ hHandle = pBomb;
+ m_Pipebombs.AddToTail( hHandle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add pipebombs to our list as they're fired
+//-----------------------------------------------------------------------------
+CBaseEntity *CTFPipebombLauncher::FireProjectile( CTFPlayer *pPlayer )
+{
+ CBaseEntity *pProjectile = BaseClass::FireProjectile( pPlayer );
+ if ( pProjectile )
+ {
+#ifdef GAME_DLL
+ // If we've gone over the max pipebomb count, detonate the oldest
+ int nMaxPipebombs = TF_WEAPON_PIPEBOMB_COUNT;
+ CALL_ATTRIB_HOOK_INT( nMaxPipebombs, add_max_pipebombs );
+ if ( m_Pipebombs.Count() >= nMaxPipebombs )
+ {
+ CTFGrenadePipebombProjectile *pTemp = m_Pipebombs[0];
+ if ( pTemp )
+ {
+ pTemp->SetTimer( gpGlobals->curtime ); // explode NOW
+ }
+
+ m_Pipebombs.Remove(0);
+ }
+
+ CTFGrenadePipebombProjectile *pPipebomb = (CTFGrenadePipebombProjectile*)pProjectile;
+
+ PipebombHandle hHandle;
+ hHandle = pPipebomb;
+ m_Pipebombs.AddToTail( hHandle );
+
+ m_iPipebombCount = m_Pipebombs.Count();
+#endif
+ }
+
+ return pProjectile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Detonate this demoman's pipebombs if secondary fire is down.
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::ItemBusyFrame( void )
+{
+#ifdef GAME_DLL
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
+ {
+ // We need to do this to catch the case of player trying to detonate
+ // pipebombs while in the middle of reloading.
+ SecondaryAttack();
+ }
+#endif
+
+ BaseClass::ItemBusyFrame();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Detonate active pipebombs
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::SecondaryAttack( void )
+{
+ if ( !CanAttack() )
+ return;
+
+ if ( m_iPipebombCount )
+ {
+ // Get a valid player.
+ CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ //If one or more pipebombs failed to detonate then play a sound.
+ if ( DetonateRemotePipebombs( false ) == true )
+ {
+ if ( m_flLastDenySoundTime <= gpGlobals->curtime )
+ {
+ // Deny!
+ m_flLastDenySoundTime = gpGlobals->curtime + 1;
+ WeaponSound( SPECIAL2 );
+ return;
+ }
+ }
+ else
+ {
+ // Play a detonate sound.
+ WeaponSound( SPECIAL3 );
+
+#ifdef GAME_DLL
+ IGameEvent *pDetEvent = gameeventmanager->CreateEvent( "demoman_det_stickies" );
+
+ if ( pDetEvent )
+ {
+ pDetEvent->SetInt( "player", pPlayer->entindex() );
+
+ // Send the event
+ gameeventmanager->FireEvent( pDetEvent );
+ }
+#endif
+ }
+ }
+}
+
+//=============================================================================
+//
+// Server specific functions.
+//
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::UpdateOnRemove(void)
+{
+ // If we just died, we want to fizzle our pipebombs.
+ // If the player switched classes, our pipebombs have already been removed.
+ DetonateRemotePipebombs( true );
+
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::ApplyPostHitEffects( const CTakeDamageInfo &inputInfo, CTFPlayer *pPlayer )
+{
+ if ( !GetTFPlayerOwner() )
+ return;
+
+ if ( pPlayer->m_Shared.GetWeaponKnockbackID() == -1 )
+ {
+ pPlayer->m_Shared.SetWeaponKnockbackID( GetTFPlayerOwner()->GetUserID() );
+ }
+}
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: If a pipebomb has been removed, remove it from our list
+//-----------------------------------------------------------------------------
+void CTFPipebombLauncher::DeathNotice( CBaseEntity *pVictim )
+{
+ Assert( dynamic_cast<CTFGrenadePipebombProjectile*>(pVictim) );
+
+ PipebombHandle hHandle;
+ hHandle = (CTFGrenadePipebombProjectile*)pVictim;
+ m_Pipebombs.FindAndRemove( hHandle );
+
+ m_iPipebombCount = m_Pipebombs.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove *with* explosions
+//-----------------------------------------------------------------------------
+bool CTFPipebombLauncher::DetonateRemotePipebombs( bool bFizzle )
+{
+ if ( GetDetonateMode() == TF_DETONATE_MODE_DOT && !bFizzle )
+ {
+ return ModifyPipebombsInView( TF_PIPEBOMB_DETONATE );
+ }
+
+ bool bFailedToDetonate = false;
+
+ int count = m_Pipebombs.Count();
+
+ for ( int i = 0; i < count; i++ )
+ {
+ CTFGrenadePipebombProjectile *pTemp = m_Pipebombs[i];
+ if ( pTemp )
+ {
+ //This guy will die soon enough.
+ if ( pTemp->IsEffectActive( EF_NODRAW ) )
+ continue;
+#ifdef GAME_DLL
+ if ( bFizzle )
+ {
+ pTemp->Fizzle();
+ }
+#endif
+
+ if ( bFizzle == false )
+ {
+ if ( ( gpGlobals->curtime - pTemp->m_flCreationTime ) < pTemp->GetLiveTime() )
+ {
+ if ( pTemp->GetLiveTime() <= 0.5f )
+ {
+ pTemp->SetDetonateOnPulse( true );
+ }
+ bFailedToDetonate = true;
+ continue;
+ }
+ }
+#ifdef GAME_DLL
+ if ( CanDestroyStickies() )
+ {
+ pTemp->DetonateStickies();
+ }
+ pTemp->Detonate();
+#endif
+ }
+ }
+
+ return bFailedToDetonate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFPipebombLauncher::ModifyPipebombsInView( int iEffect )
+{
+ CTFPlayer* pPlayer = ToTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return true;
+
+ // Dot product from the view angle to determine which bombs to detonate.
+ bool bFailedToDetonate = true;
+ int count = m_Pipebombs.Count();
+ for ( int i=0; i<count; ++i )
+ {
+ CTFGrenadePipebombProjectile *pTemp = m_Pipebombs[i];
+ if ( !pTemp || pTemp->IsEffectActive( EF_NODRAW ) )
+ continue;
+
+ Vector vecToTarget;
+ vecToTarget = pTemp->WorldSpaceCenter() - pPlayer->EyePosition();
+ vecToTarget.NormalizeInPlace();
+
+ Vector vecPlayerForward;
+ AngleVectors( pPlayer->EyeAngles(), &vecPlayerForward, NULL, NULL );
+ vecPlayerForward.NormalizeInPlace();
+
+ bool bArmed = ( ( gpGlobals->curtime - pTemp->m_flCreationTime ) > pTemp->GetLiveTime() );
+ float flDist = pPlayer->GetAbsOrigin().DistTo( pTemp->GetAbsOrigin() );
+ float flDot = DotProduct( vecToTarget, vecPlayerForward );
+
+ // Detonate sticky bombs directly under the crosshair or under our feet (to allow sticky jumping)
+ if ( flDot > 0.975f || flDist < pTemp->GetDamageRadius() )
+ {
+ switch ( iEffect )
+ {
+ case TF_PIPEBOMB_HIGHLIGHT:
+#ifdef CLIENT_DLL
+ pTemp->SetHighlight( true );
+#endif
+ break;
+ case TF_PIPEBOMB_DETONATE:
+ if ( bArmed )
+ {
+ bFailedToDetonate = false;
+#ifdef GAME_DLL
+ if ( CanDestroyStickies() )
+ {
+ pTemp->DetonateStickies();
+ }
+#endif
+ pTemp->Detonate();
+ }
+ break;
+ }
+ }
+ else if ( iEffect == TF_PIPEBOMB_HIGHLIGHT )
+ {
+#ifdef CLIENT_DLL
+ pTemp->SetHighlight( false );
+#endif
+ }
+ }
+
+ return bFailedToDetonate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFPipebombLauncher::Reload( void )
+{
+ if ( m_flChargeBeginTime > 0 )
+ return false;
+
+ return BaseClass::Reload();
+}