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.
730 lines
21 KiB
730 lines
21 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: TF Base Rockets. |
|
// |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "tf_weaponbase_rocket.h" |
|
|
|
// Server specific. |
|
#ifdef GAME_DLL |
|
#include "soundent.h" |
|
#include "te_effect_dispatch.h" |
|
#include "tf_fx.h" |
|
#include "iscorer.h" |
|
#include "tf_gamerules.h" |
|
#include "func_nogrenades.h" |
|
#include "tf_obj_sentrygun.h" |
|
|
|
extern void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); |
|
extern void SendProxy_Angles( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID ); |
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
#include "props_shared.h" |
|
#endif |
|
|
|
//w_rocket_airstrike\w_rocket_airstrike.mdl |
|
#define MINI_ROCKETS_MODEL "models/weapons/w_models/w_rocket_airstrike/w_rocket_airstrike.mdl" |
|
|
|
//============================================================================= |
|
// |
|
// TF Base Rocket tables. |
|
// |
|
|
|
IMPLEMENT_NETWORKCLASS_ALIASED( TFBaseRocket, DT_TFBaseRocket ) |
|
|
|
BEGIN_NETWORK_TABLE( CTFBaseRocket, DT_TFBaseRocket ) |
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
RecvPropVector( RECVINFO( m_vInitialVelocity ) ), |
|
|
|
RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ), |
|
RecvPropQAngles( RECVINFO_NAME( m_angNetworkAngles, m_angRotation ) ), |
|
RecvPropInt( RECVINFO( m_iDeflected ) ), |
|
RecvPropEHandle( RECVINFO( m_hLauncher ) ), |
|
|
|
// Server specific. |
|
#else |
|
SendPropVector( SENDINFO( m_vInitialVelocity ), 12 /*nbits*/, 0 /*flags*/, -3000 /*low value*/, 3000 /*high value*/ ), |
|
|
|
SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ), |
|
SendPropExclude( "DT_BaseEntity", "m_angRotation" ), |
|
|
|
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD_MP_INTEGRAL|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ), |
|
SendPropQAngles (SENDINFO(m_angRotation), 6, SPROP_CHANGES_OFTEN, SendProxy_Angles ), |
|
SendPropInt( SENDINFO( m_iDeflected ), 4, SPROP_UNSIGNED ), |
|
SendPropEHandle( SENDINFO( m_hLauncher ) ), |
|
|
|
#endif |
|
END_NETWORK_TABLE() |
|
|
|
// Server specific. |
|
#ifdef GAME_DLL |
|
BEGIN_DATADESC( CTFBaseRocket ) |
|
END_DATADESC() |
|
#endif |
|
|
|
#ifdef _DEBUG |
|
ConVar tf_rocket_show_radius( "tf_rocket_show_radius", "0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Render rocket radius." ); |
|
#endif |
|
|
|
//============================================================================= |
|
// |
|
// Shared (client/server) functions. |
|
// |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. |
|
//----------------------------------------------------------------------------- |
|
CTFBaseRocket::CTFBaseRocket() |
|
{ |
|
m_vInitialVelocity.Init(); |
|
m_iDeflected = 0; |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
|
|
m_flSpawnTime = 0.0f; |
|
m_iCachedDeflect = false; |
|
|
|
// Server specific. |
|
#else |
|
|
|
m_flDamage = 0.0f; |
|
m_flDestroyableTime = 0.0f; |
|
m_bStunOnImpact = false; |
|
m_flDamageForceScale = 1.0f; |
|
|
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. |
|
//----------------------------------------------------------------------------- |
|
CTFBaseRocket::~CTFBaseRocket() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::Precache( void ) |
|
{ |
|
BaseClass::Precache(); |
|
PrecacheParticleSystem( "Explosion_ShockWave_01" ); |
|
PrecacheModel( MINI_ROCKETS_MODEL ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::Spawn( void ) |
|
{ |
|
BaseClass::Spawn(); |
|
|
|
// Precache. |
|
Precache(); |
|
UseClientSideAnimation(); |
|
|
|
if ( GetLauncher() ) |
|
{ |
|
int iMiniRocket = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( GetLauncher(), iMiniRocket, mini_rockets ); |
|
if ( iMiniRocket ) |
|
{ |
|
SetModel( MINI_ROCKETS_MODEL ); |
|
} |
|
} |
|
|
|
// Client specific. |
|
#ifdef CLIENT_DLL |
|
|
|
m_flSpawnTime = gpGlobals->curtime; |
|
|
|
// Server specific. |
|
#else |
|
|
|
//Derived classes must have set model. |
|
Assert( GetModel() ); |
|
|
|
SetSolid( SOLID_BBOX ); |
|
SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_CUSTOM ); |
|
AddEFlags( EFL_NO_WATER_VELOCITY_CHANGE ); |
|
AddEffects( EF_NOSHADOW ); |
|
|
|
SetCollisionGroup( TFCOLLISION_GROUP_ROCKETS ); |
|
|
|
UTIL_SetSize( this, -Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) ); |
|
ResetSequence( LookupSequence("idle") ); |
|
|
|
// Setup attributes. |
|
m_takedamage = DAMAGE_NO; |
|
SetGravity( 0.0f ); |
|
|
|
// Setup the touch and think functions. |
|
SetTouch( &CTFBaseRocket::RocketTouch ); |
|
SetNextThink( gpGlobals->curtime ); |
|
|
|
AddFlag( FL_GRENADE ); |
|
|
|
m_flDestroyableTime = gpGlobals->curtime + TF_ROCKET_DESTROYABLE_TIMER; |
|
m_bCritical = false; |
|
|
|
#endif |
|
} |
|
|
|
//============================================================================= |
|
// |
|
// Client specific functions. |
|
// |
|
#ifdef CLIENT_DLL |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::PostDataUpdate( DataUpdateType_t type ) |
|
{ |
|
// Pass through to the base class. |
|
BaseClass::PostDataUpdate( type ); |
|
|
|
if ( type == DATA_UPDATE_CREATED ) |
|
{ |
|
// Now stick our initial velocity and angles into the interpolation history. |
|
CInterpolatedVar<Vector> &interpolator = GetOriginInterpolator(); |
|
interpolator.ClearHistory(); |
|
|
|
CInterpolatedVar<QAngle> &rotInterpolator = GetRotationInterpolator(); |
|
rotInterpolator.ClearHistory(); |
|
|
|
float flChangeTime = GetLastChangeTime( LATCH_SIMULATION_VAR ); |
|
|
|
// Add a sample 1 second back. |
|
Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity; |
|
interpolator.AddToHead( flChangeTime - 1.0f, &vCurOrigin, false ); |
|
|
|
QAngle vCurAngles = GetLocalAngles(); |
|
rotInterpolator.AddToHead( flChangeTime - 1.0f, &vCurAngles, false ); |
|
|
|
// Add the current sample. |
|
vCurOrigin = GetLocalOrigin(); |
|
interpolator.AddToHead( flChangeTime, &vCurOrigin, false ); |
|
|
|
rotInterpolator.AddToHead( flChangeTime - 1.0, &vCurAngles, false ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::OnDataChanged(DataUpdateType_t updateType) |
|
{ |
|
BaseClass::OnDataChanged(updateType); |
|
|
|
if ( updateType == DATA_UPDATE_CREATED || m_iCachedDeflect != GetDeflected() ) |
|
{ |
|
CreateTrails(); |
|
} |
|
|
|
m_iCachedDeflect = GetDeflected(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFBaseRocket::DrawModel( int flags ) |
|
{ |
|
// During the first 0.2 seconds of our life, don't draw ourselves. |
|
if ( gpGlobals->curtime - m_flSpawnTime < 0.2f ) |
|
return 0; |
|
|
|
return BaseClass::DrawModel( flags ); |
|
} |
|
|
|
//============================================================================= |
|
// |
|
// Server specific functions. |
|
// |
|
#else |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFBaseRocket *CTFBaseRocket::Create( CBaseEntity *pLauncher, const char *pszClassname, const Vector &vecOrigin, |
|
const QAngle &vecAngles, CBaseEntity *pOwner ) |
|
{ |
|
CTFBaseRocket *pRocket = static_cast<CTFBaseRocket*>( CBaseEntity::Create( pszClassname, vecOrigin, vecAngles, pOwner ) ); |
|
if ( !pRocket ) |
|
return NULL; |
|
|
|
pRocket->SetLauncher( pLauncher ); |
|
|
|
// Initialize the owner. |
|
pRocket->SetOwnerEntity( pOwner ); |
|
|
|
// Spawn. |
|
pRocket->Spawn(); |
|
|
|
// Setup the initial velocity. |
|
Vector vecForward, vecRight, vecUp; |
|
AngleVectors( vecAngles, &vecForward, &vecRight, &vecUp ); |
|
|
|
float flLaunchSpeed = 1100.0f; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLauncher, flLaunchSpeed, mult_projectile_speed ); |
|
|
|
// Hack: This attribute represents a bucket of attributes - one of which is projectile speed. |
|
// If the concept works we'll make the "bucket" system directly modify the attributes instead. |
|
if ( pOwner ) |
|
{ |
|
// if the owner is a Sentry, Check its owner |
|
int iRocketSpecialist = 0; |
|
CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pOwner ); |
|
if ( pSentry ) |
|
{ |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pSentry->GetOwner(), iRocketSpecialist, rocket_specialist ); |
|
} |
|
else |
|
{ |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iRocketSpecialist, rocket_specialist ); |
|
} |
|
|
|
if ( iRocketSpecialist ) |
|
{ |
|
flLaunchSpeed *= RemapValClamped( iRocketSpecialist, 1.f, 4.f, 1.15f, 1.6f ); |
|
flLaunchSpeed = Min( flLaunchSpeed, 3000.f ); |
|
} |
|
} |
|
|
|
CTFPlayer *pTFOwner = ToTFPlayer( pRocket->GetOwnerPlayer() ); |
|
|
|
if ( pTFOwner ) |
|
{ |
|
pRocket->SetTruceValidForEnt( pTFOwner->IsTruceValidForEnt() ); |
|
|
|
if ( pTFOwner->m_Shared.GetCarryingRuneType() == RUNE_PRECISION ) |
|
{ |
|
flLaunchSpeed = 3000.f; |
|
} |
|
} |
|
|
|
Vector vecVelocity = vecForward * flLaunchSpeed; |
|
pRocket->SetAbsVelocity( vecVelocity ); |
|
pRocket->SetupInitialTransmittedGrenadeVelocity( vecVelocity ); |
|
|
|
// Setup the initial angles. |
|
QAngle angles; |
|
VectorAngles( vecVelocity, angles ); |
|
pRocket->SetAbsAngles( angles ); |
|
|
|
// Set team. |
|
pRocket->ChangeTeam( pOwner->GetTeamNumber() ); |
|
|
|
return pRocket; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::RocketTouch( CBaseEntity *pOther ) |
|
{ |
|
// Verify a correct "other." |
|
Assert( pOther ); |
|
bool bShield = pOther->IsCombatItem() && !InSameTeam( pOther ); |
|
if ( pOther->IsSolidFlagSet( FSOLID_TRIGGER | FSOLID_VOLUME_CONTENTS ) && !bShield ) |
|
return; |
|
|
|
// Handle hitting skybox (disappear). |
|
const trace_t *pTrace = &CBaseEntity::GetTouchTrace(); |
|
if( pTrace->surface.flags & SURF_SKY ) |
|
{ |
|
UTIL_Remove( this ); |
|
return; |
|
} |
|
|
|
trace_t trace; |
|
memcpy( &trace, pTrace, sizeof( trace_t ) ); |
|
Explode( &trace, pOther ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
unsigned int CTFBaseRocket::PhysicsSolidMaskForEntity( void ) const |
|
{ |
|
int teamContents = 0; |
|
|
|
if ( !CanCollideWithTeammates() ) |
|
{ |
|
// Only collide with the other team |
|
teamContents = ( GetTeamNumber() == TF_TEAM_RED ) ? CONTENTS_BLUETEAM : CONTENTS_REDTEAM; |
|
} |
|
else |
|
{ |
|
// Collide with both teams |
|
teamContents = CONTENTS_REDTEAM | CONTENTS_BLUETEAM; |
|
} |
|
|
|
return BaseClass::PhysicsSolidMaskForEntity() | teamContents; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFBaseRocket::ShouldNotDetonate( void ) |
|
{ |
|
return InNoGrenadeZone( this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::Destroy( bool bBlinkOut, bool bBreakRocket ) |
|
{ |
|
if ( bBreakRocket ) |
|
{ |
|
CPVSFilter filter( GetAbsOrigin() ); |
|
UserMessageBegin( filter, "BreakModelRocketDud" ); |
|
WRITE_SHORT( GetModelIndex() ); |
|
WRITE_VEC3COORD( GetAbsOrigin() ); |
|
WRITE_ANGLES( GetAbsAngles() ); |
|
MessageEnd(); |
|
} |
|
|
|
// Kill it |
|
SetThink( &BaseClass::SUB_Remove ); |
|
SetNextThink( gpGlobals->curtime ); |
|
SetTouch( NULL ); |
|
AddEffects( EF_NODRAW ); |
|
|
|
if ( bBlinkOut ) |
|
{ |
|
// Sprite flash |
|
CSprite *pGlowSprite = CSprite::SpriteCreate( NOGRENADE_SPRITE, GetAbsOrigin(), false ); |
|
if ( pGlowSprite ) |
|
{ |
|
pGlowSprite->SetTransparency( kRenderGlow, 255, 255, 255, 255, kRenderFxFadeFast ); |
|
pGlowSprite->SetThink( &CSprite::SUB_Remove ); |
|
pGlowSprite->SetNextThink( gpGlobals->curtime + 1.0 ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::Explode( trace_t *pTrace, CBaseEntity *pOther ) |
|
{ |
|
if ( ShouldNotDetonate() ) |
|
{ |
|
Destroy( true ); |
|
return; |
|
} |
|
|
|
// Save this entity as enemy, they will take 100% damage. |
|
m_hEnemy = pOther; |
|
|
|
// Invisible. |
|
SetModelName( NULL_STRING ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
m_takedamage = DAMAGE_NO; |
|
|
|
// Pull out a bit. |
|
if ( pTrace->fraction != 1.0 ) |
|
{ |
|
SetAbsOrigin( pTrace->endpos + ( pTrace->plane.normal * 1.0f ) ); |
|
} |
|
|
|
// Play explosion sound and effect. |
|
Vector vecOrigin = GetAbsOrigin(); |
|
CPVSFilter filter( vecOrigin ); |
|
|
|
// Halloween Spell Effect Check |
|
int iHalloweenSpell = 0; |
|
int iCustomParticleIndex = INVALID_STRING_INDEX; |
|
item_definition_index_t ownerWeaponDefIndex = INVALID_ITEM_DEF_INDEX; |
|
// if the owner is a Sentry, Check its owner |
|
CBaseEntity *pPlayerOwner = GetOwnerPlayer(); |
|
|
|
if ( TF_IsHolidayActive( kHoliday_HalloweenOrFullMoon ) ) |
|
{ |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayerOwner, iHalloweenSpell, halloween_pumpkin_explosions ); |
|
if ( iHalloweenSpell > 0 ) |
|
{ |
|
iCustomParticleIndex = GetParticleSystemIndex( "halloween_explosion" ); |
|
} |
|
} |
|
|
|
CTFWeaponBase *pWeapon = dynamic_cast< CTFWeaponBase * >( GetOriginalLauncher() ); |
|
if ( pWeapon ) |
|
{ |
|
ownerWeaponDefIndex = pWeapon->GetAttributeContainer()->GetItem()->GetItemDefIndex(); |
|
} |
|
|
|
int iLargeExplosion = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayerOwner, iLargeExplosion, use_large_smoke_explosion ); |
|
if ( iLargeExplosion > 0 ) |
|
{ |
|
DispatchParticleEffect( "explosionTrail_seeds_mvm", GetAbsOrigin(), GetAbsAngles() ); |
|
DispatchParticleEffect( "fluidSmokeExpl_ring_mvm", GetAbsOrigin(), GetAbsAngles() ); |
|
} |
|
|
|
TE_TFExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal, GetWeaponID(), pOther->entindex(), ownerWeaponDefIndex, SPECIAL1, iCustomParticleIndex ); |
|
|
|
CSoundEnt::InsertSound ( SOUND_COMBAT, vecOrigin, 1024, 3.0 ); |
|
|
|
// Damage. |
|
CBaseEntity *pAttacker = GetOwnerEntity(); |
|
IScorer *pScorerInterface = dynamic_cast<IScorer*>( pAttacker ); |
|
if ( pScorerInterface ) |
|
{ |
|
pAttacker = pScorerInterface->GetScorer(); |
|
} |
|
else if ( pAttacker && pAttacker->GetOwnerEntity() ) |
|
{ |
|
pAttacker = pAttacker->GetOwnerEntity(); |
|
} |
|
|
|
float flRadius = GetRadius(); |
|
|
|
if ( pAttacker ) // No attacker, deal no damage. Otherwise we could potentially kill teammates. |
|
{ |
|
CTFPlayer *pTarget = ToTFPlayer( GetEnemy() ); |
|
if ( pTarget ) |
|
{ |
|
// Rocket Specialist |
|
CheckForStunOnImpact( pTarget ); |
|
|
|
if ( pTarget->GetTeamNumber() != pAttacker->GetTeamNumber() ) |
|
{ |
|
IGameEvent *event = gameeventmanager->CreateEvent( "projectile_direct_hit" ); |
|
if ( event ) |
|
{ |
|
event->SetInt( "attacker", pAttacker->entindex() ); |
|
event->SetInt( "victim", pTarget->entindex() ); |
|
event->SetInt( "weapon_def_index", ownerWeaponDefIndex ); |
|
|
|
gameeventmanager->FireEvent( event, true ); |
|
} |
|
} |
|
} |
|
|
|
CTakeDamageInfo info( this, pAttacker, GetOriginalLauncher(), vec3_origin, vecOrigin, GetDamage(), GetDamageType(), GetDamageCustom() ); |
|
CTFRadiusDamageInfo radiusinfo( &info, vecOrigin, flRadius, NULL, TF_ROCKET_RADIUS_FOR_RJS, GetDamageForceScale() ); |
|
TFGameRules()->RadiusDamage( radiusinfo ); |
|
} |
|
|
|
#if defined( _DEBUG ) && defined( STAGING_ONLY ) |
|
// Debug! |
|
if ( tf_rocket_show_radius.GetBool() ) |
|
{ |
|
DrawRadius( flRadius ); |
|
} |
|
#endif |
|
|
|
// Don't decal players with scorch. |
|
if ( !pOther->IsPlayer() ) |
|
{ |
|
UTIL_DecalTrace( pTrace, "Scorch" ); |
|
} |
|
|
|
// Remove the rocket. |
|
UTIL_Remove( this ); |
|
|
|
return; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::CheckForStunOnImpact( CTFPlayer* pTarget ) |
|
{ |
|
if ( !m_bStunOnImpact ) |
|
return; |
|
|
|
CTFPlayer *pAttacker = ToTFPlayer( GetOwnerPlayer() ); |
|
if ( !pAttacker ) |
|
return; |
|
|
|
int iRocketSpecialist = GetStunLevel(); |
|
if ( !iRocketSpecialist ) |
|
return; |
|
|
|
// Stun |
|
float flStunAmount = pTarget->IsMiniBoss() ? 0.85f : 1.f; |
|
float flStunTime = RemapValClamped( iRocketSpecialist, 1.f, 4.f, 0.5f, 0.75f ); |
|
|
|
pTarget->SetAbsVelocity( vec3_origin ); |
|
pTarget->m_Shared.StunPlayer( flStunTime, flStunAmount, TF_STUN_MOVEMENT | TF_STUN_NO_EFFECTS, pAttacker ); |
|
|
|
if ( TFGameRules()->IsMannVsMachineMode() && pTarget->IsBot() && ( pAttacker->GetTeamNumber() == TF_TEAM_PVE_DEFENDERS ) ) |
|
{ |
|
pAttacker->AwardAchievement( ACHIEVEMENT_TF_MVM_ROCKET_SPECIALIST_STUN_GRIND ); |
|
} |
|
|
|
|
|
// Effect |
|
CPVSFilter filter( GetAbsOrigin() ); |
|
TE_TFParticleEffect( filter, 0.0, "mvm_soldier_shockwave", GetAbsOrigin(), QAngle( 0, 0, 0 ) ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFBaseRocket::GetStunLevel( void ) |
|
{ |
|
CTFPlayer *pAttacker = ToTFPlayer( GetOwnerPlayer() ); |
|
if ( !pAttacker ) |
|
return 0; |
|
|
|
int iRocketSpecialist = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iRocketSpecialist, rocket_specialist ); |
|
return iRocketSpecialist; |
|
} |
|
|
|
#ifdef STAGING_ONLY |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFBaseRocket::DrawRadius( float flRadius ) |
|
{ |
|
Vector pos = GetAbsOrigin(); |
|
int r = 255; |
|
int g = 0, b = 0; |
|
float flLifetime = 10.0f; |
|
bool bDepthTest = true; |
|
|
|
Vector edge, lastEdge; |
|
NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, !bDepthTest, flLifetime ); |
|
|
|
lastEdge = Vector( flRadius + pos.x, pos.y, pos.z ); |
|
float angle; |
|
for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) |
|
{ |
|
edge.x = flRadius * cos( DEG2RAD( angle ) ) + pos.x; |
|
edge.y = pos.y; |
|
edge.z = flRadius * sin( DEG2RAD( angle ) ) + pos.z; |
|
|
|
NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); |
|
|
|
lastEdge = edge; |
|
} |
|
|
|
lastEdge = Vector( pos.x, flRadius + pos.y, pos.z ); |
|
for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) |
|
{ |
|
edge.x = pos.x; |
|
edge.y = flRadius * cos( DEG2RAD( angle ) ) + pos.y; |
|
edge.z = flRadius * sin( DEG2RAD( angle ) ) + pos.z; |
|
|
|
NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); |
|
|
|
lastEdge = edge; |
|
} |
|
|
|
lastEdge = Vector( pos.x, flRadius + pos.y, pos.z ); |
|
for( angle=0.0f; angle <= 360.0f; angle += 22.5f ) |
|
{ |
|
edge.x = flRadius * cos( DEG2RAD( angle ) ) + pos.x; |
|
edge.y = flRadius * sin( DEG2RAD( angle ) ) + pos.y; |
|
edge.z = pos.z; |
|
|
|
NDebugOverlay::Line( edge, lastEdge, r, g, b, !bDepthTest, flLifetime ); |
|
|
|
lastEdge = edge; |
|
} |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFBaseRocket::GetRadius() |
|
{ |
|
float flRadius = TF_ROCKET_RADIUS; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( m_hLauncher, flRadius, mult_explosion_radius ); |
|
|
|
CBaseEntity *pAttacker = GetOwnerPlayer(); |
|
if ( pAttacker ) |
|
{ |
|
int iRocketSpecialist = 0; |
|
CALL_ATTRIB_HOOK_INT_ON_OTHER( pAttacker, iRocketSpecialist, rocket_specialist ); |
|
if ( iRocketSpecialist ) |
|
{ |
|
bool bDirectHit = ( GetEnemy() && GetEnemy()->GetTeamNumber() != pAttacker->GetTeamNumber() && |
|
( GetEnemy()->IsPlayer() || GetEnemy()->MyCombatCharacterPointer() ) ); |
|
// If we have the Rocket Specialist attribute and hit an enemy combatant directly... |
|
if ( bDirectHit ) |
|
{ |
|
// Increased blast radius |
|
flRadius *= RemapValClamped( iRocketSpecialist, 1.f, 4.f, 1.15f, 1.6f ); |
|
m_bStunOnImpact = true; |
|
} |
|
} |
|
|
|
CTFPlayer *pTFPlayer = ToTFPlayer( pAttacker ); |
|
// Airstrike gets a small blast radius penalty while Rjing |
|
if ( pTFPlayer && pTFPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) ) |
|
{ |
|
// Using this attr to key in the AirStrike |
|
float flRocketJumpAttackBonus = 1.0f; |
|
CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pAttacker, flRocketJumpAttackBonus, rocketjump_attackrate_bonus ); |
|
if ( flRocketJumpAttackBonus != 1.0f ) |
|
{ |
|
flRadius *= 0.80; |
|
} |
|
} |
|
} |
|
|
|
return flRadius; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Checks if the owner is a sentry gun, if so returns the sentry guns owner |
|
//----------------------------------------------------------------------------- |
|
CBaseEntity *CTFBaseRocket::GetOwnerPlayer( void ) const |
|
{ |
|
// if the owner is a Sentry, Check its owner |
|
CBaseEntity *pOwner = GetOwnerEntity(); |
|
CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun*>( pOwner ); |
|
|
|
if ( pSentry ) |
|
{ |
|
return pSentry->GetOwner(); |
|
} |
|
return pOwner; |
|
} |
|
|
|
#endif |
|
|
|
#if defined( CLIENT_DLL ) |
|
|
|
//----------------------------------------------------------------------------- |
|
// Receive the BreakModelRocketDud user message |
|
//----------------------------------------------------------------------------- |
|
void __MsgFunc_BreakModelRocketDud( bf_read &msg ) |
|
{ |
|
int nModelIndex = (int)msg.ReadShort(); |
|
CUtlVector<breakmodel_t> aGibs; |
|
BuildGibList( aGibs, nModelIndex, 1.0f, COLLISION_GROUP_NONE ); |
|
if ( !aGibs.Count() ) |
|
return; |
|
|
|
// Get the origin & angles |
|
Vector vecOrigin, vecForward; |
|
QAngle vecAngles; |
|
msg.ReadBitVec3Coord( vecOrigin ); |
|
msg.ReadBitAngles( vecAngles ); |
|
|
|
AngleVectors( vecAngles, &vecForward ); |
|
|
|
Vector vecBreakVelocity = Vector(0,0,300) + vecForward*-400; |
|
AngularImpulse angularImpulse( 0, RandomFloat( -500, -3000 ), 0 ); |
|
breakablepropparams_t breakParams( vecOrigin, vecAngles, vecBreakVelocity, angularImpulse ); |
|
breakParams.impactEnergyScale = 1.0f; |
|
|
|
CreateGibsFromList( aGibs, nModelIndex, NULL, breakParams, NULL, -1 , false, true ); |
|
} |
|
|
|
#endif |