diff options
Diffstat (limited to 'game/shared/tf/tf_weapon_rocketlauncher.cpp')
| -rw-r--r-- | game/shared/tf/tf_weapon_rocketlauncher.cpp | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/game/shared/tf/tf_weapon_rocketlauncher.cpp b/game/shared/tf/tf_weapon_rocketlauncher.cpp new file mode 100644 index 0000000..7bde0e8 --- /dev/null +++ b/game/shared/tf/tf_weapon_rocketlauncher.cpp @@ -0,0 +1,816 @@ +//========= 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; +}
\ No newline at end of file |