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
//========= 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; |
|
} |