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.

875 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "weapon_physcannon.h"
  8. #include "hl2_player.h"
  9. #include "saverestore_utlvector.h"
  10. #include "triggers.h"
  11. //-----------------------------------------------------------------------------
  12. // Weapon-dissolve trigger; all weapons in this field (sans the physcannon) are destroyed!
  13. //-----------------------------------------------------------------------------
  14. class CTriggerWeaponDissolve : public CTriggerMultiple
  15. {
  16. DECLARE_CLASS( CTriggerWeaponDissolve, CTriggerMultiple );
  17. DECLARE_DATADESC();
  18. public:
  19. ~CTriggerWeaponDissolve( void );
  20. virtual void Spawn( void );
  21. virtual void Precache( void );
  22. virtual void Activate( void );
  23. virtual void StartTouch( CBaseEntity *pOther );
  24. inline bool HasWeapon( CBaseCombatWeapon *pWeapon );
  25. Vector GetConduitPoint( CBaseEntity *pTarget );
  26. void InputStopSound( inputdata_t &inputdata );
  27. void AddWeapon( CBaseCombatWeapon *pWeapon );
  28. void CreateBeam( const Vector &vecSource, CBaseEntity *pDest, float flLifetime );
  29. void DissolveThink( void );
  30. private:
  31. COutputEvent m_OnDissolveWeapon;
  32. COutputEvent m_OnChargingPhyscannon;
  33. CUtlVector< CHandle<CBaseCombatWeapon> > m_pWeapons;
  34. CUtlVector< CHandle<CBaseEntity> > m_pConduitPoints;
  35. string_t m_strEmitterName;
  36. int m_spriteTexture;
  37. };
  38. LINK_ENTITY_TO_CLASS( trigger_weapon_dissolve, CTriggerWeaponDissolve );
  39. BEGIN_DATADESC( CTriggerWeaponDissolve )
  40. DEFINE_KEYFIELD( m_strEmitterName, FIELD_STRING, "emittername" ),
  41. DEFINE_UTLVECTOR( m_pWeapons, FIELD_EHANDLE ),
  42. DEFINE_UTLVECTOR( m_pConduitPoints, FIELD_EHANDLE ),
  43. DEFINE_FIELD( m_spriteTexture, FIELD_MODELINDEX ),
  44. DEFINE_OUTPUT( m_OnDissolveWeapon, "OnDissolveWeapon" ),
  45. DEFINE_OUTPUT( m_OnChargingPhyscannon, "OnChargingPhyscannon" ),
  46. DEFINE_INPUTFUNC( FIELD_VOID, "StopSound", InputStopSound ),
  47. DEFINE_THINKFUNC( DissolveThink ),
  48. END_DATADESC()
  49. //-----------------------------------------------------------------------------
  50. // Destructor
  51. //-----------------------------------------------------------------------------
  52. CTriggerWeaponDissolve::~CTriggerWeaponDissolve( void )
  53. {
  54. m_pWeapons.Purge();
  55. m_pConduitPoints.Purge();
  56. }
  57. //-----------------------------------------------------------------------------
  58. // Purpose: Call precache for our sprite texture
  59. //-----------------------------------------------------------------------------
  60. void CTriggerWeaponDissolve::Spawn( void )
  61. {
  62. BaseClass::Spawn();
  63. Precache();
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose: Precache our sprite texture
  67. //-----------------------------------------------------------------------------
  68. void CTriggerWeaponDissolve::Precache( void )
  69. {
  70. BaseClass::Precache();
  71. m_spriteTexture = PrecacheModel( "sprites/lgtning.vmt" );
  72. PrecacheScriptSound( "WeaponDissolve.Dissolve" );
  73. PrecacheScriptSound( "WeaponDissolve.Charge" );
  74. PrecacheScriptSound( "WeaponDissolve.Beam" );
  75. }
  76. static const char *s_pDissolveThinkContext = "DissolveThinkContext";
  77. //-----------------------------------------------------------------------------
  78. // Purpose: Collect all our known conduit points
  79. //-----------------------------------------------------------------------------
  80. void CTriggerWeaponDissolve::Activate( void )
  81. {
  82. BaseClass::Activate();
  83. CBaseEntity *pEntity = NULL;
  84. while ( ( pEntity = gEntList.FindEntityByName( pEntity, m_strEmitterName ) ) != NULL )
  85. {
  86. m_pConduitPoints.AddToTail( pEntity );
  87. }
  88. SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + 0.1f, s_pDissolveThinkContext );
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose: Checks to see if a weapon is already known
  92. // Input : *pWeapon - weapon to check for
  93. // Output : Returns true on success, false on failure.
  94. //-----------------------------------------------------------------------------
  95. bool CTriggerWeaponDissolve::HasWeapon( CBaseCombatWeapon *pWeapon )
  96. {
  97. if ( m_pWeapons.Find( pWeapon ) == m_pWeapons.InvalidIndex() )
  98. return false;
  99. return true;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose: Adds a weapon to the known weapon list
  103. // Input : *pWeapon - weapon to add
  104. //-----------------------------------------------------------------------------
  105. void CTriggerWeaponDissolve::AddWeapon( CBaseCombatWeapon *pWeapon )
  106. {
  107. if ( HasWeapon( pWeapon ) )
  108. return;
  109. m_pWeapons.AddToTail( pWeapon );
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Collect any weapons inside our volume
  113. // Input : *pOther -
  114. //-----------------------------------------------------------------------------
  115. void CTriggerWeaponDissolve::StartTouch( CBaseEntity *pOther )
  116. {
  117. BaseClass::StartTouch( pOther );
  118. if ( PassesTriggerFilters( pOther ) == false )
  119. return;
  120. CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>(pOther);
  121. if ( pWeapon == NULL )
  122. return;
  123. AddWeapon( pWeapon );
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose: Creates a beam between a conduit point and a weapon
  127. // Input : &vecSource - conduit point
  128. // *pDest - weapon
  129. // flLifetime - amount of time
  130. //-----------------------------------------------------------------------------
  131. void CTriggerWeaponDissolve::CreateBeam( const Vector &vecSource, CBaseEntity *pDest, float flLifetime )
  132. {
  133. CBroadcastRecipientFilter filter;
  134. te->BeamEntPoint( filter, 0.0,
  135. 0,
  136. &vecSource,
  137. pDest->entindex(),
  138. &(pDest->WorldSpaceCenter()),
  139. m_spriteTexture,
  140. 0, // No halo
  141. 1, // Frame
  142. 30,
  143. flLifetime,
  144. 16.0f, // Start width
  145. 4.0f, // End width
  146. 0, // No fade
  147. 8, // Amplitude
  148. 255,
  149. 255,
  150. 255,
  151. 255,
  152. 16 ); // Speed
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Purpose: Returns the closest conduit point to a weapon
  156. // Input : *pTarget - weapon to check for
  157. // Output : Vector - position of the conduit
  158. //-----------------------------------------------------------------------------
  159. Vector CTriggerWeaponDissolve::GetConduitPoint( CBaseEntity *pTarget )
  160. {
  161. float nearDist = 9999999.0f;
  162. Vector bestPoint = vec3_origin;
  163. float testDist;
  164. // Find the nearest conduit to the target
  165. for ( int i = 0; i < m_pConduitPoints.Count(); i++ )
  166. {
  167. testDist = ( m_pConduitPoints[i]->GetAbsOrigin() - pTarget->GetAbsOrigin() ).LengthSqr();
  168. if ( testDist < nearDist )
  169. {
  170. bestPoint = m_pConduitPoints[i]->GetAbsOrigin();
  171. nearDist = testDist;
  172. }
  173. }
  174. return bestPoint;
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: Dissolve all weapons within our volume
  178. //-----------------------------------------------------------------------------
  179. void CTriggerWeaponDissolve::DissolveThink( void )
  180. {
  181. int numWeapons = m_pWeapons.Count();
  182. // Dissolve all the items within the volume
  183. for ( int i = 0; i < numWeapons; i++ )
  184. {
  185. CBaseCombatWeapon *pWeapon = m_pWeapons[i];
  186. Vector vecConduit = GetConduitPoint( pWeapon );
  187. // The physcannon upgrades when this happens
  188. if ( FClassnameIs( pWeapon, "weapon_physcannon" ) )
  189. {
  190. // This must be the last weapon for us to care
  191. if ( numWeapons > 1 )
  192. continue;
  193. //FIXME: Make them do this on a stagger!
  194. // All conduits send power to the weapon
  195. for ( int i = 0; i < m_pConduitPoints.Count(); i++ )
  196. {
  197. CreateBeam( m_pConduitPoints[i]->GetAbsOrigin(), pWeapon, 4.0f );
  198. }
  199. PhysCannonBeginUpgrade( pWeapon );
  200. m_OnChargingPhyscannon.FireOutput( this, this );
  201. EmitSound( "WeaponDissolve.Beam" );
  202. // We're done
  203. m_pWeapons.Purge();
  204. m_pConduitPoints.Purge();
  205. SetContextThink( NULL, 0, s_pDissolveThinkContext );
  206. return;
  207. }
  208. // Randomly dissolve them all
  209. float flLifetime = random->RandomFloat( 2.5f, 4.0f );
  210. CreateBeam( vecConduit, pWeapon, flLifetime );
  211. pWeapon->Dissolve( NULL, gpGlobals->curtime + ( 3.0f - flLifetime ), false );
  212. m_OnDissolveWeapon.FireOutput( this, this );
  213. CPASAttenuationFilter filter( pWeapon );
  214. EmitSound( filter, pWeapon->entindex(), "WeaponDissolve.Dissolve" );
  215. // Beam looping sound
  216. EmitSound( "WeaponDissolve.Beam" );
  217. m_pWeapons.Remove( i );
  218. SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + random->RandomFloat( 0.5f, 1.5f ), s_pDissolveThinkContext );
  219. return;
  220. }
  221. SetContextThink( &CTriggerWeaponDissolve::DissolveThink, gpGlobals->curtime + 0.1f, s_pDissolveThinkContext );
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose:
  225. // Input : &inputdata -
  226. //-----------------------------------------------------------------------------
  227. void CTriggerWeaponDissolve::InputStopSound( inputdata_t &inputdata )
  228. {
  229. StopSound( "WeaponDissolve.Beam" );
  230. StopSound( "WeaponDissolve.Charge" );
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Weapon-strip trigger; can't pick up weapons while in the field
  234. //-----------------------------------------------------------------------------
  235. class CTriggerWeaponStrip : public CTriggerMultiple
  236. {
  237. DECLARE_CLASS( CTriggerWeaponStrip, CTriggerMultiple );
  238. DECLARE_DATADESC();
  239. public:
  240. void StartTouch(CBaseEntity *pOther);
  241. void EndTouch(CBaseEntity *pOther);
  242. private:
  243. bool m_bKillWeapons;
  244. };
  245. //-----------------------------------------------------------------------------
  246. // Save/load
  247. //-----------------------------------------------------------------------------
  248. LINK_ENTITY_TO_CLASS( trigger_weapon_strip, CTriggerWeaponStrip );
  249. BEGIN_DATADESC( CTriggerWeaponStrip )
  250. DEFINE_KEYFIELD( m_bKillWeapons, FIELD_BOOLEAN, "KillWeapons" ),
  251. END_DATADESC()
  252. //-----------------------------------------------------------------------------
  253. // Drops all weapons, marks the character as not being able to pick up weapons
  254. //-----------------------------------------------------------------------------
  255. void CTriggerWeaponStrip::StartTouch(CBaseEntity *pOther)
  256. {
  257. BaseClass::StartTouch( pOther );
  258. if ( PassesTriggerFilters(pOther) == false )
  259. return;
  260. CBaseCombatCharacter *pCharacter = pOther->MyCombatCharacterPointer();
  261. if ( m_bKillWeapons )
  262. {
  263. for ( int i = 0 ; i < pCharacter->WeaponCount(); ++i )
  264. {
  265. CBaseCombatWeapon *pWeapon = pCharacter->GetWeapon( i );
  266. if ( !pWeapon )
  267. continue;
  268. pCharacter->Weapon_Drop( pWeapon );
  269. UTIL_Remove( pWeapon );
  270. }
  271. return;
  272. }
  273. // Strip the player of his weapons
  274. if ( pCharacter && pCharacter->IsAllowedToPickupWeapons() )
  275. {
  276. CBaseCombatWeapon *pBugbait = pCharacter->Weapon_OwnsThisType( "weapon_bugbait" );
  277. if ( pBugbait )
  278. {
  279. pCharacter->Weapon_Drop( pBugbait );
  280. UTIL_Remove( pBugbait );
  281. }
  282. pCharacter->Weapon_DropAll( true );
  283. pCharacter->SetPreventWeaponPickup( true );
  284. }
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Called when an entity stops touching us.
  288. // Input : pOther - The entity that was touching us.
  289. //-----------------------------------------------------------------------------
  290. void CTriggerWeaponStrip::EndTouch(CBaseEntity *pOther)
  291. {
  292. if ( IsTouching( pOther ) )
  293. {
  294. CBaseCombatCharacter *pCharacter = pOther->MyCombatCharacterPointer();
  295. if ( pCharacter )
  296. {
  297. pCharacter->SetPreventWeaponPickup( false );
  298. }
  299. }
  300. BaseClass::EndTouch( pOther );
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Teleport trigger
  304. //-----------------------------------------------------------------------------
  305. class CTriggerPhysicsTrap : public CTriggerMultiple
  306. {
  307. DECLARE_CLASS( CTriggerPhysicsTrap, CTriggerMultiple );
  308. DECLARE_DATADESC();
  309. public:
  310. void Touch( CBaseEntity *pOther );
  311. private:
  312. void InputEnable( inputdata_t &inputdata );
  313. void InputDisable( inputdata_t &inputdata );
  314. void InputToggle( inputdata_t &inputdata );
  315. int m_nDissolveType;
  316. };
  317. //-----------------------------------------------------------------------------
  318. // Save/load
  319. //-----------------------------------------------------------------------------
  320. LINK_ENTITY_TO_CLASS( trigger_physics_trap, CTriggerPhysicsTrap );
  321. BEGIN_DATADESC( CTriggerPhysicsTrap )
  322. DEFINE_KEYFIELD( m_nDissolveType, FIELD_INTEGER, "dissolvetype" ),
  323. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  324. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  325. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  326. END_DATADESC()
  327. //------------------------------------------------------------------------------
  328. // Inputs
  329. //------------------------------------------------------------------------------
  330. void CTriggerPhysicsTrap::InputToggle( inputdata_t &inputdata )
  331. {
  332. if ( m_bDisabled )
  333. {
  334. InputEnable( inputdata );
  335. }
  336. else
  337. {
  338. InputDisable( inputdata );
  339. }
  340. }
  341. void CTriggerPhysicsTrap::InputEnable( inputdata_t &inputdata )
  342. {
  343. if ( m_bDisabled )
  344. {
  345. Enable();
  346. }
  347. }
  348. void CTriggerPhysicsTrap::InputDisable( inputdata_t &inputdata )
  349. {
  350. if ( !m_bDisabled )
  351. {
  352. Disable();
  353. }
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Traps the entities
  357. //-----------------------------------------------------------------------------
  358. #define JOINTS_TO_CONSTRAIN 1
  359. void CTriggerPhysicsTrap::Touch( CBaseEntity *pOther )
  360. {
  361. if ( !PassesTriggerFilters(pOther) )
  362. return;
  363. CBaseAnimating *pAnim = pOther->GetBaseAnimating();
  364. if ( !pAnim )
  365. return;
  366. #ifdef HL2_DLL
  367. // HACK: Upgrade the physcannon
  368. if ( FClassnameIs( pAnim, "weapon_physcannon" ) )
  369. {
  370. PhysCannonBeginUpgrade( pAnim );
  371. return;
  372. }
  373. #endif
  374. pAnim->Dissolve( NULL, gpGlobals->curtime, false, m_nDissolveType );
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose:
  378. //-----------------------------------------------------------------------------
  379. class CWateryDeathLeech : public CBaseAnimating
  380. {
  381. DECLARE_CLASS( CWateryDeathLeech, CBaseAnimating );
  382. public:
  383. DECLARE_DATADESC();
  384. void Spawn( void );
  385. void Precache( void );
  386. void LeechThink( void );
  387. int m_iFadeState;
  388. };
  389. LINK_ENTITY_TO_CLASS( ent_watery_leech, CWateryDeathLeech );
  390. BEGIN_DATADESC( CWateryDeathLeech )
  391. DEFINE_THINKFUNC( LeechThink ),
  392. DEFINE_FIELD( m_iFadeState, FIELD_INTEGER ),
  393. END_DATADESC()
  394. void CWateryDeathLeech::Precache( void )
  395. {
  396. //Ugh this is temporary until Jakob finishes the animations and doesn't need the command anymore.
  397. bool allowPrecache = CBaseEntity::IsPrecacheAllowed();
  398. CBaseEntity::SetAllowPrecache( true );
  399. BaseClass::Precache();
  400. PrecacheModel( "models/leech.mdl" );
  401. CBaseEntity::SetAllowPrecache( allowPrecache );
  402. }
  403. void CWateryDeathLeech::Spawn( void )
  404. {
  405. Precache();
  406. BaseClass::Spawn();
  407. SetSolid ( SOLID_NONE );
  408. SetMoveType( MOVETYPE_NONE );
  409. AddEffects( EF_NOSHADOW );
  410. SetModel( "models/leech.mdl" );
  411. SetThink( &CWateryDeathLeech::LeechThink );
  412. SetNextThink( gpGlobals->curtime + 0.1 );
  413. m_flPlaybackRate = random->RandomFloat( 0.5, 1.5 );
  414. SetCycle( random->RandomFloat( 0.0f, 0.9f ) );
  415. QAngle vAngle;
  416. vAngle[YAW] = random->RandomFloat( 0, 360 );
  417. SetAbsAngles( vAngle );
  418. m_iFadeState = 1;
  419. SetRenderColorA( 1 );
  420. }
  421. void CWateryDeathLeech::LeechThink( void )
  422. {
  423. if ( IsMarkedForDeletion() )
  424. return;
  425. StudioFrameAdvance();
  426. SetNextThink( gpGlobals->curtime + 0.1 );
  427. if ( m_iFadeState != 0 )
  428. {
  429. float dt = gpGlobals->frametime;
  430. if ( dt > 0.1f )
  431. {
  432. dt = 0.1f;
  433. }
  434. m_nRenderMode = kRenderTransTexture;
  435. int speed = MAX(1,256*dt); // fade out over 1 second
  436. if ( m_iFadeState == -1 )
  437. SetRenderColorA( UTIL_Approach( 0, m_clrRender->a, speed ) );
  438. else
  439. SetRenderColorA( UTIL_Approach( 255, m_clrRender->a, speed ) );
  440. if ( m_clrRender->a == 0 )
  441. {
  442. UTIL_Remove(this);
  443. }
  444. else if ( m_clrRender->a == 255 )
  445. {
  446. m_iFadeState = 0;
  447. }
  448. else
  449. {
  450. SetNextThink( gpGlobals->curtime );
  451. }
  452. }
  453. if ( GetOwnerEntity() )
  454. {
  455. if ( GetOwnerEntity()->GetWaterLevel() < 3 )
  456. {
  457. AddEffects( EF_NODRAW );
  458. }
  459. else
  460. {
  461. RemoveEffects( EF_NODRAW );
  462. }
  463. SetAbsOrigin( GetOwnerEntity()->GetAbsOrigin() + GetOwnerEntity()->GetViewOffset() );
  464. }
  465. }
  466. class CTriggerWateryDeath : public CBaseTrigger
  467. {
  468. DECLARE_CLASS( CTriggerWateryDeath, CBaseTrigger );
  469. public:
  470. DECLARE_DATADESC();
  471. void Spawn( void );
  472. void Precache( void );
  473. void Touch( CBaseEntity *pOther );
  474. void SpawnLeeches( CBaseEntity *pOther );
  475. // Ignore non-living entities
  476. virtual bool PassesTriggerFilters(CBaseEntity *pOther)
  477. {
  478. if ( !BaseClass::PassesTriggerFilters(pOther) )
  479. return false;
  480. return (pOther->m_takedamage == DAMAGE_YES);
  481. }
  482. virtual void StartTouch(CBaseEntity *pOther);
  483. virtual void EndTouch(CBaseEntity *pOther);
  484. private:
  485. CUtlVector< EHANDLE > m_hLeeches;
  486. // Kill times for entities I'm touching
  487. CUtlVector< float > m_flEntityKillTimes;
  488. float m_flNextPullSound;
  489. float m_flPainValue;
  490. };
  491. BEGIN_DATADESC( CTriggerWateryDeath )
  492. DEFINE_UTLVECTOR( m_flEntityKillTimes, FIELD_TIME ),
  493. DEFINE_UTLVECTOR( m_hLeeches, FIELD_EHANDLE ),
  494. DEFINE_FIELD( m_flNextPullSound, FIELD_TIME ),
  495. DEFINE_FIELD( m_flPainValue, FIELD_FLOAT ),
  496. END_DATADESC()
  497. LINK_ENTITY_TO_CLASS( trigger_waterydeath, CTriggerWateryDeath );
  498. // Stages of the waterydeath trigger, in time offsets from the initial touch
  499. #define WD_KILLTIME_NEXT_BITE 0.3
  500. #define WD_PAINVALUE_STEP 2.0
  501. #define WD_MAX_DAMAGE 15.0f
  502. //-----------------------------------------------------------------------------
  503. // Purpose: Called when spawning, after keyvalues have been handled.
  504. //-----------------------------------------------------------------------------
  505. void CTriggerWateryDeath::Spawn( void )
  506. {
  507. BaseClass::Spawn();
  508. Precache();
  509. m_flNextPullSound = 0;
  510. m_flPainValue = 0;
  511. InitTrigger();
  512. }
  513. void CTriggerWateryDeath::Precache( void )
  514. {
  515. //Ugh this is temporary until Jakob finishes the animations and doesn't need the command anymore.
  516. BaseClass::Precache();
  517. PrecacheModel( "models/leech.mdl" );
  518. PrecacheScriptSound( "coast.leech_bites_loop" );
  519. PrecacheScriptSound( "coast.leech_water_churn_loop" );
  520. }
  521. void CTriggerWateryDeath::SpawnLeeches( CBaseEntity *pOther )
  522. {
  523. if ( pOther == NULL )
  524. return;
  525. if ( m_hLeeches.Count() > 0 )
  526. return;
  527. int iMaxLeeches = 12;
  528. for ( int i = 0; i < iMaxLeeches; i++ )
  529. {
  530. CWateryDeathLeech *pLeech = (CWateryDeathLeech*)CreateEntityByName( "ent_watery_leech" );
  531. if ( pLeech )
  532. {
  533. m_hLeeches.AddToTail( pLeech );
  534. pLeech->Spawn();
  535. pLeech->SetAbsOrigin( pOther->GetAbsOrigin() );
  536. pLeech->SetOwnerEntity( pOther );
  537. if ( i <= 8 )
  538. pLeech->SetSequence( i % 4 );
  539. else
  540. pLeech->SetSequence( ( i % 4 ) + 4 ) ;
  541. pLeech->ResetSequenceInfo();
  542. }
  543. }
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Purpose:
  547. //-----------------------------------------------------------------------------
  548. void CTriggerWateryDeath::Touch( CBaseEntity *pOther )
  549. {
  550. if (!PassesTriggerFilters(pOther))
  551. return;
  552. // Find our index
  553. EHANDLE hOther;
  554. hOther = pOther;
  555. int iIndex = m_hTouchingEntities.Find( hOther );
  556. if ( iIndex == m_hTouchingEntities.InvalidIndex() )
  557. return;
  558. float flKillTime = m_flEntityKillTimes[iIndex];
  559. // Time to kill it?
  560. if ( gpGlobals->curtime > flKillTime )
  561. {
  562. //EmitSound( filter, entindex(), "WateryDeath.Bite", &pOther->GetAbsOrigin() );
  563. // Kill it
  564. if ( pOther->IsPlayer() )
  565. {
  566. m_flPainValue = MIN( m_flPainValue + WD_PAINVALUE_STEP, WD_MAX_DAMAGE );
  567. }
  568. else
  569. {
  570. m_flPainValue = WD_MAX_DAMAGE;
  571. }
  572. // Use DMG_GENERIC & make the target inflict the damage on himself.
  573. // This ensures that if the target is the player, the damage isn't modified by skill
  574. CTakeDamageInfo info = CTakeDamageInfo( pOther, pOther, m_flPainValue, DMG_GENERIC );
  575. GuessDamageForce( &info, (pOther->GetAbsOrigin() - GetAbsOrigin()), pOther->GetAbsOrigin() );
  576. pOther->TakeDamage( info );
  577. m_flEntityKillTimes[iIndex] = gpGlobals->curtime + WD_KILLTIME_NEXT_BITE;
  578. }
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Purpose: Called when an entity starts touching us.
  582. // Input : pOther - The entity that is touching us.
  583. //-----------------------------------------------------------------------------
  584. void CTriggerWateryDeath::StartTouch(CBaseEntity *pOther)
  585. {
  586. BaseClass::StartTouch( pOther );
  587. m_flPainValue = 0.0f;
  588. // If we added him to our list, store the start time
  589. EHANDLE hOther;
  590. hOther = pOther;
  591. if ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() )
  592. {
  593. // Always added to the end
  594. // Players get warned, everything else gets et quick.
  595. if ( pOther->IsPlayer() )
  596. {
  597. m_flEntityKillTimes.AddToTail( gpGlobals->curtime + WD_KILLTIME_NEXT_BITE );
  598. }
  599. else
  600. {
  601. m_flEntityKillTimes.AddToTail( gpGlobals->curtime + WD_KILLTIME_NEXT_BITE );
  602. }
  603. }
  604. #ifdef HL2_DLL
  605. if ( pOther->IsPlayer() )
  606. {
  607. SpawnLeeches( pOther );
  608. CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pOther );
  609. if ( pHL2Player )
  610. {
  611. pHL2Player->StartWaterDeathSounds();
  612. }
  613. }
  614. #endif
  615. }
  616. //-----------------------------------------------------------------------------
  617. // Purpose: Called when an entity stops touching us.
  618. // Input : pOther - The entity that was touching us.
  619. //-----------------------------------------------------------------------------
  620. void CTriggerWateryDeath::EndTouch( CBaseEntity *pOther )
  621. {
  622. if ( IsTouching( pOther ) )
  623. {
  624. EHANDLE hOther;
  625. hOther = pOther;
  626. // Remove the time from our list
  627. int iIndex = m_hTouchingEntities.Find( hOther );
  628. if ( iIndex != m_hTouchingEntities.InvalidIndex() )
  629. {
  630. m_flEntityKillTimes.Remove( iIndex );
  631. }
  632. }
  633. #ifdef HL2_DLL
  634. if ( pOther->IsPlayer() )
  635. {
  636. for (int i = 0; i < m_hLeeches.Count(); i++ )
  637. {
  638. CWateryDeathLeech *pLeech = dynamic_cast<CWateryDeathLeech*>( m_hLeeches[i].Get() );
  639. if ( pLeech )
  640. {
  641. pLeech->m_iFadeState = -1;
  642. }
  643. }
  644. if ( m_hLeeches.Count() > 0 )
  645. m_hLeeches.Purge();
  646. CHL2_Player *pHL2Player = dynamic_cast<CHL2_Player*>( pOther );
  647. if ( pHL2Player )
  648. {
  649. //Adrian: Hi, you might be wondering why I'm doing this, yes?
  650. // Well, EndTouch is called not only when the player leaves
  651. // the trigger, but also on level shutdown. We can't let the
  652. // soundpatch fade the sound out since we'll hit a nasty assert
  653. // cause it'll try to fade out a sound using an entity that might
  654. // be gone since we're shutting down the server.
  655. if ( !(pHL2Player->GetFlags() & FL_DONTTOUCH ) )
  656. pHL2Player->StopWaterDeathSounds();
  657. }
  658. }
  659. #endif
  660. BaseClass::EndTouch( pOther );
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Purpose: Triggers whenever an RPG is fired within it
  664. //-----------------------------------------------------------------------------
  665. class CTriggerRPGFire : public CTriggerMultiple
  666. {
  667. DECLARE_CLASS( CTriggerRPGFire, CTriggerMultiple );
  668. public:
  669. ~CTriggerRPGFire();
  670. void Spawn( void );
  671. void OnRestore( void );
  672. };
  673. LINK_ENTITY_TO_CLASS( trigger_rpgfire, CTriggerRPGFire );
  674. //-----------------------------------------------------------------------------
  675. // Purpose:
  676. //-----------------------------------------------------------------------------
  677. CTriggerRPGFire::~CTriggerRPGFire( void )
  678. {
  679. g_hWeaponFireTriggers.FindAndRemove( this );
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose: Called when spawning, after keyvalues have been handled.
  683. //-----------------------------------------------------------------------------
  684. void CTriggerRPGFire::Spawn( void )
  685. {
  686. BaseClass::Spawn();
  687. InitTrigger();
  688. g_hWeaponFireTriggers.AddToTail( this );
  689. // Stomp the touch function, because we don't want to respond to touch
  690. SetTouch( NULL );
  691. }
  692. //------------------------------------------------------------------------------
  693. // Purpose:
  694. //------------------------------------------------------------------------------
  695. void CTriggerRPGFire::OnRestore()
  696. {
  697. BaseClass::OnRestore();
  698. g_hWeaponFireTriggers.AddToTail( this );
  699. }