aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/hl2/hl2_triggers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mp/src/game/server/hl2/hl2_triggers.cpp')
-rw-r--r--mp/src/game/server/hl2/hl2_triggers.cpp875
1 files changed, 875 insertions, 0 deletions
diff --git a/mp/src/game/server/hl2/hl2_triggers.cpp b/mp/src/game/server/hl2/hl2_triggers.cpp
new file mode 100644
index 00000000..91d22fb3
--- /dev/null
+++ b/mp/src/game/server/hl2/hl2_triggers.cpp
@@ -0,0 +1,875 @@
+//========= 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 );
+}