Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1021 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Team Fortress specific special triggers
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "player.h"
  8. #include "gamerules.h"
  9. #include "entityapi.h"
  10. #include "entitylist.h"
  11. #include "saverestore_utlvector.h"
  12. #include "tf_player.h"
  13. #include "triggers.h"
  14. #include "tf_triggers.h"
  15. #include "tf_weapon_compound_bow.h"
  16. #include "doors.h"
  17. #include "bot/tf_bot.h"
  18. #include "trigger_area_capture.h"
  19. #include "particle_parse.h"
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. BEGIN_DATADESC( CTriggerStun )
  23. // Function Pointers
  24. DEFINE_FUNCTION( StunThink ),
  25. // Fields
  26. DEFINE_KEYFIELD( m_flTriggerDelay, FIELD_FLOAT, "trigger_delay" ),
  27. DEFINE_KEYFIELD( m_flStunDuration, FIELD_FLOAT, "stun_duration" ),
  28. DEFINE_KEYFIELD( m_flMoveSpeedReduction, FIELD_FLOAT, "move_speed_reduction" ),
  29. DEFINE_KEYFIELD( m_iStunType, FIELD_INTEGER, "stun_type" ),
  30. DEFINE_KEYFIELD( m_bStunEffects, FIELD_INTEGER, "stun_effects" ),
  31. DEFINE_UTLVECTOR( m_stunEntities, FIELD_EHANDLE ),
  32. // Outputs
  33. DEFINE_OUTPUT( m_OnStunPlayer, "OnStunPlayer" ),
  34. END_DATADESC()
  35. LINK_ENTITY_TO_CLASS( trigger_stun, CTriggerStun );
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Called when spawning, after keyvalues have been handled.
  38. //-----------------------------------------------------------------------------
  39. void CTriggerStun::Spawn( void )
  40. {
  41. BaseClass::Spawn();
  42. InitTrigger();
  43. SetNextThink( TICK_NEVER_THINK );
  44. SetThink( NULL );
  45. }
  46. //-----------------------------------------------------------------------------
  47. // Purpose: When touched, a stun trigger applies its stunflags to the other for a duration.
  48. // Input : pOther - The entity that is touching us.
  49. //-----------------------------------------------------------------------------
  50. bool CTriggerStun::StunEntity( CBaseEntity *pOther )
  51. {
  52. if ( !pOther->m_takedamage || !PassesTriggerFilters(pOther) )
  53. return false;
  54. CTFPlayer* pTFPlayer = ToTFPlayer( pOther );
  55. if ( !pTFPlayer )
  56. return false;
  57. int iStunFlags = TF_STUN_MOVEMENT;
  58. switch ( m_iStunType )
  59. {
  60. case 0:
  61. // Movement Only
  62. break;
  63. case 1:
  64. // Controls
  65. iStunFlags |= TF_STUN_CONTROLS;
  66. break;
  67. case 2:
  68. // Loser State
  69. iStunFlags |= TF_STUN_LOSER_STATE;
  70. break;
  71. }
  72. if ( !m_bStunEffects )
  73. {
  74. iStunFlags |= TF_STUN_NO_EFFECTS;
  75. }
  76. iStunFlags |= TF_STUN_BY_TRIGGER;
  77. pTFPlayer->m_Shared.StunPlayer( m_flStunDuration, m_flMoveSpeedReduction, iStunFlags, NULL );
  78. m_OnStunPlayer.FireOutput( pOther, this );
  79. m_stunEntities.AddToTail( EHANDLE(pOther) );
  80. return true;
  81. }
  82. void CTriggerStun::StunThink()
  83. {
  84. // If I stun anyone, think again.
  85. if ( StunAllTouchers( 0.5 ) <= 0 )
  86. {
  87. SetThink(NULL);
  88. }
  89. else
  90. {
  91. SetNextThink( gpGlobals->curtime + 0.5f );
  92. }
  93. }
  94. void CTriggerStun::EndTouch( CBaseEntity *pOther )
  95. {
  96. if ( PassesTriggerFilters(pOther) )
  97. {
  98. EHANDLE hOther;
  99. hOther = pOther;
  100. // If this guy has never been stunned...
  101. if ( !m_stunEntities.HasElement( hOther ) )
  102. {
  103. StunEntity( pOther );
  104. }
  105. }
  106. BaseClass::EndTouch( pOther );
  107. }
  108. int CTriggerStun::StunAllTouchers( float dt )
  109. {
  110. m_flLastStunTime = gpGlobals->curtime;
  111. m_stunEntities.RemoveAll();
  112. int stunCount = 0;
  113. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  114. if ( root )
  115. {
  116. for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
  117. {
  118. CBaseEntity *pTouch = link->entityTouched;
  119. if ( pTouch )
  120. {
  121. if ( StunEntity( pTouch ) )
  122. {
  123. stunCount++;
  124. }
  125. }
  126. }
  127. }
  128. return stunCount;
  129. }
  130. void CTriggerStun::Touch( CBaseEntity *pOther )
  131. {
  132. if ( m_pfnThink == NULL )
  133. {
  134. SetThink( &CTriggerStun::StunThink );
  135. SetNextThink( gpGlobals->curtime + m_flTriggerDelay );
  136. }
  137. }
  138. //-----------------------------------------------------------------------------
  139. // Purpose: Ignites the arrows of any bow carried by a player who touches this trigger
  140. //-----------------------------------------------------------------------------
  141. class CTriggerIgniteArrows : public CBaseTrigger
  142. {
  143. public:
  144. DECLARE_CLASS( CTriggerIgniteArrows, CBaseTrigger );
  145. void Spawn( void );
  146. void Touch( CBaseEntity *pOther );
  147. DECLARE_DATADESC();
  148. };
  149. BEGIN_DATADESC( CTriggerIgniteArrows )
  150. END_DATADESC()
  151. LINK_ENTITY_TO_CLASS( trigger_ignite_arrows, CTriggerIgniteArrows );
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CTriggerIgniteArrows::Spawn( void )
  156. {
  157. BaseClass::Spawn();
  158. InitTrigger();
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Purpose: Ignites the arrows of any bow carried by a player who touches this trigger
  162. //-----------------------------------------------------------------------------
  163. void CTriggerIgniteArrows::Touch( CBaseEntity *pOther )
  164. {
  165. if (!PassesTriggerFilters(pOther))
  166. return;
  167. if ( !pOther->IsPlayer() )
  168. return;
  169. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  170. // Ignore non-snipers
  171. if ( !pPlayer || !pPlayer->IsPlayerClass(TF_CLASS_SNIPER) )
  172. return;
  173. // Make sure they're looking at the origin
  174. Vector vecPos, vecForward, vecUp, vecRight;
  175. pPlayer->EyePositionAndVectors( &vecPos, &vecForward, &vecUp, &vecRight );
  176. Vector vTargetDir = GetAbsOrigin() - vecPos;
  177. VectorNormalize(vTargetDir);
  178. float fDotPr = DotProduct(vecForward,vTargetDir);
  179. if (fDotPr < 0.95)
  180. return;
  181. // Does he have the bow?
  182. CTFWeaponBase *pWpn = pPlayer->GetActiveTFWeapon();
  183. if ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_COMPOUND_BOW )
  184. {
  185. CTFCompoundBow *pBow = static_cast<CTFCompoundBow*>( pWpn );
  186. pBow->SetArrowAlight( true );
  187. }
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose: Trigger that controls door speed by specified time
  191. //-----------------------------------------------------------------------------
  192. class CTriggerTimerDoor : public CTriggerAreaCapture
  193. {
  194. public:
  195. DECLARE_CLASS( CTriggerTimerDoor, CTriggerAreaCapture );
  196. DECLARE_DATADESC();
  197. virtual void Spawn( void ) OVERRIDE;
  198. virtual void StartTouch( CBaseEntity *pOther ) OVERRIDE;
  199. virtual void OnStartCapture( int iTeam ) OVERRIDE;
  200. virtual void OnEndCapture( int iTeam ) OVERRIDE;
  201. protected:
  202. virtual bool CaptureModeScalesWithPlayers() const OVERRIDE { return false; }
  203. private:
  204. CHandle<CBaseDoor> m_hDoor; //the door that we are linked to!
  205. string_t m_iszDoorName;
  206. };
  207. BEGIN_DATADESC( CTriggerTimerDoor )
  208. DEFINE_KEYFIELD( m_iszDoorName, FIELD_STRING, "door_name" ),
  209. END_DATADESC()
  210. LINK_ENTITY_TO_CLASS( trigger_timer_door, CTriggerTimerDoor );
  211. #define GATE_THINK_TIME 0.1f
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CTriggerTimerDoor::Spawn( void )
  216. {
  217. BaseClass::Spawn();
  218. InitTrigger();
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose: Bot enters the trigger, open the door
  222. //-----------------------------------------------------------------------------
  223. void CTriggerTimerDoor::StartTouch( CBaseEntity *pOther )
  224. {
  225. if ( m_bDisabled )
  226. return;
  227. if (!PassesTriggerFilters(pOther))
  228. return;
  229. if ( !m_hDoor )
  230. {
  231. m_hDoor = dynamic_cast< CBaseDoor* >( gEntList.FindEntityByName(NULL, m_iszDoorName ) );
  232. if ( !m_hDoor )
  233. {
  234. Warning( "trigger_bot_gate failed to find \"%s\" door entity", STRING( m_iszDoorName ) );
  235. return;
  236. }
  237. else
  238. {
  239. float flDoorTravelDistance = ( m_hDoor->m_vecPosition2 - m_hDoor->m_vecPosition1 ).Length();
  240. m_hDoor->m_flSpeed = flDoorTravelDistance / GetCapTime();
  241. }
  242. }
  243. BaseClass::StartTouch( pOther );
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Purpose: Bot starts opening the door
  247. //-----------------------------------------------------------------------------
  248. void CTriggerTimerDoor::OnStartCapture( int iTeam )
  249. {
  250. BaseClass::OnStartCapture( iTeam );
  251. if ( FStrEq( gpGlobals->mapname.ToCStr(), "mvm_mannhattan" ) )
  252. {
  253. TFGameRules()->RandomPlayersSpeakConceptIfAllowed( MP_CONCEPT_MANNHATTAN_GATE_ATK, 1, TF_TEAM_RED );
  254. }
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Bot finishes opening the door
  258. //-----------------------------------------------------------------------------
  259. void CTriggerTimerDoor::OnEndCapture( int iTeam )
  260. {
  261. BaseClass::OnEndCapture( iTeam );
  262. if ( FStrEq( gpGlobals->mapname.ToCStr(), "mvm_mannhattan" ) )
  263. {
  264. TFGameRules()->RandomPlayersSpeakConceptIfAllowed( MP_CONCEPT_MANNHATTAN_GATE_TAKE, 1, TF_TEAM_RED );
  265. }
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose: Trigger that adds tag to bots
  269. //-----------------------------------------------------------------------------
  270. class CTriggerBotTag : public CBaseTrigger
  271. {
  272. public:
  273. DECLARE_DATADESC();
  274. DECLARE_CLASS( CTriggerBotTag, CBaseTrigger );
  275. virtual void Spawn( void );
  276. virtual void Touch( CBaseEntity *pOther );
  277. private:
  278. string_t m_iszTags;
  279. bool m_bAdd;
  280. CUtlStringList m_tags;
  281. };
  282. BEGIN_DATADESC( CTriggerBotTag )
  283. DEFINE_KEYFIELD( m_iszTags, FIELD_STRING, "tags" ),
  284. DEFINE_KEYFIELD( m_bAdd, FIELD_BOOLEAN, "add" ),
  285. END_DATADESC()
  286. LINK_ENTITY_TO_CLASS( trigger_bot_tag, CTriggerBotTag );
  287. void CTriggerBotTag::Spawn()
  288. {
  289. BaseClass::Spawn();
  290. InitTrigger();
  291. m_tags.RemoveAll();
  292. // chop space-delimited string into individual tokens
  293. const char *tags = STRING( m_iszTags );
  294. if ( tags )
  295. {
  296. CSplitString splitStrings( tags, " " );
  297. for( int i=0; i<splitStrings.Count(); ++i )
  298. {
  299. m_tags.CopyAndAddToTail( splitStrings.Element( i ) );
  300. }
  301. }
  302. }
  303. void CTriggerBotTag::Touch( CBaseEntity *pOther )
  304. {
  305. if ( m_bDisabled )
  306. {
  307. return;
  308. }
  309. if ( !pOther->IsPlayer() )
  310. {
  311. return;
  312. }
  313. CTFBot *pBot = ToTFBot( pOther );
  314. if ( !pBot )
  315. {
  316. return;
  317. }
  318. if ( m_bAdd )
  319. {
  320. for ( int i=0; i<m_tags.Count(); ++i )
  321. {
  322. pBot->AddTag( m_tags[i] );
  323. }
  324. }
  325. else
  326. {
  327. for ( int i=0; i<m_tags.Count(); ++i )
  328. {
  329. pBot->RemoveTag( m_tags[i] );
  330. }
  331. }
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose: Trigger that adds a condition to players
  335. //-----------------------------------------------------------------------------
  336. class CTriggerAddTFPlayerCondition : public CBaseTrigger
  337. {
  338. public:
  339. DECLARE_DATADESC();
  340. DECLARE_CLASS( CTriggerAddTFPlayerCondition, CBaseTrigger );
  341. virtual void Spawn( void );
  342. virtual void StartTouch( CBaseEntity *pOther );
  343. virtual void EndTouch( CBaseEntity *pOther );
  344. private:
  345. ETFCond m_nCondition;
  346. float m_flDuration;
  347. };
  348. BEGIN_DATADESC( CTriggerAddTFPlayerCondition )
  349. DEFINE_KEYFIELD( m_nCondition, FIELD_INTEGER, "condition" ),
  350. DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
  351. END_DATADESC()
  352. LINK_ENTITY_TO_CLASS( trigger_add_tf_player_condition, CTriggerAddTFPlayerCondition );
  353. void CTriggerAddTFPlayerCondition::Spawn()
  354. {
  355. BaseClass::Spawn();
  356. InitTrigger();
  357. }
  358. void CTriggerAddTFPlayerCondition::StartTouch( CBaseEntity *pOther )
  359. {
  360. if ( m_bDisabled )
  361. {
  362. return;
  363. }
  364. if ( !PassesTriggerFilters(pOther) )
  365. {
  366. return;
  367. }
  368. if ( !pOther->IsPlayer() )
  369. {
  370. return;
  371. }
  372. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  373. if ( !pPlayer )
  374. {
  375. return;
  376. }
  377. if ( m_nCondition != TF_COND_INVALID )
  378. {
  379. pPlayer->m_Shared.AddCond( m_nCondition, m_flDuration );
  380. BaseClass::StartTouch( pOther );
  381. }
  382. else
  383. {
  384. Warning( "Invalid Condition ID [%d] in trigger %s\n", m_nCondition, GetEntityName().ToCStr() );
  385. }
  386. }
  387. void CTriggerAddTFPlayerCondition::EndTouch( CBaseEntity *pOther )
  388. {
  389. if ( m_flDuration != PERMANENT_CONDITION )
  390. {
  391. return;
  392. }
  393. if ( m_bDisabled )
  394. {
  395. return;
  396. }
  397. if ( !PassesTriggerFilters(pOther) )
  398. {
  399. return;
  400. }
  401. if ( !pOther->IsPlayer() )
  402. {
  403. return;
  404. }
  405. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  406. if ( !pPlayer )
  407. {
  408. return;
  409. }
  410. if ( m_nCondition != TF_COND_INVALID )
  411. {
  412. pPlayer->m_Shared.RemoveCond( m_nCondition );
  413. BaseClass::EndTouch( pOther );
  414. }
  415. else
  416. {
  417. Warning( "Invalid Condition ID [%d] in trigger %s\n", m_nCondition, GetEntityName().ToCStr() );
  418. }
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Purpose: CTriggerPlayerRespawnOverride
  422. //-----------------------------------------------------------------------------
  423. BEGIN_DATADESC( CTriggerPlayerRespawnOverride )
  424. DEFINE_KEYFIELD( m_flRespawnTime, FIELD_FLOAT, "RespawnTime" ),
  425. DEFINE_KEYFIELD( m_strRespawnEnt, FIELD_STRING, "RespawnName" ),
  426. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetRespawnTime", InputSetRespawnTime ),
  427. DEFINE_INPUTFUNC( FIELD_STRING, "SetRespawnName", InputSetRespawnName ),
  428. END_DATADESC()
  429. LINK_ENTITY_TO_CLASS( trigger_player_respawn_override, CTriggerPlayerRespawnOverride );
  430. IMPLEMENT_AUTO_LIST( ITriggerPlayerRespawnOverride );
  431. //-----------------------------------------------------------------------------
  432. // Purpose: Trigger that ignites players
  433. //-----------------------------------------------------------------------------
  434. class CTriggerIgnite : public CBaseTrigger
  435. {
  436. public:
  437. DECLARE_DATADESC();
  438. DECLARE_CLASS( CTriggerIgnite, CBaseTrigger );
  439. CTriggerIgnite();
  440. virtual void Spawn( void );
  441. virtual void Precache( void );
  442. virtual void StartTouch( CBaseEntity *pOther );
  443. void BurnThink();
  444. private:
  445. void IgniteEntity( CBaseEntity *pOther );
  446. int BurnEntities();
  447. float m_flBurnDuration;
  448. float m_flDamagePercentPerSecond;
  449. string_t m_iszIgniteParticleName;
  450. string_t m_iszIgniteSoundName;
  451. float m_flLastBurnTime;
  452. };
  453. BEGIN_DATADESC( CTriggerIgnite )
  454. DEFINE_KEYFIELD( m_flBurnDuration, FIELD_FLOAT, "burn_duration" ),
  455. DEFINE_KEYFIELD( m_flDamagePercentPerSecond, FIELD_FLOAT, "damage_percent_per_second" ),
  456. DEFINE_KEYFIELD( m_iszIgniteParticleName, FIELD_STRING, "ignite_particle_name" ),
  457. DEFINE_KEYFIELD( m_iszIgniteSoundName, FIELD_STRING, "ignite_sound_name" ),
  458. END_DATADESC()
  459. LINK_ENTITY_TO_CLASS( trigger_ignite, CTriggerIgnite );
  460. #define BURN_INTERVAL 0.1f
  461. CTriggerIgnite::CTriggerIgnite()
  462. {
  463. m_flBurnDuration = 1.f;
  464. m_flDamagePercentPerSecond = 10.f;
  465. m_flLastBurnTime = 0.f;
  466. }
  467. void CTriggerIgnite::Spawn()
  468. {
  469. BaseClass::Spawn();
  470. InitTrigger();
  471. SetNextThink( TICK_NEVER_THINK );
  472. SetThink( NULL );
  473. }
  474. void CTriggerIgnite::Precache( void )
  475. {
  476. BaseClass::Precache();
  477. const char *pszParticleName = STRING( m_iszIgniteParticleName );
  478. if ( pszParticleName && *pszParticleName )
  479. {
  480. PrecacheParticleSystem( pszParticleName );
  481. }
  482. const char *pszSoundName = STRING( m_iszIgniteSoundName );
  483. if ( pszSoundName && *pszSoundName )
  484. {
  485. PrecacheScriptSound( pszSoundName );
  486. }
  487. }
  488. void CTriggerIgnite::BurnThink()
  489. {
  490. if ( BurnEntities() > 0 )
  491. {
  492. SetNextThink( gpGlobals->curtime + BURN_INTERVAL );
  493. }
  494. else
  495. {
  496. SetThink( NULL );
  497. }
  498. }
  499. void CTriggerIgnite::StartTouch( CBaseEntity *pOther )
  500. {
  501. if ( m_bDisabled )
  502. {
  503. return;
  504. }
  505. if ( !PassesTriggerFilters(pOther) )
  506. {
  507. return;
  508. }
  509. IgniteEntity( pOther );
  510. BaseClass::StartTouch( pOther );
  511. if ( m_pfnThink == NULL )
  512. {
  513. m_flLastBurnTime = gpGlobals->curtime;
  514. SetThink( &CTriggerIgnite::BurnThink );
  515. SetNextThink( gpGlobals->curtime + BURN_INTERVAL );
  516. }
  517. }
  518. void CTriggerIgnite::IgniteEntity( CBaseEntity *pOther )
  519. {
  520. Vector vecEffectPos = pOther->GetAbsOrigin();
  521. const char *pszParticleName = STRING( m_iszIgniteParticleName );
  522. if ( pszParticleName && *pszParticleName )
  523. {
  524. DispatchParticleEffect( pszParticleName, vecEffectPos, vec3_angle );
  525. }
  526. const char *pszSoundName = STRING( m_iszIgniteSoundName );
  527. if ( pszSoundName && *pszSoundName )
  528. {
  529. CSoundParameters params;
  530. if ( CBaseEntity::GetParametersForSound( pszSoundName, params, NULL ) )
  531. {
  532. CPASAttenuationFilter soundFilter( vecEffectPos, params.soundlevel );
  533. EmitSound_t ep( params );
  534. ep.m_pOrigin = &vecEffectPos;
  535. EmitSound( soundFilter, entindex(), ep );
  536. }
  537. }
  538. if ( pOther->IsPlayer() )
  539. {
  540. CTFPlayer *pTFPlayer = ToTFPlayer( pOther );
  541. if ( pTFPlayer && !pTFPlayer->m_Shared.IsInvulnerable() && !pTFPlayer->m_Shared.InCond( TF_COND_BURNING ) )
  542. {
  543. pTFPlayer->m_Shared.SelfBurn( m_flBurnDuration );
  544. }
  545. }
  546. else
  547. {
  548. UTIL_Remove( pOther );
  549. }
  550. }
  551. int CTriggerIgnite::BurnEntities()
  552. {
  553. if ( m_hTouchingEntities.IsEmpty() )
  554. return 0;
  555. float flDT = gpGlobals->curtime - m_flLastBurnTime;
  556. m_flLastBurnTime = gpGlobals->curtime;
  557. int nBurn = 0;
  558. FOR_EACH_VEC( m_hTouchingEntities, i )
  559. {
  560. CBaseEntity *pEnt = m_hTouchingEntities[i];
  561. if ( pEnt && pEnt->IsPlayer() )
  562. {
  563. CTFPlayer *pTFPlayer = ToTFPlayer( pEnt );
  564. if ( pTFPlayer )
  565. {
  566. float flDamageScale = m_flDamagePercentPerSecond * 0.01f;
  567. float flDamage = flDT * flDamageScale * pTFPlayer->GetMaxHealth();
  568. CTakeDamageInfo info( this, this, flDamage, DMG_BURN );
  569. 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
  570. {
  571. IgniteEntity( pTFPlayer );
  572. }
  573. pTFPlayer->TakeDamage( info );
  574. nBurn++;
  575. }
  576. }
  577. }
  578. return nBurn;
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Purpose: Trigger that spawn particles on entities
  582. //-----------------------------------------------------------------------------
  583. class CTriggerParticle : public CBaseTrigger
  584. {
  585. public:
  586. DECLARE_DATADESC();
  587. DECLARE_CLASS( CTriggerParticle, CBaseTrigger );
  588. CTriggerParticle();
  589. virtual void Spawn( void );
  590. virtual void Precache( void );
  591. virtual void StartTouch( CBaseEntity *pOther );
  592. private:
  593. string_t m_iszParticleName;
  594. string_t m_iszAttachmentName;
  595. ParticleAttachment_t m_nAttachType;
  596. };
  597. BEGIN_DATADESC( CTriggerParticle )
  598. DEFINE_KEYFIELD( m_iszParticleName, FIELD_STRING, "particle_name" ),
  599. DEFINE_KEYFIELD( m_iszAttachmentName, FIELD_STRING, "attachment_name" ),
  600. DEFINE_KEYFIELD( m_nAttachType, FIELD_INTEGER, "attachment_type" ),
  601. END_DATADESC()
  602. LINK_ENTITY_TO_CLASS( trigger_particle, CTriggerParticle );
  603. CTriggerParticle::CTriggerParticle()
  604. {
  605. m_nAttachType = PATTACH_ABSORIGIN;
  606. }
  607. void CTriggerParticle::Spawn()
  608. {
  609. BaseClass::Spawn();
  610. InitTrigger();
  611. SetNextThink( TICK_NEVER_THINK );
  612. SetThink( NULL );
  613. }
  614. void CTriggerParticle::Precache( void )
  615. {
  616. BaseClass::Precache();
  617. const char *pszParticleName = STRING( m_iszParticleName );
  618. if ( pszParticleName && *pszParticleName )
  619. {
  620. PrecacheParticleSystem( pszParticleName );
  621. }
  622. }
  623. void CTriggerParticle::StartTouch( CBaseEntity *pOther )
  624. {
  625. if ( m_bDisabled )
  626. {
  627. return;
  628. }
  629. if ( !PassesTriggerFilters(pOther) )
  630. {
  631. return;
  632. }
  633. BaseClass::StartTouch( pOther );
  634. const char *pszParticleName = STRING( m_iszParticleName );
  635. const char *pszAttachmentName = STRING( m_iszAttachmentName );
  636. int iAttachment = -1;
  637. if ( pszAttachmentName && *pszAttachmentName )
  638. {
  639. iAttachment = pOther->GetBaseAnimating()->LookupAttachment( pszAttachmentName );
  640. }
  641. DispatchParticleEffect( pszParticleName, m_nAttachType, pOther, iAttachment );
  642. }
  643. class CTriggerRemoveTFPlayerCondition : public CBaseTrigger
  644. {
  645. public:
  646. DECLARE_DATADESC();
  647. DECLARE_CLASS( CTriggerRemoveTFPlayerCondition, CBaseTrigger );
  648. virtual void Spawn( void );
  649. virtual void StartTouch( CBaseEntity *pOther );
  650. private:
  651. ETFCond m_nCondition;
  652. };
  653. BEGIN_DATADESC( CTriggerRemoveTFPlayerCondition )
  654. DEFINE_KEYFIELD( m_nCondition, FIELD_INTEGER, "condition" ),
  655. END_DATADESC()
  656. LINK_ENTITY_TO_CLASS( trigger_remove_tf_player_condition, CTriggerRemoveTFPlayerCondition );
  657. void CTriggerRemoveTFPlayerCondition::Spawn()
  658. {
  659. BaseClass::Spawn();
  660. InitTrigger();
  661. }
  662. void CTriggerRemoveTFPlayerCondition::StartTouch( CBaseEntity *pOther )
  663. {
  664. if ( m_bDisabled )
  665. {
  666. return;
  667. }
  668. if ( !PassesTriggerFilters(pOther) )
  669. {
  670. return;
  671. }
  672. if ( !pOther->IsPlayer() )
  673. {
  674. return;
  675. }
  676. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  677. if ( !pPlayer )
  678. {
  679. return;
  680. }
  681. if ( m_nCondition != TF_COND_INVALID )
  682. {
  683. pPlayer->m_Shared.RemoveCond( m_nCondition );
  684. // Hack for Bank until we re-address this for parachuting MvM bots
  685. CTFBot *pTFBot = dynamic_cast< CTFBot* >( pPlayer );
  686. if ( pTFBot )
  687. {
  688. pTFBot->ClearLastKnownArea();
  689. }
  690. }
  691. else
  692. {
  693. pPlayer->m_Shared.RemoveAllCond();
  694. }
  695. BaseClass::StartTouch( pOther );
  696. }
  697. class CTriggerAddOrRemoveTFPlayerAttributes : public CBaseTrigger
  698. {
  699. public:
  700. DECLARE_DATADESC();
  701. DECLARE_CLASS( CTriggerAddOrRemoveTFPlayerAttributes, CBaseTrigger );
  702. virtual void Spawn( void );
  703. virtual void StartTouch( CBaseEntity *pOther );
  704. virtual void EndTouch( CBaseEntity *pOther );
  705. private:
  706. bool m_bRemove;
  707. string_t m_iszAttributeName;
  708. float m_flAttributeValue;
  709. float m_flDuration;
  710. bool m_bValidAttribute;
  711. };
  712. BEGIN_DATADESC( CTriggerAddOrRemoveTFPlayerAttributes )
  713. DEFINE_KEYFIELD( m_bRemove, FIELD_BOOLEAN, "add_or_remove" ),
  714. DEFINE_KEYFIELD( m_iszAttributeName, FIELD_STRING, "attribute_name" ),
  715. DEFINE_KEYFIELD( m_flAttributeValue, FIELD_FLOAT, "value" ),
  716. DEFINE_KEYFIELD( m_flDuration, FIELD_FLOAT, "duration" ),
  717. END_DATADESC()
  718. LINK_ENTITY_TO_CLASS( trigger_add_or_remove_tf_player_attributes, CTriggerAddOrRemoveTFPlayerAttributes );
  719. void CTriggerAddOrRemoveTFPlayerAttributes::Spawn()
  720. {
  721. BaseClass::Spawn();
  722. InitTrigger();
  723. m_bValidAttribute = false;
  724. const char *pszAttrName = STRING( m_iszAttributeName );
  725. if ( pszAttrName && *pszAttrName )
  726. {
  727. CSchemaAttributeDefHandle pAttrTest( pszAttrName );
  728. if ( pAttrTest )
  729. {
  730. m_bValidAttribute = true;
  731. }
  732. else
  733. {
  734. Warning( "Invalid attribute name '%s' from trigger trigger_add_or_remove_tf_player_attributes name '%s'\n", pszAttrName, STRING( GetEntityName() ) );
  735. }
  736. }
  737. }
  738. void CTriggerAddOrRemoveTFPlayerAttributes::StartTouch( CBaseEntity *pOther )
  739. {
  740. if ( m_bDisabled )
  741. {
  742. return;
  743. }
  744. if ( !m_bValidAttribute )
  745. {
  746. return;
  747. }
  748. if ( !PassesTriggerFilters(pOther) )
  749. {
  750. return;
  751. }
  752. if ( !pOther->IsPlayer() )
  753. {
  754. return;
  755. }
  756. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  757. if ( !pPlayer )
  758. {
  759. return;
  760. }
  761. const char *pszAttrName = STRING( m_iszAttributeName );
  762. if ( m_bRemove )
  763. {
  764. pPlayer->RemoveCustomAttribute( pszAttrName );
  765. }
  766. else
  767. {
  768. pPlayer->AddCustomAttribute( pszAttrName, m_flAttributeValue, m_flDuration );
  769. }
  770. BaseClass::StartTouch( pOther );
  771. }
  772. void CTriggerAddOrRemoveTFPlayerAttributes::EndTouch( CBaseEntity *pOther )
  773. {
  774. if ( m_bDisabled )
  775. {
  776. return;
  777. }
  778. if ( !m_bValidAttribute )
  779. {
  780. return;
  781. }
  782. // only run auto remove on added attribute
  783. if ( m_bRemove )
  784. {
  785. return;
  786. }
  787. // ignore timer attribute. it'll remove itself
  788. if ( m_flDuration > 0.f )
  789. {
  790. return;
  791. }
  792. if ( !PassesTriggerFilters(pOther) )
  793. {
  794. return;
  795. }
  796. if ( !pOther->IsPlayer() )
  797. {
  798. return;
  799. }
  800. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  801. if ( !pPlayer )
  802. {
  803. return;
  804. }
  805. // remove permanent attribute on exist trigger
  806. const char *pszAttrName = STRING( m_iszAttributeName );
  807. pPlayer->RemoveCustomAttribute( pszAttrName );
  808. BaseClass::EndTouch( pOther );
  809. }