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.
1175 lines
39 KiB
1175 lines
39 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// An ingenious device. We call it "The Magnusson Device". Not my chosen label, |
|
// you understand, but it seemed to please the personnel. |
|
// |
|
// From your point of view you simply throw it at a strider and then blow it up. |
|
// |
|
//============================================================================= |
|
|
|
#include "cbase.h" |
|
#include "props.h" |
|
#include "vphysics/constraints.h" |
|
#include "physics_saverestore.h" |
|
#include "model_types.h" |
|
#include "ai_utils.h" |
|
#include "particle_system.h" |
|
#include "Sprite.h" |
|
#include "citadel_effects_shared.h" |
|
#include "soundent.h" |
|
#include "SpriteTrail.h" |
|
#include "te_effect_dispatch.h" |
|
#include "beam_shared.h" |
|
#include "npc_strider.h" |
|
#include "npc_hunter.h" |
|
#include "particle_parse.h" |
|
#include "gameweaponmanager.h" |
|
#include "gamestats.h" |
|
|
|
extern ConVar hunter_hate_held_striderbusters; |
|
extern ConVar hunter_hate_thrown_striderbusters; |
|
extern ConVar hunter_hate_attached_striderbusters; |
|
|
|
|
|
ConVar striderbuster_health( "striderbuster_health", "14" ); |
|
ConVar striderbuster_autoaim_radius( "striderbuster_autoaim_radius", "64.0f" ); |
|
ConVar striderbuster_shot_velocity( "striderbuster_shot_velocity", "2500.0", FCVAR_NONE, "Speed at which launch the bomb from the physcannon" ); |
|
ConVar striderbuster_allow_all_damage( "striderbuster_allow_all_damage", "0", FCVAR_NONE, "If set to '1' the bomb will detonate on any damage taken. Otherwise only the player may trigger it." ); |
|
|
|
//ConVar striderbuster_magnetic_radius("striderbuster_magnetic_radius","400.0f", FCVAR_NONE,"Maximum distance at which magnade experiences attraction to a target. Set to 0 to disable magnetism."); |
|
ConVar striderbuster_magnetic_force_strider("striderbuster_magnetic_force_strider", "750000.0f", FCVAR_NONE,"Intensity of magnade's attraction to a strider."); |
|
ConVar striderbuster_magnetic_force_hunter("striderbuster_magnetic_force_hunter","1750000.0f",FCVAR_NONE,"Intensity of magnade's attraction to a hunter."); |
|
ConVar striderbuster_falloff_power("striderbuster_falloff_power","4",FCVAR_NONE,"Order of the distance falloff. 1 = linear 2 = quadratic"); |
|
ConVar striderbuster_leg_stick_dist( "striderbuster_leg_stick_dist", "80.0", FCVAR_NONE, "If the buster hits a strider's leg, the max distance from the head at which it sticks anyway." ); |
|
ConVar striderbuster_debugseek( "striderbuster_debugseek", "0" ); |
|
|
|
ConVar sk_striderbuster_magnet_multiplier( "sk_striderbuster_magnet_multiplier", "2.25" ); |
|
|
|
ConVar striderbuster_die_detach( "striderbuster_die_detach", "1" ); // Drop off the strider if a hunter shoots me. (Instead of exploding) |
|
ConVar striderbuster_dive_force( "striderbuster_dive_force", "-200" ); // How much force to apply to a nosediving (dead in the air) striderbuster |
|
|
|
ConVar striderbuster_use_particle_flare( "striderbuster_use_particle_flare", "1" ); |
|
|
|
#define STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER 0x00000001 // We were knocked off of a strider after the player attached me. |
|
|
|
#define SF_DONT_WEAPON_MANAGE 0x800000 |
|
|
|
#define STRIDERBUSTER_SPRITE_TRAIL "sprites/bluelaser1.vmt" |
|
|
|
string_t g_iszVehicle; |
|
|
|
#define BUSTER_PING_SOUND_FREQ 3.0f // How often (seconds) to issue the ping sound to remind players we are attached |
|
|
|
static const char *s_pBusterPingThinkContext = "BusterPing"; |
|
|
|
class CWeaponStriderBuster : public CPhysicsProp |
|
{ |
|
DECLARE_CLASS( CWeaponStriderBuster, CPhysicsProp ); |
|
DECLARE_DATADESC(); |
|
|
|
public: |
|
CWeaponStriderBuster( void ); |
|
|
|
virtual void Precache( void ); |
|
virtual void Spawn( void ); |
|
virtual void Activate( void ); |
|
|
|
// Treat as a live target so hunters can attack us |
|
virtual bool IsAlive() { return true; } |
|
|
|
virtual void OnRestore( void ); |
|
virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ); |
|
virtual void UpdateOnRemove( void ); |
|
virtual int OnTakeDamage( const CTakeDamageInfo &info ); |
|
virtual bool ShouldPuntUseLaunchForces( PhysGunForce_t reason ) { return ( reason == PHYSGUN_FORCE_LAUNCHED ); } |
|
virtual QAngle PreferredCarryAngles( void ) { return m_CarryAngles; } |
|
virtual bool HasPreferredCarryAnglesForPlayer( CBasePlayer *pPlayer ) { return true; } |
|
|
|
virtual void OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ); |
|
virtual void OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason ); |
|
virtual Vector PhysGunLaunchVelocity( const Vector &forward, float flMass ); |
|
virtual float GetAutoAimRadius( void ) { return striderbuster_autoaim_radius.GetFloat(); } |
|
virtual void BusterTouch( CBaseEntity *pOther ); |
|
|
|
virtual bool ShouldAttractAutoAim( CBaseEntity *pAimingEnt ) { return IsAttachedToStrider(); } |
|
|
|
void InputConstraintBroken( inputdata_t &inputdata ); |
|
void BusterFlyThink(); |
|
void BusterDetachThink(); |
|
void BusterPingThink(); |
|
|
|
void OnAddToCargoHold(); |
|
void OnFlechetteAttach( Vector &vecForceDir ); |
|
int NumFlechettesAttached() { return m_nAttachedFlechettes; } |
|
|
|
float GetPickupTime() { return m_PickupTime; } |
|
|
|
int GetStriderBusterFlags() { return m_iBusterFlags; } // I added a flags field so we don't have to keep added bools for all of these contingencies (sjb) |
|
|
|
private: |
|
|
|
void Launch( CBasePlayer *pPhysGunUser ); |
|
void Detonate( void ); |
|
void Shatter( CBaseEntity *pAttacker ); |
|
bool StickToEntity( CBaseEntity *pOther ); |
|
bool CreateConstraintToObject( CBaseEntity *pObject ); |
|
void DestroyConstraint( void ); |
|
bool ShouldStickToEntity( CBaseEntity *pEntity ); |
|
void CreateDestroyedEffect( void ); |
|
|
|
inline bool IsAttachedToStrider( void ) const; |
|
|
|
bool m_bDud; |
|
bool m_bLaunched; |
|
bool m_bNoseDiving; // No magnetism, nosedive and break. Hunter flechettes set this. |
|
int m_nAttachedFlechettes; |
|
float m_flCollisionSpeedSqr; |
|
int m_nAttachedBoneFollowerIndex; |
|
float m_PickupTime; |
|
|
|
IPhysicsConstraint *m_pConstraint; |
|
EHANDLE m_hConstrainedEntity; |
|
|
|
CHandle<CSprite> m_hGlowSprite; |
|
CHandle<CSprite> m_hMainGlow; |
|
|
|
//CHandle<CParticleSystem> m_hGlowTrail; |
|
EHANDLE m_hParticleEffect; |
|
|
|
int m_nRingTexture; |
|
|
|
QAngle m_CarryAngles; |
|
|
|
int m_iBusterFlags; |
|
|
|
COutputEvent m_OnAttachToStrider; |
|
COutputEvent m_OnDetonate; |
|
COutputEvent m_OnShatter; |
|
COutputEvent m_OnShotDown; |
|
|
|
friend bool StriderBuster_IsAttachedStriderBuster( CBaseEntity *pEntity, CBaseEntity * ); |
|
|
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( prop_stickybomb, CWeaponStriderBuster ); |
|
LINK_ENTITY_TO_CLASS( weapon_striderbuster, CWeaponStriderBuster ); |
|
|
|
BEGIN_DATADESC( CWeaponStriderBuster ) |
|
DEFINE_KEYFIELD( m_bDud, FIELD_BOOLEAN, "dud" ), |
|
|
|
DEFINE_FIELD( m_bLaunched, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_bNoseDiving, FIELD_BOOLEAN ), |
|
DEFINE_FIELD( m_nAttachedFlechettes, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_flCollisionSpeedSqr, FIELD_FLOAT ), |
|
DEFINE_FIELD( m_hConstrainedEntity, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hGlowSprite, FIELD_EHANDLE ), |
|
DEFINE_FIELD( m_hMainGlow, FIELD_EHANDLE ), |
|
//DEFINE_FIELD( m_hGlowTrail, FIELD_EHANDLE ), |
|
|
|
DEFINE_FIELD( m_nRingTexture, FIELD_INTEGER ), |
|
DEFINE_FIELD( m_nAttachedBoneFollowerIndex, FIELD_INTEGER ), |
|
|
|
DEFINE_FIELD( m_PickupTime, FIELD_TIME ), |
|
|
|
DEFINE_FIELD( m_hParticleEffect, FIELD_EHANDLE ), |
|
|
|
DEFINE_FIELD( m_CarryAngles, FIELD_VECTOR ), |
|
|
|
DEFINE_FIELD( m_iBusterFlags, FIELD_INTEGER ), |
|
DEFINE_PHYSPTR( m_pConstraint ), |
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "ConstraintBroken", InputConstraintBroken ), |
|
|
|
DEFINE_OUTPUT( m_OnAttachToStrider, "OnAttachToStrider" ), |
|
DEFINE_OUTPUT( m_OnDetonate, "OnDetonate" ), |
|
DEFINE_OUTPUT( m_OnShatter, "OnShatter" ), |
|
DEFINE_OUTPUT( m_OnShotDown, "OnShotDown" ), |
|
|
|
DEFINE_ENTITYFUNC( BusterTouch ), |
|
DEFINE_THINKFUNC( BusterFlyThink ), |
|
DEFINE_THINKFUNC( BusterDetachThink ), |
|
DEFINE_THINKFUNC( BusterPingThink ), |
|
END_DATADESC() |
|
|
|
CWeaponStriderBuster::CWeaponStriderBuster( void ) : |
|
m_pConstraint( NULL ), |
|
m_flCollisionSpeedSqr( -1.0f ), |
|
m_hConstrainedEntity( NULL ), |
|
m_nAttachedBoneFollowerIndex( -1 ) |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::Precache( void ) |
|
{ |
|
PrecacheScriptSound( "Weapon_StriderBuster.StickToEntity" ); |
|
PrecacheScriptSound( "Weapon_StriderBuster.Detonate" ); |
|
PrecacheScriptSound( "Weapon_StriderBuster.Dud_Detonate" ); |
|
PrecacheScriptSound( "Weapon_StriderBuster.Ping" ); |
|
|
|
PrecacheModel("sprites/orangeflare1.vmt"); |
|
|
|
UTIL_PrecacheOther( "env_citadel_energy_core" ); |
|
UTIL_PrecacheOther( "sparktrail" ); |
|
|
|
m_nRingTexture = PrecacheModel( "sprites/lgtning.vmt" ); |
|
|
|
PrecacheParticleSystem( "striderbuster_attach" ); |
|
PrecacheParticleSystem( "striderbuster_attached_pulse" ); |
|
PrecacheParticleSystem( "striderbuster_explode_core" ); |
|
PrecacheParticleSystem( "striderbuster_explode_dummy_core" ); |
|
PrecacheParticleSystem( "striderbuster_break_flechette" ); |
|
PrecacheParticleSystem( "striderbuster_trail" ); |
|
PrecacheParticleSystem( "striderbuster_shotdown_trail" ); |
|
PrecacheParticleSystem( "striderbuster_break" ); |
|
PrecacheParticleSystem( "striderbuster_flechette_attached" ); |
|
|
|
SetModelName( AllocPooledString("models/magnusson_device.mdl") ); |
|
BaseClass::Precache(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::Spawn( void ) |
|
{ |
|
SetModelName( AllocPooledString("models/magnusson_device.mdl") ); |
|
BaseClass::Spawn(); |
|
|
|
// Setup for being shot by the player |
|
m_takedamage = DAMAGE_EVENTS_ONLY; |
|
|
|
// Ignore touches until launched. |
|
SetTouch ( NULL ); |
|
|
|
AddFlag( FL_AIMTARGET|FL_OBJECT ); |
|
|
|
m_hParticleEffect = CreateEntityByName( "info_particle_system" ); |
|
if ( m_hParticleEffect ) |
|
{ |
|
m_hParticleEffect->KeyValue( "start_active", "1" ); |
|
m_hParticleEffect->KeyValue( "effect_name", "striderbuster_smoke" ); |
|
DispatchSpawn( m_hParticleEffect ); |
|
if ( gpGlobals->curtime > 0.2f ) |
|
{ |
|
m_hParticleEffect->Activate(); |
|
} |
|
m_hParticleEffect->SetAbsOrigin( GetAbsOrigin() ); |
|
m_hParticleEffect->SetParent( this ); |
|
} |
|
|
|
SetHealth( striderbuster_health.GetFloat() ); |
|
|
|
SetNextThink(gpGlobals->curtime + 0.01f); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::Activate( void ) |
|
{ |
|
g_iszVehicle = AllocPooledString( "prop_vehicle_jeep" ); |
|
BaseClass::Activate(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::OnRestore( void ) |
|
{ |
|
BaseClass::OnRestore(); |
|
|
|
// If we have an entity we're attached to, attempt to reconstruct our bone follower setup |
|
if ( m_hConstrainedEntity != NULL ) |
|
{ |
|
CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(m_hConstrainedEntity.Get()); |
|
if ( pStrider != NULL ) |
|
{ |
|
// Make sure we've done this step or we'll have no controller to attach to |
|
pStrider->InitBoneFollowers(); |
|
|
|
// Attempt to make a connection to the same bone follower we attached to previously |
|
CBoneFollower *pBoneFollower = pStrider->GetBoneFollowerByIndex( m_nAttachedBoneFollowerIndex ); |
|
if ( CreateConstraintToObject( pBoneFollower ) == false ) |
|
{ |
|
Msg( "Failed to reattach to bone follower %d\n", m_nAttachedBoneFollowerIndex ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::DestroyConstraint( void ) |
|
{ |
|
// Destroy the constraint |
|
if ( m_pConstraint != NULL ) |
|
{ |
|
physenv->DestroyConstraint( m_pConstraint ); |
|
m_pConstraint = NULL; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create a constraint between this object and another |
|
// Input : *pObject - Object to constrain ourselves to |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CWeaponStriderBuster::CreateConstraintToObject( CBaseEntity *pObject ) |
|
{ |
|
if ( m_pConstraint != NULL ) |
|
{ |
|
// Should we destroy the constraint and make a new one at this point? |
|
Assert( 0 ); |
|
return false; |
|
} |
|
|
|
if ( pObject == NULL ) |
|
return false; |
|
|
|
IPhysicsObject *pPhysObject = pObject->VPhysicsGetObject(); |
|
if ( pPhysObject == NULL ) |
|
return false; |
|
|
|
IPhysicsObject *pMyPhysObject = VPhysicsGetObject(); |
|
if ( pPhysObject == NULL ) |
|
return false; |
|
|
|
// Create the fixed constraint |
|
constraint_fixedparams_t fixedConstraint; |
|
fixedConstraint.Defaults(); |
|
fixedConstraint.InitWithCurrentObjectState( pPhysObject, pMyPhysObject ); |
|
|
|
IPhysicsConstraint *pConstraint = physenv->CreateFixedConstraint( pPhysObject, pMyPhysObject, NULL, fixedConstraint ); |
|
if ( pConstraint == NULL ) |
|
return false; |
|
|
|
// Hold on to us |
|
m_pConstraint = pConstraint; |
|
pConstraint->SetGameData( (void *)this ); |
|
m_hConstrainedEntity = pObject->GetOwnerEntity();; |
|
|
|
// Disable collisions between the two ents |
|
PhysDisableObjectCollisions( pPhysObject, pMyPhysObject ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Physics system has just told us our constraint has been broken |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::InputConstraintBroken( inputdata_t &inputdata ) |
|
{ |
|
// Shatter with no real explosion effect |
|
Shatter( NULL ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::UpdateOnRemove( void ) |
|
{ |
|
DestroyConstraint(); |
|
|
|
if ( m_hGlowSprite != NULL ) |
|
{ |
|
m_hGlowSprite->FadeAndDie( 0.5f ); |
|
m_hGlowSprite = NULL; |
|
} |
|
|
|
if ( m_hParticleEffect ) |
|
{ |
|
UTIL_Remove( m_hParticleEffect ); |
|
} |
|
|
|
BaseClass::UpdateOnRemove(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pEntity - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CWeaponStriderBuster::ShouldStickToEntity( CBaseEntity *pEntity ) |
|
{ |
|
if ( pEntity == NULL ) |
|
return false; |
|
|
|
// Must have a follow parent |
|
CBaseEntity *pFollowParent = pEntity->GetOwnerEntity(); |
|
if ( pFollowParent == NULL ) |
|
return false; |
|
|
|
// Must be a strider |
|
CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pFollowParent); |
|
if ( pStrider == NULL ) |
|
return false; |
|
|
|
if( m_bNoseDiving ) |
|
return false; |
|
|
|
// Don't attach to legs |
|
CBoneFollower *pFollower = static_cast<CBoneFollower *>(pEntity); |
|
if ( pStrider->IsLegBoneFollower( pFollower ) ) |
|
{ |
|
Vector vecDelta = pStrider->GetAdjustedOrigin() - GetAbsOrigin(); |
|
if ( vecDelta.Length() > striderbuster_leg_stick_dist.GetFloat() ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
// Ick, this is kind of ugly, but it's also ugly having to pass pointer into this to avoid multiple castings! |
|
// Save this to patch up save/restore later |
|
m_nAttachedBoneFollowerIndex = pStrider->GetBoneFollowerIndex( pFollower ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Stick to an entity (using hierarchy if we can) |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CWeaponStriderBuster::StickToEntity( CBaseEntity *pOther ) |
|
{ |
|
// Make sure the object is travelling fast enough to stick |
|
if ( m_flCollisionSpeedSqr > 50 && !m_bNoseDiving ) |
|
{ |
|
// See if this is a valid strider bit |
|
if ( ShouldStickToEntity( pOther ) ) |
|
{ |
|
// Attempt to constraint to it |
|
if ( CreateConstraintToObject( pOther ) ) |
|
{ |
|
// Only works for striders, at the moment |
|
CBaseEntity *pFollowParent = pOther->GetOwnerEntity(); |
|
if ( pFollowParent == NULL ) |
|
return false; |
|
|
|
// Allows us to identify our constrained object later |
|
SetOwnerEntity( pFollowParent ); |
|
|
|
// Make a sound |
|
EmitSound( "Weapon_StriderBuster.StickToEntity" ); |
|
|
|
DispatchParticleEffect( "striderbuster_attach", GetAbsOrigin(), GetAbsAngles(), NULL ); |
|
|
|
if( striderbuster_use_particle_flare.GetBool() ) |
|
{ |
|
// We don't have to save any pointers or handles to this because it's parented to the buster. |
|
// So it will die when the buster dies. Yay. |
|
CParticleSystem *pFlare = (CParticleSystem *) CreateEntityByName( "info_particle_system" ); |
|
|
|
if ( pFlare != NULL ) |
|
{ |
|
pFlare->KeyValue( "start_active", "1" ); |
|
pFlare->KeyValue( "effect_name", "striderbuster_attached_pulse" ); |
|
pFlare->SetParent( this ); |
|
pFlare->SetLocalOrigin( vec3_origin ); |
|
DispatchSpawn( pFlare ); |
|
pFlare->Activate(); |
|
} |
|
} |
|
else |
|
{ |
|
// Create a glow sprite |
|
m_hGlowSprite = CSprite::SpriteCreate( "sprites/orangeflare1.vmt", GetLocalOrigin(), false ); |
|
|
|
Assert( m_hGlowSprite ); |
|
if ( m_hGlowSprite != NULL ) |
|
{ |
|
m_hGlowSprite->TurnOn(); |
|
m_hGlowSprite->SetTransparency( kRenderWorldGlow, 255, 255, 255, 255, kRenderFxNoDissipation ); |
|
m_hGlowSprite->SetAbsOrigin( GetAbsOrigin() ); |
|
m_hGlowSprite->SetScale( 5.0f ); |
|
m_hGlowSprite->m_nRenderFX = kRenderFxStrobeFaster; |
|
m_hGlowSprite->SetGlowProxySize( 16.0f ); |
|
m_hGlowSprite->SetParent( this ); |
|
} |
|
} |
|
|
|
// Stop touching things |
|
SetTouch( NULL ); |
|
|
|
// Must be a strider |
|
CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pFollowParent); |
|
if ( pStrider == NULL ) |
|
return false; |
|
|
|
// Notify the strider we're attaching to him |
|
pStrider->StriderBusterAttached( this ); |
|
|
|
m_OnAttachToStrider.FireOutput( this, this ); |
|
|
|
// Start the ping sound. |
|
SetContextThink( &CWeaponStriderBuster::BusterPingThink, gpGlobals->curtime + BUSTER_PING_SOUND_FREQ, s_pBusterPingThinkContext ); |
|
|
|
// Don't autodelete this one! |
|
WeaponManager_RemoveManaged( this ); |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create the explosion effect for the final big boom |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::CreateDestroyedEffect( void ) |
|
{ |
|
CBaseEntity *pTrail; |
|
|
|
StopParticleEffects( this ); |
|
|
|
for ( int i = 0; i < 3; i++ ) |
|
{ |
|
pTrail = CreateEntityByName( "sparktrail" ); |
|
pTrail->SetOwnerEntity( this ); |
|
DispatchSpawn( pTrail ); |
|
} |
|
|
|
DispatchParticleEffect( "striderbuster_explode_core", GetAbsOrigin(), GetAbsAngles() ); |
|
|
|
// Create liquid fountain gushtacular effect here! |
|
CEffectData data; |
|
|
|
int nNumSteps = 6; |
|
float flRadStep = (2*M_PI) / nNumSteps; |
|
for ( int i = 0; i < nNumSteps; i++ ) |
|
{ |
|
data.m_vOrigin = GetAbsOrigin() + RandomVector( -32.0f, 32.0f ); |
|
data.m_vNormal.x = cos( flRadStep*i ); |
|
data.m_vNormal.y = sin( flRadStep*i ); |
|
data.m_vNormal.z = 0.0f; |
|
data.m_flScale = ( random->RandomInt( 0, 5 ) == 0 ) ? 1 : 2; |
|
|
|
DispatchEffect( "StriderBlood", data ); |
|
} |
|
|
|
// More effects |
|
UTIL_ScreenShake( GetAbsOrigin(), 20.0f, 150.0, 1.0, 1250.0f, SHAKE_START ); |
|
|
|
data.m_vOrigin = GetAbsOrigin(); |
|
DispatchEffect( "cball_explode", data ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handle a collision using our special behavior |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) |
|
{ |
|
// Find out what we hit. |
|
// Don't do anything special if we're already attached to a strider. |
|
CBaseEntity *pVictim = pEvent->pEntities[!index]; |
|
if ( pVictim == NULL || m_pConstraint != NULL ) |
|
{ |
|
BaseClass::VPhysicsCollision( index, pEvent ); |
|
return; |
|
} |
|
|
|
// Don't attach if we're being held by the player |
|
if ( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) |
|
{ |
|
BaseClass::VPhysicsCollision( index, pEvent ); |
|
return; |
|
} |
|
|
|
// Save off the speed of the object |
|
m_flCollisionSpeedSqr = ( pEvent->preVelocity[ index ] ).LengthSqr(); |
|
|
|
// Break if we hit the world while going fast enough. |
|
// Launched duds detonate if they hit the world at any speed. |
|
if ( pVictim->IsWorld() && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) ) |
|
{ |
|
m_OnShatter.FireOutput( this, this ); |
|
Shatter( pVictim ); |
|
return; |
|
} |
|
|
|
// We'll handle this later in our touch call |
|
if ( ShouldStickToEntity( pVictim ) ) |
|
return; |
|
|
|
// Determine if we should shatter |
|
CBaseEntity *pOwnerEntity = pVictim->GetOwnerEntity(); |
|
bool bVictimIsStrider = ( ( pOwnerEntity != NULL ) && FClassnameIs( pOwnerEntity, "npc_strider" ) ); |
|
|
|
// Break if we hit anything other than a strider while going fast enough. |
|
// Launched duds detonate if they hit anything other than a strider any speed. |
|
if ( ( bVictimIsStrider == false ) && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) ) |
|
{ |
|
m_OnShatter.FireOutput( this, this ); |
|
Shatter( pVictim ); |
|
return; |
|
} |
|
|
|
// Just bounce |
|
BaseClass::VPhysicsCollision( index, pEvent ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called to see if we should attach to the victim |
|
// Input : *pOther - the victim |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::BusterTouch( CBaseEntity *pOther ) |
|
{ |
|
// Attempt to stick to the entity |
|
StickToEntity( pOther ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline bool CWeaponStriderBuster::IsAttachedToStrider( void ) const |
|
{ |
|
CBaseEntity *pAttachedEnt = GetOwnerEntity(); |
|
if ( pAttachedEnt && FClassnameIs( pAttachedEnt, "npc_strider" ) ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::Detonate( void ) |
|
{ |
|
CBaseEntity *pVictim = GetOwnerEntity(); |
|
if ( !m_bDud && pVictim ) |
|
{ |
|
// Kill the strider (with magic effect) |
|
CBasePlayer *pPlayer = AI_GetSinglePlayer(); |
|
CTakeDamageInfo info( pPlayer, this, RandomVector( -100.0f, 100.0f ), GetAbsOrigin(), pVictim->GetHealth(), DMG_GENERIC ); |
|
pVictim->TakeDamage( info ); |
|
|
|
gamestats->Event_WeaponHit( ToBasePlayer( pPlayer ), true, GetClassname(), info ); |
|
|
|
// Tracker 62293: There's a bug where the inflictor/attacker are reversed when calling TakeDamage above so the player never gets |
|
// credit for the strider buster kills. The code has a bunch of assumptions lower level, so it's safer to just fix it here by |
|
// crediting a kill to the player directly. |
|
gamestats->Event_PlayerKilledOther( pPlayer, pVictim, info ); |
|
} |
|
|
|
m_OnDetonate.FireOutput( this, this ); |
|
|
|
// Explode |
|
if ( !m_bDud ) |
|
{ |
|
CreateDestroyedEffect(); |
|
EmitSound( "Weapon_StriderBuster.Detonate" ); |
|
} |
|
else |
|
{ |
|
DispatchParticleEffect( "striderbuster_explode_dummy_core", GetAbsOrigin(), GetAbsAngles() ); |
|
EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); |
|
} |
|
|
|
// Go to bits! |
|
Shatter( pVictim ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Intercept damage and decide whether or not we want to trigger |
|
// Input : &info - |
|
//----------------------------------------------------------------------------- |
|
int CWeaponStriderBuster::OnTakeDamage( const CTakeDamageInfo &info ) |
|
{ |
|
// If we're attached, any damage from the player makes us trigger |
|
CBaseEntity *pInflictor = info.GetInflictor(); |
|
CBaseEntity *pAttacker = info.GetAttacker(); |
|
bool bInflictorIsPlayer = ( pInflictor != NULL && pInflictor->IsPlayer() ); |
|
bool bAttackerIsPlayer = ( pAttacker != NULL && pAttacker->IsPlayer() ); |
|
|
|
if ( GetParent() && GetParent()->ClassMatches( g_iszVehicle ) ) |
|
{ |
|
return 0; |
|
} |
|
|
|
// Only take damage from a player, for the moment |
|
if ( striderbuster_allow_all_damage.GetBool() || ( IsAttachedToStrider() && ( bAttackerIsPlayer || bInflictorIsPlayer ) ) ) |
|
{ |
|
Detonate(); |
|
return 0; |
|
} |
|
|
|
if ( pAttacker && ( pAttacker->Classify() == CLASS_COMBINE || pAttacker->Classify() == CLASS_COMBINE_HUNTER ) ) |
|
{ |
|
if ( VPhysicsGetObject() && !VPhysicsGetObject()->IsMoveable() ) |
|
{ |
|
return 0; |
|
} |
|
} |
|
|
|
// Hunters are able to destroy strider busters |
|
if ( hunter_hate_held_striderbusters.GetBool() || hunter_hate_thrown_striderbusters.GetBool() || hunter_hate_attached_striderbusters.GetBool() ) |
|
{ |
|
if ( ( GetHealth() > 0 ) && ( pInflictor != NULL ) && FClassnameIs( pInflictor, "hunter_flechette" ) ) |
|
{ |
|
// |
|
// Flechette impacts don't hurt the striderbuster unless it's attached to a strider, |
|
// but the explosions always do. This is so that held or thrown striderbusters fly |
|
// awry because of the flechette, but attached striderbusters break instantly to make |
|
// the hunters more effective at defending the strider. |
|
// |
|
if ( IsAttachedToStrider() || !( info.GetDamageType() & DMG_NEVERGIB ) ) |
|
{ |
|
if( striderbuster_die_detach.GetBool() && IsAttachedToStrider() ) |
|
{ |
|
// Make the buster fall off and break. |
|
m_takedamage = DAMAGE_NO; |
|
|
|
CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(GetOwnerEntity()); |
|
Assert( pStrider != NULL ); |
|
pStrider->StriderBusterDetached( this ); |
|
DestroyConstraint(); |
|
|
|
// Amplify some lateral force. |
|
Vector vecForce = info.GetDamageForce(); |
|
vecForce.z = 0.0f; |
|
VPhysicsGetObject()->ApplyForceCenter( vecForce * 5.0f ); |
|
|
|
SetContextThink( NULL, gpGlobals->curtime, s_pBusterPingThinkContext ); |
|
|
|
SetThink( &CWeaponStriderBuster::BusterDetachThink ); |
|
SetNextThink( gpGlobals->curtime ); |
|
m_iBusterFlags |= STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER; |
|
|
|
return 0; |
|
} |
|
else |
|
{ |
|
// Destroy the buster in place |
|
// Make sure they know it blew up prematurely. |
|
EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); |
|
DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() ); |
|
SetHealth( 0 ); |
|
|
|
Shatter( info.GetAttacker() ); |
|
return 0; |
|
} |
|
} |
|
|
|
if ( info.GetDamage() < 5 ) |
|
{ |
|
bool bFirst = ( m_CarryAngles.x == 45 && m_CarryAngles.y == 0 && m_CarryAngles.z == 0); |
|
float sinTime = sin( gpGlobals->curtime ); |
|
bool bSubtractX = ( bFirst ) ? ( sinTime < 0 ) : ( m_CarryAngles.x < 45 ); |
|
|
|
m_CarryAngles.x += ( 10.0 + 10.0 * fabsf( sinTime ) + random->RandomFloat( -2.5, 2.5 ) + random->RandomFloat( -2.5, 2.5 ) ) * ( ( bSubtractX ) ? -1.0 : 1.0 ); |
|
m_CarryAngles.y = 15 * ( sin( gpGlobals->curtime ) + cos( gpGlobals->curtime * 0.5 ) ) * .5 + random->RandomFloat( -15, 15 ); |
|
m_CarryAngles.z = 7.5 * ( sin( gpGlobals->curtime ) + sin( gpGlobals->curtime * 2.0 ) ) * .5 + random->RandomFloat( -7.5, 7.5 ); |
|
} |
|
|
|
return 1; |
|
} |
|
} |
|
|
|
// Allow crushing damage |
|
if ( info.GetDamageType() & DMG_CRUSH ) |
|
return BaseClass::OnTakeDamage( info ); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::OnPhysGunPickup( CBasePlayer *pPhysGunUser, PhysGunPickup_t reason ) |
|
{ |
|
m_PickupTime = gpGlobals->curtime; |
|
m_CarryAngles.Init( 45, 0, 0 ); |
|
if ( ( reason == PICKED_UP_BY_CANNON ) && ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) ) |
|
{ |
|
WeaponManager_RemoveManaged( this ); |
|
} |
|
else if ( reason == PUNTED_BY_CANNON ) |
|
{ |
|
Launch( pPhysGunUser ); |
|
} |
|
|
|
BaseClass::OnPhysGunPickup( pPhysGunUser, reason ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::OnPhysGunDrop( CBasePlayer *pPhysGunUser, PhysGunDrop_t Reason ) |
|
{ |
|
if ( Reason == LAUNCHED_BY_CANNON ) |
|
{ |
|
Launch( pPhysGunUser ); |
|
} |
|
else if ( ( Reason == DROPPED_BY_CANNON ) && ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) ) |
|
{ |
|
// This striderbuster is now fair game for autodeletion. |
|
WeaponManager_AddManaged( this ); |
|
} |
|
|
|
BaseClass::OnPhysGunDrop( pPhysGunUser, Reason ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Fling the buster with the physcannon either via punt or launch. |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::Launch( CBasePlayer *pPhysGunUser ) |
|
{ |
|
if ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) |
|
{ |
|
WeaponManager_RemoveManaged( this ); |
|
} |
|
|
|
m_bLaunched = true; |
|
|
|
// Notify all nearby hunters that we were launched. |
|
Hunter_StriderBusterLaunched( this ); |
|
|
|
// Start up the eye glow |
|
m_hMainGlow = CSprite::SpriteCreate( "sprites/blueglow1.vmt", GetLocalOrigin(), false ); |
|
|
|
if ( m_hMainGlow != NULL ) |
|
{ |
|
m_hMainGlow->FollowEntity( this ); |
|
m_hMainGlow->SetTransparency( kRenderGlow, 255, 255, 255, 140, kRenderFxNoDissipation ); |
|
m_hMainGlow->SetScale( 2.0f ); |
|
m_hMainGlow->SetGlowProxySize( 8.0f ); |
|
} |
|
|
|
if ( !m_bNoseDiving ) |
|
{ |
|
DispatchParticleEffect( "striderbuster_trail", PATTACH_ABSORIGIN_FOLLOW, this ); |
|
} |
|
else |
|
{ |
|
DispatchParticleEffect( "striderbuster_shotdown_trail", PATTACH_ABSORIGIN_FOLLOW, this ); |
|
} |
|
|
|
// We get our touch function from the physics system |
|
SetTouch ( &CWeaponStriderBuster::BusterTouch ); |
|
|
|
SetThink( &CWeaponStriderBuster::BusterFlyThink ); |
|
SetNextThink( gpGlobals->curtime ); |
|
|
|
gamestats->Event_WeaponFired( pPhysGunUser, true, GetClassname() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : &forward - |
|
// flMass - |
|
// Output : Vector |
|
//----------------------------------------------------------------------------- |
|
Vector CWeaponStriderBuster::PhysGunLaunchVelocity( const Vector &forward, float flMass ) |
|
{ |
|
return ( striderbuster_shot_velocity.GetFloat() * forward ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::Shatter( CBaseEntity *pAttacker ) |
|
{ |
|
if( m_bNoseDiving ) |
|
m_OnShotDown.FireOutput( this, this ); |
|
|
|
m_takedamage = DAMAGE_YES; |
|
|
|
if( !IsAttachedToStrider() ) |
|
{ |
|
// Don't display this particular effect if we're attached to a strider. This effect just gets lost |
|
// in the big strider explosion anyway, so let's recover some perf. |
|
DispatchParticleEffect( "striderbuster_break", GetAbsOrigin(), GetAbsAngles() ); |
|
} |
|
|
|
// Buster is useless now. Stop thinking, touching. |
|
SetThink( NULL ); |
|
SetTouch( NULL ); |
|
SetContextThink( NULL, gpGlobals->curtime, s_pBusterPingThinkContext ); |
|
|
|
// Deal deadly damage to ourselves (DMG_CRUSH is allowed, others are blocked) |
|
CTakeDamageInfo info( pAttacker, pAttacker, RandomVector( -100, 100 ), GetAbsOrigin(), 100.0f, DMG_CRUSH ); |
|
TakeDamage( info ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Give the buster a slight attraction to striders. |
|
// Ported back from the magnade. |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::BusterFlyThink() |
|
{ |
|
if (IsAttachedToStrider()) |
|
return; // early out. Think no more. |
|
|
|
// If we're nosediving, forget about magnetism. |
|
if ( m_bNoseDiving ) |
|
{ |
|
if ( VPhysicsGetObject() ) |
|
VPhysicsGetObject()->ApplyForceCenter( Vector( 0, 0, striderbuster_dive_force.GetFloat() ) ); |
|
SetNextThink(gpGlobals->curtime + 0.01f); |
|
return; |
|
} |
|
|
|
// seek? |
|
const float magradius = 38.0 * sk_striderbuster_magnet_multiplier.GetFloat(); // radius of strider hull times multiplier |
|
if (magradius > 0 && |
|
GetMoveType() == MOVETYPE_VPHYSICS && |
|
VPhysicsGetObject() |
|
) |
|
{ |
|
// find the nearest enemy. |
|
CBaseEntity *pList[16]; |
|
Vector origin = GetAbsOrigin(); |
|
|
|
// do a find in box ( a little faster than sphere ) |
|
int count; |
|
{ |
|
Vector mins,maxs; |
|
mins = origin; |
|
mins -= magradius; |
|
|
|
maxs = origin; |
|
maxs += magradius; |
|
|
|
count = UTIL_EntitiesInBox(pList, 16, mins, maxs, FL_NPC); |
|
} |
|
|
|
float magradiusSq = Square( magradius ); |
|
float nearestDistSq = magradiusSq + 1; |
|
int bestFit = -1; |
|
Vector toTarget; // will be garbage unless something good is found |
|
CNPC_Strider *pBestStrider = NULL; |
|
|
|
for ( int ii = 0 ; ii < count ; ++ii ) |
|
{ |
|
CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(pList[ii]); |
|
if ( pStrider && !pStrider->CarriedByDropship() ) // ShouldStickToEntity() doesn't work because the strider NPC isn't what we glue to |
|
{ |
|
// get distance squared |
|
VectorSubtract( pStrider->GetAdjustedOrigin(), GetAbsOrigin(), toTarget ); |
|
|
|
//NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + toTarget, 128, 0, 128, false, 0.1 ); |
|
|
|
float dSq = toTarget.LengthSqr(); |
|
if (dSq < nearestDistSq) |
|
{ |
|
bestFit = ii; nearestDistSq = dSq; |
|
pBestStrider = pStrider; |
|
} |
|
} |
|
} |
|
|
|
if (bestFit >= 0) // we found something and should attract towards it. (hysterisis later?) |
|
{ |
|
if ( striderbuster_debugseek.GetBool() ) |
|
{ |
|
NDebugOverlay::Circle( GetAbsOrigin() + toTarget, magradius, 255, 255, 255, 255, true, .1 ); |
|
NDebugOverlay::Cross3D( GetAbsOrigin() + toTarget, magradius, 255, 255, 255, true, .1 ); |
|
} |
|
|
|
// force magnitude. |
|
float magnitude = GetMass() * striderbuster_magnetic_force_strider.GetFloat(); |
|
int falloff = striderbuster_falloff_power.GetInt(); |
|
switch (falloff) |
|
{ |
|
case 1: |
|
VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / nearestDistSq) ); // dividing through by distance squared normalizes toTarget and gives a linear falloff |
|
break; |
|
case 2: |
|
VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / (nearestDistSq * sqrtf(nearestDistSq))) ); // dividing through by distance cubed normalizes toTarget and gives a quadratic falloff |
|
break; |
|
case 3: |
|
VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude / (nearestDistSq * nearestDistSq)) ); // dividing through by distance fourth normalizes toTarget and gives a cubic falloff |
|
break; |
|
case 4: |
|
{ |
|
Vector toTarget; |
|
pBestStrider->GetAttachment( "buster_target", toTarget ); |
|
|
|
if ( striderbuster_debugseek.GetBool() ) |
|
{ |
|
NDebugOverlay::Cross3D( toTarget, magradius, 255, 0, 255, true, .1 ); |
|
NDebugOverlay::Cross3D( toTarget, magradius, 255, 0, 255, true, .1 ); |
|
} |
|
|
|
toTarget -= GetAbsOrigin(); |
|
toTarget.NormalizeInPlace(); |
|
VPhysicsGetObject()->ApplyForceCenter( toTarget * magnitude ); |
|
|
|
} |
|
break; |
|
default: // arbitrary powers |
|
VPhysicsGetObject()->ApplyForceCenter( toTarget * (magnitude * powf(nearestDistSq,(falloff+1.0f)/2)) ); // square root for distance instead of squared, add one to normalize toTarget |
|
break; |
|
} |
|
} |
|
|
|
SetNextThink(gpGlobals->curtime + 0.01f); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::BusterDetachThink() |
|
{ |
|
SetNextThink( gpGlobals->curtime + 0.1f ); |
|
|
|
trace_t tr; |
|
UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1200), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if( fabs(tr.startpos.z - tr.endpos.z) < 240.0f ) |
|
{ |
|
SetThink(NULL); |
|
EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); |
|
DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() ); |
|
SetHealth( 0 ); |
|
CTakeDamageInfo info; |
|
info.SetDamage( 1.0f ); |
|
info.SetAttacker( this ); |
|
info.SetInflictor( this ); |
|
Shatter(this); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::BusterPingThink() |
|
{ |
|
EmitSound( "Weapon_StriderBuster.Ping" ); |
|
|
|
SetContextThink( &CWeaponStriderBuster::BusterPingThink, gpGlobals->curtime + BUSTER_PING_SOUND_FREQ, s_pBusterPingThinkContext ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::OnAddToCargoHold() |
|
{ |
|
if ( !HasSpawnFlags( SF_DONT_WEAPON_MANAGE ) ) |
|
{ |
|
WeaponManager_RemoveManaged( this ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CWeaponStriderBuster::OnFlechetteAttach( Vector &vecFlechetteVelocity ) |
|
{ |
|
if ( m_bLaunched ) |
|
{ |
|
Vector vecForce = vecFlechetteVelocity; |
|
VectorNormalize( vecForce ); |
|
|
|
vecForce *= 1000; |
|
vecForce.z = -5000; |
|
|
|
VPhysicsGetObject()->ApplyForceCenter( vecForce ); |
|
} |
|
|
|
if ( !GetParent() || !GetParent()->ClassMatches( g_iszVehicle ) ) |
|
{ |
|
if ( !m_bNoseDiving ) |
|
{ |
|
//m_hGlowTrail->StopParticleSystem(); |
|
StopParticleEffects( this ); |
|
|
|
if( m_iBusterFlags & STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER ) |
|
{ |
|
DispatchParticleEffect( "striderbuster_shotdown_trail", PATTACH_ABSORIGIN_FOLLOW, this ); |
|
} |
|
else |
|
{ |
|
DispatchParticleEffect( "striderbuster_flechette_attached", PATTACH_ABSORIGIN_FOLLOW, this ); |
|
} |
|
} |
|
|
|
m_bNoseDiving = true; |
|
} |
|
m_nAttachedFlechettes++; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool StriderBuster_IsAttachedStriderBuster( CBaseEntity *pEntity, CBaseEntity *pAttachedTo ) |
|
{ |
|
Assert(dynamic_cast<CWeaponStriderBuster *>(pEntity)); |
|
if ( !pAttachedTo ) |
|
return static_cast<CWeaponStriderBuster *>(pEntity)->m_hConstrainedEntity != NULL; |
|
else |
|
return static_cast<CWeaponStriderBuster *>(pEntity)->m_hConstrainedEntity == pAttachedTo; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Called when the striderbuster is placed in the jalopy's cargo container. |
|
//----------------------------------------------------------------------------- |
|
void StriderBuster_OnAddToCargoHold( CBaseEntity *pEntity ) |
|
{ |
|
CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity ); |
|
if ( pBuster ) |
|
{ |
|
pBuster->OnAddToCargoHold(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool StriderBuster_OnFlechetteAttach( CBaseEntity *pEntity, Vector &vecFlechetteVelocity ) |
|
{ |
|
CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity ); |
|
if ( pBuster ) |
|
{ |
|
pBuster->OnFlechetteAttach( vecFlechetteVelocity ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
int StriderBuster_NumFlechettesAttached( CBaseEntity *pEntity ) |
|
{ |
|
CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity ); |
|
if ( pBuster ) |
|
{ |
|
return pBuster->NumFlechettesAttached(); |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
float StriderBuster_GetPickupTime( CBaseEntity *pEntity ) |
|
{ |
|
CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity ); |
|
if ( pBuster ) |
|
{ |
|
return pBuster->GetPickupTime(); |
|
} |
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool StriderBuster_WasKnockedOffStrider( CBaseEntity *pEntity ) |
|
{ |
|
CWeaponStriderBuster *pBuster = dynamic_cast <CWeaponStriderBuster *>( pEntity ); |
|
if ( pBuster ) |
|
{ |
|
return ((pBuster->GetStriderBusterFlags() & STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER) != 0); |
|
} |
|
|
|
return false; |
|
}
|
|
|