You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
816 lines
24 KiB
816 lines
24 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// TF Rocket Launcher
|
||
|
//
|
||
|
//=============================================================================
|
||
|
#include "cbase.h"
|
||
|
#include "tf_weapon_rocketlauncher.h"
|
||
|
#include "tf_fx_shared.h"
|
||
|
#include "tf_weaponbase_rocket.h"
|
||
|
#include "in_buttons.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 "soundenvelope.h"
|
||
|
#include "particle_property.h"
|
||
|
// Server specific.
|
||
|
#else
|
||
|
#include "tf_player.h"
|
||
|
#include "tf_obj_sentrygun.h"
|
||
|
#include "tf_projectile_arrow.h"
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#define BOMBARDMENT_ROCKET_MODEL "models/buildables/sentry3_rockets.mdl"
|
||
|
|
||
|
//=============================================================================
|
||
|
//
|
||
|
// Weapon Rocket Launcher tables.
|
||
|
//
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher, DT_WeaponRocketLauncher )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( CTFRocketLauncher, DT_WeaponRocketLauncher )
|
||
|
#ifndef CLIENT_DLL
|
||
|
// SendPropInt( SENDINFO( m_iSecondaryShotsFired ) ),
|
||
|
#else
|
||
|
// RecvPropInt( RECVINFO( m_iSecondaryShotsFired ) ),
|
||
|
#endif
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CTFRocketLauncher )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher, CTFRocketLauncher );
|
||
|
PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher );
|
||
|
|
||
|
// Server specific.
|
||
|
#ifndef CLIENT_DLL
|
||
|
BEGIN_DATADESC( CTFRocketLauncher )
|
||
|
END_DATADESC()
|
||
|
#endif
|
||
|
|
||
|
//=============================================================================
|
||
|
//
|
||
|
// Direct Hit tables.
|
||
|
//
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher_DirectHit, DT_WeaponRocketLauncher_DirectHit )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( CTFRocketLauncher_DirectHit, DT_WeaponRocketLauncher_DirectHit )
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CTFRocketLauncher_DirectHit )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher_directhit, CTFRocketLauncher_DirectHit );
|
||
|
PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher_directhit );
|
||
|
|
||
|
// Server specific.
|
||
|
#ifndef CLIENT_DLL
|
||
|
BEGIN_DATADESC( CTFRocketLauncher_DirectHit )
|
||
|
END_DATADESC()
|
||
|
#endif
|
||
|
|
||
|
//=============================================================================
|
||
|
//
|
||
|
// AIRSTRIKE BEGIN
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher_AirStrike, DT_WeaponRocketLauncher_AirStrike )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( CTFRocketLauncher_AirStrike, DT_WeaponRocketLauncher_AirStrike )
|
||
|
#ifndef CLIENT_DLL
|
||
|
// SendPropInt( SENDINFO( m_iRocketKills ) ),
|
||
|
#else
|
||
|
// RecvPropInt( RECVINFO( m_iRocketKills ) ),
|
||
|
#endif
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CTFRocketLauncher_AirStrike )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher_airstrike, CTFRocketLauncher_AirStrike );
|
||
|
PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher_airstrike );
|
||
|
|
||
|
// Server specific.
|
||
|
#ifndef CLIENT_DLL
|
||
|
BEGIN_DATADESC( CTFRocketLauncher_AirStrike )
|
||
|
END_DATADESC()
|
||
|
#endif
|
||
|
// AIRSTRIKE END
|
||
|
|
||
|
//CREATE_SIMPLE_WEAPON_TABLE( TFRocketLauncher_AirStrike, tf_weapon_rocketlauncher_airstrike )
|
||
|
//CREATE_SIMPLE_WEAPON_TABLE( TFRocketLauncher_Mortar, tf_weapon_rocketlauncher_mortar )
|
||
|
//=============================================================================
|
||
|
//
|
||
|
// Mortar tables.
|
||
|
//
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher_Mortar, DT_WeaponRocketLauncher_Mortar )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( CTFRocketLauncher_Mortar, DT_WeaponRocketLauncher_Mortar )
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CTFRocketLauncher_Mortar )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher_mortar, CTFRocketLauncher_Mortar );
|
||
|
PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher_mortar );
|
||
|
#endif // STAGING_ONLY
|
||
|
|
||
|
// Server specific.
|
||
|
#ifndef CLIENT_DLL
|
||
|
BEGIN_DATADESC( CTFRocketLauncher_Mortar )
|
||
|
END_DATADESC()
|
||
|
#endif
|
||
|
|
||
|
//=============================================================================
|
||
|
//
|
||
|
// Crossbow tables.
|
||
|
//
|
||
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFCrossbow, DT_Crossbow )
|
||
|
|
||
|
BEGIN_NETWORK_TABLE( CTFCrossbow, DT_Crossbow )
|
||
|
#ifdef CLIENT_DLL
|
||
|
RecvPropFloat( RECVINFO( m_flRegenerateDuration ) ),
|
||
|
RecvPropFloat( RECVINFO( m_flLastUsedTimestamp ) ),
|
||
|
#else
|
||
|
SendPropFloat( SENDINFO( m_flRegenerateDuration ), 0, SPROP_NOSCALE ),
|
||
|
SendPropFloat( SENDINFO( m_flLastUsedTimestamp ), 0, SPROP_NOSCALE ),
|
||
|
#endif
|
||
|
END_NETWORK_TABLE()
|
||
|
|
||
|
BEGIN_PREDICTION_DATA( CTFCrossbow )
|
||
|
END_PREDICTION_DATA()
|
||
|
|
||
|
LINK_ENTITY_TO_CLASS( tf_weapon_crossbow, CTFCrossbow );
|
||
|
PRECACHE_WEAPON_REGISTER( tf_weapon_crossbow );
|
||
|
|
||
|
// Server specific.
|
||
|
#ifndef CLIENT_DLL
|
||
|
BEGIN_DATADESC( CTFCrossbow )
|
||
|
END_DATADESC()
|
||
|
#endif
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
ConVar tf_airstrike_dmg_scale( "tf_airstrike_dmg_scale", "0.65", FCVAR_REPLICATED, "How much damage the mini rockets do compared to regular rocket" );
|
||
|
ConVar tf_mortar_allow_fulltracking( "tf_mortar_allow_fulltracking", "0.0", FCVAR_REPLICATED, "Enable to allow full tracking / infinte redirects for Mortar Launcher" );
|
||
|
#endif // STAGING_ONLY
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFRocketLauncher::CTFRocketLauncher()
|
||
|
{
|
||
|
m_bReloadsSingly = true;
|
||
|
m_nReloadPitchStep = 0;
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
m_bIsOverloading = false;
|
||
|
#endif //GAME_DLL
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Input : -
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CTFRocketLauncher::~CTFRocketLauncher()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
#ifndef CLIENT_DLL
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher::Precache()
|
||
|
{
|
||
|
BaseClass::Precache();
|
||
|
PrecacheParticleSystem( "rocketbackblast" );
|
||
|
|
||
|
// FIXME: DO WE STILL NEED THESE??
|
||
|
PrecacheScriptSound( "MVM.GiantSoldierRocketShoot" );
|
||
|
PrecacheScriptSound( "MVM.GiantSoldierRocketShootCrit" );
|
||
|
PrecacheScriptSound( "MVM.GiantSoldierRocketExplode" );
|
||
|
|
||
|
PrecacheScriptSound( "Weapon_Airstrike.AltFire" );
|
||
|
PrecacheScriptSound( "Weapon_Airstrike.Fail" );
|
||
|
//Building_Sentrygun.FireRocket
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void CTFRocketLauncher::ModifyEmitSoundParams( EmitSound_t ¶ms )
|
||
|
{
|
||
|
bool bBaseReloadSound = V_strcmp( params.m_pSoundName, "Weapon_RPG.Reload" ) == 0;
|
||
|
if ( AutoFiresFullClip() && ( bBaseReloadSound || V_strcmp( params.m_pSoundName, "Weapon_DumpsterRocket.Reload" ) == 0 ) )
|
||
|
{
|
||
|
float fMaxAmmoInClip = GetMaxClip1();
|
||
|
float fAmmoPercentage = static_cast< float >( m_nReloadPitchStep ) / fMaxAmmoInClip;
|
||
|
|
||
|
// Play a sound that gets higher pitched as more ammo is added
|
||
|
if ( bBaseReloadSound )
|
||
|
{
|
||
|
params.m_pSoundName = "Weapon_DumpsterRocket.Reload_FP";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
params.m_pSoundName = "Weapon_DumpsterRocket.Reload";
|
||
|
}
|
||
|
|
||
|
params.m_nPitch *= RemapVal( fAmmoPercentage, 0.0f, ( fMaxAmmoInClip - 1.0f ) / fMaxAmmoInClip, 0.79f, 1.19f );
|
||
|
params.m_nFlags |= SND_CHANGE_PITCH;
|
||
|
|
||
|
m_nReloadPitchStep = MIN( GetMaxClip1() - 1, m_nReloadPitchStep + 1 );
|
||
|
|
||
|
// The last rocket goes in right when this sound happens so that you can launch it before a misfire
|
||
|
IncrementAmmo();
|
||
|
m_bReloadedThroughAnimEvent = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CTFRocketLauncher::Misfire( void )
|
||
|
{
|
||
|
BaseClass::Misfire();
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
if ( CanOverload() )
|
||
|
{
|
||
|
CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
|
||
|
if ( !pPlayer )
|
||
|
return;
|
||
|
|
||
|
CTFBaseRocket *pRocket = dynamic_cast< CTFBaseRocket* >( BaseClass::FireProjectile( pPlayer ) );
|
||
|
if ( pRocket )
|
||
|
{
|
||
|
trace_t tr;
|
||
|
UTIL_TraceLine( pRocket->GetAbsOrigin(), pPlayer->EyePosition(), MASK_SOLID, pRocket, COLLISION_GROUP_NONE, &tr );
|
||
|
pRocket->Explode( &tr, pPlayer );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFRocketLauncher::CheckReloadMisfire( void )
|
||
|
{
|
||
|
if ( !CanOverload() )
|
||
|
return false;
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
CTFPlayer *pPlayer = GetTFPlayerOwner();
|
||
|
|
||
|
if ( m_bIsOverloading )
|
||
|
{
|
||
|
if ( Clip1() > 0 )
|
||
|
{
|
||
|
Misfire();
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_bIsOverloading = false;
|
||
|
}
|
||
|
}
|
||
|
else if ( Clip1() >= GetMaxClip1() || ( Clip1() > 0 && pPlayer && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) == 0 ) )
|
||
|
{
|
||
|
Misfire();
|
||
|
m_bIsOverloading = true;
|
||
|
return true;
|
||
|
}
|
||
|
#endif // GAME_DLL
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFRocketLauncher::ShouldBlockPrimaryFire()
|
||
|
{
|
||
|
return !AutoFiresFullClip();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBaseEntity *CTFRocketLauncher::FireProjectile( CTFPlayer *pPlayer )
|
||
|
{
|
||
|
m_flShowReloadHintAt = gpGlobals->curtime + 30;
|
||
|
CBaseEntity *pRocket = BaseClass::FireProjectile( pPlayer );
|
||
|
|
||
|
m_nReloadPitchStep = MAX( 0, m_nReloadPitchStep - 1 );
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
int iProjectile = 0;
|
||
|
CALL_ATTRIB_HOOK_INT( iProjectile, override_projectile_type );
|
||
|
if ( iProjectile == 0 )
|
||
|
{
|
||
|
iProjectile = GetWeaponProjectileType();
|
||
|
}
|
||
|
if ( pPlayer->IsPlayerClass( TF_CLASS_SOLDIER ) && IsCurrentAttackARandomCrit() && ( iProjectile == TF_PROJECTILE_ROCKET ) )
|
||
|
{
|
||
|
// Track consecutive crit shots for achievements
|
||
|
m_iConsecutiveCrits++;
|
||
|
if ( m_iConsecutiveCrits == 2 )
|
||
|
{
|
||
|
pPlayer->AwardAchievement( ACHIEVEMENT_TF_SOLDIER_SHOOT_MULT_CRITS );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_iConsecutiveCrits = 0;
|
||
|
}
|
||
|
m_bIsOverloading = false;
|
||
|
#endif
|
||
|
|
||
|
if ( TFGameRules()->GameModeUsesUpgrades() )
|
||
|
{
|
||
|
PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
|
||
|
}
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
#ifdef GAME_DLL
|
||
|
if ( pRocket && pPlayer && pPlayer->RocketJumped() )
|
||
|
{
|
||
|
int iRocketsApplyImpuse = 0;
|
||
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iRocketsApplyImpuse, mod_rocket_launch_impulse );
|
||
|
if ( iRocketsApplyImpuse )
|
||
|
{
|
||
|
// Apply force in opposite direction of rocket
|
||
|
Vector vecDir = pRocket->GetAbsVelocity();
|
||
|
Vector vecFlightDir = -vecDir;
|
||
|
VectorNormalize( vecFlightDir );
|
||
|
|
||
|
// Apply more force if looking down
|
||
|
QAngle angEye = EyeAngles();
|
||
|
float flForce = ( angEye.x > 60.f ) ? 700.f : 400.f;
|
||
|
Vector vecForce = vecFlightDir * flForce;
|
||
|
|
||
|
// DevMsg( "x.Ang: %f\tForce: %f\n", angEye.x, flForce );
|
||
|
|
||
|
// Prevent insane speeds
|
||
|
float flSpeed = vecForce.NormalizeInPlace();
|
||
|
const float flLimit = Min( 800.f, flSpeed );
|
||
|
pPlayer->ApplyAbsVelocityImpulse( flLimit * vecForce );
|
||
|
}
|
||
|
}
|
||
|
#endif // GAME_DLL
|
||
|
#endif // STAGING_ONLY
|
||
|
|
||
|
return pRocket;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher::ItemPostFrame( void )
|
||
|
{
|
||
|
CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
|
||
|
if ( !pOwner )
|
||
|
return;
|
||
|
|
||
|
BaseClass::ItemPostFrame();
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
|
||
|
if ( m_flShowReloadHintAt && m_flShowReloadHintAt < gpGlobals->curtime )
|
||
|
{
|
||
|
if ( Clip1() < GetMaxClip1() )
|
||
|
{
|
||
|
pOwner->HintMessage( HINT_SOLDIER_RPG_RELOAD );
|
||
|
}
|
||
|
m_flShowReloadHintAt = 0;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFRocketLauncher::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
|
||
|
{
|
||
|
m_flShowReloadHintAt = 0;
|
||
|
return BaseClass::DefaultReload( iClipSize1, iClipSize2, iActivity );
|
||
|
}
|
||
|
|
||
|
#ifdef CLIENT_DLL
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher::CreateMuzzleFlashEffects( C_BaseEntity *pAttachEnt, int nIndex )
|
||
|
{
|
||
|
BaseClass::CreateMuzzleFlashEffects( pAttachEnt, nIndex );
|
||
|
|
||
|
// Don't do backblast effects in first person
|
||
|
C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
|
||
|
if ( pOwner->IsLocalPlayer() )
|
||
|
return;
|
||
|
|
||
|
ParticleProp()->Init( this );
|
||
|
ParticleProp()->Create( "rocketbackblast", PATTACH_POINT_FOLLOW, "backblast" );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CTFRocketLauncher::GetWeaponProjectileType( void ) const
|
||
|
{
|
||
|
return BaseClass::GetWeaponProjectileType();
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
// CTFRocketLauncher_AirStrike BEGIN
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
CTFRocketLauncher_AirStrike::CTFRocketLauncher_AirStrike()
|
||
|
{
|
||
|
//m_iSecondaryShotsFired = 0;
|
||
|
}
|
||
|
|
||
|
#ifdef GAME_DLL
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher_AirStrike::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
|
||
|
{
|
||
|
BaseClass::OnPlayerKill( pVictim, info );
|
||
|
|
||
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
|
||
|
if ( !pOwner )
|
||
|
return;
|
||
|
|
||
|
int iDecap = pOwner->m_Shared.GetDecapitations() + 1;
|
||
|
if ( pVictim )
|
||
|
{
|
||
|
iDecap += pVictim->m_Shared.GetDecapitations();
|
||
|
}
|
||
|
pOwner->m_Shared.SetDecapitations( iDecap );
|
||
|
|
||
|
int iClipSizeOnKills = 0;
|
||
|
CALL_ATTRIB_HOOK_INT( iClipSizeOnKills, clipsize_increase_on_kill );
|
||
|
if ( iClipSizeOnKills && ( iDecap >= iClipSizeOnKills ) )
|
||
|
{
|
||
|
pOwner->AwardAchievement( ACHIEVEMENT_TF_SOLDIER_AIRSTRIKE_MAX_CLIP );
|
||
|
}
|
||
|
}
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
#endif
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CTFRocketLauncher_AirStrike::GetCount( void )
|
||
|
{
|
||
|
CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
|
||
|
if ( !pOwner )
|
||
|
return 0;
|
||
|
|
||
|
return pOwner->m_Shared.GetDecapitations();
|
||
|
}
|
||
|
|
||
|
////----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
//void CTFRocketLauncher_AirStrike::PrimaryAttack( void )
|
||
|
//{
|
||
|
// CTFPlayer *pPlayer = GetTFPlayerOwner();
|
||
|
// if ( !pPlayer )
|
||
|
// return;
|
||
|
//
|
||
|
// // If the player is blast jumping and hasn't fired a shot yet, we can initiate
|
||
|
// if ( pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) && m_iSecondaryShotsFired == 0 )
|
||
|
// {
|
||
|
// FireSecondaryRockets();
|
||
|
// }
|
||
|
// else
|
||
|
// {
|
||
|
// BaseClass::PrimaryAttack();
|
||
|
// }
|
||
|
//}
|
||
|
////----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
//bool CTFRocketLauncher_AirStrike::CanHolster( void )
|
||
|
//{
|
||
|
// if ( m_iSecondaryShotsFired > 0 )
|
||
|
// return false;
|
||
|
//
|
||
|
// return BaseClass::CanHolster();
|
||
|
//}
|
||
|
////-----------------------------------------------------------------------------
|
||
|
//void CTFRocketLauncher_AirStrike::ItemPostFrame( void )
|
||
|
//{
|
||
|
// // If allowed
|
||
|
// FireSecondaryRockets();
|
||
|
// BaseClass::ItemPostFrame();
|
||
|
//}
|
||
|
////-----------------------------------------------------------------------------
|
||
|
//void CTFRocketLauncher_AirStrike::ItemBusyFrame( void )
|
||
|
//{
|
||
|
// // If allowed
|
||
|
// FireSecondaryRockets();
|
||
|
// BaseClass::ItemBusyFrame();
|
||
|
//}
|
||
|
//
|
||
|
////-----------------------------------------------------------------------------
|
||
|
//void CTFRocketLauncher_AirStrike::FireSecondaryRockets()
|
||
|
//{
|
||
|
//#ifdef STAGING_ONLY
|
||
|
// if ( m_flNextPrimaryAttack >= gpGlobals->curtime )
|
||
|
// return;
|
||
|
//
|
||
|
// CTFPlayer *pPlayer = GetTFPlayerOwner();
|
||
|
// if ( !pPlayer )
|
||
|
// return;
|
||
|
//
|
||
|
// if ( !( pPlayer->m_nButtons & IN_ATTACK ) && m_iSecondaryShotsFired == 0 )
|
||
|
// return;
|
||
|
//
|
||
|
// int iAirBombardment = 0;
|
||
|
// CALL_ATTRIB_HOOK_INT( iAirBombardment, rj_air_bombardment );
|
||
|
// if ( !iAirBombardment )
|
||
|
// return;
|
||
|
//
|
||
|
// // This function and its checks are only on the server
|
||
|
// if ( !pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) )
|
||
|
// {
|
||
|
//#ifdef CLIENT_DLL
|
||
|
// // play fail sound locally
|
||
|
// //pPlayer->EmitSound( "Weapon_Airstrike.Fail" );
|
||
|
//#endif
|
||
|
// m_iSecondaryShotsFired = 0;
|
||
|
// return;
|
||
|
// }
|
||
|
//
|
||
|
// if ( m_iClip1 <= 0 && m_iSecondaryShotsFired == 0 )
|
||
|
// return;
|
||
|
//
|
||
|
// if ( m_bReloadsSingly )
|
||
|
// {
|
||
|
// m_iReloadMode.Set( TF_RELOAD_START );
|
||
|
// }
|
||
|
//
|
||
|
// float flFireDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
|
||
|
// flFireDelay += GetFireDelay();
|
||
|
// CALL_ATTRIB_HOOK_FLOAT( flFireDelay, mult_postfiredelay );
|
||
|
// CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flFireDelay, hwn_mult_postfiredelay );
|
||
|
//
|
||
|
// SendWeaponAnim( ACT_VM_PRIMARYATTACK );
|
||
|
// pPlayer->SetAnimation( PLAYER_ATTACK1 );
|
||
|
// pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
|
||
|
// m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay / 2.0f;
|
||
|
//
|
||
|
// // Want a different sound
|
||
|
// pPlayer->EmitSound( "Weapon_Airstrike.AltFire" );
|
||
|
//
|
||
|
//#ifdef GAME_DLL
|
||
|
// // Server only - create the rocket.
|
||
|
// Vector vecSrc;
|
||
|
// QAngle angForward;
|
||
|
// Vector vecOffset( 23.5f, 12.0f, -3.0f );
|
||
|
// GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
|
||
|
//
|
||
|
// CTFProjectile_SentryRocket *pProjectile = CTFProjectile_SentryRocket::Create( vecSrc, angForward, this, pPlayer );
|
||
|
//
|
||
|
// if ( pProjectile )
|
||
|
// {
|
||
|
// pProjectile->SetCritical( IsCurrentAttackACrit() );
|
||
|
// pProjectile->SetDamage( GetProjectileDamage() * tf_airstrike_dmg_scale.GetFloat() );
|
||
|
// pProjectile->SetDamageForceScale( tf_airstrike_dmg_scale.GetFloat() );
|
||
|
// }
|
||
|
//
|
||
|
// if ( m_iSecondaryShotsFired == 0 )
|
||
|
// {
|
||
|
// RemoveProjectileAmmo( pPlayer );
|
||
|
// }
|
||
|
//
|
||
|
// m_iSecondaryShotsFired++;
|
||
|
// if ( m_iSecondaryShotsFired >= 3 )
|
||
|
// {
|
||
|
// // Decrement ammo and reset
|
||
|
// m_iSecondaryShotsFired = 0;
|
||
|
// // Give normal delay between shots here
|
||
|
// m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
|
||
|
// }
|
||
|
//#endif
|
||
|
//
|
||
|
//#endif
|
||
|
//}
|
||
|
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
// CTFRocketLauncher_Mortar BEGIN
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
//CTFRocketLauncher_Mortar::CTFRocketLauncher_Mortar()
|
||
|
//{
|
||
|
//
|
||
|
//}
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
CBaseEntity *CTFRocketLauncher_Mortar::FireProjectile( CTFPlayer *pPlayer )
|
||
|
{
|
||
|
// Fire the rocket
|
||
|
CBaseEntity* pRocket = BaseClass::FireProjectile( pPlayer );
|
||
|
// Add it to my list
|
||
|
#ifdef GAME_DLL
|
||
|
m_vecRockets.AddToTail( pRocket );
|
||
|
#endif
|
||
|
|
||
|
return pRocket;
|
||
|
}
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher_Mortar::SecondaryAttack( void )
|
||
|
{
|
||
|
RedirectRockets();
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher_Mortar::ItemPostFrame( void )
|
||
|
{
|
||
|
#ifdef GAME_DLL
|
||
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||
|
if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
|
||
|
{
|
||
|
// If allowed
|
||
|
RedirectRockets();
|
||
|
}
|
||
|
#endif
|
||
|
BaseClass::ItemPostFrame();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher_Mortar::ItemBusyFrame( void )
|
||
|
{
|
||
|
#ifdef GAME_DLL
|
||
|
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
|
||
|
if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
|
||
|
{
|
||
|
// If allowed
|
||
|
RedirectRockets();
|
||
|
}
|
||
|
#endif
|
||
|
BaseClass::ItemBusyFrame();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFRocketLauncher_Mortar::RedirectRockets( void )
|
||
|
{
|
||
|
#ifdef GAME_DLL
|
||
|
if ( m_vecRockets.Count() <= 0 )
|
||
|
return;
|
||
|
|
||
|
CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
|
||
|
if ( !pOwner )
|
||
|
return;
|
||
|
|
||
|
Vector vecEye = pOwner->EyePosition();
|
||
|
Vector vecForward, vecRight, vecUp;
|
||
|
AngleVectors( pOwner->EyeAngles(), &vecForward, &vecRight, &vecUp );
|
||
|
|
||
|
trace_t tr;
|
||
|
UTIL_TraceLine( vecEye, vecEye + vecForward * MAX_TRACE_LENGTH, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
|
||
|
float flVel = 1100.0f;
|
||
|
|
||
|
FOR_EACH_VEC_BACK( m_vecRockets, i )
|
||
|
{
|
||
|
CBaseEntity* pRocket = m_vecRockets[i].Get();
|
||
|
// Remove targets that have disappeared
|
||
|
if ( !pRocket || pRocket->GetOwnerEntity() != GetOwnerEntity() )
|
||
|
{
|
||
|
m_vecRockets.Remove( i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Give the rocket a new target
|
||
|
Vector vecDir = pRocket->WorldSpaceCenter() - tr.endpos;
|
||
|
VectorNormalize( vecDir );
|
||
|
|
||
|
Vector vecVel = pRocket->GetAbsVelocity();
|
||
|
vecVel = -flVel * vecDir;
|
||
|
pRocket->SetAbsVelocity( vecVel );
|
||
|
|
||
|
QAngle newAngles;
|
||
|
VectorAngles( -vecDir, newAngles );
|
||
|
pRocket->SetAbsAngles( newAngles );
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
if ( !tf_mortar_allow_fulltracking.GetBool() )
|
||
|
{
|
||
|
// only allow a single redirect
|
||
|
m_vecRockets.Remove( i );
|
||
|
}
|
||
|
#else
|
||
|
m_vecRockets.Remove( i );
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
// CROSSBOW BEGIN
|
||
|
//----------------------------------------------------------------------------------------------------------------------------------------------------------
|
||
|
bool CTFCrossbow::Holster( CBaseCombatWeapon *pSwitchingTo )
|
||
|
{
|
||
|
// Allow Crossbow to silently reload like the flaregun
|
||
|
if ( m_iClip1 == 0 )
|
||
|
{
|
||
|
// These Values need to match the anim times since all this stuff is actually driven by animation sequence time in the base code
|
||
|
float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
|
||
|
|
||
|
float flReloadTime = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeReload;
|
||
|
CALL_ATTRIB_HOOK_FLOAT( flReloadTime, mult_reload_time );
|
||
|
CALL_ATTRIB_HOOK_FLOAT( flReloadTime, mult_reload_time_hidden );
|
||
|
CALL_ATTRIB_HOOK_FLOAT( flReloadTime, fast_reload );
|
||
|
|
||
|
float flIdleTime = GetLastPrimaryAttackTime() + flFireDelay + flReloadTime;
|
||
|
if ( GetWeaponIdleTime() < flIdleTime )
|
||
|
{
|
||
|
SetWeaponIdleTime( flIdleTime );
|
||
|
m_flNextPrimaryAttack = flIdleTime;
|
||
|
}
|
||
|
|
||
|
IncrementAmmo();
|
||
|
}
|
||
|
|
||
|
return BaseClass::Holster( pSwitchingTo );
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFCrossbow::SecondaryAttack( void )
|
||
|
{
|
||
|
// If this is the jarate bolt crossbow, make sure we are allowed to do it
|
||
|
int iMilkBolt = 0;
|
||
|
CALL_ATTRIB_HOOK_INT( iMilkBolt, fires_milk_bolt );
|
||
|
if ( iMilkBolt )
|
||
|
{
|
||
|
CTFPlayer *pPlayer = GetTFPlayerOwner();
|
||
|
if ( !pPlayer )
|
||
|
return;
|
||
|
|
||
|
if ( !CanAttack() )
|
||
|
return;
|
||
|
|
||
|
if ( m_flNextPrimaryAttack > gpGlobals->curtime )
|
||
|
return;
|
||
|
|
||
|
// Can we attack
|
||
|
if ( GetProgress() >= 1.0f )
|
||
|
{
|
||
|
// Call Primary Attack and modify the projectile
|
||
|
m_bMilkNextAttack = true;
|
||
|
PrimaryAttack();
|
||
|
m_flRegenerateDuration = iMilkBolt;
|
||
|
m_flLastUsedTimestamp = gpGlobals->curtime;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFCrossbow::ModifyProjectile( CBaseEntity* pProj )
|
||
|
{
|
||
|
#ifdef GAME_DLL
|
||
|
if ( m_bMilkNextAttack )
|
||
|
{
|
||
|
CTFProjectile_Arrow* pMainArrow = assert_cast<CTFProjectile_Arrow*>( pProj );
|
||
|
if ( pMainArrow )
|
||
|
{
|
||
|
pMainArrow->SetApplyMilkOnHit();
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
m_bMilkNextAttack = false;
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFCrossbow::ItemPostFrame( void )
|
||
|
{
|
||
|
BaseClass::ItemPostFrame();
|
||
|
m_bMilkNextAttack = false;
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CTFCrossbow::GetProjectileSpeed( void )
|
||
|
{
|
||
|
return RemapValClamped( 0.75f, 0.0f, 1.f, 1800, 2600 ); // Temp, if we want to ramp.
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CTFCrossbow::GetProjectileGravity( void )
|
||
|
{
|
||
|
return RemapValClamped( 0.75f, 0.0f, 1.f, 0.5, 0.1 ); // Temp, if we want to ramp.
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTFCrossbow::IsViewModelFlipped( void )
|
||
|
{
|
||
|
return !BaseClass::IsViewModelFlipped(); // Invert because arrows are backwards by default.
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTFCrossbow::WeaponRegenerate( void )
|
||
|
{
|
||
|
BaseClass::WeaponRegenerate();
|
||
|
m_flLastUsedTimestamp = 0;
|
||
|
}
|
||
|
//-----------------------------------------------------------------------------
|
||
|
inline float CTFCrossbow::GetProgress( void )
|
||
|
{
|
||
|
int iMilkBolt = 0;
|
||
|
CALL_ATTRIB_HOOK_INT( iMilkBolt, fires_milk_bolt );
|
||
|
if ( iMilkBolt == 0 )
|
||
|
return 0;
|
||
|
|
||
|
float meltedTime = gpGlobals->curtime - m_flLastUsedTimestamp;
|
||
|
return meltedTime / m_flRegenerateDuration;
|
||
|
}
|