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.
292 lines
7.8 KiB
292 lines
7.8 KiB
#include "cbase.h" |
|
#include "world.h" |
|
#include "asw_barrel_explosive.h" |
|
#include "asw_marine.h" |
|
#include "asw_player.h" |
|
#include "asw_gamerules.h" |
|
#include "particle_parse.h" |
|
#include "asw_util_shared.h" |
|
#include "cvisibilitymonitor.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#define ASW_EXPLOSIVE_BARREL_MODEL_NAME "models/swarm/Barrel/barrel.mdl" |
|
|
|
|
|
ConVar asw_barrel_health_base( "asw_barrel_health_base", "3", FCVAR_CHEAT, "Health of barrel at level 1" ); |
|
ConVar asw_barrel_health_growth( "asw_barrel_health_growth", "0.15", FCVAR_CHEAT, "% change in health per level" ); |
|
|
|
|
|
LINK_ENTITY_TO_CLASS( asw_barrel_explosive, CASW_Barrel_Explosive ); |
|
|
|
BEGIN_DATADESC( CASW_Barrel_Explosive ) |
|
|
|
END_DATADESC() |
|
|
|
|
|
CASW_Barrel_Explosive::CASW_Barrel_Explosive() |
|
{ |
|
m_iExplosionDamage = 200; |
|
} |
|
|
|
void CASW_Barrel_Explosive::Spawn() |
|
{ |
|
DisableAutoFade(); |
|
SetModelName( AllocPooledString( ASW_EXPLOSIVE_BARREL_MODEL_NAME ) ); |
|
Precache(); |
|
SetModel( ASW_EXPLOSIVE_BARREL_MODEL_NAME ); |
|
|
|
AddSpawnFlags( SF_PHYSPROP_AIMTARGET ); |
|
BaseClass::Spawn(); |
|
|
|
InitHealth(); |
|
|
|
VisibilityMonitor_AddEntity( this, asw_visrange_generic.GetFloat() * 0.9f, NULL, NULL ); |
|
} |
|
|
|
void CASW_Barrel_Explosive::Precache() |
|
{ |
|
PrecacheParticleSystem( "explosion_barrel" ); |
|
PrecacheScriptSound( "ASWBarrel.Explode" ); |
|
PrecacheModel( ASW_EXPLOSIVE_BARREL_MODEL_NAME ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
int CASW_Barrel_Explosive::OnTakeDamage( const CTakeDamageInfo &info ) |
|
{ |
|
int saveFlags = m_takedamage; |
|
|
|
// don't be destroyed by buzzers |
|
if ( info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_BUZZER ) |
|
{ |
|
return 0; |
|
} |
|
|
|
// prevent barrel exploding when knocked around |
|
if ( info.GetDamageType() & DMG_CRUSH ) |
|
return 0; |
|
|
|
CASW_Marine* pMarine = NULL; |
|
if ( info.GetAttacker() && info.GetAttacker()->Classify() == CLASS_ASW_MARINE ) |
|
{ |
|
m_hAttacker = info.GetAttacker(); |
|
|
|
pMarine = assert_cast< CASW_Marine* >( info.GetAttacker() ); |
|
// prevent AI marines blowing up barrels as it makes the player ANGRY ANGRY |
|
if ( pMarine && !pMarine->IsInhabited() ) |
|
return 0; |
|
} |
|
|
|
// don't burst open if melee'd |
|
if ( info.GetDamageType() & ( DMG_CLUB | DMG_SLASH ) ) |
|
{ |
|
m_takedamage = DAMAGE_EVENTS_ONLY; |
|
|
|
if( !m_bMeleeHit ) |
|
{ |
|
if ( pMarine ) |
|
{ |
|
IGameEvent * event = gameeventmanager->CreateEvent( "physics_melee" ); |
|
if ( event ) |
|
{ |
|
CASW_Player *pCommander = pMarine->GetCommander(); |
|
event->SetInt( "attacker", pCommander ? pCommander->GetUserID() : 0 ); |
|
event->SetInt( "entindex", entindex() ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
m_bMeleeHit = true; |
|
} |
|
} |
|
|
|
if ( pMarine ) |
|
{ |
|
pMarine->HurtJunkItem(this, info); |
|
} |
|
|
|
// skip the breakable prop's complex damage handling and just hurt us |
|
int iResult = CBaseEntity::OnTakeDamage(info); |
|
m_takedamage = saveFlags; |
|
|
|
return iResult; |
|
} |
|
|
|
void CASW_Barrel_Explosive::Event_Killed( const CTakeDamageInfo &info ) |
|
{ |
|
IPhysicsObject *pPhysics = VPhysicsGetObject(); |
|
if ( pPhysics && !pPhysics->IsMoveable() ) |
|
{ |
|
pPhysics->EnableMotion( true ); |
|
VPhysicsTakeDamage( info ); |
|
} |
|
|
|
QueueForExplode( info ); |
|
|
|
// Break( info.GetInflictor(), info ); |
|
// DoExplosion(); |
|
} |
|
|
|
void CASW_Barrel_Explosive::ExplodeNow( const CTakeDamageInfo &info ) |
|
{ |
|
Break( info.GetInflictor(), info ); |
|
DoExplosion(); |
|
} |
|
|
|
void CASW_Barrel_Explosive::DoExplosion() |
|
{ |
|
// scorch the ground |
|
trace_t tr; |
|
UTIL_TraceLine ( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -80 ), MASK_SHOT, |
|
this, COLLISION_GROUP_NONE, &tr); |
|
|
|
if ((tr.m_pEnt != GetWorldEntity()) || (tr.hitbox != 0)) |
|
{ |
|
// non-world needs smaller decals |
|
if( tr.m_pEnt && !tr.m_pEnt->IsNPC() ) |
|
{ |
|
UTIL_DecalTrace( &tr, "SmallScorch" ); |
|
} |
|
} |
|
else |
|
{ |
|
UTIL_DecalTrace( &tr, "Scorch" ); |
|
} |
|
|
|
UTIL_ASW_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START ); |
|
|
|
// explosion effects |
|
Vector vecExplosionPos = GetAbsOrigin(); |
|
CPASFilter filter( vecExplosionPos ); |
|
UserMessageBegin( filter, "ASWBarrelExplosion" ); |
|
WRITE_FLOAT( vecExplosionPos.x ); |
|
WRITE_FLOAT( vecExplosionPos.y ); |
|
WRITE_FLOAT( vecExplosionPos.z ); |
|
WRITE_FLOAT( 160.0f ); |
|
MessageEnd(); |
|
|
|
EmitSound( "ASWBarrel.Explode" ); |
|
|
|
// damage to nearby things |
|
CTakeDamageInfo info( this, ( m_hAttacker.Get() ? m_hAttacker.Get() : this ), m_iExplosionDamage, DMG_BLAST ); |
|
info.SetDamageCustom( DAMAGE_FLAG_HALF_FALLOFF ); |
|
ASWGameRules()->RadiusDamage( info, GetAbsOrigin(), 160.0f, CLASS_NONE, NULL ); |
|
} |
|
|
|
void CASW_Barrel_Explosive::OnDifficultyChanged( int iDifficulty ) |
|
{ |
|
InitHealth(); |
|
} |
|
|
|
void CASW_Barrel_Explosive::InitHealth() |
|
{ |
|
m_iMinHealthDmg = 0; |
|
m_iHealth = asw_barrel_health_base.GetInt(); |
|
const float flHealthGrowth = asw_barrel_health_growth.GetFloat(); |
|
m_iHealth = m_iHealth + m_iHealth * flHealthGrowth * ( ASWGameRules()->GetMissionDifficulty() - 1 ); |
|
SetMaxHealth( m_iHealth ); |
|
|
|
m_iExplosionDamage = 200.0f; |
|
/* |
|
int iExplosiveDamage = 120.0f; |
|
m_iExplosionDamage = iExplosiveDamage + iExplosiveDamage * flHealthGrowth * ( ASWGameRules()->GetMissionDifficulty() - 1 ); |
|
//SetExplosiveRadius( 160.0f ); |
|
*/ |
|
} |
|
|
|
|
|
// the queue of explodable objects |
|
class CDeferredExplosionQueue : public CAutoGameSystemPerFrame |
|
{ |
|
public: |
|
CDeferredExplosionQueue() : CAutoGameSystemPerFrame("CDeferredExplosionQueue"), m_fLastExplosionTime(0) {}; |
|
|
|
/// enqueue an object for explosion. may call ExplodeNow() immediately |
|
/// if nothing has or is slated to detonate this frame. |
|
void Enqueue( CASW_Exploding_Prop *pBoom, const CTakeDamageInfo &damageInfo, bool bImmediateExplosionAllowed = true ); |
|
|
|
// game system interface |
|
virtual void FrameUpdatePreEntityThink( ); |
|
virtual void LevelInitPreEntity(); |
|
virtual void LevelShutdownPostEntity(); |
|
|
|
float m_fLastExplosionTime; //< the gpglobals->tickcount when the last explosion happened |
|
protected: |
|
// tell an enqued object to detonate. |
|
inline void Detonate( CASW_Exploding_Prop *pBoom, const CTakeDamageInfo &damageInfo ); |
|
|
|
class QueueCell_t |
|
{ |
|
public: |
|
QueueCell_t(){}; |
|
QueueCell_t( const CHandle<CASW_Exploding_Prop> &handle, const CTakeDamageInfo &damageInfo ) : m_hHandle(handle), m_damageInfo(damageInfo) |
|
{}; |
|
|
|
CHandle<CASW_Exploding_Prop> m_hHandle; |
|
CTakeDamageInfo m_damageInfo; |
|
}; |
|
|
|
CUtlQueue< QueueCell_t > m_Explodables; |
|
}; |
|
CDeferredExplosionQueue g_ASWDeferredExplosionQueue; |
|
|
|
|
|
void CDeferredExplosionQueue::Enqueue( CASW_Exploding_Prop *pBoom, const CTakeDamageInfo &damageInfo, bool bImmediateExplosionAllowed ) |
|
{ |
|
// can it detonate immediately? |
|
if ( bImmediateExplosionAllowed && |
|
m_fLastExplosionTime <= gpGlobals->curtime - ASW_EXPLODING_PROP_INTERVAL && |
|
m_Explodables.Count() == 0 ) |
|
{ |
|
Detonate( pBoom, damageInfo ); |
|
} |
|
else |
|
{ |
|
// must be queued for later detonation. (unless it's already queued) |
|
const int count = m_Explodables.Count(); |
|
for ( int i = 0 ; i < count ; ++i ) |
|
{ |
|
if ( m_Explodables[i].m_hHandle == pBoom ) |
|
return; // bail out |
|
} |
|
m_Explodables.Insert( QueueCell_t(pBoom, damageInfo) ); |
|
} |
|
} |
|
|
|
inline void CDeferredExplosionQueue::Detonate( CASW_Exploding_Prop *pBoom, const CTakeDamageInfo &damageInfo ) |
|
{ |
|
m_fLastExplosionTime = gpGlobals->curtime; |
|
pBoom->ExplodeNow( damageInfo ); |
|
} |
|
|
|
void CDeferredExplosionQueue::FrameUpdatePreEntityThink( ) |
|
{ |
|
// if nothing has exploded this frame and we've got something in the queue, blow it up |
|
if ( m_fLastExplosionTime <= gpGlobals->curtime - ASW_EXPLODING_PROP_INTERVAL && |
|
m_Explodables.Count() > 0 ) |
|
{ |
|
CASW_Exploding_Prop *pBoom = m_Explodables.Head().m_hHandle.Get(); |
|
if ( pBoom ) |
|
{ |
|
Detonate( pBoom, m_Explodables.Head().m_damageInfo ); |
|
} |
|
m_Explodables.RemoveAtHead(); |
|
} |
|
} |
|
|
|
void CDeferredExplosionQueue::LevelInitPreEntity() |
|
{ |
|
m_fLastExplosionTime = 0; |
|
} |
|
|
|
void CDeferredExplosionQueue::LevelShutdownPostEntity() |
|
{ |
|
// empty out everything in the queue |
|
m_Explodables.Purge(); |
|
} |
|
|
|
void CASW_Exploding_Prop::QueueForExplode( const CTakeDamageInfo &damageInfo ) |
|
{ |
|
g_ASWDeferredExplosionQueue.Enqueue( this, damageInfo ); |
|
}
|
|
|