summaryrefslogtreecommitdiff
path: root/game/server/tf/tf_triggers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf/tf_triggers.cpp')
-rw-r--r--game/server/tf/tf_triggers.cpp1021
1 files changed, 1021 insertions, 0 deletions
diff --git a/game/server/tf/tf_triggers.cpp b/game/server/tf/tf_triggers.cpp
new file mode 100644
index 0000000..2001d09
--- /dev/null
+++ b/game/server/tf/tf_triggers.cpp
@@ -0,0 +1,1021 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Team Fortress specific special triggers
+//
+//===========================================================================//
+
+#include "cbase.h"
+#include "player.h"
+#include "gamerules.h"
+#include "entityapi.h"
+#include "entitylist.h"
+#include "saverestore_utlvector.h"
+#include "tf_player.h"
+#include "triggers.h"
+#include "tf_triggers.h"
+#include "tf_weapon_compound_bow.h"
+#include "doors.h"
+#include "bot/tf_bot.h"
+#include "trigger_area_capture.h"
+#include "particle_parse.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+BEGIN_DATADESC( CTriggerStun )
+
+ // Function Pointers
+ DEFINE_FUNCTION( StunThink ),
+
+ // Fields
+
+ DEFINE_KEYFIELD( m_flTriggerDelay, FIELD_FLOAT, "trigger_delay" ),
+ DEFINE_KEYFIELD( m_flStunDuration, FIELD_FLOAT, "stun_duration" ),
+ DEFINE_KEYFIELD( m_flMoveSpeedReduction, FIELD_FLOAT, "move_speed_reduction" ),
+ DEFINE_KEYFIELD( m_iStunType, FIELD_INTEGER, "stun_type" ),
+ DEFINE_KEYFIELD( m_bStunEffects, FIELD_INTEGER, "stun_effects" ),
+
+ DEFINE_UTLVECTOR( m_stunEntities, FIELD_EHANDLE ),
+
+ // Outputs
+ DEFINE_OUTPUT( m_OnStunPlayer, "OnStunPlayer" ),
+
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( trigger_stun, CTriggerStun );
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when spawning, after keyvalues have been handled.
+//-----------------------------------------------------------------------------
+void CTriggerStun::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ InitTrigger();
+
+ SetNextThink( TICK_NEVER_THINK );
+ SetThink( NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: When touched, a stun trigger applies its stunflags to the other for a duration.
+// Input : pOther - The entity that is touching us.
+//-----------------------------------------------------------------------------
+bool CTriggerStun::StunEntity( CBaseEntity *pOther )
+{
+ if ( !pOther->m_takedamage || !PassesTriggerFilters(pOther) )
+ return false;
+
+ CTFPlayer* pTFPlayer = ToTFPlayer( pOther );
+ if ( !pTFPlayer )
+ return false;
+
+ int iStunFlags = TF_STUN_MOVEMENT;
+ switch ( m_iStunType )
+ {
+ case 0:
+ // Movement Only
+ break;
+ case 1:
+ // Controls
+ iStunFlags |= TF_STUN_CONTROLS;
+ break;
+ case 2:
+ // Loser State
+ iStunFlags |= TF_STUN_LOSER_STATE;
+ break;
+ }
+
+ if ( !m_bStunEffects )
+ {
+ iStunFlags |= TF_STUN_NO_EFFECTS;
+ }
+
+ iStunFlags |= TF_STUN_BY_TRIGGER;
+
+ pTFPlayer->m_Shared.StunPlayer( m_flStunDuration, m_flMoveSpeedReduction, iStunFlags, NULL );
+
+ m_OnStunPlayer.FireOutput( pOther, this );
+ m_stunEntities.AddToTail( EHANDLE(pOther) );
+
+ return true;
+}
+
+void CTriggerStun::StunThink()
+{
+ // If I stun anyone, think again.
+ if ( StunAllTouchers( 0.5 ) <= 0 )
+ {
+ SetThink(NULL);
+ }
+ else
+ {
+ SetNextThink( gpGlobals->curtime + 0.5f );
+ }
+}
+
+void CTriggerStun::EndTouch( CBaseEntity *pOther )
+{
+ if ( PassesTriggerFilters(pOther) )
+ {
+ EHANDLE hOther;
+ hOther = pOther;
+
+ // If this guy has never been stunned...
+ if ( !m_stunEntities.HasElement( hOther ) )
+ {
+ StunEntity( pOther );
+ }
+ }
+ BaseClass::EndTouch( pOther );
+}
+
+int CTriggerStun::StunAllTouchers( float dt )
+{
+ m_flLastStunTime = gpGlobals->curtime;
+ m_stunEntities.RemoveAll();
+
+ int stunCount = 0;
+ touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
+ if ( root )
+ {
+ for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
+ {
+ CBaseEntity *pTouch = link->entityTouched;
+ if ( pTouch )
+ {
+ if ( StunEntity( pTouch ) )
+ {
+ stunCount++;
+ }
+ }
+ }
+ }
+
+ return stunCount;
+}
+
+void CTriggerStun::Touch( CBaseEntity *pOther )
+{
+ if ( m_pfnThink == NULL )
+ {
+ SetThink( &CTriggerStun::StunThink );
+ SetNextThink( gpGlobals->curtime + m_flTriggerDelay );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Ignites the arrows of any bow carried by a player who touches this trigger
+//-----------------------------------------------------------------------------
+class CTriggerIgniteArrows : public CBaseTrigger
+{
+public:
+ DECLARE_CLASS( CTriggerIgniteArrows, CBaseTrigger );
+
+ void Spawn( void );
+ void Touch( CBaseEntity *pOther );
+
+ DECLARE_DATADESC();
+};
+
+BEGIN_DATADESC( CTriggerIgniteArrows )
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_ignite_arrows, CTriggerIgniteArrows );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTriggerIgniteArrows::Spawn( void )
+{
+ BaseClass::Spawn();
+ InitTrigger();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ignites the arrows of any bow carried by a player who touches this trigger
+//-----------------------------------------------------------------------------
+void CTriggerIgniteArrows::Touch( CBaseEntity *pOther )
+{
+ if (!PassesTriggerFilters(pOther))
+ return;
+
+ if ( !pOther->IsPlayer() )
+ return;
+
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+
+ // Ignore non-snipers
+ if ( !pPlayer || !pPlayer->IsPlayerClass(TF_CLASS_SNIPER) )
+ return;
+
+ // Make sure they're looking at the origin
+ Vector vecPos, vecForward, vecUp, vecRight;
+ pPlayer->EyePositionAndVectors( &vecPos, &vecForward, &vecUp, &vecRight );
+ Vector vTargetDir = GetAbsOrigin() - vecPos;
+ VectorNormalize(vTargetDir);
+ float fDotPr = DotProduct(vecForward,vTargetDir);
+ if (fDotPr < 0.95)
+ return;
+
+ // Does he have the bow?
+ CTFWeaponBase *pWpn = pPlayer->GetActiveTFWeapon();
+ if ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_COMPOUND_BOW )
+ {
+ CTFCompoundBow *pBow = static_cast<CTFCompoundBow*>( pWpn );
+ pBow->SetArrowAlight( true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Trigger that controls door speed by specified time
+//-----------------------------------------------------------------------------
+class CTriggerTimerDoor : public CTriggerAreaCapture
+{
+public:
+ DECLARE_CLASS( CTriggerTimerDoor, CTriggerAreaCapture );
+ DECLARE_DATADESC();
+
+ virtual void Spawn( void ) OVERRIDE;
+ virtual void StartTouch( CBaseEntity *pOther ) OVERRIDE;
+
+ virtual void OnStartCapture( int iTeam ) OVERRIDE;
+ virtual void OnEndCapture( int iTeam ) OVERRIDE;
+
+protected:
+ virtual bool CaptureModeScalesWithPlayers() const OVERRIDE { return false; }
+
+private:
+ CHandle<CBaseDoor> m_hDoor; //the door that we are linked to!
+
+ string_t m_iszDoorName;
+};
+
+BEGIN_DATADESC( CTriggerTimerDoor )
+ DEFINE_KEYFIELD( m_iszDoorName, FIELD_STRING, "door_name" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_timer_door, CTriggerTimerDoor );
+
+#define GATE_THINK_TIME 0.1f
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTriggerTimerDoor::Spawn( void )
+{
+ BaseClass::Spawn();
+ InitTrigger();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Bot enters the trigger, open the door
+//-----------------------------------------------------------------------------
+void CTriggerTimerDoor::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ return;
+
+ if (!PassesTriggerFilters(pOther))
+ return;
+
+ if ( !m_hDoor )
+ {
+ m_hDoor = dynamic_cast< CBaseDoor* >( gEntList.FindEntityByName(NULL, m_iszDoorName ) );
+ if ( !m_hDoor )
+ {
+ Warning( "trigger_bot_gate failed to find \"%s\" door entity", STRING( m_iszDoorName ) );
+ return;
+ }
+ else
+ {
+ float flDoorTravelDistance = ( m_hDoor->m_vecPosition2 - m_hDoor->m_vecPosition1 ).Length();
+ m_hDoor->m_flSpeed = flDoorTravelDistance / GetCapTime();
+ }
+ }
+
+ BaseClass::StartTouch( pOther );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Bot starts opening the door
+//-----------------------------------------------------------------------------
+void CTriggerTimerDoor::OnStartCapture( int iTeam )
+{
+ BaseClass::OnStartCapture( iTeam );
+
+ if ( FStrEq( gpGlobals->mapname.ToCStr(), "mvm_mannhattan" ) )
+ {
+ TFGameRules()->RandomPlayersSpeakConceptIfAllowed( MP_CONCEPT_MANNHATTAN_GATE_ATK, 1, TF_TEAM_RED );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Bot finishes opening the door
+//-----------------------------------------------------------------------------
+void CTriggerTimerDoor::OnEndCapture( int iTeam )
+{
+ BaseClass::OnEndCapture( iTeam );
+
+ if ( FStrEq( gpGlobals->mapname.ToCStr(), "mvm_mannhattan" ) )
+ {
+ TFGameRules()->RandomPlayersSpeakConceptIfAllowed( MP_CONCEPT_MANNHATTAN_GATE_TAKE, 1, TF_TEAM_RED );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Trigger that adds tag to bots
+//-----------------------------------------------------------------------------
+class CTriggerBotTag : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerBotTag, CBaseTrigger );
+
+ virtual void Spawn( void );
+
+ virtual void Touch( CBaseEntity *pOther );
+
+private:
+ string_t m_iszTags;
+
+ bool m_bAdd;
+ CUtlStringList m_tags;
+};
+
+BEGIN_DATADESC( CTriggerBotTag )
+ DEFINE_KEYFIELD( m_iszTags, FIELD_STRING, "tags" ),
+ DEFINE_KEYFIELD( m_bAdd, FIELD_BOOLEAN, "add" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_bot_tag, CTriggerBotTag );
+
+void CTriggerBotTag::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+
+ m_tags.RemoveAll();
+ // chop space-delimited string into individual tokens
+ const char *tags = STRING( m_iszTags );
+ if ( tags )
+ {
+ CSplitString splitStrings( tags, " " );
+ for( int i=0; i<splitStrings.Count(); ++i )
+ {
+ m_tags.CopyAndAddToTail( splitStrings.Element( i ) );
+ }
+ }
+}
+
+
+void CTriggerBotTag::Touch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !pOther->IsPlayer() )
+ {
+ return;
+ }
+
+ CTFBot *pBot = ToTFBot( pOther );
+ if ( !pBot )
+ {
+ return;
+ }
+
+ if ( m_bAdd )
+ {
+ for ( int i=0; i<m_tags.Count(); ++i )
+ {
+ pBot->AddTag( m_tags[i] );
+ }
+ }
+ else
+ {
+ for ( int i=0; i<m_tags.Count(); ++i )
+ {
+ pBot->RemoveTag( m_tags[i] );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Trigger that adds a condition to players
+//-----------------------------------------------------------------------------
+class CTriggerAddTFPlayerCondition : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerAddTFPlayerCondition, CBaseTrigger );
+
+ virtual void Spawn( void );
+
+ virtual void StartTouch( CBaseEntity *pOther );
+ virtual void EndTouch( CBaseEntity *pOther );
+
+private:
+ ETFCond m_nCondition;
+
+ float m_flDuration;
+};
+
+BEGIN_DATADESC( CTriggerAddTFPlayerCondition )
+ DEFINE_KEYFIELD( m_nCondition, FIELD_INTEGER, "condition" ),
+ DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_add_tf_player_condition, CTriggerAddTFPlayerCondition );
+
+void CTriggerAddTFPlayerCondition::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+}
+
+
+void CTriggerAddTFPlayerCondition::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ if ( !pOther->IsPlayer() )
+ {
+ return;
+ }
+
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ if ( m_nCondition != TF_COND_INVALID )
+ {
+ pPlayer->m_Shared.AddCond( m_nCondition, m_flDuration );
+ BaseClass::StartTouch( pOther );
+ }
+ else
+ {
+ Warning( "Invalid Condition ID [%d] in trigger %s\n", m_nCondition, GetEntityName().ToCStr() );
+ }
+}
+
+void CTriggerAddTFPlayerCondition::EndTouch( CBaseEntity *pOther )
+{
+ if ( m_flDuration != PERMANENT_CONDITION )
+ {
+ return;
+ }
+
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ if ( !pOther->IsPlayer() )
+ {
+ return;
+ }
+
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ if ( m_nCondition != TF_COND_INVALID )
+ {
+ pPlayer->m_Shared.RemoveCond( m_nCondition );
+ BaseClass::EndTouch( pOther );
+ }
+ else
+ {
+ Warning( "Invalid Condition ID [%d] in trigger %s\n", m_nCondition, GetEntityName().ToCStr() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: CTriggerPlayerRespawnOverride
+//-----------------------------------------------------------------------------
+BEGIN_DATADESC( CTriggerPlayerRespawnOverride )
+ DEFINE_KEYFIELD( m_flRespawnTime, FIELD_FLOAT, "RespawnTime" ),
+ DEFINE_KEYFIELD( m_strRespawnEnt, FIELD_STRING, "RespawnName" ),
+ DEFINE_INPUTFUNC( FIELD_FLOAT, "SetRespawnTime", InputSetRespawnTime ),
+ DEFINE_INPUTFUNC( FIELD_STRING, "SetRespawnName", InputSetRespawnName ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_player_respawn_override, CTriggerPlayerRespawnOverride );
+
+IMPLEMENT_AUTO_LIST( ITriggerPlayerRespawnOverride );
+
+//-----------------------------------------------------------------------------
+// Purpose: Trigger that ignites players
+//-----------------------------------------------------------------------------
+class CTriggerIgnite : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerIgnite, CBaseTrigger );
+
+ CTriggerIgnite();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+
+ virtual void StartTouch( CBaseEntity *pOther );
+
+ void BurnThink();
+
+private:
+ void IgniteEntity( CBaseEntity *pOther );
+ int BurnEntities();
+
+ float m_flBurnDuration;
+ float m_flDamagePercentPerSecond;
+ string_t m_iszIgniteParticleName;
+ string_t m_iszIgniteSoundName;
+
+ float m_flLastBurnTime;
+};
+
+BEGIN_DATADESC( CTriggerIgnite )
+ DEFINE_KEYFIELD( m_flBurnDuration, FIELD_FLOAT, "burn_duration" ),
+ DEFINE_KEYFIELD( m_flDamagePercentPerSecond, FIELD_FLOAT, "damage_percent_per_second" ),
+ DEFINE_KEYFIELD( m_iszIgniteParticleName, FIELD_STRING, "ignite_particle_name" ),
+ DEFINE_KEYFIELD( m_iszIgniteSoundName, FIELD_STRING, "ignite_sound_name" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_ignite, CTriggerIgnite );
+
+#define BURN_INTERVAL 0.1f
+
+
+CTriggerIgnite::CTriggerIgnite()
+{
+ m_flBurnDuration = 1.f;
+ m_flDamagePercentPerSecond = 10.f;
+ m_flLastBurnTime = 0.f;
+}
+
+
+void CTriggerIgnite::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+
+ SetNextThink( TICK_NEVER_THINK );
+ SetThink( NULL );
+}
+
+
+void CTriggerIgnite::Precache( void )
+{
+ BaseClass::Precache();
+
+ const char *pszParticleName = STRING( m_iszIgniteParticleName );
+ if ( pszParticleName && *pszParticleName )
+ {
+ PrecacheParticleSystem( pszParticleName );
+ }
+
+ const char *pszSoundName = STRING( m_iszIgniteSoundName );
+ if ( pszSoundName && *pszSoundName )
+ {
+ PrecacheScriptSound( pszSoundName );
+ }
+}
+
+
+void CTriggerIgnite::BurnThink()
+{
+ if ( BurnEntities() > 0 )
+ {
+ SetNextThink( gpGlobals->curtime + BURN_INTERVAL );
+ }
+ else
+ {
+ SetThink( NULL );
+ }
+}
+
+
+void CTriggerIgnite::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ IgniteEntity( pOther );
+
+ BaseClass::StartTouch( pOther );
+
+ if ( m_pfnThink == NULL )
+ {
+ m_flLastBurnTime = gpGlobals->curtime;
+ SetThink( &CTriggerIgnite::BurnThink );
+ SetNextThink( gpGlobals->curtime + BURN_INTERVAL );
+ }
+}
+
+
+void CTriggerIgnite::IgniteEntity( CBaseEntity *pOther )
+{
+ Vector vecEffectPos = pOther->GetAbsOrigin();
+ const char *pszParticleName = STRING( m_iszIgniteParticleName );
+ if ( pszParticleName && *pszParticleName )
+ {
+ DispatchParticleEffect( pszParticleName, vecEffectPos, vec3_angle );
+ }
+
+ const char *pszSoundName = STRING( m_iszIgniteSoundName );
+ if ( pszSoundName && *pszSoundName )
+ {
+ CSoundParameters params;
+ if ( CBaseEntity::GetParametersForSound( pszSoundName, params, NULL ) )
+ {
+ CPASAttenuationFilter soundFilter( vecEffectPos, params.soundlevel );
+ EmitSound_t ep( params );
+ ep.m_pOrigin = &vecEffectPos;
+ EmitSound( soundFilter, entindex(), ep );
+ }
+ }
+
+ if ( pOther->IsPlayer() )
+ {
+ CTFPlayer *pTFPlayer = ToTFPlayer( pOther );
+ if ( pTFPlayer && !pTFPlayer->m_Shared.IsInvulnerable() && !pTFPlayer->m_Shared.InCond( TF_COND_BURNING ) )
+ {
+ pTFPlayer->m_Shared.SelfBurn( m_flBurnDuration );
+ }
+ }
+ else
+ {
+ UTIL_Remove( pOther );
+ }
+}
+
+
+int CTriggerIgnite::BurnEntities()
+{
+ if ( m_hTouchingEntities.IsEmpty() )
+ return 0;
+
+ float flDT = gpGlobals->curtime - m_flLastBurnTime;
+ m_flLastBurnTime = gpGlobals->curtime;
+ int nBurn = 0;
+ FOR_EACH_VEC( m_hTouchingEntities, i )
+ {
+ CBaseEntity *pEnt = m_hTouchingEntities[i];
+ if ( pEnt && pEnt->IsPlayer() )
+ {
+ CTFPlayer *pTFPlayer = ToTFPlayer( pEnt );
+ if ( pTFPlayer )
+ {
+ float flDamageScale = m_flDamagePercentPerSecond * 0.01f;
+ float flDamage = flDT * flDamageScale * pTFPlayer->GetMaxHealth();
+ CTakeDamageInfo info( this, this, flDamage, DMG_BURN );
+ if ( !pTFPlayer->m_Shared.IsInvulnerable() && !pTFPlayer->m_Shared.InCond( TF_COND_BURNING ) ) // if player enters trigger invuln, we need to ignite them when it wears off. We also don't want to ignite an already burning player
+ {
+ IgniteEntity( pTFPlayer );
+ }
+
+ pTFPlayer->TakeDamage( info );
+ nBurn++;
+ }
+ }
+ }
+
+ return nBurn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Trigger that spawn particles on entities
+//-----------------------------------------------------------------------------
+class CTriggerParticle : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerParticle, CBaseTrigger );
+
+ CTriggerParticle();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+
+ virtual void StartTouch( CBaseEntity *pOther );
+
+private:
+ string_t m_iszParticleName;
+ string_t m_iszAttachmentName;
+ ParticleAttachment_t m_nAttachType;
+};
+
+BEGIN_DATADESC( CTriggerParticle )
+ DEFINE_KEYFIELD( m_iszParticleName, FIELD_STRING, "particle_name" ),
+ DEFINE_KEYFIELD( m_iszAttachmentName, FIELD_STRING, "attachment_name" ),
+ DEFINE_KEYFIELD( m_nAttachType, FIELD_INTEGER, "attachment_type" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_particle, CTriggerParticle );
+
+
+CTriggerParticle::CTriggerParticle()
+{
+ m_nAttachType = PATTACH_ABSORIGIN;
+}
+
+
+void CTriggerParticle::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+
+ SetNextThink( TICK_NEVER_THINK );
+ SetThink( NULL );
+}
+
+
+void CTriggerParticle::Precache( void )
+{
+ BaseClass::Precache();
+
+ const char *pszParticleName = STRING( m_iszParticleName );
+ if ( pszParticleName && *pszParticleName )
+ {
+ PrecacheParticleSystem( pszParticleName );
+ }
+}
+
+
+void CTriggerParticle::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ BaseClass::StartTouch( pOther );
+
+ const char *pszParticleName = STRING( m_iszParticleName );
+ const char *pszAttachmentName = STRING( m_iszAttachmentName );
+ int iAttachment = -1;
+ if ( pszAttachmentName && *pszAttachmentName )
+ {
+ iAttachment = pOther->GetBaseAnimating()->LookupAttachment( pszAttachmentName );
+ }
+
+ DispatchParticleEffect( pszParticleName, m_nAttachType, pOther, iAttachment );
+}
+
+
+class CTriggerRemoveTFPlayerCondition : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerRemoveTFPlayerCondition, CBaseTrigger );
+
+ virtual void Spawn( void );
+
+ virtual void StartTouch( CBaseEntity *pOther );
+
+private:
+ ETFCond m_nCondition;
+};
+
+
+BEGIN_DATADESC( CTriggerRemoveTFPlayerCondition )
+ DEFINE_KEYFIELD( m_nCondition, FIELD_INTEGER, "condition" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_remove_tf_player_condition, CTriggerRemoveTFPlayerCondition );
+
+
+void CTriggerRemoveTFPlayerCondition::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+}
+
+
+void CTriggerRemoveTFPlayerCondition::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ if ( !pOther->IsPlayer() )
+ {
+ return;
+ }
+
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ if ( m_nCondition != TF_COND_INVALID )
+ {
+ pPlayer->m_Shared.RemoveCond( m_nCondition );
+
+ // Hack for Bank until we re-address this for parachuting MvM bots
+ CTFBot *pTFBot = dynamic_cast< CTFBot* >( pPlayer );
+ if ( pTFBot )
+ {
+ pTFBot->ClearLastKnownArea();
+ }
+ }
+ else
+ {
+ pPlayer->m_Shared.RemoveAllCond();
+ }
+
+ BaseClass::StartTouch( pOther );
+}
+
+
+class CTriggerAddOrRemoveTFPlayerAttributes : public CBaseTrigger
+{
+public:
+ DECLARE_DATADESC();
+ DECLARE_CLASS( CTriggerAddOrRemoveTFPlayerAttributes, CBaseTrigger );
+
+ virtual void Spawn( void );
+
+ virtual void StartTouch( CBaseEntity *pOther );
+ virtual void EndTouch( CBaseEntity *pOther );
+
+private:
+ bool m_bRemove;
+ string_t m_iszAttributeName;
+ float m_flAttributeValue;
+ float m_flDuration;
+ bool m_bValidAttribute;
+};
+
+
+BEGIN_DATADESC( CTriggerAddOrRemoveTFPlayerAttributes )
+ DEFINE_KEYFIELD( m_bRemove, FIELD_BOOLEAN, "add_or_remove" ),
+ DEFINE_KEYFIELD( m_iszAttributeName, FIELD_STRING, "attribute_name" ),
+ DEFINE_KEYFIELD( m_flAttributeValue, FIELD_FLOAT, "value" ),
+ DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( trigger_add_or_remove_tf_player_attributes, CTriggerAddOrRemoveTFPlayerAttributes );
+
+
+void CTriggerAddOrRemoveTFPlayerAttributes::Spawn()
+{
+ BaseClass::Spawn();
+ InitTrigger();
+
+ m_bValidAttribute = false;
+ const char *pszAttrName = STRING( m_iszAttributeName );
+ if ( pszAttrName && *pszAttrName )
+ {
+ CSchemaAttributeDefHandle pAttrTest( pszAttrName );
+ if ( pAttrTest )
+ {
+ m_bValidAttribute = true;
+ }
+ else
+ {
+ Warning( "Invalid attribute name '%s' from trigger trigger_add_or_remove_tf_player_attributes name '%s'\n", pszAttrName, STRING( GetEntityName() ) );
+ }
+ }
+}
+
+
+void CTriggerAddOrRemoveTFPlayerAttributes::StartTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !m_bValidAttribute )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ if ( !pOther->IsPlayer() )
+ {
+ return;
+ }
+
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ const char *pszAttrName = STRING( m_iszAttributeName );
+ if ( m_bRemove )
+ {
+ pPlayer->RemoveCustomAttribute( pszAttrName );
+ }
+ else
+ {
+ pPlayer->AddCustomAttribute( pszAttrName, m_flAttributeValue, m_flDuration );
+ }
+
+ BaseClass::StartTouch( pOther );
+}
+
+
+void CTriggerAddOrRemoveTFPlayerAttributes::EndTouch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ {
+ return;
+ }
+
+ if ( !m_bValidAttribute )
+ {
+ return;
+ }
+
+ // only run auto remove on added attribute
+ if ( m_bRemove )
+ {
+ return;
+ }
+
+ // ignore timer attribute. it'll remove itself
+ if ( m_flDuration > 0.f )
+ {
+ return;
+ }
+
+ if ( !PassesTriggerFilters(pOther) )
+ {
+ return;
+ }
+
+ if ( !pOther->IsPlayer() )
+ {
+ return;
+ }
+
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ // remove permanent attribute on exist trigger
+ const char *pszAttrName = STRING( m_iszAttributeName );
+ pPlayer->RemoveCustomAttribute( pszAttrName );
+
+ BaseClass::EndTouch( pOther );
+}