diff options
Diffstat (limited to 'game/shared/tf/tf_weapon_pipebomblauncher.cpp')
| -rw-r--r-- | game/shared/tf/tf_weapon_pipebomblauncher.cpp | 643 |
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(); +} |