source-engine/game/server/hl2/hl2_triggers.cpp

876 lines
24 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================//
#include "cbase.h"
#include "weapon_physcannon.h"
#include "hl2_player.h"
#include "saverestore_utlvector.h"
#include "triggers.h"
//-----------------------------------------------------------------------------
// Weapon-dissolve trigger; all weapons in this field (sans the physcannon) are destroyed!
//-----------------------------------------------------------------------------
class CTriggerWeaponDissolve : public CTriggerMultiple
{
DECLARE_CLASS( CTriggerWeaponDissolve, CTriggerMultiple );
DECLARE_DATADESC();
public:
~CTriggerWeaponDissolve( void );
virtual void Spawn( void );
virtual void Precache( void );
virtual void Activate( void );
virtual void StartTouch( CBaseEntity *pOther );
inline bool HasWeapon( CBaseCombatWeapon *pWeapon );
Vector GetConduitPoint( CBaseEntity *pTarget );
void InputStopSound( inputdata_t &inputdata );
void AddWeapon( CBaseCombatWeapon *pWeapon );
void CreateBeam( const Vector &vecSource, CBaseEntity *pDest, float flLifetime );
void DissolveThink( void );
private:
COutputEvent m_OnDissolveWeapon;
COutputEvent m_OnChargingPhyscannon;
CUtlVector< CHandle<CBaseCombatWeapon> > m_pWeapons;
CUtlVector< CHandle<CBaseEntity> > m_pConduitPoints;
string_t m_strEmitterName;
int m_spriteTexture;
};
LINK_ENTITY_TO_CLASS( trigger_weapon_dissolve, CTriggerWeaponDissolve );
BEGIN_DATADESC( CTriggerWeaponDissolve )
DEFINE_KEYFIELD( m_strEmitterName, FIELD_STRING, "emittername" ),
DEFINE_UTLVECTOR( m_pWeapons, FIELD_EHANDLE ),
DEFINE_UTLVECTOR( m_pConduitPoints, FIELD_EHANDLE ),
DEFINE_FIELD( m_spriteTexture, FIELD_MODELINDEX ),
DEFINE_OUTPUT( m_OnDissolveWeapon, "OnDissolveWeapon" ),
DEFINE_OUTPUT( m_OnChargingPhyscannon, "OnChargingPhyscannon" ),
DEFINE_INPUTFUNC( FIELD_VOID, "StopSound", InputStopSound ),
DEFINE_THINKFUNC( DissolveThink ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
CTriggerWeaponDissolve::~CTriggerWeaponDissolve( void )
{
m_pWeapons.Purge();
m_pConduitPoints.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Call precache for our sprite texture
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::Spawn( void )
{
BaseClass::Spawn();
Precache();
}
//-----------------------------------------------------------------------------
// Purpose: Precache our sprite texture
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::Precache( void )
{
BaseClass::Precache();
m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" );
PrecacheScriptSound( "WeaponDissolve.Dissolve" );
PrecacheScriptSound( "WeaponDissolve.Charge" );
PrecacheScriptSound( "WeaponDissolve.Beam" );
}
static const char *s_pDissolveThinkContext = "DissolveThinkContext";
//-----------------------------------------------------------------------------
// Purpose: Collect all our known conduit points
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::Activate( void )
{
BaseClass::Activate();
CBaseEntity *pEntity = NULL;
while ( ( pEntity = gEntList.FindEntityByName( pEntity, m_strEmitterName ) ) != NULL )
{
m_pConduitPoints.AddToTail( pEntity );
}
SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + 0.1f, s_pDissolveThinkContext );
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if a weapon is already known
// Input : *pWeapon - weapon to check for
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CTriggerWeaponDissolve::HasWeapon( CBaseCombatWeapon *pWeapon )
{
if ( m_pWeapons.Find( pWeapon ) == m_pWeapons.InvalidIndex() )
return false;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Adds a weapon to the known weapon list
// Input : *pWeapon - weapon to add
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::AddWeapon( CBaseCombatWeapon *pWeapon )
{
if ( HasWeapon( pWeapon ) )
return;
m_pWeapons.AddToTail( pWeapon );
}
//-----------------------------------------------------------------------------
// Purpose: Collect any weapons inside our volume
// Input : *pOther -
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::StartTouch( CBaseEntity *pOther )
{
BaseClass::StartTouch( pOther );
if ( PassesTriggerFilters( pOther ) == false )
return;
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(pOther);
if ( pWeapon == NULL )
return;
AddWeapon( pWeapon );
}
//-----------------------------------------------------------------------------
// Purpose: Creates a beam between a conduit point and a weapon
// Input : &vecSource - conduit point
// *pDest - weapon
// flLifetime - amount of time
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::CreateBeam( const Vector &vecSource, CBaseEntity *pDest, float flLifetime )
{
CBroadcastRecipientFilter filter;
te->BeamEntPoint( filter, 0.0,
0,
&vecSource,
pDest->entindex(),
&(pDest->WorldSpaceCenter()),
m_spriteTexture,
0, // No halo
1, // Frame
30,
flLifetime,
16.0f, // Start width
4.0f, // End width
0, // No fade
8, // Amplitude
255,
255,
255,
255,
16 ); // Speed
}
//-----------------------------------------------------------------------------
// Purpose: Returns the closest conduit point to a weapon
// Input : *pTarget - weapon to check for
// Output : Vector - position of the conduit
//-----------------------------------------------------------------------------
Vector CTriggerWeaponDissolve::GetConduitPoint( CBaseEntity *pTarget )
{
float nearDist = 9999999.0f;
Vector bestPoint = vec3_origin;
float testDist;
// Find the nearest conduit to the target
for ( int i = 0; i < m_pConduitPoints.Count(); i++ )
{
testDist = ( m_pConduitPoints[i]->GetAbsOrigin() - pTarget->GetAbsOrigin() ).LengthSqr();
if ( testDist < nearDist )
{
bestPoint = m_pConduitPoints[i]->GetAbsOrigin();
nearDist = testDist;
}
}
return bestPoint;
}
//-----------------------------------------------------------------------------
// Purpose: Dissolve all weapons within our volume
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::DissolveThink( void )
{
int numWeapons = m_pWeapons.Count();
// Dissolve all the items within the volume
for ( int i = 0; i < numWeapons; i++ )
{
CBaseCombatWeapon *pWeapon = m_pWeapons[i];
Vector vecConduit = GetConduitPoint( pWeapon );
// The physcannon upgrades when this happens
if ( FClassnameIs( pWeapon, "weapon_physcannon" ) )
{
// This must be the last weapon for us to care
if ( numWeapons > 1 )
continue;
//FIXME: Make them do this on a stagger!
// All conduits send power to the weapon
for ( int i = 0; i < m_pConduitPoints.Count(); i++ )
{
CreateBeam( m_pConduitPoints[i]->GetAbsOrigin(), pWeapon, 4.0f );
}
PhysCannonBeginUpgrade( pWeapon );
m_OnChargingPhyscannon.FireOutput( this, this );
EmitSound( "WeaponDissolve.Beam" );
// We're done
m_pWeapons.Purge();
m_pConduitPoints.Purge();
SetContextThink( NULL, 0, s_pDissolveThinkContext );
return;
}
// Randomly dissolve them all
float flLifetime = random->RandomFloat( 2.5f, 4.0f );
CreateBeam( vecConduit, pWeapon, flLifetime );
pWeapon->Dissolve( NULL, gpGlobals->curtime + ( 3.0f - flLifetime ), false );
m_OnDissolveWeapon.FireOutput( this, this );
CPASAttenuationFilter filter( pWeapon );
EmitSound( filter, pWeapon->entindex(), "WeaponDissolve.Dissolve" );
// Beam looping sound
EmitSound( "WeaponDissolve.Beam" );
m_pWeapons.Remove( i );
SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + random->RandomFloat( 0.5f, 1.5f ), s_pDissolveThinkContext );
return;
}
SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + 0.1f, s_pDissolveThinkContext );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &inputdata -
//-----------------------------------------------------------------------------
void CTriggerWeaponDissolve::InputStopSound( inputdata_t &inputdata )
{
StopSound( "WeaponDissolve.Beam" );
StopSound( "WeaponDissolve.Charge" );
}
//-----------------------------------------------------------------------------
// Weapon-strip trigger; can't pick up weapons while in the field
//-----------------------------------------------------------------------------
class CTriggerWeaponStrip : public CTriggerMultiple
{
DECLARE_CLASS( CTriggerWeaponStrip, CTriggerMultiple );
DECLARE_DATADESC();
public:
void StartTouch(CBaseEntity *pOther);
void EndTouch(CBaseEntity *pOther);
private:
bool m_bKillWeapons;
};
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( trigger_weapon_strip, CTriggerWeaponStrip );
BEGIN_DATADESC( CTriggerWeaponStrip )
DEFINE_KEYFIELD( m_bKillWeapons, FIELD_BOOLEAN, "KillWeapons" ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Drops all weapons, marks the character as not being able to pick up weapons
//-----------------------------------------------------------------------------
void CTriggerWeaponStrip::StartTouch(CBaseEntity *pOther)
{
BaseClass::StartTouch( pOther );
if ( PassesTriggerFilters(pOther) == false )
return;
CBaseCombatCharacter *pCharacter = pOther->MyCombatCharacterPointer();
if ( m_bKillWeapons )
{
for ( int i = 0 ; i < pCharacter->WeaponCount(); ++i )
{
CBaseCombatWeapon *pWeapon = pCharacter->GetWeapon( i );
if ( !pWeapon )
continue;
pCharacter->Weapon_Drop( pWeapon );
UTIL_Remove( pWeapon );
}
return;
}
// Strip the player of his weapons
if ( pCharacter && pCharacter->IsAllowedToPickupWeapons() )
{
CBaseCombatWeapon *pBugbait = pCharacter->Weapon_OwnsThisType( "weapon_bugbait" );
if ( pBugbait )
{
pCharacter->Weapon_Drop( pBugbait );
UTIL_Remove( pBugbait );
}
pCharacter->Weapon_DropAll( true );
pCharacter->SetPreventWeaponPickup( true );
}
}
//-----------------------------------------------------------------------------
// Purpose: Called when an entity stops touching us.
// Input : pOther - The entity that was touching us.
//-----------------------------------------------------------------------------
void CTriggerWeaponStrip::EndTouch(CBaseEntity *pOther)
{
if ( IsTouching( pOther ) )
{
CBaseCombatCharacter *pCharacter = pOther->MyCombatCharacterPointer();
if ( pCharacter )
{
pCharacter->SetPreventWeaponPickup( false );
}
}
BaseClass::EndTouch( pOther );
}
//-----------------------------------------------------------------------------
// Teleport trigger
//-----------------------------------------------------------------------------
class CTriggerPhysicsTrap : public CTriggerMultiple
{
DECLARE_CLASS( CTriggerPhysicsTrap, CTriggerMultiple );
DECLARE_DATADESC();
public:
void Touch( CBaseEntity *pOther );
private:
void InputEnable( inputdata_t &inputdata );
void InputDisable( inputdata_t &inputdata );
void InputToggle( inputdata_t &inputdata );
int m_nDissolveType;
};
//-----------------------------------------------------------------------------
// Save/load
//-----------------------------------------------------------------------------
LINK_ENTITY_TO_CLASS( trigger_physics_trap, CTriggerPhysicsTrap );
BEGIN_DATADESC( CTriggerPhysicsTrap )
DEFINE_KEYFIELD( m_nDissolveType, FIELD_INTEGER, "dissolvetype" ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
END_DATADESC()
//------------------------------------------------------------------------------
// Inputs
//------------------------------------------------------------------------------
void CTriggerPhysicsTrap::InputToggle( inputdata_t &inputdata )
{
if ( m_bDisabled )
{
InputEnable( inputdata );
}
else
{
InputDisable( inputdata );
}
}
void CTriggerPhysicsTrap::InputEnable( inputdata_t &inputdata )
{
if ( m_bDisabled )
{
Enable();
}
}
void CTriggerPhysicsTrap::InputDisable( inputdata_t &inputdata )
{
if ( !m_bDisabled )
{
Disable();
}
}
//-----------------------------------------------------------------------------
// Traps the entities
//-----------------------------------------------------------------------------
#define JOINTS_TO_CONSTRAIN 1
void CTriggerPhysicsTrap::Touch( CBaseEntity *pOther )
{
if ( !PassesTriggerFilters(pOther) )
return;
CBaseAnimating *pAnim = pOther->GetBaseAnimating();
if ( !pAnim )
return;
#ifdef HL2_DLL
// HACK: Upgrade the physcannon
if ( FClassnameIs( pAnim, "weapon_physcannon" ) )
{
PhysCannonBeginUpgrade( pAnim );
return;
}
#endif
pAnim->Dissolve( NULL, gpGlobals->curtime, false, m_nDissolveType );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CWateryDeathLeech : public CBaseAnimating
{
DECLARE_CLASS( CWateryDeathLeech, CBaseAnimating );
public:
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void LeechThink( void );
int m_iFadeState;
};
LINK_ENTITY_TO_CLASS( ent_watery_leech, CWateryDeathLeech );
BEGIN_DATADESC( CWateryDeathLeech )
DEFINE_THINKFUNC( LeechThink ),
DEFINE_FIELD( m_iFadeState, FIELD_INTEGER ),
END_DATADESC()
void CWateryDeathLeech::Precache( void )
{
//Ugh this is temporary until Jakob finishes the animations and doesn't need the command anymore.
bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
CBaseEntity::SetAllowPrecache( true );
BaseClass::Precache();
PrecacheModel( "models/leech.mdl" );
CBaseEntity::SetAllowPrecache( allowPrecache );
}
void CWateryDeathLeech::Spawn( void )
{
Precache();
BaseClass::Spawn();
SetSolid ( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
AddEffects( EF_NOSHADOW );
SetModel( "models/leech.mdl" );
SetThink( &CWateryDeathLeech::LeechThink );
SetNextThink( gpGlobals->curtime + 0.1 );
m_flPlaybackRate = random->RandomFloat( 0.5, 1.5 );
SetCycle( random->RandomFloat( 0.0f, 0.9f ) );
QAngle vAngle;
vAngle[YAW] = random->RandomFloat( 0, 360 );
SetAbsAngles( vAngle );
m_iFadeState = 1;
SetRenderColorA( 1 );
}
void CWateryDeathLeech::LeechThink( void )
{
if ( IsMarkedForDeletion() )
return;
StudioFrameAdvance();
SetNextThink( gpGlobals->curtime + 0.1 );
if ( m_iFadeState != 0 )
{
float dt = gpGlobals->frametime;
if ( dt > 0.1f )
{
dt = 0.1f;
}
m_nRenderMode = kRenderTransTexture;
int speed = MAX(1,256*dt); // fade out over 1 second
if ( m_iFadeState == -1 )
SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) );
else
SetRenderColorA( UTIL_Approach( 255, m_clrRender->a, speed ) );
if ( m_clrRender->a == 0 )
{
UTIL_Remove(this);
}
else if ( m_clrRender->a == 255 )
{
m_iFadeState = 0;
}
else
{
SetNextThink( gpGlobals->curtime );
}
}
if ( GetOwnerEntity() )
{
if ( GetOwnerEntity()->GetWaterLevel() < 3 )
{
AddEffects( EF_NODRAW );
}
else
{
RemoveEffects( EF_NODRAW );
}
SetAbsOrigin( GetOwnerEntity()->GetAbsOrigin() + GetOwnerEntity()->GetViewOffset() );
}
}
class CTriggerWateryDeath : public CBaseTrigger
{
DECLARE_CLASS( CTriggerWateryDeath, CBaseTrigger );
public:
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void Touch( CBaseEntity *pOther );
void SpawnLeeches( CBaseEntity *pOther );
// Ignore non-living entities
virtual bool PassesTriggerFilters(CBaseEntity *pOther)
{
if ( !BaseClass::PassesTriggerFilters(pOther) )
return false;
return (pOther->m_takedamage == DAMAGE_YES);
}
virtual void StartTouch(CBaseEntity *pOther);
virtual void EndTouch(CBaseEntity *pOther);
private:
CUtlVector< EHANDLE > m_hLeeches;
// Kill times for entities I'm touching
CUtlVector< float > m_flEntityKillTimes;
float m_flNextPullSound;
float m_flPainValue;
};
BEGIN_DATADESC( CTriggerWateryDeath )
DEFINE_UTLVECTOR( m_flEntityKillTimes, FIELD_TIME ),
DEFINE_UTLVECTOR( m_hLeeches, FIELD_EHANDLE ),
DEFINE_FIELD( m_flNextPullSound, FIELD_TIME ),
DEFINE_FIELD( m_flPainValue, FIELD_FLOAT ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( trigger_waterydeath, CTriggerWateryDeath );
// Stages of the waterydeath trigger, in time offsets from the initial touch
#define WD_KILLTIME_NEXT_BITE 0.3
#define WD_PAINVALUE_STEP 2.0
#define WD_MAX_DAMAGE 15.0f
//-----------------------------------------------------------------------------
// Purpose: Called when spawning, after keyvalues have been handled.
//-----------------------------------------------------------------------------
void CTriggerWateryDeath::Spawn( void )
{
BaseClass::Spawn();
Precache();
m_flNextPullSound = 0;
m_flPainValue = 0;
InitTrigger();
}
void CTriggerWateryDeath::Precache( void )
{
//Ugh this is temporary until Jakob finishes the animations and doesn't need the command anymore.
BaseClass::Precache();
PrecacheModel( "models/leech.mdl" );
PrecacheScriptSound( "coast.leech_bites_loop" );
PrecacheScriptSound( "coast.leech_water_churn_loop" );
}
void CTriggerWateryDeath::SpawnLeeches( CBaseEntity *pOther )
{
if ( pOther == NULL )
return;
if ( m_hLeeches.Count() > 0 )
return;
int iMaxLeeches = 12;
for ( int i = 0; i < iMaxLeeches; i++ )
{
CWateryDeathLeech *pLeech = (CWateryDeathLeech*)CreateEntityByName( "ent_watery_leech" );
if ( pLeech )
{
m_hLeeches.AddToTail( pLeech );
pLeech->Spawn();
pLeech->SetAbsOrigin( pOther->GetAbsOrigin() );
pLeech->SetOwnerEntity( pOther );
if ( i <= 8 )
pLeech->SetSequence( i % 4 );
else
pLeech->SetSequence( ( i % 4 ) + 4 ) ;
pLeech->ResetSequenceInfo();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTriggerWateryDeath::Touch( CBaseEntity *pOther )
{
if (!PassesTriggerFilters(pOther))
return;
// Find our index
EHANDLE hOther;
hOther = pOther;
int iIndex = m_hTouchingEntities.Find( hOther );
if ( iIndex == m_hTouchingEntities.InvalidIndex() )
return;
float flKillTime = m_flEntityKillTimes[iIndex];
// Time to kill it?
if ( gpGlobals->curtime > flKillTime )
{
//EmitSound( filter, entindex(), "WateryDeath.Bite", &pOther->GetAbsOrigin() );
// Kill it
if ( pOther->IsPlayer() )
{
m_flPainValue = MIN( m_flPainValue + WD_PAINVALUE_STEP, WD_MAX_DAMAGE );
}
else
{
m_flPainValue = WD_MAX_DAMAGE;
}
// Use DMG_GENERIC & make the target inflict the damage on himself.
// This ensures that if the target is the player, the damage isn't modified by skill
CTakeDamageInfo info = CTakeDamageInfo( pOther, pOther, m_flPainValue, DMG_GENERIC );
GuessDamageForce( &info, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() );
pOther->TakeDamage( info );
m_flEntityKillTimes[iIndex] = gpGlobals->curtime + WD_KILLTIME_NEXT_BITE;
}
}
//-----------------------------------------------------------------------------
// Purpose: Called when an entity starts touching us.
// Input : pOther - The entity that is touching us.
//-----------------------------------------------------------------------------
void CTriggerWateryDeath::StartTouch(CBaseEntity *pOther)
{
BaseClass::StartTouch( pOther );
m_flPainValue = 0.0f;
// If we added him to our list, store the start time
EHANDLE hOther;
hOther = pOther;
if ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() )
{
// Always added to the end
// Players get warned, everything else gets et quick.
if ( pOther->IsPlayer() )
{
m_flEntityKillTimes.AddToTail( gpGlobals->curtime + WD_KILLTIME_NEXT_BITE );
}
else
{
m_flEntityKillTimes.AddToTail( gpGlobals->curtime + WD_KILLTIME_NEXT_BITE );
}
}
#ifdef HL2_DLL
if ( pOther->IsPlayer() )
{
SpawnLeeches( pOther );
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pOther );
if ( pHL2Player )
{
pHL2Player->StartWaterDeathSounds();
}
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Called when an entity stops touching us.
// Input : pOther - The entity that was touching us.
//-----------------------------------------------------------------------------
void CTriggerWateryDeath::EndTouch( CBaseEntity *pOther )
{
if ( IsTouching( pOther ) )
{
EHANDLE hOther;
hOther = pOther;
// Remove the time from our list
int iIndex = m_hTouchingEntities.Find( hOther );
if ( iIndex != m_hTouchingEntities.InvalidIndex() )
{
m_flEntityKillTimes.Remove( iIndex );
}
}
#ifdef HL2_DLL
if ( pOther->IsPlayer() )
{
for (int i = 0; i < m_hLeeches.Count(); i++ )
{
CWateryDeathLeech *pLeech = dynamic_cast<CWateryDeathLeech*>( m_hLeeches[i].Get() );
if ( pLeech )
{
pLeech->m_iFadeState = -1;
}
}
if ( m_hLeeches.Count() > 0 )
m_hLeeches.Purge();
CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pOther );
if ( pHL2Player )
{
//Adrian: Hi, you might be wondering why I'm doing this, yes?
// Well, EndTouch is called not only when the player leaves
// the trigger, but also on level shutdown. We can't let the
// soundpatch fade the sound out since we'll hit a nasty assert
// cause it'll try to fade out a sound using an entity that might
// be gone since we're shutting down the server.
if ( !(pHL2Player->GetFlags() & FL_DONTTOUCH ) )
pHL2Player->StopWaterDeathSounds();
}
}
#endif
BaseClass::EndTouch( pOther );
}
//-----------------------------------------------------------------------------
// Purpose: Triggers whenever an RPG is fired within it
//-----------------------------------------------------------------------------
class CTriggerRPGFire : public CTriggerMultiple
{
DECLARE_CLASS( CTriggerRPGFire, CTriggerMultiple );
public:
~CTriggerRPGFire();
void Spawn( void );
void OnRestore( void );
};
LINK_ENTITY_TO_CLASS( trigger_rpgfire, CTriggerRPGFire );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTriggerRPGFire::~CTriggerRPGFire( void )
{
g_hWeaponFireTriggers.FindAndRemove( this );
}
//-----------------------------------------------------------------------------
// Purpose: Called when spawning, after keyvalues have been handled.
//-----------------------------------------------------------------------------
void CTriggerRPGFire::Spawn( void )
{
BaseClass::Spawn();
InitTrigger();
g_hWeaponFireTriggers.AddToTail( this );
// Stomp the touch function, because we don't want to respond to touch
SetTouch( NULL );
}
//------------------------------------------------------------------------------
// Purpose:
//------------------------------------------------------------------------------
void CTriggerRPGFire::OnRestore()
{
BaseClass::OnRestore();
g_hWeaponFireTriggers.AddToTail( this );
}