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.
625 lines
19 KiB
625 lines
19 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// TF Nail |
|
// |
|
//============================================================================= |
|
#include "cbase.h" |
|
#include "tf_projectile_flare.h" |
|
#include "soundent.h" |
|
#include "tf_fx.h" |
|
#include "tf_player.h" |
|
#include "tf_weapon_flaregun.h" |
|
#include "tf_gamerules.h" |
|
|
|
//============================================================================= |
|
// |
|
// TF Flare Projectile functions (Server specific). |
|
// |
|
#define FLARE_MODEL "models/weapons/w_models/w_flaregun_shell.mdl" |
|
#define FLARE_GRAVITY 0.3f |
|
#define FLARE_SPEED 2000.0f |
|
|
|
#define FLARE_THINK_CONTEXT "CTFProjectile_FlareThink" |
|
|
|
LINK_ENTITY_TO_CLASS( tf_projectile_flare, CTFProjectile_Flare ); |
|
PRECACHE_WEAPON_REGISTER( tf_projectile_flare ); |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Flare, DT_TFProjectile_Flare ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFProjectile_Flare, DT_TFProjectile_Flare ) |
|
SendPropBool( SENDINFO( m_bCritical ) ), |
|
END_NETWORK_TABLE() |
|
|
|
BEGIN_DATADESC( CTFProjectile_Flare ) |
|
DEFINE_THINKFUNC( ImpactThink ), |
|
END_DATADESC() |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Flare::CTFProjectile_Flare() |
|
{ |
|
m_bIsFromTaunt = false; |
|
m_bCritical = false; |
|
m_bImpact = false; |
|
m_flImpactTime = 0.0f; |
|
m_flNextSeekUpdate = 0.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Flare::~CTFProjectile_Flare() |
|
{ |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFProjectile_Flare *CTFProjectile_Flare::Create( CBaseEntity *pLauncher, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, CBaseEntity *pScorer ) |
|
{ |
|
CTFProjectile_Flare *pFlare = static_cast<CTFProjectile_Flare*>( CBaseEntity::CreateNoSpawn( "tf_projectile_flare", vecOrigin, vecAngles, pOwner ) ); |
|
if ( !pFlare ) |
|
return NULL; |
|
|
|
pFlare->SetLauncher( pLauncher ); |
|
|
|
// Initialize the owner. |
|
pFlare->SetOwnerEntity( pOwner ); |
|
|
|
// Set team. |
|
pFlare->ChangeTeam( pOwner->GetTeamNumber() ); |
|
|
|
// Save the scoring player. |
|
pFlare->SetScorer( pScorer ); |
|
|
|
// Spawn. |
|
DispatchSpawn( pFlare ); |
|
|
|
// Setup the initial velocity. |
|
Vector vecForward, vecRight, vecUp; |
|
AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp ); |
|
|
|
float flLaunchSpeed = pFlare->GetProjectileSpeed(); |
|
|
|
Vector vecVelocity = vecForward * flLaunchSpeed; |
|
pFlare->SetAbsVelocity( vecVelocity ); |
|
pFlare->SetupInitialTransmittedGrenadeVelocity( vecVelocity ); |
|
|
|
float flGravity = FLARE_GRAVITY; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLauncher, flGravity, mult_projectile_speed ); |
|
pFlare->SetGravity( flGravity ); |
|
|
|
// Setup the initial angles. |
|
QAngle angles; |
|
VectorAngles( vecVelocity, angles ); |
|
pFlare->SetAbsAngles( angles ); |
|
|
|
return pFlare; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::Spawn() |
|
{ |
|
SetModel( FLARE_MODEL ); |
|
BaseClass::Spawn(); |
|
|
|
float flHeatSeekPower = GetHeatSeekPower(); |
|
|
|
if ( flHeatSeekPower > 0.0f ) |
|
{ |
|
SetMoveType( MOVETYPE_CUSTOM, MOVECOLLIDE_DEFAULT ); |
|
SetGravity( FLARE_GRAVITY ); |
|
} |
|
else |
|
{ |
|
SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM ); |
|
SetGravity( FLARE_GRAVITY ); |
|
} |
|
|
|
// Set team. |
|
m_nSkin = ( GetTeamNumber() == TF_TEAM_BLUE ) ? 1 : 0; |
|
|
|
m_flCreationTime = gpGlobals->curtime; |
|
|
|
CTFPlayer *pScorer = ToTFPlayer( GetScorer() ); |
|
if ( pScorer && pScorer->IsTaunting() && m_flCreationTime >= pScorer->GetTauntAttackTime() ) |
|
{ |
|
m_bIsFromTaunt = true; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::Precache() |
|
{ |
|
PrecacheModel( FLARE_MODEL ); |
|
PrecacheParticleSystem( "flaregun_trail_red" ); |
|
PrecacheParticleSystem( "flaregun_trail_crit_red" ); |
|
PrecacheParticleSystem( "flaregun_trail_blue" ); |
|
PrecacheParticleSystem( "flaregun_trail_crit_blue" ); |
|
PrecacheParticleSystem( "drg_manmelter_projectile" ); |
|
PrecacheParticleSystem( "scorchshot_trail_red" ); |
|
PrecacheParticleSystem( "scorchshot_trail_crit_red" ); |
|
PrecacheParticleSystem( "scorchshot_trail_blue" ); |
|
PrecacheParticleSystem( "scorchshot_trail_crit_blue" ); |
|
PrecacheParticleSystem( "Explosions_MA_FlyingEmbers" ); |
|
|
|
PrecacheParticleSystem( "pyrovision_flaregun_trail_blue" ); |
|
PrecacheParticleSystem( "pyrovision_flaregun_trail_red" ); |
|
PrecacheParticleSystem( "pyrovision_flaregun_trail_crit_blue" ); |
|
PrecacheParticleSystem( "pyrovision_flaregun_trail_crit_red" ); |
|
PrecacheParticleSystem( "pyrovision_scorchshot_trail_blue" ); |
|
PrecacheParticleSystem( "pyrovision_scorchshot_trail_red" ); |
|
PrecacheParticleSystem( "pyrovision_scorchshot_trail_crit_blue" ); |
|
PrecacheParticleSystem( "pyrovision_scorchshot_trail_crit_red" ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::SetScorer( CBaseEntity *pScorer ) |
|
{ |
|
m_Scorer = pScorer; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CBasePlayer *CTFProjectile_Flare::GetScorer( void ) |
|
{ |
|
return dynamic_cast<CBasePlayer *>( m_Scorer.Get() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFProjectile_Flare::GetDamageType() |
|
{ |
|
int iDmgType = BaseClass::GetDamageType(); |
|
if ( m_bCritical ) |
|
{ |
|
iDmgType |= DMG_CRITICAL; |
|
} |
|
|
|
return iDmgType; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::Explode( trace_t *pTrace, CBaseEntity *pOther ) |
|
{ |
|
Vector vecOrigin = GetAbsOrigin(); |
|
|
|
CBaseEntity *pAttacker = GetOwnerEntity(); |
|
IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker ); |
|
if ( pScorerInterface ) |
|
{ |
|
pAttacker = pScorerInterface->GetScorer(); |
|
} |
|
|
|
CTFPlayer *pTFVictim = ToTFPlayer( pOther ); |
|
|
|
CTFFlareGun *pFlareGun = dynamic_cast< CTFFlareGun* >( GetLauncher() ); |
|
if ( pFlareGun ) |
|
{ |
|
if ( pFlareGun->GetFlareGunType() == FLAREGUN_SCORCHSHOT ) |
|
{ |
|
// When the scorch shot hits a player... |
|
if ( pTFVictim ) |
|
{ |
|
// Now it only collides with the world |
|
SetCollisionGroup( COLLISION_GROUP_DEBRIS ); |
|
|
|
Vector vVelocity = GetAbsVelocity(); |
|
|
|
// Check if burning before damage |
|
bool bIsBurningVictim = pTFVictim->m_Shared.InCond( TF_COND_BURNING ); |
|
int iDamageType = GetDamageType(); |
|
|
|
// Prevent the normal push force cause we are going to add it |
|
iDamageType |= DMG_PREVENT_PHYSICS_FORCE; |
|
|
|
// Damage the player to push them back |
|
CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), iDamageType, m_bIsFromTaunt ? TF_DMG_CUSTOM_FLARE_PELLET : 0 ); |
|
pTFVictim->TakeDamage( info ); |
|
|
|
bool bIsEnemy = pAttacker && pTFVictim->GetTeamNumber() != pAttacker->GetTeamNumber(); |
|
|
|
// Quick Fix Uber and teammates are immune to the force |
|
if ( !pTFVictim->m_Shared.InCond( TF_COND_MEGAHEAL ) && bIsEnemy ) |
|
{ |
|
Vector vecToTarget; |
|
vecToTarget = vVelocity; |
|
VectorNormalize( vecToTarget ); |
|
vecToTarget.z = 1.0; |
|
|
|
// apply airblast - Apply stun if they are effectively grounded so we can knock them up |
|
if ( !pTFVictim->m_Shared.InCond( TF_COND_KNOCKED_INTO_AIR ) ) |
|
{ |
|
pTFVictim->m_Shared.StunPlayer( 0.5, 1.f, TF_STUN_MOVEMENT, ToTFPlayer( pAttacker ) ); |
|
} |
|
|
|
float flForce = bIsBurningVictim ? 400.0f : 100.0f; |
|
pTFVictim->ApplyAirBlastImpulse( vecToTarget * flForce ); |
|
} |
|
|
|
// It loses almost all of its speed and pops into the air |
|
vVelocity.x *= 0.07f; |
|
vVelocity.y *= 0.07f; |
|
vVelocity.z = 100.0f; |
|
SetAbsVelocity( vVelocity + RandomVector( -2.0f, 2.0f ) ); |
|
|
|
// Point the new direction and randomly flip |
|
QAngle angForward; |
|
VectorAngles( vVelocity, angForward ); |
|
SetAbsAngles( angForward ); |
|
|
|
QAngle angRotation = RandomAngle( 180.0f, 720.0f ); |
|
angRotation.x *= ( RandomInt( 0, 1 ) == 0 ? 1 : -1 ); |
|
angRotation.y *= ( RandomInt( 0, 1 ) == 0 ? 1 : -1 ); |
|
angRotation.z *= ( RandomInt( 0, 1 ) == 0 ? 1 : -1 ); |
|
|
|
SetLocalAngularVelocity( angRotation ); |
|
|
|
CPVSFilter filter( vecOrigin ); |
|
EmitSound( filter, entindex(), "Rubber.BulletImpact" ); |
|
|
|
// Save this entity as enemy, they will take 100% damage. |
|
if ( m_hEnemy.Get() == NULL ) |
|
{ |
|
m_hEnemy = pTFVictim; |
|
} |
|
|
|
return; |
|
} |
|
} |
|
} |
|
|
|
// If we've already got an impact time, don't impact again. |
|
if ( m_flImpactTime > 0.0 ) |
|
return; |
|
|
|
// Save this entity as enemy, they will take 100% damage. |
|
if ( m_hEnemy.Get() == NULL ) |
|
{ |
|
m_hEnemy = pOther; |
|
} |
|
|
|
if ( !pTFVictim ) |
|
{ |
|
m_bImpact = true; |
|
} |
|
|
|
// Invisible. |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
m_takedamage = DAMAGE_NO; |
|
|
|
bool bDetonate = false; |
|
bool bNoRandomCrit = false; |
|
if ( pFlareGun ) |
|
{ |
|
switch ( pFlareGun->GetFlareGunType() ) |
|
{ |
|
case FLAREGUN_DETONATE: |
|
bDetonate = true; |
|
break; |
|
|
|
case FLAREGUN_GRORDBORT: |
|
bNoRandomCrit = true; |
|
break; |
|
|
|
case FLAREGUN_SCORCHSHOT: |
|
bDetonate = true; |
|
bNoRandomCrit = true; |
|
break; |
|
} |
|
} |
|
|
|
// Flares that hit a burning player crit, unless it's a detonate flare - they mini-crit |
|
if ( pTFVictim && pTFVictim->m_Shared.InCond( TF_COND_BURNING ) && !bDetonate && !bNoRandomCrit ) |
|
{ |
|
m_bCritical = true; |
|
} |
|
|
|
CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), GetDamageType(), TF_DMG_CUSTOM_BURNING_FLARE ); |
|
pOther->TakeDamage( info ); |
|
|
|
// Remove the flare. |
|
if ( m_bImpact ) |
|
{ |
|
SetMoveType( MOVETYPE_FLY ); |
|
SetAbsVelocity( vec3_origin ); |
|
|
|
m_vecImpactNormal = pTrace->plane.normal; |
|
m_flImpactTime = gpGlobals->curtime + 0.1f; |
|
|
|
// Stick into and object and fizzle a little while. |
|
SetContextThink( &CTFProjectile_Flare::ImpactThink, gpGlobals->curtime, FLARE_THINK_CONTEXT ); |
|
|
|
// Only do this for the Detonator |
|
if ( bDetonate ) |
|
{ |
|
// Scorch Shot can still light others in this case |
|
Detonate( pFlareGun->GetFlareGunType() != FLAREGUN_SCORCHSHOT ); |
|
} |
|
} |
|
else |
|
{ |
|
// Impact player sound. |
|
CPVSFilter filter( vecOrigin ); |
|
EmitSound( filter, pOther->entindex(), "TFPlayer.FlareImpact" ); |
|
|
|
SendDeathNotice(); |
|
UTIL_Remove( this ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Custom explode for air burst flare |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::Explode_Air( trace_t *pTrace, int bitsDamageType, bool bSelfOnly ) |
|
{ |
|
// Invisible. |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
m_takedamage = DAMAGE_NO; |
|
|
|
// Play explosion sound and effect. |
|
Vector vecOrigin = GetAbsOrigin(); |
|
CPVSFilter filter( vecOrigin ); |
|
|
|
CBaseEntity *pAttacker = GetOwnerEntity(); |
|
int nDefID = -1; |
|
WeaponSound_t nSound = SPECIAL1; |
|
if ( pAttacker ) |
|
{ |
|
CTFFlareGun *pFlareGun = dynamic_cast<CTFFlareGun*>( ToTFPlayer( pAttacker )->GetActiveWeapon() ); |
|
if ( pFlareGun ) |
|
{ |
|
CEconItemView *pItem = pFlareGun->GetAttributeContainer()->GetItem(); |
|
nDefID = pItem->GetItemDefIndex(); |
|
} |
|
|
|
// Damage. |
|
IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker ); |
|
if ( pScorerInterface ) |
|
{ |
|
pAttacker = pScorerInterface->GetScorer(); |
|
} |
|
|
|
float flRadius = bSelfOnly ? 0.f : GetRadius(); |
|
|
|
if ( bSelfOnly ) |
|
{ |
|
bitsDamageType |= DMG_BLAST; |
|
nSound = SPECIAL2; |
|
} |
|
|
|
#if defined( _DEBUG ) && defined( STAGING_ONLY ) |
|
// Debug! |
|
ConVarRef tf_rocket_show_radius( "tf_rocket_show_radius" ); |
|
if ( tf_rocket_show_radius.GetBool() ) |
|
{ |
|
DrawRadius( flRadius ); |
|
} |
|
#endif |
|
CTakeDamageInfo info( this, pAttacker, m_hLauncher, vec3_origin, vecOrigin, GetDamage(), bitsDamageType | DMG_HALF_FALLOFF, TF_DMG_CUSTOM_FLARE_EXPLOSION ); |
|
CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, TF_FLARE_RADIUS_FOR_FJS ); |
|
TFGameRules()->RadiusDamage( radiusinfo ); |
|
} |
|
|
|
const char *pszParticle = bSelfOnly ? "Explosions_MA_FlyingEmbers" : "ExplosionCore_MidAir_Flare"; |
|
TE_TFExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), entindex(), nDefID, nSound ); |
|
TE_TFParticleEffect( filter, 0.0f, pszParticle, vecOrigin, pTrace->plane.normal, vec3_angle ); |
|
CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 ); |
|
|
|
SendDeathNotice(); |
|
UTIL_Remove( this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Alt-fire air burst flare |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::Detonate( bool bSelfOnly ) |
|
{ |
|
trace_t tr; |
|
Vector vecSpot; |
|
|
|
SetThink( NULL ); |
|
|
|
vecSpot = GetAbsOrigin() + Vector ( 0 , 0 , 8 ); |
|
UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -32 ), MASK_SHOT_HULL, this, COLLISION_GROUP_NONE, & tr); |
|
|
|
Explode_Air( &tr, GetDamageType(), bSelfOnly ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFProjectile_Flare::GetRadius( void ) |
|
{ |
|
float flRadius = TF_FLARE_DET_RADIUS; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flRadius, mult_explosion_radius ); |
|
return flRadius; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Let the flaregun know we're gone |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::SendDeathNotice( void ) |
|
{ |
|
CBaseEntity *pAttacker = GetOwnerEntity(); |
|
if ( !pAttacker ) |
|
return; |
|
|
|
CTFFlareGun *pFlareGun = dynamic_cast<CTFFlareGun*>( ToTFPlayer( pAttacker )->GetActiveWeapon() ); |
|
if ( pFlareGun && pFlareGun->GetFlareGunType() == FLAREGUN_DETONATE ) |
|
{ |
|
pFlareGun->DeathNotice( this ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::ImpactThink( void ) |
|
{ |
|
if ( gpGlobals->curtime > m_flImpactTime ) |
|
{ |
|
// If we hit anything other than the player create an impact - effect, sound, etc. |
|
if ( m_hEnemy.Get() ) |
|
{ |
|
Vector vecOrigin = GetAbsOrigin(); |
|
CPVSFilter filter( vecOrigin ); |
|
TE_TFExplosion( filter, 0.0f, vecOrigin, m_vecImpactNormal, GetWeaponID(), m_hEnemy.Get()->entindex() ); |
|
} |
|
|
|
SendDeathNotice(); |
|
UTIL_Remove( this ); |
|
SetContextThink( NULL, 0, FLARE_THINK_CONTEXT ); |
|
} |
|
else |
|
{ |
|
SetContextThink( &CTFProjectile_Flare::ImpactThink, gpGlobals->curtime + 0.1f, FLARE_THINK_CONTEXT ); |
|
} |
|
} |
|
|
|
void CTFProjectile_Flare::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity ) |
|
{ |
|
if ( m_flNextSeekUpdate < gpGlobals->curtime ) |
|
{ |
|
CTFPlayer *pBestTarget = NULL; |
|
const float flMaxSeekDistanceSqr = ( 1024.0f * 1024.0f ); |
|
float flBestDistance = flMaxSeekDistanceSqr; |
|
|
|
// Loop through players and attempt to find a seek target |
|
int i; |
|
for ( i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); |
|
if ( !pPlayer ) |
|
continue; |
|
|
|
bool bBurning = pPlayer->m_Shared.InCond( TF_COND_BURNING ); |
|
|
|
if ( !bBurning || |
|
pPlayer->InSameTeam( this ) || |
|
( pPlayer->m_Shared.GetDisguiseTeam() == GetTeamNumber() && !bBurning ) || |
|
( pPlayer->m_Shared.IsStealthed() && !bBurning ) || |
|
pPlayer->GetTeamNumber() == TEAM_SPECTATOR || |
|
!pPlayer->IsAlive() ) |
|
{ |
|
continue; |
|
} |
|
|
|
Vector vToTarget = pPlayer->WorldSpaceCenter() - GetAbsOrigin(); |
|
VectorNormalize( vToTarget ); |
|
|
|
Vector vForward = *pNewVelocity; |
|
VectorNormalize( vForward ); |
|
|
|
if ( vToTarget.Dot( vForward ) < -0.25f ) |
|
continue; |
|
|
|
float flDistanceSqr = pPlayer->WorldSpaceCenter().DistToSqr( GetAbsOrigin() ); |
|
if ( flBestDistance > flDistanceSqr ) |
|
{ |
|
trace_t tr; |
|
UTIL_TraceLine( pPlayer->WorldSpaceCenter(), GetAbsOrigin(), MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr ); |
|
if ( !tr.DidHit() || tr.m_pEnt == this ) |
|
{ |
|
flBestDistance = flDistanceSqr; |
|
pBestTarget = pPlayer; |
|
} |
|
} |
|
} |
|
|
|
float flHeatSeekPower = GetHeatSeekPower(); |
|
|
|
QAngle angToTarget = *pNewAngles; |
|
|
|
if ( pBestTarget ) |
|
{ |
|
Vector vToTarget = pBestTarget->WorldSpaceCenter() - GetAbsOrigin(); |
|
VectorAngles( vToTarget, angToTarget ); |
|
} |
|
|
|
const float flUpdatesPerSecond = 4.0f; |
|
|
|
if ( angToTarget != *pNewAngles ) |
|
{ |
|
pNewAngVelocity->x = Approach( UTIL_AngleDiff( angToTarget.x, pNewAngles->x ) * flUpdatesPerSecond, pNewAngVelocity->x, flHeatSeekPower ); |
|
pNewAngVelocity->y = Approach( UTIL_AngleDiff( angToTarget.y, pNewAngles->y ) * flUpdatesPerSecond, pNewAngVelocity->y, flHeatSeekPower ); |
|
pNewAngVelocity->z = Approach( UTIL_AngleDiff( angToTarget.z, pNewAngles->z ) * flUpdatesPerSecond, pNewAngVelocity->z, flHeatSeekPower ); |
|
|
|
const float flMaxAngularVelocity = 360.0f; |
|
pNewAngVelocity->x = clamp( pNewAngVelocity->x, -flMaxAngularVelocity, flMaxAngularVelocity ); |
|
pNewAngVelocity->y = clamp( pNewAngVelocity->y, -flMaxAngularVelocity, flMaxAngularVelocity ); |
|
pNewAngVelocity->z = clamp( pNewAngVelocity->z, -flMaxAngularVelocity, flMaxAngularVelocity ); |
|
} |
|
|
|
m_flNextSeekUpdate = gpGlobals->curtime + ( 1.0f / flUpdatesPerSecond ); |
|
} |
|
|
|
*pNewAngles += *pNewAngVelocity * gpGlobals->frametime; |
|
|
|
Vector vForward; |
|
AngleVectors( *pNewAngles, &vForward ); |
|
*pNewVelocity = vForward * GetProjectileSpeed(); |
|
|
|
*pNewPosition += *pNewVelocity * gpGlobals->frametime; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Flare was deflected. |
|
//----------------------------------------------------------------------------- |
|
void CTFProjectile_Flare::Deflected( CBaseEntity *pDeflectedBy, Vector &vecDir ) |
|
{ |
|
CTFPlayer *pTFDeflector = ToTFPlayer( pDeflectedBy ); |
|
if ( !pTFDeflector ) |
|
return; |
|
|
|
ChangeTeam( pTFDeflector->GetTeamNumber() ); |
|
SetLauncher( pTFDeflector->GetActiveWeapon() ); |
|
|
|
CTFPlayer* pOldOwner = ToTFPlayer( GetOwnerEntity() ); |
|
SetOwnerEntity( pTFDeflector ); |
|
|
|
if ( pOldOwner ) |
|
{ |
|
pOldOwner->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "projectile:1,victim:1" ); |
|
} |
|
|
|
if ( pTFDeflector->m_Shared.IsCritBoosted() ) |
|
{ |
|
SetCritical( true ); |
|
} |
|
|
|
CTFWeaponBase::SendObjectDeflectedEvent( pTFDeflector, pOldOwner, GetWeaponID(), this ); |
|
|
|
IncrementDeflected(); |
|
SetScorer( pTFDeflector ); |
|
} |
|
|
|
float CTFProjectile_Flare::GetProjectileSpeed( void ) const |
|
{ |
|
float flLaunchSpeed = FLARE_SPEED; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flLaunchSpeed, mult_projectile_speed ); |
|
|
|
return flLaunchSpeed; |
|
} |
|
|
|
float CTFProjectile_Flare::GetHeatSeekPower( void ) const |
|
{ |
|
float flHeatSeekPower = 0.0; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flHeatSeekPower, mod_projectile_heat_seek_power ); |
|
|
|
return flHeatSeekPower; |
|
} |