mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-29 17:43:01 +00:00
876 lines
24 KiB
C++
876 lines
24 KiB
C++
//========= 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 );
|
|
}
|