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.

5069 lines
138 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Spawn and use functions for editor-placed triggers.
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "ai_basenpc.h"
  8. #include "player.h"
  9. #include "saverestore.h"
  10. #include "gamerules.h"
  11. #include "entityapi.h"
  12. #include "entitylist.h"
  13. #include "ndebugoverlay.h"
  14. #include "globalstate.h"
  15. #include "filters.h"
  16. #include "vstdlib/random.h"
  17. #include "triggers.h"
  18. #include "saverestoretypes.h"
  19. #include "hierarchy.h"
  20. #include "bspfile.h"
  21. #include "saverestore_utlvector.h"
  22. #include "physics_saverestore.h"
  23. #include "te_effect_dispatch.h"
  24. #include "ammodef.h"
  25. #include "iservervehicle.h"
  26. #include "movevars_shared.h"
  27. #include "physics_prop_ragdoll.h"
  28. #include "props.h"
  29. #include "RagdollBoogie.h"
  30. #include "EntityParticleTrail.h"
  31. #include "in_buttons.h"
  32. #include "ai_behavior_follow.h"
  33. #include "ai_behavior_lead.h"
  34. #include "gameinterface.h"
  35. #include "ilagcompensationmanager.h"
  36. #ifdef HL2_DLL
  37. #include "hl2_player.h"
  38. #endif
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. #define DEBUG_TRANSITIONS_VERBOSE 2
  42. ConVar g_debug_transitions( "g_debug_transitions", "0", FCVAR_NONE, "Set to 1 and restart the map to be warned if the map has no trigger_transition volumes. Set to 2 to see a dump of all entities & associated results during a transition." );
  43. // Global list of triggers that care about weapon fire
  44. // Doesn't need saving, the triggers re-add themselves on restore.
  45. CUtlVector< CHandle<CTriggerMultiple> > g_hWeaponFireTriggers;
  46. extern CServerGameDLL g_ServerGameDLL;
  47. extern bool g_fGameOver;
  48. ConVar showtriggers( "showtriggers", "0", FCVAR_CHEAT, "Shows trigger brushes" );
  49. bool IsTriggerClass( CBaseEntity *pEntity );
  50. // Command to dynamically toggle trigger visibility
  51. void Cmd_ShowtriggersToggle_f( const CCommand &args )
  52. {
  53. // Loop through the entities in the game and make visible anything derived from CBaseTrigger
  54. CBaseEntity *pEntity = gEntList.FirstEnt();
  55. while ( pEntity )
  56. {
  57. if ( IsTriggerClass(pEntity) )
  58. {
  59. // If a classname is specified, only show triggles of that type
  60. if ( args.ArgC() > 1 )
  61. {
  62. const char *sClassname = args[1];
  63. if ( sClassname && sClassname[0] )
  64. {
  65. if ( !FClassnameIs( pEntity, sClassname ) )
  66. {
  67. pEntity = gEntList.NextEnt( pEntity );
  68. continue;
  69. }
  70. }
  71. }
  72. if ( pEntity->IsEffectActive( EF_NODRAW ) )
  73. {
  74. pEntity->RemoveEffects( EF_NODRAW );
  75. }
  76. else
  77. {
  78. pEntity->AddEffects( EF_NODRAW );
  79. }
  80. }
  81. pEntity = gEntList.NextEnt( pEntity );
  82. }
  83. }
  84. static ConCommand showtriggers_toggle( "showtriggers_toggle", Cmd_ShowtriggersToggle_f, "Toggle show triggers", FCVAR_CHEAT );
  85. // Global Savedata for base trigger
  86. BEGIN_DATADESC( CBaseTrigger )
  87. // Keyfields
  88. DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
  89. DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
  90. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  91. DEFINE_UTLVECTOR( m_hTouchingEntities, FIELD_EHANDLE ),
  92. // Inputs
  93. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  94. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  95. DEFINE_INPUTFUNC( FIELD_VOID, "DisableAndEndTouch", InputDisableAndEndTouch ),
  96. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  97. DEFINE_INPUTFUNC( FIELD_VOID, "TouchTest", InputTouchTest ),
  98. DEFINE_INPUTFUNC( FIELD_VOID, "StartTouch", InputStartTouch ),
  99. DEFINE_INPUTFUNC( FIELD_VOID, "EndTouch", InputEndTouch ),
  100. // Outputs
  101. DEFINE_OUTPUT( m_OnStartTouch, "OnStartTouch"),
  102. DEFINE_OUTPUT( m_OnStartTouchAll, "OnStartTouchAll"),
  103. DEFINE_OUTPUT( m_OnEndTouch, "OnEndTouch"),
  104. DEFINE_OUTPUT( m_OnEndTouchAll, "OnEndTouchAll"),
  105. DEFINE_OUTPUT( m_OnTouching, "OnTouching" ),
  106. DEFINE_OUTPUT( m_OnNotTouching, "OnNotTouching" ),
  107. END_DATADESC()
  108. LINK_ENTITY_TO_CLASS( trigger, CBaseTrigger );
  109. CBaseTrigger::CBaseTrigger()
  110. {
  111. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  112. }
  113. //------------------------------------------------------------------------------
  114. // Purpose: Input handler to turn on this trigger.
  115. //------------------------------------------------------------------------------
  116. void CBaseTrigger::InputEnable( inputdata_t &inputdata )
  117. {
  118. Enable();
  119. }
  120. //------------------------------------------------------------------------------
  121. // Purpose: Input handler to turn off this trigger.
  122. //------------------------------------------------------------------------------
  123. void CBaseTrigger::InputDisable( inputdata_t &inputdata )
  124. {
  125. Disable();
  126. }
  127. //------------------------------------------------------------------------------
  128. // Purpose: Input handler to call EndTouch on all touching entities, and then
  129. // turn off this trigger
  130. //------------------------------------------------------------------------------
  131. void CBaseTrigger::InputDisableAndEndTouch( inputdata_t &inputdata )
  132. {
  133. FOR_EACH_VEC_BACK( m_hTouchingEntities, i )
  134. {
  135. if ( m_hTouchingEntities[i] )
  136. {
  137. EndTouch( m_hTouchingEntities[ i ] );
  138. }
  139. else
  140. {
  141. m_hTouchingEntities.Remove( i );
  142. }
  143. }
  144. Disable();
  145. }
  146. void CBaseTrigger::InputTouchTest( inputdata_t &inputdata )
  147. {
  148. TouchTest();
  149. }
  150. //------------------------------------------------------------------------------
  151. //------------------------------------------------------------------------------
  152. void CBaseTrigger::Spawn()
  153. {
  154. if ( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) || HasSpawnFlags( SF_TRIGGER_ONLY_NPCS_IN_VEHICLES ) )
  155. {
  156. // Automatically set this trigger to work with NPC's.
  157. AddSpawnFlags( SF_TRIGGER_ALLOW_NPCS );
  158. }
  159. if ( HasSpawnFlags( SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES ) )
  160. {
  161. AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
  162. }
  163. if ( HasSpawnFlags( SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES ) )
  164. {
  165. AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
  166. }
  167. BaseClass::Spawn();
  168. }
  169. //------------------------------------------------------------------------------
  170. // Cleanup
  171. //------------------------------------------------------------------------------
  172. void CBaseTrigger::UpdateOnRemove( void )
  173. {
  174. if ( VPhysicsGetObject())
  175. {
  176. VPhysicsGetObject()->RemoveTrigger();
  177. }
  178. BaseClass::UpdateOnRemove();
  179. }
  180. //------------------------------------------------------------------------------
  181. // Purpose: Turns on this trigger.
  182. //------------------------------------------------------------------------------
  183. void CBaseTrigger::Enable( void )
  184. {
  185. m_bDisabled = false;
  186. if ( VPhysicsGetObject())
  187. {
  188. VPhysicsGetObject()->EnableCollisions( true );
  189. }
  190. if (!IsSolidFlagSet( FSOLID_TRIGGER ))
  191. {
  192. AddSolidFlags( FSOLID_TRIGGER );
  193. PhysicsTouchTriggers();
  194. }
  195. }
  196. //------------------------------------------------------------------------------
  197. // Purpose :
  198. //------------------------------------------------------------------------------
  199. void CBaseTrigger::Activate( void )
  200. {
  201. // Get a handle to my filter entity if there is one
  202. if (m_iFilterName != NULL_STRING)
  203. {
  204. m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
  205. }
  206. BaseClass::Activate();
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Purpose: Called after player becomes active in the game
  210. //-----------------------------------------------------------------------------
  211. void CBaseTrigger::PostClientActive( void )
  212. {
  213. BaseClass::PostClientActive();
  214. if ( !m_bDisabled )
  215. {
  216. PhysicsTouchTriggers();
  217. }
  218. }
  219. //------------------------------------------------------------------------------
  220. // Purpose: Turns off this trigger.
  221. //------------------------------------------------------------------------------
  222. void CBaseTrigger::Disable( void )
  223. {
  224. m_bDisabled = true;
  225. if ( VPhysicsGetObject())
  226. {
  227. VPhysicsGetObject()->EnableCollisions( false );
  228. }
  229. if (IsSolidFlagSet(FSOLID_TRIGGER))
  230. {
  231. RemoveSolidFlags( FSOLID_TRIGGER );
  232. PhysicsTouchTriggers();
  233. }
  234. }
  235. //------------------------------------------------------------------------------
  236. // Purpose: Tests to see if anything is touching this trigger.
  237. //------------------------------------------------------------------------------
  238. void CBaseTrigger::TouchTest( void )
  239. {
  240. // If the trigger is disabled don't test to see if anything is touching it.
  241. if ( !m_bDisabled )
  242. {
  243. if ( m_hTouchingEntities.Count() !=0 )
  244. {
  245. m_OnTouching.FireOutput( this, this );
  246. }
  247. else
  248. {
  249. m_OnNotTouching.FireOutput( this, this );
  250. }
  251. }
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Draw any debug text overlays
  255. // Output : Current text offset from the top
  256. //-----------------------------------------------------------------------------
  257. int CBaseTrigger::DrawDebugTextOverlays(void)
  258. {
  259. int text_offset = BaseClass::DrawDebugTextOverlays();
  260. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  261. {
  262. // --------------
  263. // Print Target
  264. // --------------
  265. char tempstr[255];
  266. if (IsSolidFlagSet(FSOLID_TRIGGER))
  267. {
  268. Q_strncpy(tempstr,"State: Enabled",sizeof(tempstr));
  269. }
  270. else
  271. {
  272. Q_strncpy(tempstr,"State: Disabled",sizeof(tempstr));
  273. }
  274. EntityText(text_offset,tempstr,0);
  275. text_offset++;
  276. }
  277. return text_offset;
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose: Return true if the specified point is within this zone
  281. //-----------------------------------------------------------------------------
  282. bool CBaseTrigger::PointIsWithin( const Vector &vecPoint )
  283. {
  284. Ray_t ray;
  285. trace_t tr;
  286. ICollideable *pCollide = CollisionProp();
  287. ray.Init( vecPoint, vecPoint );
  288. enginetrace->ClipRayToCollideable( ray, MASK_ALL, pCollide, &tr );
  289. return ( tr.startsolid );
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose:
  293. //-----------------------------------------------------------------------------
  294. void CBaseTrigger::InitTrigger( )
  295. {
  296. SetSolid( GetParent() ? SOLID_VPHYSICS : SOLID_BSP );
  297. AddSolidFlags( FSOLID_NOT_SOLID );
  298. if (m_bDisabled)
  299. {
  300. RemoveSolidFlags( FSOLID_TRIGGER );
  301. }
  302. else
  303. {
  304. AddSolidFlags( FSOLID_TRIGGER );
  305. }
  306. SetMoveType( MOVETYPE_NONE );
  307. SetModel( STRING( GetModelName() ) ); // set size and link into world
  308. if ( showtriggers.GetInt() == 0 )
  309. {
  310. AddEffects( EF_NODRAW );
  311. }
  312. m_hTouchingEntities.Purge();
  313. if ( HasSpawnFlags( SF_TRIG_TOUCH_DEBRIS ) )
  314. {
  315. CollisionProp()->AddSolidFlags( FSOLID_TRIGGER_TOUCH_DEBRIS );
  316. }
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: Returns true if this entity passes the filter criteria, false if not.
  320. // Input : pOther - The entity to be filtered.
  321. //-----------------------------------------------------------------------------
  322. bool CBaseTrigger::PassesTriggerFilters(CBaseEntity *pOther)
  323. {
  324. // First test spawn flag filters
  325. if ( HasSpawnFlags(SF_TRIGGER_ALLOW_ALL) ||
  326. (HasSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS) && (pOther->GetFlags() & FL_CLIENT)) ||
  327. (HasSpawnFlags(SF_TRIGGER_ALLOW_NPCS) && (pOther->GetFlags() & FL_NPC)) ||
  328. (HasSpawnFlags(SF_TRIGGER_ALLOW_PUSHABLES) && FClassnameIs(pOther, "func_pushable")) ||
  329. (HasSpawnFlags(SF_TRIGGER_ALLOW_PHYSICS) && pOther->GetMoveType() == MOVETYPE_VPHYSICS)
  330. #if defined( HL2_EPISODIC ) || defined( TF_DLL )
  331. ||
  332. ( HasSpawnFlags(SF_TRIG_TOUCH_DEBRIS) &&
  333. (pOther->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
  334. pOther->GetCollisionGroup() == COLLISION_GROUP_DEBRIS_TRIGGER ||
  335. pOther->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS)
  336. )
  337. #endif
  338. )
  339. {
  340. if ( pOther->GetFlags() & FL_NPC )
  341. {
  342. CAI_BaseNPC *pNPC = pOther->MyNPCPointer();
  343. if ( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) )
  344. {
  345. if ( !pNPC || !pNPC->IsPlayerAlly() )
  346. {
  347. return false;
  348. }
  349. }
  350. if ( HasSpawnFlags( SF_TRIGGER_ONLY_NPCS_IN_VEHICLES ) )
  351. {
  352. if ( !pNPC || !pNPC->IsInAVehicle() )
  353. return false;
  354. }
  355. }
  356. bool bOtherIsPlayer = pOther->IsPlayer();
  357. if ( bOtherIsPlayer )
  358. {
  359. CBasePlayer *pPlayer = (CBasePlayer*)pOther;
  360. if ( !pPlayer->IsAlive() )
  361. return false;
  362. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES) )
  363. {
  364. if ( !pPlayer->IsInAVehicle() )
  365. return false;
  366. // Make sure we're also not exiting the vehicle at the moment
  367. IServerVehicle *pVehicleServer = pPlayer->GetVehicle();
  368. if ( pVehicleServer == NULL )
  369. return false;
  370. if ( pVehicleServer->IsPassengerExiting() )
  371. return false;
  372. }
  373. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES) )
  374. {
  375. if ( pPlayer->IsInAVehicle() )
  376. return false;
  377. }
  378. if ( HasSpawnFlags( SF_TRIGGER_DISALLOW_BOTS ) )
  379. {
  380. if ( pPlayer->IsFakeClient() )
  381. return false;
  382. }
  383. }
  384. CBaseFilter *pFilter = m_hFilter.Get();
  385. return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
  386. }
  387. return false;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: Called to simulate what happens when an entity touches the trigger.
  391. // Input : pOther - The entity that is touching us.
  392. //-----------------------------------------------------------------------------
  393. void CBaseTrigger::InputStartTouch( inputdata_t &inputdata )
  394. {
  395. //Pretend we just touched the trigger.
  396. StartTouch( inputdata.pCaller );
  397. }
  398. //-----------------------------------------------------------------------------
  399. // Purpose: Called to simulate what happens when an entity leaves the trigger.
  400. // Input : pOther - The entity that is touching us.
  401. //-----------------------------------------------------------------------------
  402. void CBaseTrigger::InputEndTouch( inputdata_t &inputdata )
  403. {
  404. //And... pretend we left the trigger.
  405. EndTouch( inputdata.pCaller );
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose: Called when an entity starts touching us.
  409. // Input : pOther - The entity that is touching us.
  410. //-----------------------------------------------------------------------------
  411. void CBaseTrigger::StartTouch(CBaseEntity *pOther)
  412. {
  413. if (PassesTriggerFilters(pOther) )
  414. {
  415. EHANDLE hOther;
  416. hOther = pOther;
  417. bool bAdded = false;
  418. if ( m_hTouchingEntities.Find( hOther ) == m_hTouchingEntities.InvalidIndex() )
  419. {
  420. m_hTouchingEntities.AddToTail( hOther );
  421. bAdded = true;
  422. }
  423. m_OnStartTouch.FireOutput(pOther, this);
  424. if ( bAdded && ( m_hTouchingEntities.Count() == 1 ) )
  425. {
  426. // First entity to touch us that passes our filters
  427. m_OnStartTouchAll.FireOutput( pOther, this );
  428. StartTouchAll();
  429. }
  430. }
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose: Called when an entity stops touching us.
  434. // Input : pOther - The entity that was touching us.
  435. //-----------------------------------------------------------------------------
  436. void CBaseTrigger::EndTouch(CBaseEntity *pOther)
  437. {
  438. if ( IsTouching( pOther ) )
  439. {
  440. EHANDLE hOther;
  441. hOther = pOther;
  442. m_hTouchingEntities.FindAndRemove( hOther );
  443. //FIXME: Without this, triggers fire their EndTouch outputs when they are disabled!
  444. //if ( !m_bDisabled )
  445. //{
  446. m_OnEndTouch.FireOutput(pOther, this);
  447. //}
  448. // If there are no more entities touching this trigger, fire the lost all touches
  449. // Loop through the touching entities backwards. Clean out old ones, and look for existing
  450. bool bFoundOtherTouchee = false;
  451. int iSize = m_hTouchingEntities.Count();
  452. for ( int i = iSize-1; i >= 0; i-- )
  453. {
  454. EHANDLE hOther;
  455. hOther = m_hTouchingEntities[i];
  456. if ( !hOther )
  457. {
  458. m_hTouchingEntities.Remove( i );
  459. }
  460. else if ( hOther->IsPlayer() && !hOther->IsAlive() )
  461. {
  462. #ifdef STAGING_ONLY
  463. if ( !HushAsserts() )
  464. {
  465. AssertMsg( false, "Dead player [%s] is still touching this trigger at [%f %f %f]", hOther->GetEntityName().ToCStr(), XYZ( hOther->GetAbsOrigin() ) );
  466. }
  467. Warning( "Dead player [%s] is still touching this trigger at [%f %f %f]", hOther->GetEntityName().ToCStr(), XYZ( hOther->GetAbsOrigin() ) );
  468. #endif
  469. m_hTouchingEntities.Remove( i );
  470. }
  471. else
  472. {
  473. bFoundOtherTouchee = true;
  474. }
  475. }
  476. //FIXME: Without this, triggers fire their EndTouch outputs when they are disabled!
  477. // Didn't find one?
  478. if ( !bFoundOtherTouchee /*&& !m_bDisabled*/ )
  479. {
  480. m_OnEndTouchAll.FireOutput(pOther, this);
  481. EndTouchAll();
  482. }
  483. }
  484. }
  485. //-----------------------------------------------------------------------------
  486. // Purpose: Return true if the specified entity is touching us
  487. //-----------------------------------------------------------------------------
  488. bool CBaseTrigger::IsTouching( const CBaseEntity *pOther ) const
  489. {
  490. EHANDLE hOther;
  491. hOther = pOther;
  492. return ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() );
  493. }
  494. //-----------------------------------------------------------------------------
  495. // Purpose: Return a pointer to the first entity of the specified type being touched by this trigger
  496. //-----------------------------------------------------------------------------
  497. CBaseEntity *CBaseTrigger::GetTouchedEntityOfType( const char *sClassName )
  498. {
  499. int iCount = m_hTouchingEntities.Count();
  500. for ( int i = 0; i < iCount; i++ )
  501. {
  502. CBaseEntity *pEntity = m_hTouchingEntities[i];
  503. if ( FClassnameIs( pEntity, sClassName ) )
  504. return pEntity;
  505. }
  506. return NULL;
  507. }
  508. //-----------------------------------------------------------------------------
  509. // Purpose: Toggles this trigger between enabled and disabled.
  510. //-----------------------------------------------------------------------------
  511. void CBaseTrigger::InputToggle( inputdata_t &inputdata )
  512. {
  513. if (IsSolidFlagSet( FSOLID_TRIGGER ))
  514. {
  515. RemoveSolidFlags(FSOLID_TRIGGER);
  516. }
  517. else
  518. {
  519. AddSolidFlags(FSOLID_TRIGGER);
  520. }
  521. PhysicsTouchTriggers();
  522. }
  523. //-----------------------------------------------------------------------------
  524. // Purpose: Removes anything that touches it. If the trigger has a targetname,
  525. // firing it will toggle state.
  526. //-----------------------------------------------------------------------------
  527. class CTriggerRemove : public CBaseTrigger
  528. {
  529. public:
  530. DECLARE_CLASS( CTriggerRemove, CBaseTrigger );
  531. void Spawn( void );
  532. void Touch( CBaseEntity *pOther );
  533. DECLARE_DATADESC();
  534. // Outputs
  535. COutputEvent m_OnRemove;
  536. };
  537. BEGIN_DATADESC( CTriggerRemove )
  538. // Outputs
  539. DEFINE_OUTPUT( m_OnRemove, "OnRemove" ),
  540. END_DATADESC()
  541. LINK_ENTITY_TO_CLASS( trigger_remove, CTriggerRemove );
  542. //-----------------------------------------------------------------------------
  543. // Purpose:
  544. //-----------------------------------------------------------------------------
  545. void CTriggerRemove::Spawn( void )
  546. {
  547. BaseClass::Spawn();
  548. InitTrigger();
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose: Trigger hurt that causes radiation will do a radius check and set
  552. // the player's geiger counter level according to distance from center
  553. // of trigger.
  554. //-----------------------------------------------------------------------------
  555. void CTriggerRemove::Touch( CBaseEntity *pOther )
  556. {
  557. if (!PassesTriggerFilters(pOther))
  558. return;
  559. UTIL_Remove( pOther );
  560. }
  561. BEGIN_DATADESC( CTriggerHurt )
  562. // Function Pointers
  563. DEFINE_FUNCTION( CTriggerHurtShim::RadiationThinkShim ),
  564. DEFINE_FUNCTION( CTriggerHurtShim::HurtThinkShim ),
  565. // Fields
  566. DEFINE_FIELD( m_flOriginalDamage, FIELD_FLOAT ),
  567. DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "damage" ),
  568. DEFINE_KEYFIELD( m_flDamageCap, FIELD_FLOAT, "damagecap" ),
  569. DEFINE_KEYFIELD( m_bitsDamageInflict, FIELD_INTEGER, "damagetype" ),
  570. DEFINE_KEYFIELD( m_damageModel, FIELD_INTEGER, "damagemodel" ),
  571. DEFINE_KEYFIELD( m_bNoDmgForce, FIELD_BOOLEAN, "nodmgforce" ),
  572. DEFINE_FIELD( m_flLastDmgTime, FIELD_TIME ),
  573. DEFINE_FIELD( m_flDmgResetTime, FIELD_TIME ),
  574. DEFINE_UTLVECTOR( m_hurtEntities, FIELD_EHANDLE ),
  575. // Inputs
  576. DEFINE_INPUT( m_flDamage, FIELD_FLOAT, "SetDamage" ),
  577. // Outputs
  578. DEFINE_OUTPUT( m_OnHurt, "OnHurt" ),
  579. DEFINE_OUTPUT( m_OnHurtPlayer, "OnHurtPlayer" ),
  580. END_DATADESC()
  581. LINK_ENTITY_TO_CLASS( trigger_hurt, CTriggerHurt );
  582. IMPLEMENT_AUTO_LIST( ITriggerHurtAutoList );
  583. //-----------------------------------------------------------------------------
  584. // Purpose: Called when spawning, after keyvalues have been handled.
  585. //-----------------------------------------------------------------------------
  586. void CTriggerHurt::Spawn( void )
  587. {
  588. BaseClass::Spawn();
  589. InitTrigger();
  590. m_flOriginalDamage = m_flDamage;
  591. SetNextThink( TICK_NEVER_THINK );
  592. SetThink( NULL );
  593. if (m_bitsDamageInflict & DMG_RADIATION)
  594. {
  595. SetThink ( &CTriggerHurtShim::RadiationThinkShim );
  596. SetNextThink( gpGlobals->curtime + random->RandomFloat(0.0, 0.5) );
  597. }
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Purpose: Trigger hurt that causes radiation will do a radius check and set
  601. // the player's geiger counter level according to distance from center
  602. // of trigger.
  603. //-----------------------------------------------------------------------------
  604. void CTriggerHurt::RadiationThink( void )
  605. {
  606. // check to see if a player is in pvs
  607. // if not, continue
  608. Vector vecSurroundMins, vecSurroundMaxs;
  609. CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  610. CBasePlayer *pPlayer = static_cast<CBasePlayer *>(UTIL_FindClientInPVS( vecSurroundMins, vecSurroundMaxs ));
  611. if (pPlayer)
  612. {
  613. // get range to player;
  614. float flRange = CollisionProp()->CalcDistanceFromPoint( pPlayer->WorldSpaceCenter() );
  615. flRange *= 3.0f;
  616. pPlayer->NotifyNearbyRadiationSource(flRange);
  617. }
  618. float dt = gpGlobals->curtime - m_flLastDmgTime;
  619. if ( dt >= 0.5 )
  620. {
  621. HurtAllTouchers( dt );
  622. }
  623. SetNextThink( gpGlobals->curtime + 0.25 );
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Purpose: When touched, a hurt trigger does m_flDamage points of damage each half-second.
  627. // Input : pOther - The entity that is touching us.
  628. //-----------------------------------------------------------------------------
  629. bool CTriggerHurt::HurtEntity( CBaseEntity *pOther, float damage )
  630. {
  631. if ( !pOther->m_takedamage || !PassesTriggerFilters(pOther) )
  632. return false;
  633. // If player is disconnected, we're probably in this routine via the
  634. // PhysicsRemoveTouchedList() function to make sure all Untouch()'s are called for the
  635. // player. Calling TakeDamage() in this case can get into the speaking criteria, which
  636. // will then loop through the control points and the touched list again. We shouldn't
  637. // need to hurt players that are disconnected, so skip all of this...
  638. bool bPlayerDisconnected = pOther->IsPlayer() && ( ((CBasePlayer *)pOther)->IsConnected() == false );
  639. if ( bPlayerDisconnected )
  640. return false;
  641. if ( damage < 0 )
  642. {
  643. pOther->TakeHealth( -damage, m_bitsDamageInflict );
  644. }
  645. else
  646. {
  647. // The damage position is the nearest point on the damaged entity
  648. // to the trigger's center. Not perfect, but better than nothing.
  649. Vector vecCenter = CollisionProp()->WorldSpaceCenter();
  650. Vector vecDamagePos;
  651. pOther->CollisionProp()->CalcNearestPoint( vecCenter, &vecDamagePos );
  652. CTakeDamageInfo info( this, this, damage, m_bitsDamageInflict );
  653. info.SetDamagePosition( vecDamagePos );
  654. if ( !m_bNoDmgForce )
  655. {
  656. GuessDamageForce( &info, ( vecDamagePos - vecCenter ), vecDamagePos );
  657. }
  658. else
  659. {
  660. info.SetDamageForce( vec3_origin );
  661. }
  662. pOther->TakeDamage( info );
  663. }
  664. if (pOther->IsPlayer())
  665. {
  666. m_OnHurtPlayer.FireOutput(pOther, this);
  667. }
  668. else
  669. {
  670. m_OnHurt.FireOutput(pOther, this);
  671. }
  672. m_hurtEntities.AddToTail( EHANDLE(pOther) );
  673. //NDebugOverlay::Box( pOther->GetAbsOrigin(), pOther->WorldAlignMins(), pOther->WorldAlignMaxs(), 255,0,0,0,0.5 );
  674. return true;
  675. }
  676. void CTriggerHurt::HurtThink()
  677. {
  678. // if I hurt anyone, think again
  679. if ( HurtAllTouchers( 0.5 ) <= 0 )
  680. {
  681. SetThink(NULL);
  682. }
  683. else
  684. {
  685. SetNextThink( gpGlobals->curtime + 0.5f );
  686. }
  687. }
  688. void CTriggerHurt::EndTouch( CBaseEntity *pOther )
  689. {
  690. if (PassesTriggerFilters(pOther))
  691. {
  692. EHANDLE hOther;
  693. hOther = pOther;
  694. // if this guy has never taken damage, hurt him now
  695. if ( !m_hurtEntities.HasElement( hOther ) )
  696. {
  697. HurtEntity( pOther, m_flDamage * 0.5 );
  698. }
  699. }
  700. BaseClass::EndTouch( pOther );
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Purpose: called from RadiationThink() as well as HurtThink()
  704. // This function applies damage to any entities currently touching the
  705. // trigger
  706. // Input : dt - time since last call
  707. // Output : int - number of entities actually hurt
  708. //-----------------------------------------------------------------------------
  709. #define TRIGGER_HURT_FORGIVE_TIME 3.0f // time in seconds
  710. int CTriggerHurt::HurtAllTouchers( float dt )
  711. {
  712. int hurtCount = 0;
  713. // half second worth of damage
  714. float fldmg = m_flDamage * dt;
  715. m_flLastDmgTime = gpGlobals->curtime;
  716. m_hurtEntities.RemoveAll();
  717. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  718. if ( root )
  719. {
  720. for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
  721. {
  722. CBaseEntity *pTouch = link->entityTouched;
  723. if ( pTouch )
  724. {
  725. if ( HurtEntity( pTouch, fldmg ) )
  726. {
  727. hurtCount++;
  728. }
  729. }
  730. }
  731. }
  732. if( m_damageModel == DAMAGEMODEL_DOUBLE_FORGIVENESS )
  733. {
  734. if( hurtCount == 0 )
  735. {
  736. if( gpGlobals->curtime > m_flDmgResetTime )
  737. {
  738. // Didn't hurt anyone. Reset the damage if it's time. (hence, the forgiveness)
  739. m_flDamage = m_flOriginalDamage;
  740. }
  741. }
  742. else
  743. {
  744. // Hurt someone! double the damage
  745. m_flDamage *= 2.0f;
  746. if( m_flDamage > m_flDamageCap )
  747. {
  748. // Clamp
  749. m_flDamage = m_flDamageCap;
  750. }
  751. // Now, put the damage reset time into the future. The forgive time is how long the trigger
  752. // must go without harming anyone in order that its accumulated damage be reset to the amount
  753. // set by the level designer. This is a stop-gap for an exploit where players could hop through
  754. // slime and barely take any damage because the trigger would reset damage anytime there was no
  755. // one in the trigger when this function was called. (sjb)
  756. m_flDmgResetTime = gpGlobals->curtime + TRIGGER_HURT_FORGIVE_TIME;
  757. }
  758. }
  759. return hurtCount;
  760. }
  761. void CTriggerHurt::Touch( CBaseEntity *pOther )
  762. {
  763. if ( m_pfnThink == NULL )
  764. {
  765. SetThink( &CTriggerHurtShim::HurtThinkShim );
  766. SetNextThink( gpGlobals->curtime );
  767. }
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Purpose: Checks if this point is in any trigger_hurt zones with positive damage
  771. //-----------------------------------------------------------------------------
  772. bool IsTakingTriggerHurtDamageAtPoint( const Vector &vecPoint )
  773. {
  774. for ( int i = 0; i < ITriggerHurtAutoList::AutoList().Count(); i++ )
  775. {
  776. // Some maps use trigger_hurt with negative values as healing triggers; don't consider those
  777. CTriggerHurt *pTrigger = static_cast<CTriggerHurt*>( ITriggerHurtAutoList::AutoList()[i] );
  778. if ( !pTrigger->m_bDisabled && pTrigger->PointIsWithin( vecPoint ) && pTrigger->m_flDamage > 0.f )
  779. {
  780. return true;
  781. }
  782. }
  783. return false;
  784. }
  785. // ##################################################################################
  786. // >> TriggerMultiple
  787. // ##################################################################################
  788. LINK_ENTITY_TO_CLASS( trigger_multiple, CTriggerMultiple );
  789. BEGIN_DATADESC( CTriggerMultiple )
  790. // Function Pointers
  791. DEFINE_FUNCTION(MultiTouch),
  792. DEFINE_FUNCTION(MultiWaitOver ),
  793. // Outputs
  794. DEFINE_OUTPUT(m_OnTrigger, "OnTrigger")
  795. END_DATADESC()
  796. //-----------------------------------------------------------------------------
  797. // Purpose: Called when spawning, after keyvalues have been handled.
  798. //-----------------------------------------------------------------------------
  799. void CTriggerMultiple::Spawn( void )
  800. {
  801. BaseClass::Spawn();
  802. InitTrigger();
  803. if (m_flWait == 0)
  804. {
  805. m_flWait = 0.2;
  806. }
  807. ASSERTSZ(m_iHealth == 0, "trigger_multiple with health");
  808. SetTouch( &CTriggerMultiple::MultiTouch );
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose: Touch function. Activates the trigger.
  812. // Input : pOther - The thing that touched us.
  813. //-----------------------------------------------------------------------------
  814. void CTriggerMultiple::MultiTouch(CBaseEntity *pOther)
  815. {
  816. if (PassesTriggerFilters(pOther))
  817. {
  818. ActivateMultiTrigger( pOther );
  819. }
  820. }
  821. //-----------------------------------------------------------------------------
  822. // Purpose:
  823. // Input : pActivator -
  824. //-----------------------------------------------------------------------------
  825. void CTriggerMultiple::ActivateMultiTrigger(CBaseEntity *pActivator)
  826. {
  827. if (GetNextThink() > gpGlobals->curtime)
  828. return; // still waiting for reset time
  829. m_hActivator = pActivator;
  830. m_OnTrigger.FireOutput(m_hActivator, this);
  831. if (m_flWait > 0)
  832. {
  833. SetThink( &CTriggerMultiple::MultiWaitOver );
  834. SetNextThink( gpGlobals->curtime + m_flWait );
  835. }
  836. else
  837. {
  838. // we can't just remove (self) here, because this is a touch function
  839. // called while C code is looping through area links...
  840. SetTouch( NULL );
  841. SetNextThink( gpGlobals->curtime + 0.1f );
  842. SetThink( &CTriggerMultiple::SUB_Remove );
  843. }
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose: The wait time has passed, so set back up for another activation
  847. //-----------------------------------------------------------------------------
  848. void CTriggerMultiple::MultiWaitOver( void )
  849. {
  850. SetThink( NULL );
  851. }
  852. // ##################################################################################
  853. // >> TriggerOnce
  854. // ##################################################################################
  855. class CTriggerOnce : public CTriggerMultiple
  856. {
  857. DECLARE_CLASS( CTriggerOnce, CTriggerMultiple );
  858. public:
  859. void Spawn( void );
  860. };
  861. LINK_ENTITY_TO_CLASS( trigger_once, CTriggerOnce );
  862. void CTriggerOnce::Spawn( void )
  863. {
  864. BaseClass::Spawn();
  865. m_flWait = -1;
  866. }
  867. // ##################################################################################
  868. // >> TriggerLook
  869. //
  870. // Triggers once when player is looking at m_target
  871. //
  872. // ##################################################################################
  873. #define SF_TRIGGERLOOK_FIREONCE 128
  874. #define SF_TRIGGERLOOK_USEVELOCITY 256
  875. class CTriggerLook : public CTriggerOnce
  876. {
  877. DECLARE_CLASS( CTriggerLook, CTriggerOnce );
  878. public:
  879. EHANDLE m_hLookTarget;
  880. float m_flFieldOfView;
  881. float m_flLookTime; // How long must I look for
  882. float m_flLookTimeTotal; // How long have I looked
  883. float m_flLookTimeLast; // When did I last look
  884. float m_flTimeoutDuration; // Number of seconds after start touch to fire anyway
  885. bool m_bTimeoutFired; // True if the OnTimeout output fired since the last StartTouch.
  886. EHANDLE m_hActivator; // The entity that triggered us.
  887. void Spawn( void );
  888. void Touch( CBaseEntity *pOther );
  889. void StartTouch(CBaseEntity *pOther);
  890. void EndTouch( CBaseEntity *pOther );
  891. int DrawDebugTextOverlays(void);
  892. DECLARE_DATADESC();
  893. private:
  894. void Trigger(CBaseEntity *pActivator, bool bTimeout);
  895. void TimeoutThink();
  896. COutputEvent m_OnTimeout;
  897. };
  898. LINK_ENTITY_TO_CLASS( trigger_look, CTriggerLook );
  899. BEGIN_DATADESC( CTriggerLook )
  900. DEFINE_FIELD( m_hLookTarget, FIELD_EHANDLE ),
  901. DEFINE_FIELD( m_flLookTimeTotal, FIELD_FLOAT ),
  902. DEFINE_FIELD( m_flLookTimeLast, FIELD_TIME ),
  903. DEFINE_KEYFIELD( m_flTimeoutDuration, FIELD_FLOAT, "timeout" ),
  904. DEFINE_FIELD( m_bTimeoutFired, FIELD_BOOLEAN ),
  905. DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
  906. DEFINE_OUTPUT( m_OnTimeout, "OnTimeout" ),
  907. DEFINE_FUNCTION( TimeoutThink ),
  908. // Inputs
  909. DEFINE_INPUT( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ),
  910. DEFINE_INPUT( m_flLookTime, FIELD_FLOAT, "LookTime" ),
  911. END_DATADESC()
  912. //------------------------------------------------------------------------------
  913. // Purpose:
  914. //------------------------------------------------------------------------------
  915. void CTriggerLook::Spawn( void )
  916. {
  917. m_hLookTarget = NULL;
  918. m_flLookTimeTotal = -1;
  919. m_bTimeoutFired = false;
  920. BaseClass::Spawn();
  921. }
  922. //-----------------------------------------------------------------------------
  923. // Purpose:
  924. // Input : pOther -
  925. //-----------------------------------------------------------------------------
  926. void CTriggerLook::StartTouch(CBaseEntity *pOther)
  927. {
  928. BaseClass::StartTouch(pOther);
  929. if (pOther->IsPlayer() && m_flTimeoutDuration)
  930. {
  931. m_bTimeoutFired = false;
  932. m_hActivator = pOther;
  933. SetThink(&CTriggerLook::TimeoutThink);
  934. SetNextThink(gpGlobals->curtime + m_flTimeoutDuration);
  935. }
  936. }
  937. //-----------------------------------------------------------------------------
  938. // Purpose:
  939. //-----------------------------------------------------------------------------
  940. void CTriggerLook::TimeoutThink(void)
  941. {
  942. Trigger(m_hActivator, true);
  943. }
  944. //------------------------------------------------------------------------------
  945. // Purpose:
  946. //------------------------------------------------------------------------------
  947. void CTriggerLook::EndTouch(CBaseEntity *pOther)
  948. {
  949. BaseClass::EndTouch(pOther);
  950. if (pOther->IsPlayer())
  951. {
  952. SetThink(NULL);
  953. SetNextThink( TICK_NEVER_THINK );
  954. m_flLookTimeTotal = -1;
  955. }
  956. }
  957. //------------------------------------------------------------------------------
  958. // Purpose:
  959. //------------------------------------------------------------------------------
  960. void CTriggerLook::Touch(CBaseEntity *pOther)
  961. {
  962. // Don't fire the OnTrigger if we've already fired the OnTimeout. This will be
  963. // reset in OnEndTouch.
  964. if (m_bTimeoutFired)
  965. return;
  966. // --------------------------------
  967. // Make sure we have a look target
  968. // --------------------------------
  969. if (m_hLookTarget == NULL)
  970. {
  971. m_hLookTarget = GetNextTarget();
  972. if (m_hLookTarget == NULL)
  973. {
  974. return;
  975. }
  976. }
  977. // This is designed for single player only
  978. // so we'll always have the same player
  979. if (pOther->IsPlayer())
  980. {
  981. // ----------------------------------------
  982. // Check that toucher is facing the target
  983. // ----------------------------------------
  984. Vector vLookDir;
  985. if ( HasSpawnFlags( SF_TRIGGERLOOK_USEVELOCITY ) )
  986. {
  987. vLookDir = pOther->GetAbsVelocity();
  988. if ( vLookDir == vec3_origin )
  989. {
  990. // See if they're in a vehicle
  991. CBasePlayer *pPlayer = (CBasePlayer *)pOther;
  992. if ( pPlayer->IsInAVehicle() )
  993. {
  994. vLookDir = pPlayer->GetVehicle()->GetVehicleEnt()->GetSmoothedVelocity();
  995. }
  996. }
  997. VectorNormalize( vLookDir );
  998. }
  999. else
  1000. {
  1001. vLookDir = ((CBaseCombatCharacter*)pOther)->EyeDirection3D( );
  1002. }
  1003. Vector vTargetDir = m_hLookTarget->GetAbsOrigin() - pOther->EyePosition();
  1004. VectorNormalize(vTargetDir);
  1005. float fDotPr = DotProduct(vLookDir,vTargetDir);
  1006. if (fDotPr > m_flFieldOfView)
  1007. {
  1008. // Is it the first time I'm looking?
  1009. if (m_flLookTimeTotal == -1)
  1010. {
  1011. m_flLookTimeLast = gpGlobals->curtime;
  1012. m_flLookTimeTotal = 0;
  1013. }
  1014. else
  1015. {
  1016. m_flLookTimeTotal += gpGlobals->curtime - m_flLookTimeLast;
  1017. m_flLookTimeLast = gpGlobals->curtime;
  1018. }
  1019. if (m_flLookTimeTotal >= m_flLookTime)
  1020. {
  1021. Trigger(pOther, false);
  1022. }
  1023. }
  1024. else
  1025. {
  1026. m_flLookTimeTotal = -1;
  1027. }
  1028. }
  1029. }
  1030. //-----------------------------------------------------------------------------
  1031. // Purpose: Called when the trigger is fired by look logic or timeout.
  1032. //-----------------------------------------------------------------------------
  1033. void CTriggerLook::Trigger(CBaseEntity *pActivator, bool bTimeout)
  1034. {
  1035. if (bTimeout)
  1036. {
  1037. // Fired due to timeout (player never looked at the target).
  1038. m_OnTimeout.FireOutput(pActivator, this);
  1039. // Don't fire the OnTrigger for this toucher.
  1040. m_bTimeoutFired = true;
  1041. }
  1042. else
  1043. {
  1044. // Fire because the player looked at the target.
  1045. m_OnTrigger.FireOutput(pActivator, this);
  1046. m_flLookTimeTotal = -1;
  1047. // Cancel the timeout think.
  1048. SetThink(NULL);
  1049. SetNextThink( TICK_NEVER_THINK );
  1050. }
  1051. if (HasSpawnFlags(SF_TRIGGERLOOK_FIREONCE))
  1052. {
  1053. SetThink(&CTriggerLook::SUB_Remove);
  1054. SetNextThink(gpGlobals->curtime);
  1055. }
  1056. }
  1057. //-----------------------------------------------------------------------------
  1058. // Purpose: Draw any debug text overlays
  1059. // Output : Current text offset from the top
  1060. //-----------------------------------------------------------------------------
  1061. int CTriggerLook::DrawDebugTextOverlays(void)
  1062. {
  1063. int text_offset = BaseClass::DrawDebugTextOverlays();
  1064. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1065. {
  1066. // ----------------
  1067. // Print Look time
  1068. // ----------------
  1069. char tempstr[255];
  1070. Q_snprintf(tempstr,sizeof(tempstr),"Time: %3.2f",m_flLookTime - MAX(0,m_flLookTimeTotal));
  1071. EntityText(text_offset,tempstr,0);
  1072. text_offset++;
  1073. }
  1074. return text_offset;
  1075. }
  1076. // ##################################################################################
  1077. // >> TriggerVolume
  1078. // ##################################################################################
  1079. class CTriggerVolume : public CPointEntity // Derive from point entity so this doesn't move across levels
  1080. {
  1081. public:
  1082. DECLARE_CLASS( CTriggerVolume, CPointEntity );
  1083. void Spawn( void );
  1084. };
  1085. LINK_ENTITY_TO_CLASS( trigger_transition, CTriggerVolume );
  1086. // Define space that travels across a level transition
  1087. void CTriggerVolume::Spawn( void )
  1088. {
  1089. SetSolid( SOLID_BSP );
  1090. AddSolidFlags( FSOLID_NOT_SOLID );
  1091. SetMoveType( MOVETYPE_NONE );
  1092. SetModel( STRING( GetModelName() ) ); // set size and link into world
  1093. if ( showtriggers.GetInt() == 0 )
  1094. {
  1095. AddEffects( EF_NODRAW );
  1096. }
  1097. }
  1098. #define SF_CHANGELEVEL_NOTOUCH 0x0002
  1099. #define SF_CHANGELEVEL_CHAPTER 0x0004
  1100. #define cchMapNameMost 32
  1101. enum
  1102. {
  1103. TRANSITION_VOLUME_SCREENED_OUT = 0,
  1104. TRANSITION_VOLUME_NOT_FOUND = 1,
  1105. TRANSITION_VOLUME_PASSED = 2,
  1106. };
  1107. //------------------------------------------------------------------------------
  1108. // Reesponsible for changing levels when the player touches it
  1109. //------------------------------------------------------------------------------
  1110. class CChangeLevel : public CBaseTrigger
  1111. {
  1112. DECLARE_DATADESC();
  1113. public:
  1114. DECLARE_CLASS( CChangeLevel, CBaseTrigger );
  1115. void Spawn( void );
  1116. void Activate( void );
  1117. bool KeyValue( const char *szKeyName, const char *szValue );
  1118. static int ChangeList( levellist_t *pLevelList, int maxList );
  1119. private:
  1120. void TouchChangeLevel( CBaseEntity *pOther );
  1121. void ChangeLevelNow( CBaseEntity *pActivator );
  1122. void InputChangeLevel( inputdata_t &inputdata );
  1123. bool IsEntityInTransition( CBaseEntity *pEntity );
  1124. void NotifyEntitiesOutOfTransition();
  1125. void WarnAboutActiveLead( void );
  1126. static CBaseEntity *FindLandmark( const char *pLandmarkName );
  1127. static int AddTransitionToList( levellist_t *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark );
  1128. static int InTransitionVolume( CBaseEntity *pEntity, const char *pVolumeName );
  1129. // Builds the list of entities to save when moving across a transition
  1130. static int BuildChangeLevelList( levellist_t *pLevelList, int maxList );
  1131. // Builds the list of entities to bring across a particular transition
  1132. static int BuildEntityTransitionList( CBaseEntity *pLandmarkEntity, const char *pLandmarkName, CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList );
  1133. // Adds a single entity to the transition list, if appropriate. Returns the new count
  1134. static int AddEntityToTransitionList( CBaseEntity *pEntity, int flags, int nCount, CBaseEntity **ppEntList, int *pEntityFlags );
  1135. // Adds in all entities depended on by entities near the transition
  1136. static int AddDependentEntities( int nCount, CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList );
  1137. // Figures out save flags for the entity
  1138. static int ComputeEntitySaveFlags( CBaseEntity *pEntity );
  1139. private:
  1140. char m_szMapName[cchMapNameMost]; // trigger_changelevel only: next map
  1141. char m_szLandmarkName[cchMapNameMost]; // trigger_changelevel only: landmark on next map
  1142. bool m_bTouched;
  1143. // Outputs
  1144. COutputEvent m_OnChangeLevel;
  1145. };
  1146. LINK_ENTITY_TO_CLASS( trigger_changelevel, CChangeLevel );
  1147. // Global Savedata for changelevel trigger
  1148. BEGIN_DATADESC( CChangeLevel )
  1149. DEFINE_AUTO_ARRAY( m_szMapName, FIELD_CHARACTER ),
  1150. DEFINE_AUTO_ARRAY( m_szLandmarkName, FIELD_CHARACTER ),
  1151. // DEFINE_FIELD( m_touchTime, FIELD_TIME ), // don't save
  1152. // DEFINE_FIELD( m_bTouched, FIELD_BOOLEAN ),
  1153. // Function Pointers
  1154. DEFINE_FUNCTION( TouchChangeLevel ),
  1155. DEFINE_INPUTFUNC( FIELD_VOID, "ChangeLevel", InputChangeLevel ),
  1156. // Outputs
  1157. DEFINE_OUTPUT( m_OnChangeLevel, "OnChangeLevel"),
  1158. END_DATADESC()
  1159. //
  1160. // Cache user-entity-field values until spawn is called.
  1161. //
  1162. bool CChangeLevel::KeyValue( const char *szKeyName, const char *szValue )
  1163. {
  1164. if (FStrEq(szKeyName, "map"))
  1165. {
  1166. if (strlen(szValue) >= cchMapNameMost)
  1167. {
  1168. Warning( "Map name '%s' too long (32 chars)\n", szValue );
  1169. Assert(0);
  1170. }
  1171. Q_strncpy(m_szMapName, szValue, sizeof(m_szMapName));
  1172. }
  1173. else if (FStrEq(szKeyName, "landmark"))
  1174. {
  1175. if (strlen(szValue) >= cchMapNameMost)
  1176. {
  1177. Warning( "Landmark name '%s' too long (32 chars)\n", szValue );
  1178. Assert(0);
  1179. }
  1180. Q_strncpy(m_szLandmarkName, szValue, sizeof( m_szLandmarkName ));
  1181. }
  1182. else
  1183. return BaseClass::KeyValue( szKeyName, szValue );
  1184. return true;
  1185. }
  1186. void CChangeLevel::Spawn( void )
  1187. {
  1188. if ( FStrEq( m_szMapName, "" ) )
  1189. {
  1190. Msg( "a trigger_changelevel doesn't have a map" );
  1191. }
  1192. if ( FStrEq( m_szLandmarkName, "" ) )
  1193. {
  1194. Msg( "trigger_changelevel to %s doesn't have a landmark", m_szMapName );
  1195. }
  1196. InitTrigger();
  1197. if ( !HasSpawnFlags(SF_CHANGELEVEL_NOTOUCH) )
  1198. {
  1199. SetTouch( &CChangeLevel::TouchChangeLevel );
  1200. }
  1201. // Msg( "TRANSITION: %s (%s)\n", m_szMapName, m_szLandmarkName );
  1202. }
  1203. void CChangeLevel::Activate( void )
  1204. {
  1205. BaseClass::Activate();
  1206. if ( gpGlobals->eLoadType == MapLoad_NewGame )
  1207. {
  1208. if ( HasSpawnFlags( SF_CHANGELEVEL_CHAPTER ) )
  1209. {
  1210. VPhysicsInitStatic();
  1211. RemoveSolidFlags( FSOLID_NOT_SOLID | FSOLID_TRIGGER );
  1212. SetTouch( NULL );
  1213. return;
  1214. }
  1215. }
  1216. // Level transitions will bust if they are in solid
  1217. CBaseEntity *pLandmark = FindLandmark( m_szLandmarkName );
  1218. if ( pLandmark )
  1219. {
  1220. int clusterIndex = engine->GetClusterForOrigin( pLandmark->GetAbsOrigin() );
  1221. if ( clusterIndex < 0 )
  1222. {
  1223. Warning( "trigger_changelevel to map %s has a landmark embedded in solid!\n"
  1224. "This will break level transitions!\n", m_szMapName );
  1225. }
  1226. if ( g_debug_transitions.GetInt() )
  1227. {
  1228. if ( !gEntList.FindEntityByClassname( NULL, "trigger_transition" ) )
  1229. {
  1230. Warning( "Map has no trigger_transition volumes for landmark %s\n", m_szLandmarkName );
  1231. }
  1232. }
  1233. }
  1234. m_bTouched = false;
  1235. }
  1236. static char st_szNextMap[cchMapNameMost];
  1237. static char st_szNextSpot[cchMapNameMost];
  1238. // Used to show debug for only the transition volume we're currently in
  1239. static int g_iDebuggingTransition = 0;
  1240. CBaseEntity *CChangeLevel::FindLandmark( const char *pLandmarkName )
  1241. {
  1242. CBaseEntity *pentLandmark;
  1243. pentLandmark = gEntList.FindEntityByName( NULL, pLandmarkName );
  1244. while ( pentLandmark )
  1245. {
  1246. // Found the landmark
  1247. if ( FClassnameIs( pentLandmark, "info_landmark" ) )
  1248. return pentLandmark;
  1249. else
  1250. pentLandmark = gEntList.FindEntityByName( pentLandmark, pLandmarkName );
  1251. }
  1252. Warning( "Can't find landmark %s\n", pLandmarkName );
  1253. return NULL;
  1254. }
  1255. //-----------------------------------------------------------------------------
  1256. // Purpose: Allows level transitions to be triggered by buttons, etc.
  1257. //-----------------------------------------------------------------------------
  1258. void CChangeLevel::InputChangeLevel( inputdata_t &inputdata )
  1259. {
  1260. // Ignore changelevel transitions if the player's dead or attempting a challenge
  1261. if ( gpGlobals->maxClients == 1 )
  1262. {
  1263. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  1264. if ( pPlayer && ( !pPlayer->IsAlive() || pPlayer->GetBonusChallenge() > 0 ) )
  1265. return;
  1266. }
  1267. ChangeLevelNow( inputdata.pActivator );
  1268. }
  1269. //-----------------------------------------------------------------------------
  1270. // Purpose: Performs the level change and fires targets.
  1271. // Input : pActivator -
  1272. //-----------------------------------------------------------------------------
  1273. bool CChangeLevel::IsEntityInTransition( CBaseEntity *pEntity )
  1274. {
  1275. int transitionState = InTransitionVolume(pEntity, m_szLandmarkName);
  1276. if ( transitionState == TRANSITION_VOLUME_SCREENED_OUT )
  1277. {
  1278. return false;
  1279. }
  1280. // look for a landmark entity
  1281. CBaseEntity *pLandmark = FindLandmark( m_szLandmarkName );
  1282. if ( !pLandmark )
  1283. return false;
  1284. // Check to make sure it's also in the PVS of landmark
  1285. byte pvs[MAX_MAP_CLUSTERS/8];
  1286. int clusterIndex = engine->GetClusterForOrigin( pLandmark->GetAbsOrigin() );
  1287. engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs );
  1288. Vector vecSurroundMins, vecSurroundMaxs;
  1289. pEntity->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  1290. return engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) );
  1291. }
  1292. void CChangeLevel::NotifyEntitiesOutOfTransition()
  1293. {
  1294. CBaseEntity *pEnt = gEntList.FirstEnt();
  1295. while ( pEnt )
  1296. {
  1297. // Found the landmark
  1298. if ( pEnt->ObjectCaps() & FCAP_NOTIFY_ON_TRANSITION )
  1299. {
  1300. variant_t emptyVariant;
  1301. if ( !(pEnt->ObjectCaps() & (FCAP_ACROSS_TRANSITION|FCAP_FORCE_TRANSITION)) || !IsEntityInTransition( pEnt ) )
  1302. {
  1303. pEnt->AcceptInput( "OutsideTransition", this, this, emptyVariant, 0 );
  1304. }
  1305. else
  1306. {
  1307. pEnt->AcceptInput( "InsideTransition", this, this, emptyVariant, 0 );
  1308. }
  1309. }
  1310. pEnt = gEntList.NextEnt( pEnt );
  1311. }
  1312. }
  1313. //------------------------------------------------------------------------------
  1314. // Purpose : Checks all spawned AIs and prints a warning if any are actively leading
  1315. // Input :
  1316. // Output :
  1317. //------------------------------------------------------------------------------
  1318. void CChangeLevel::WarnAboutActiveLead( void )
  1319. {
  1320. int i;
  1321. CAI_BaseNPC * ai;
  1322. CAI_BehaviorBase * behavior;
  1323. for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
  1324. {
  1325. ai = g_AI_Manager.AccessAIs()[i];
  1326. behavior = ai->GetRunningBehavior();
  1327. if ( behavior )
  1328. {
  1329. if ( dynamic_cast<CAI_LeadBehavior *>( behavior ) )
  1330. {
  1331. Warning( "Entity '%s' is still actively leading\n", STRING( ai->GetEntityName() ) );
  1332. }
  1333. }
  1334. }
  1335. }
  1336. void CChangeLevel::ChangeLevelNow( CBaseEntity *pActivator )
  1337. {
  1338. CBaseEntity *pLandmark;
  1339. levellist_t levels[16];
  1340. Assert(!FStrEq(m_szMapName, ""));
  1341. // Don't work in deathmatch
  1342. if ( g_pGameRules->IsDeathmatch() )
  1343. return;
  1344. // Some people are firing these multiple times in a frame, disable
  1345. if ( m_bTouched )
  1346. return;
  1347. m_bTouched = true;
  1348. CBaseEntity *pPlayer = (pActivator && pActivator->IsPlayer()) ? pActivator : UTIL_GetLocalPlayer();
  1349. int transitionState = InTransitionVolume(pPlayer, m_szLandmarkName);
  1350. if ( transitionState == TRANSITION_VOLUME_SCREENED_OUT )
  1351. {
  1352. DevMsg( 2, "Player isn't in the transition volume %s, aborting\n", m_szLandmarkName );
  1353. return;
  1354. }
  1355. // look for a landmark entity
  1356. pLandmark = FindLandmark( m_szLandmarkName );
  1357. if ( !pLandmark )
  1358. return;
  1359. // no transition volumes, check PVS of landmark
  1360. if ( transitionState == TRANSITION_VOLUME_NOT_FOUND )
  1361. {
  1362. byte pvs[MAX_MAP_CLUSTERS/8];
  1363. int clusterIndex = engine->GetClusterForOrigin( pLandmark->GetAbsOrigin() );
  1364. engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs );
  1365. if ( pPlayer )
  1366. {
  1367. Vector vecSurroundMins, vecSurroundMaxs;
  1368. pPlayer->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  1369. bool playerInPVS = engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) );
  1370. //Assert( playerInPVS );
  1371. if ( !playerInPVS )
  1372. {
  1373. Warning( "Player isn't in the landmark's (%s) PVS, aborting\n", m_szLandmarkName );
  1374. #ifndef HL1_DLL
  1375. // HL1 works even with these errors!
  1376. return;
  1377. #endif
  1378. }
  1379. }
  1380. }
  1381. WarnAboutActiveLead();
  1382. g_iDebuggingTransition = 0;
  1383. st_szNextSpot[0] = 0; // Init landmark to NULL
  1384. Q_strncpy(st_szNextSpot, m_szLandmarkName,sizeof(st_szNextSpot));
  1385. // This object will get removed in the call to engine->ChangeLevel, copy the params into "safe" memory
  1386. Q_strncpy(st_szNextMap, m_szMapName, sizeof(st_szNextMap));
  1387. m_hActivator = pActivator;
  1388. m_OnChangeLevel.FireOutput(pActivator, this);
  1389. NotifyEntitiesOutOfTransition();
  1390. //// Msg( "Level touches %d levels\n", ChangeList( levels, 16 ) );
  1391. if ( g_debug_transitions.GetInt() )
  1392. {
  1393. Msg( "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot );
  1394. }
  1395. // If we're debugging, don't actually change level
  1396. if ( g_debug_transitions.GetInt() == 0 )
  1397. {
  1398. engine->ChangeLevel( st_szNextMap, st_szNextSpot );
  1399. }
  1400. else
  1401. {
  1402. // Build a change list so we can see what would be transitioning
  1403. CSaveRestoreData *pSaveData = SaveInit( 0 );
  1404. if ( pSaveData )
  1405. {
  1406. g_pGameSaveRestoreBlockSet->PreSave( pSaveData );
  1407. pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS );
  1408. g_pGameSaveRestoreBlockSet->PostSave();
  1409. }
  1410. SetTouch( NULL );
  1411. }
  1412. }
  1413. //
  1414. // GLOBALS ASSUMED SET: st_szNextMap
  1415. //
  1416. void CChangeLevel::TouchChangeLevel( CBaseEntity *pOther )
  1417. {
  1418. CBasePlayer *pPlayer = ToBasePlayer(pOther);
  1419. if ( !pPlayer )
  1420. return;
  1421. if( pPlayer->IsSinglePlayerGameEnding() )
  1422. {
  1423. // Some semblance of deceleration, but allow player to fall normally.
  1424. // Also, disable controls.
  1425. Vector vecVelocity = pPlayer->GetAbsVelocity();
  1426. vecVelocity.x *= 0.5f;
  1427. vecVelocity.y *= 0.5f;
  1428. pPlayer->SetAbsVelocity( vecVelocity );
  1429. pPlayer->AddFlag( FL_FROZEN );
  1430. return;
  1431. }
  1432. if ( !pPlayer->IsInAVehicle() && pPlayer->GetMoveType() == MOVETYPE_NOCLIP )
  1433. {
  1434. DevMsg("In level transition: %s %s\n", st_szNextMap, st_szNextSpot );
  1435. return;
  1436. }
  1437. ChangeLevelNow( pOther );
  1438. }
  1439. // Add a transition to the list, but ignore duplicates
  1440. // (a designer may have placed multiple trigger_changelevels with the same landmark)
  1441. int CChangeLevel::AddTransitionToList( levellist_t *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark )
  1442. {
  1443. int i;
  1444. if ( !pLevelList || !pMapName || !pLandmarkName || !pentLandmark )
  1445. return 0;
  1446. // Ignore changelevels to the level we're ready in. Mapmakers love to do this!
  1447. if ( stricmp( pMapName, STRING(gpGlobals->mapname) ) == 0 )
  1448. return 0;
  1449. for ( i = 0; i < listCount; i++ )
  1450. {
  1451. if ( pLevelList[i].pentLandmark == pentLandmark && stricmp( pLevelList[i].mapName, pMapName ) == 0 )
  1452. return 0;
  1453. }
  1454. Q_strncpy( pLevelList[listCount].mapName, pMapName, sizeof(pLevelList[listCount].mapName) );
  1455. Q_strncpy( pLevelList[listCount].landmarkName, pLandmarkName, sizeof(pLevelList[listCount].landmarkName) );
  1456. pLevelList[listCount].pentLandmark = pentLandmark;
  1457. CBaseEntity *ent = CBaseEntity::Instance( pentLandmark );
  1458. Assert( ent );
  1459. pLevelList[listCount].vecLandmarkOrigin = ent->GetAbsOrigin();
  1460. return 1;
  1461. }
  1462. int BuildChangeList( levellist_t *pLevelList, int maxList )
  1463. {
  1464. return CChangeLevel::ChangeList( pLevelList, maxList );
  1465. }
  1466. struct collidelist_t
  1467. {
  1468. const CPhysCollide *pCollide;
  1469. Vector origin;
  1470. QAngle angles;
  1471. };
  1472. // NOTE: This routine is relatively slow. If you need to use it for per-frame work, consider that fact.
  1473. // UNDONE: Expand this to the full matrix of solid types on each side and move into enginetrace
  1474. static bool TestEntityTriggerIntersection_Accurate( CBaseEntity *pTrigger, CBaseEntity *pEntity )
  1475. {
  1476. Assert( pTrigger->GetSolid() == SOLID_BSP );
  1477. if ( pTrigger->Intersects( pEntity ) ) // It touches one, it's in the volume
  1478. {
  1479. switch ( pEntity->GetSolid() )
  1480. {
  1481. case SOLID_BBOX:
  1482. {
  1483. ICollideable *pCollide = pTrigger->CollisionProp();
  1484. Ray_t ray;
  1485. trace_t tr;
  1486. ray.Init( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin(), pEntity->WorldAlignMins(), pEntity->WorldAlignMaxs() );
  1487. enginetrace->ClipRayToCollideable( ray, MASK_ALL, pCollide, &tr );
  1488. if ( tr.startsolid )
  1489. return true;
  1490. }
  1491. break;
  1492. case SOLID_BSP:
  1493. case SOLID_VPHYSICS:
  1494. {
  1495. CPhysCollide *pTriggerCollide = modelinfo->GetVCollide( pTrigger->GetModelIndex() )->solids[0];
  1496. Assert( pTriggerCollide );
  1497. CUtlVector<collidelist_t> collideList;
  1498. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1499. int physicsCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1500. if ( physicsCount )
  1501. {
  1502. for ( int i = 0; i < physicsCount; i++ )
  1503. {
  1504. const CPhysCollide *pCollide = pList[i]->GetCollide();
  1505. if ( pCollide )
  1506. {
  1507. collidelist_t element;
  1508. element.pCollide = pCollide;
  1509. pList[i]->GetPosition( &element.origin, &element.angles );
  1510. collideList.AddToTail( element );
  1511. }
  1512. }
  1513. }
  1514. else
  1515. {
  1516. vcollide_t *pVCollide = modelinfo->GetVCollide( pEntity->GetModelIndex() );
  1517. if ( pVCollide && pVCollide->solidCount )
  1518. {
  1519. collidelist_t element;
  1520. element.pCollide = pVCollide->solids[0];
  1521. element.origin = pEntity->GetAbsOrigin();
  1522. element.angles = pEntity->GetAbsAngles();
  1523. collideList.AddToTail( element );
  1524. }
  1525. }
  1526. for ( int i = collideList.Count()-1; i >= 0; --i )
  1527. {
  1528. const collidelist_t &element = collideList[i];
  1529. trace_t tr;
  1530. physcollision->TraceCollide( element.origin, element.origin, element.pCollide, element.angles, pTriggerCollide, pTrigger->GetAbsOrigin(), pTrigger->GetAbsAngles(), &tr );
  1531. if ( tr.startsolid )
  1532. return true;
  1533. }
  1534. }
  1535. break;
  1536. default:
  1537. return true;
  1538. }
  1539. }
  1540. return false;
  1541. }
  1542. int CChangeLevel::InTransitionVolume( CBaseEntity *pEntity, const char *pVolumeName )
  1543. {
  1544. CBaseEntity *pVolume;
  1545. if ( pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION )
  1546. return TRANSITION_VOLUME_PASSED;
  1547. // If you're following another entity, follow it through the transition (weapons follow the player)
  1548. pEntity = pEntity->GetRootMoveParent();
  1549. int inVolume = TRANSITION_VOLUME_NOT_FOUND; // Unless we find a trigger_transition, everything is in the volume
  1550. pVolume = gEntList.FindEntityByName( NULL, pVolumeName );
  1551. while ( pVolume )
  1552. {
  1553. if ( pVolume && FClassnameIs( pVolume, "trigger_transition" ) )
  1554. {
  1555. if ( TestEntityTriggerIntersection_Accurate(pVolume, pEntity ) ) // It touches one, it's in the volume
  1556. return TRANSITION_VOLUME_PASSED;
  1557. inVolume = TRANSITION_VOLUME_SCREENED_OUT; // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go!
  1558. }
  1559. pVolume = gEntList.FindEntityByName( pVolume, pVolumeName );
  1560. }
  1561. return inVolume;
  1562. }
  1563. //------------------------------------------------------------------------------
  1564. // Builds the list of entities to save when moving across a transition
  1565. //------------------------------------------------------------------------------
  1566. int CChangeLevel::BuildChangeLevelList( levellist_t *pLevelList, int maxList )
  1567. {
  1568. int nCount = 0;
  1569. CBaseEntity *pentChangelevel = gEntList.FindEntityByClassname( NULL, "trigger_changelevel" );
  1570. while ( pentChangelevel )
  1571. {
  1572. CChangeLevel *pTrigger = dynamic_cast<CChangeLevel *>(pentChangelevel);
  1573. if ( pTrigger )
  1574. {
  1575. // Find the corresponding landmark
  1576. CBaseEntity *pentLandmark = FindLandmark( pTrigger->m_szLandmarkName );
  1577. if ( pentLandmark )
  1578. {
  1579. // Build a list of unique transitions
  1580. if ( AddTransitionToList( pLevelList, nCount, pTrigger->m_szMapName, pTrigger->m_szLandmarkName, pentLandmark->edict() ) )
  1581. {
  1582. ++nCount;
  1583. if ( nCount >= maxList ) // FULL!!
  1584. break;
  1585. }
  1586. }
  1587. }
  1588. pentChangelevel = gEntList.FindEntityByClassname( pentChangelevel, "trigger_changelevel" );
  1589. }
  1590. return nCount;
  1591. }
  1592. //------------------------------------------------------------------------------
  1593. // Adds a single entity to the transition list, if appropriate. Returns the new count
  1594. //------------------------------------------------------------------------------
  1595. int CChangeLevel::ComputeEntitySaveFlags( CBaseEntity *pEntity )
  1596. {
  1597. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1598. {
  1599. Msg( "Trying %s (%s): ", pEntity->GetClassname(), pEntity->GetDebugName() );
  1600. }
  1601. int caps = pEntity->ObjectCaps();
  1602. if ( caps & FCAP_DONT_SAVE )
  1603. {
  1604. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1605. {
  1606. Msg( "IGNORED due to being marked \"Don't save\".\n" );
  1607. }
  1608. return 0;
  1609. }
  1610. // If this entity can be moved or is global, mark it
  1611. int flags = 0;
  1612. if ( caps & FCAP_ACROSS_TRANSITION )
  1613. {
  1614. flags |= FENTTABLE_MOVEABLE;
  1615. }
  1616. if ( pEntity->m_iGlobalname != NULL_STRING && !pEntity->IsDormant() )
  1617. {
  1618. flags |= FENTTABLE_GLOBAL;
  1619. }
  1620. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE && !flags )
  1621. {
  1622. Msg( "IGNORED, no across_transition flag & no globalname\n" );
  1623. }
  1624. return flags;
  1625. }
  1626. //------------------------------------------------------------------------------
  1627. // Adds a single entity to the transition list, if appropriate. Returns the new count
  1628. //------------------------------------------------------------------------------
  1629. inline int CChangeLevel::AddEntityToTransitionList( CBaseEntity *pEntity, int flags, int nCount, CBaseEntity **ppEntList, int *pEntityFlags )
  1630. {
  1631. ppEntList[ nCount ] = pEntity;
  1632. pEntityFlags[ nCount ] = flags;
  1633. ++nCount;
  1634. // If we're debugging, make it visible
  1635. if ( g_iDebuggingTransition )
  1636. {
  1637. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1638. {
  1639. // In verbose mode we've already printed out what the entity is
  1640. Msg("ADDED.\n");
  1641. }
  1642. else
  1643. {
  1644. // In non-verbose mode, we just print this line
  1645. Msg( "ADDED %s (%s) to transition.\n", pEntity->GetClassname(), pEntity->GetDebugName() );
  1646. }
  1647. pEntity->m_debugOverlays |= (OVERLAY_BBOX_BIT | OVERLAY_NAME_BIT);
  1648. }
  1649. return nCount;
  1650. }
  1651. //------------------------------------------------------------------------------
  1652. // Builds the list of entities to bring across a particular transition
  1653. //------------------------------------------------------------------------------
  1654. int CChangeLevel::BuildEntityTransitionList( CBaseEntity *pLandmarkEntity, const char *pLandmarkName,
  1655. CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList )
  1656. {
  1657. int iEntity = 0;
  1658. // Only show debug for the transition to the level we're going to
  1659. if ( g_debug_transitions.GetInt() && pLandmarkEntity->NameMatches(st_szNextSpot) )
  1660. {
  1661. g_iDebuggingTransition = g_debug_transitions.GetInt();
  1662. // Show us where the landmark entity is
  1663. pLandmarkEntity->m_debugOverlays |= (OVERLAY_PIVOT_BIT | OVERLAY_BBOX_BIT | OVERLAY_NAME_BIT);
  1664. }
  1665. else
  1666. {
  1667. g_iDebuggingTransition = 0;
  1668. }
  1669. // Follow the linked list of entities in the PVS of the transition landmark
  1670. CBaseEntity *pEntity = NULL;
  1671. while ( (pEntity = UTIL_EntitiesInPVS( pLandmarkEntity, pEntity)) != NULL )
  1672. {
  1673. int flags = ComputeEntitySaveFlags( pEntity );
  1674. if ( !flags )
  1675. continue;
  1676. // Check to make sure the entity isn't screened out by a trigger_transition
  1677. if ( !InTransitionVolume( pEntity, pLandmarkName ) )
  1678. {
  1679. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1680. {
  1681. Msg( "IGNORED, outside transition volume.\n" );
  1682. }
  1683. continue;
  1684. }
  1685. if ( iEntity >= nMaxList )
  1686. {
  1687. Warning( "Too many entities across a transition!\n" );
  1688. Assert( 0 );
  1689. return iEntity;
  1690. }
  1691. iEntity = AddEntityToTransitionList( pEntity, flags, iEntity, ppEntList, pEntityFlags );
  1692. }
  1693. return iEntity;
  1694. }
  1695. //------------------------------------------------------------------------------
  1696. // Tests bits in a bitfield
  1697. //------------------------------------------------------------------------------
  1698. static inline bool IsBitSet( char *pBuf, int nBit )
  1699. {
  1700. return (pBuf[ nBit >> 3 ] & ( 1 << (nBit & 0x7) )) != 0;
  1701. }
  1702. static inline void Set( char *pBuf, int nBit )
  1703. {
  1704. pBuf[ nBit >> 3 ] |= 1 << (nBit & 0x7);
  1705. }
  1706. //------------------------------------------------------------------------------
  1707. // Adds in all entities depended on by entities near the transition
  1708. //------------------------------------------------------------------------------
  1709. #define MAX_ENTITY_BYTE_COUNT (NUM_ENT_ENTRIES >> 3)
  1710. int CChangeLevel::AddDependentEntities( int nCount, CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList )
  1711. {
  1712. char pEntitiesSaved[MAX_ENTITY_BYTE_COUNT];
  1713. memset( pEntitiesSaved, 0, MAX_ENTITY_BYTE_COUNT * sizeof(char) );
  1714. // Populate the initial bitfield
  1715. int i;
  1716. for ( i = 0; i < nCount; ++i )
  1717. {
  1718. // NOTE: Must use GetEntryIndex because we're saving non-networked entities
  1719. int nEntIndex = ppEntList[i]->GetRefEHandle().GetEntryIndex();
  1720. // We shouldn't already have this entity in the list!
  1721. Assert( !IsBitSet( pEntitiesSaved, nEntIndex ) );
  1722. // Mark the entity as being in the list
  1723. Set( pEntitiesSaved, nEntIndex );
  1724. }
  1725. IEntitySaveUtils *pSaveUtils = GetEntitySaveUtils();
  1726. // Iterate over entities whose dependencies we've not yet processed
  1727. // NOTE: nCount will change value during this loop in AddEntityToTransitionList
  1728. for ( i = 0; i < nCount; ++i )
  1729. {
  1730. CBaseEntity *pEntity = ppEntList[i];
  1731. // Find dependencies in the hash.
  1732. int nDepCount = pSaveUtils->GetEntityDependencyCount( pEntity );
  1733. if ( !nDepCount )
  1734. continue;
  1735. CBaseEntity **ppDependentEntities = (CBaseEntity**)stackalloc( nDepCount * sizeof(CBaseEntity*) );
  1736. pSaveUtils->GetEntityDependencies( pEntity, nDepCount, ppDependentEntities );
  1737. for ( int j = 0; j < nDepCount; ++j )
  1738. {
  1739. CBaseEntity *pDependent = ppDependentEntities[j];
  1740. if ( !pDependent )
  1741. continue;
  1742. // NOTE: Must use GetEntryIndex because we're saving non-networked entities
  1743. int nEntIndex = pDependent->GetRefEHandle().GetEntryIndex();
  1744. // Don't re-add it if it's already in the list
  1745. if ( IsBitSet( pEntitiesSaved, nEntIndex ) )
  1746. continue;
  1747. // Mark the entity as being in the list
  1748. Set( pEntitiesSaved, nEntIndex );
  1749. int flags = ComputeEntitySaveFlags( pEntity );
  1750. if ( flags )
  1751. {
  1752. if ( nCount >= nMaxList )
  1753. {
  1754. Warning( "Too many entities across a transition!\n" );
  1755. Assert( 0 );
  1756. return false;
  1757. }
  1758. if ( g_debug_transitions.GetInt() )
  1759. {
  1760. Msg( "ADDED DEPENDANCY: %s (%s)\n", pEntity->GetClassname(), pEntity->GetDebugName() );
  1761. }
  1762. nCount = AddEntityToTransitionList( pEntity, flags, nCount, ppEntList, pEntityFlags );
  1763. }
  1764. else
  1765. {
  1766. Warning("Warning!! Save dependency is linked to an entity that doesn't want to be saved!\n");
  1767. }
  1768. }
  1769. }
  1770. return nCount;
  1771. }
  1772. //------------------------------------------------------------------------------
  1773. // This builds the list of all transitions on this level and which entities
  1774. // are in their PVS's and can / should be moved across.
  1775. //------------------------------------------------------------------------------
  1776. // We can only ever move 512 entities across a transition
  1777. #define MAX_ENTITY 512
  1778. // FIXME: This has grown into a complicated beast. Can we make this more elegant?
  1779. int CChangeLevel::ChangeList( levellist_t *pLevelList, int maxList )
  1780. {
  1781. // Find all of the possible level changes on this BSP
  1782. int count = BuildChangeLevelList( pLevelList, maxList );
  1783. if ( !gpGlobals->pSaveData || ( static_cast<CSaveRestoreData *>(gpGlobals->pSaveData)->NumEntities() == 0 ) )
  1784. return count;
  1785. CSave saveHelper( static_cast<CSaveRestoreData *>(gpGlobals->pSaveData) );
  1786. // For each level change, find nearby entities and save them
  1787. int i;
  1788. for ( i = 0; i < count; i++ )
  1789. {
  1790. CBaseEntity *pEntList[ MAX_ENTITY ];
  1791. int entityFlags[ MAX_ENTITY ];
  1792. // First, figure out which entities are near the transition
  1793. CBaseEntity *pLandmarkEntity = CBaseEntity::Instance( pLevelList[i].pentLandmark );
  1794. int iEntity = BuildEntityTransitionList( pLandmarkEntity, pLevelList[i].landmarkName, pEntList, entityFlags, MAX_ENTITY );
  1795. // FIXME: Activate if we have a dependency problem on level transition
  1796. // Next, add in all entities depended on by entities near the transition
  1797. // iEntity = AddDependentEntities( iEntity, pEntList, entityFlags, MAX_ENTITY );
  1798. int j;
  1799. for ( j = 0; j < iEntity; j++ )
  1800. {
  1801. // Mark entity table with 1<<i
  1802. int index = saveHelper.EntityIndex( pEntList[j] );
  1803. // Flag it with the level number
  1804. saveHelper.EntityFlagsSet( index, entityFlags[j] | (1<<i) );
  1805. }
  1806. }
  1807. return count;
  1808. }
  1809. //-----------------------------------------------------------------------------
  1810. // Purpose: A trigger that pushes the player, NPCs, or objects.
  1811. //-----------------------------------------------------------------------------
  1812. class CTriggerPush : public CBaseTrigger
  1813. {
  1814. public:
  1815. DECLARE_CLASS( CTriggerPush, CBaseTrigger );
  1816. void Spawn( void );
  1817. void Activate( void );
  1818. void Touch( CBaseEntity *pOther );
  1819. void Untouch( CBaseEntity *pOther );
  1820. Vector m_vecPushDir;
  1821. DECLARE_DATADESC();
  1822. float m_flAlternateTicksFix; // Scale factor to apply to the push speed when running with alternate ticks
  1823. float m_flPushSpeed;
  1824. };
  1825. BEGIN_DATADESC( CTriggerPush )
  1826. DEFINE_KEYFIELD( m_vecPushDir, FIELD_VECTOR, "pushdir" ),
  1827. DEFINE_KEYFIELD( m_flAlternateTicksFix, FIELD_FLOAT, "alternateticksfix" ),
  1828. //DEFINE_FIELD( m_flPushSpeed, FIELD_FLOAT ),
  1829. END_DATADESC()
  1830. LINK_ENTITY_TO_CLASS( trigger_push, CTriggerPush );
  1831. //-----------------------------------------------------------------------------
  1832. // Purpose: Called when spawning, after keyvalues have been handled.
  1833. //-----------------------------------------------------------------------------
  1834. void CTriggerPush::Spawn()
  1835. {
  1836. // Convert pushdir from angles to a vector
  1837. Vector vecAbsDir;
  1838. QAngle angPushDir = QAngle(m_vecPushDir.x, m_vecPushDir.y, m_vecPushDir.z);
  1839. AngleVectors(angPushDir, &vecAbsDir);
  1840. // Transform the vector into entity space
  1841. VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecPushDir );
  1842. BaseClass::Spawn();
  1843. InitTrigger();
  1844. if (m_flSpeed == 0)
  1845. {
  1846. m_flSpeed = 100;
  1847. }
  1848. }
  1849. //-----------------------------------------------------------------------------
  1850. //-----------------------------------------------------------------------------
  1851. void CTriggerPush::Activate()
  1852. {
  1853. // Fix problems with triggers pushing too hard under sv_alternateticks.
  1854. // This is somewhat hacky, but it's simple and we're really close to shipping.
  1855. ConVarRef sv_alternateticks( "sv_alternateticks" );
  1856. if ( ( m_flAlternateTicksFix != 0 ) && sv_alternateticks.GetBool() )
  1857. {
  1858. m_flPushSpeed = m_flSpeed * m_flAlternateTicksFix;
  1859. }
  1860. else
  1861. {
  1862. m_flPushSpeed = m_flSpeed;
  1863. }
  1864. BaseClass::Activate();
  1865. }
  1866. //-----------------------------------------------------------------------------
  1867. // Purpose:
  1868. // Input : *pOther -
  1869. //-----------------------------------------------------------------------------
  1870. void CTriggerPush::Touch( CBaseEntity *pOther )
  1871. {
  1872. if ( !pOther->IsSolid() || (pOther->GetMoveType() == MOVETYPE_PUSH || pOther->GetMoveType() == MOVETYPE_NONE ) )
  1873. return;
  1874. if (!PassesTriggerFilters(pOther))
  1875. return;
  1876. // FIXME: If something is hierarchically attached, should we try to push the parent?
  1877. if (pOther->GetMoveParent())
  1878. return;
  1879. // Transform the push dir into global space
  1880. Vector vecAbsDir;
  1881. VectorRotate( m_vecPushDir, EntityToWorldTransform(), vecAbsDir );
  1882. // Instant trigger, just transfer velocity and remove
  1883. if (HasSpawnFlags(SF_TRIG_PUSH_ONCE))
  1884. {
  1885. pOther->ApplyAbsVelocityImpulse( m_flPushSpeed * vecAbsDir );
  1886. if ( vecAbsDir.z > 0 )
  1887. {
  1888. pOther->SetGroundEntity( NULL );
  1889. }
  1890. UTIL_Remove( this );
  1891. return;
  1892. }
  1893. switch( pOther->GetMoveType() )
  1894. {
  1895. case MOVETYPE_NONE:
  1896. case MOVETYPE_PUSH:
  1897. case MOVETYPE_NOCLIP:
  1898. break;
  1899. case MOVETYPE_VPHYSICS:
  1900. {
  1901. IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
  1902. if ( pPhys )
  1903. {
  1904. // UNDONE: Assume the velocity is for a 100kg object, scale with mass
  1905. pPhys->ApplyForceCenter( m_flPushSpeed * vecAbsDir * 100.0f * gpGlobals->frametime );
  1906. return;
  1907. }
  1908. }
  1909. break;
  1910. default:
  1911. {
  1912. #if defined( HL2_DLL )
  1913. // HACK HACK HL2 players on ladders will only be disengaged if the sf is set, otherwise no push occurs.
  1914. if ( pOther->IsPlayer() &&
  1915. pOther->GetMoveType() == MOVETYPE_LADDER )
  1916. {
  1917. if ( !HasSpawnFlags(SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER) )
  1918. {
  1919. // Ignore the push
  1920. return;
  1921. }
  1922. }
  1923. #endif
  1924. Vector vecPush = (m_flPushSpeed * vecAbsDir);
  1925. if ( ( pOther->GetFlags() & FL_BASEVELOCITY ) && !lagcompensation->IsCurrentlyDoingLagCompensation() )
  1926. {
  1927. vecPush = vecPush + pOther->GetBaseVelocity();
  1928. }
  1929. if ( vecPush.z > 0 && (pOther->GetFlags() & FL_ONGROUND) )
  1930. {
  1931. pOther->SetGroundEntity( NULL );
  1932. Vector origin = pOther->GetAbsOrigin();
  1933. origin.z += 1.0f;
  1934. pOther->SetAbsOrigin( origin );
  1935. }
  1936. #ifdef HL1_DLL
  1937. // Apply the z velocity as a force so it counteracts gravity properly
  1938. Vector vecImpulse( 0, 0, vecPush.z * 0.025 );//magic hack number
  1939. pOther->ApplyAbsVelocityImpulse( vecImpulse );
  1940. // apply x, y as a base velocity so we travel at constant speed on conveyors
  1941. vecPush.z = 0;
  1942. #endif
  1943. pOther->SetBaseVelocity( vecPush );
  1944. pOther->AddFlag( FL_BASEVELOCITY );
  1945. }
  1946. break;
  1947. }
  1948. }
  1949. //-----------------------------------------------------------------------------
  1950. // Teleport trigger
  1951. //-----------------------------------------------------------------------------
  1952. const int SF_TELEPORT_PRESERVE_ANGLES = 0x20; // Preserve angles even when a local landmark is not specified
  1953. class CTriggerTeleport : public CBaseTrigger
  1954. {
  1955. public:
  1956. DECLARE_CLASS( CTriggerTeleport, CBaseTrigger );
  1957. virtual void Spawn( void ) OVERRIDE;
  1958. virtual void Touch( CBaseEntity *pOther ) OVERRIDE;
  1959. string_t m_iLandmark;
  1960. DECLARE_DATADESC();
  1961. };
  1962. LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport );
  1963. BEGIN_DATADESC( CTriggerTeleport )
  1964. DEFINE_KEYFIELD( m_iLandmark, FIELD_STRING, "landmark" ),
  1965. END_DATADESC()
  1966. void CTriggerTeleport::Spawn( void )
  1967. {
  1968. InitTrigger();
  1969. }
  1970. //-----------------------------------------------------------------------------
  1971. // Purpose: Teleports the entity that touched us to the location of our target,
  1972. // setting the toucher's angles to our target's angles if they are a
  1973. // player.
  1974. //
  1975. // If a landmark was specified, the toucher is offset from the target
  1976. // by their initial offset from the landmark and their angles are
  1977. // left alone.
  1978. //
  1979. // Input : pOther - The entity that touched us.
  1980. //-----------------------------------------------------------------------------
  1981. void CTriggerTeleport::Touch( CBaseEntity *pOther )
  1982. {
  1983. CBaseEntity *pentTarget = NULL;
  1984. if (!PassesTriggerFilters(pOther))
  1985. {
  1986. return;
  1987. }
  1988. // The activator and caller are the same
  1989. pentTarget = gEntList.FindEntityByName( pentTarget, m_target, NULL, pOther, pOther );
  1990. if (!pentTarget)
  1991. {
  1992. return;
  1993. }
  1994. //
  1995. // If a landmark was specified, offset the player relative to the landmark.
  1996. //
  1997. CBaseEntity *pentLandmark = NULL;
  1998. Vector vecLandmarkOffset(0, 0, 0);
  1999. if (m_iLandmark != NULL_STRING)
  2000. {
  2001. // The activator and caller are the same
  2002. pentLandmark = gEntList.FindEntityByName(pentLandmark, m_iLandmark, NULL, pOther, pOther );
  2003. if (pentLandmark)
  2004. {
  2005. vecLandmarkOffset = pOther->GetAbsOrigin() - pentLandmark->GetAbsOrigin();
  2006. }
  2007. }
  2008. pOther->SetGroundEntity( NULL );
  2009. Vector tmp = pentTarget->GetAbsOrigin();
  2010. if (!pentLandmark && pOther->IsPlayer())
  2011. {
  2012. // make origin adjustments in case the teleportee is a player. (origin in center, not at feet)
  2013. tmp.z -= pOther->WorldAlignMins().z;
  2014. }
  2015. //
  2016. // Only modify the toucher's angles and zero their velocity if no landmark was specified.
  2017. //
  2018. const QAngle *pAngles = NULL;
  2019. Vector *pVelocity = NULL;
  2020. #ifdef HL1_DLL
  2021. Vector vecZero(0,0,0);
  2022. #endif
  2023. if (!pentLandmark && !HasSpawnFlags(SF_TELEPORT_PRESERVE_ANGLES) )
  2024. {
  2025. pAngles = &pentTarget->GetAbsAngles();
  2026. #ifdef HL1_DLL
  2027. pVelocity = &vecZero;
  2028. #else
  2029. pVelocity = NULL; //BUGBUG - This does not set the player's velocity to zero!!!
  2030. #endif
  2031. }
  2032. tmp += vecLandmarkOffset;
  2033. pOther->Teleport( &tmp, pAngles, pVelocity );
  2034. }
  2035. LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity );
  2036. //-----------------------------------------------------------------------------
  2037. // Teleport Relative trigger
  2038. //-----------------------------------------------------------------------------
  2039. class CTriggerTeleportRelative : public CBaseTrigger
  2040. {
  2041. public:
  2042. DECLARE_CLASS(CTriggerTeleportRelative, CBaseTrigger);
  2043. virtual void Spawn( void ) OVERRIDE;
  2044. virtual void Touch( CBaseEntity *pOther ) OVERRIDE;
  2045. Vector m_TeleportOffset;
  2046. DECLARE_DATADESC();
  2047. };
  2048. LINK_ENTITY_TO_CLASS( trigger_teleport_relative, CTriggerTeleportRelative );
  2049. BEGIN_DATADESC( CTriggerTeleportRelative )
  2050. DEFINE_KEYFIELD( m_TeleportOffset, FIELD_VECTOR, "teleportoffset" )
  2051. END_DATADESC()
  2052. void CTriggerTeleportRelative::Spawn( void )
  2053. {
  2054. InitTrigger();
  2055. }
  2056. void CTriggerTeleportRelative::Touch( CBaseEntity *pOther )
  2057. {
  2058. if ( !PassesTriggerFilters(pOther) )
  2059. {
  2060. return;
  2061. }
  2062. const Vector finalPos = m_TeleportOffset + WorldSpaceCenter();
  2063. const Vector *momentum = &vec3_origin;
  2064. pOther->Teleport( &finalPos, NULL, momentum );
  2065. }
  2066. //-----------------------------------------------------------------------------
  2067. // Purpose: Saves the game when the player touches the trigger. Can be enabled or disabled
  2068. //-----------------------------------------------------------------------------
  2069. class CTriggerToggleSave : public CBaseTrigger
  2070. {
  2071. public:
  2072. DECLARE_CLASS( CTriggerToggleSave, CBaseTrigger );
  2073. void Spawn( void );
  2074. void Touch( CBaseEntity *pOther );
  2075. void InputEnable( inputdata_t &inputdata )
  2076. {
  2077. m_bDisabled = false;
  2078. }
  2079. void InputDisable( inputdata_t &inputdata )
  2080. {
  2081. m_bDisabled = true;
  2082. }
  2083. bool m_bDisabled; // Initial state
  2084. DECLARE_DATADESC();
  2085. };
  2086. BEGIN_DATADESC( CTriggerToggleSave )
  2087. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  2088. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  2089. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  2090. END_DATADESC()
  2091. LINK_ENTITY_TO_CLASS( trigger_togglesave, CTriggerToggleSave );
  2092. //-----------------------------------------------------------------------------
  2093. // Purpose: Called when spawning, after keyvalues have been set.
  2094. //-----------------------------------------------------------------------------
  2095. void CTriggerToggleSave::Spawn( void )
  2096. {
  2097. if ( g_pGameRules->IsDeathmatch() )
  2098. {
  2099. UTIL_Remove( this );
  2100. return;
  2101. }
  2102. InitTrigger();
  2103. }
  2104. //-----------------------------------------------------------------------------
  2105. // Purpose: Performs the autosave when the player touches us.
  2106. // Input : pOther -
  2107. //-----------------------------------------------------------------------------
  2108. void CTriggerToggleSave::Touch( CBaseEntity *pOther )
  2109. {
  2110. if( m_bDisabled )
  2111. return;
  2112. // Only save on clients
  2113. if ( !pOther->IsPlayer() )
  2114. return;
  2115. // Can be re-enabled
  2116. m_bDisabled = true;
  2117. engine->ServerCommand( "autosave\n" );
  2118. }
  2119. //-----------------------------------------------------------------------------
  2120. // Purpose: Saves the game when the player touches the trigger.
  2121. //-----------------------------------------------------------------------------
  2122. class CTriggerSave : public CBaseTrigger
  2123. {
  2124. public:
  2125. DECLARE_CLASS( CTriggerSave, CBaseTrigger );
  2126. void Spawn( void );
  2127. void Touch( CBaseEntity *pOther );
  2128. DECLARE_DATADESC();
  2129. bool m_bForceNewLevelUnit;
  2130. float m_fDangerousTimer;
  2131. int m_minHitPoints;
  2132. };
  2133. BEGIN_DATADESC( CTriggerSave )
  2134. DEFINE_KEYFIELD( m_bForceNewLevelUnit, FIELD_BOOLEAN, "NewLevelUnit" ),
  2135. DEFINE_KEYFIELD( m_minHitPoints, FIELD_INTEGER, "MinimumHitPoints" ),
  2136. DEFINE_KEYFIELD( m_fDangerousTimer, FIELD_FLOAT, "DangerousTimer" ),
  2137. END_DATADESC()
  2138. LINK_ENTITY_TO_CLASS( trigger_autosave, CTriggerSave );
  2139. //-----------------------------------------------------------------------------
  2140. // Purpose: Called when spawning, after keyvalues have been set.
  2141. //-----------------------------------------------------------------------------
  2142. void CTriggerSave::Spawn( void )
  2143. {
  2144. if ( g_pGameRules->IsDeathmatch() )
  2145. {
  2146. UTIL_Remove( this );
  2147. return;
  2148. }
  2149. InitTrigger();
  2150. }
  2151. //-----------------------------------------------------------------------------
  2152. // Purpose: Performs the autosave when the player touches us.
  2153. // Input : pOther -
  2154. //-----------------------------------------------------------------------------
  2155. void CTriggerSave::Touch( CBaseEntity *pOther )
  2156. {
  2157. // Only save on clients
  2158. if ( !pOther->IsPlayer() )
  2159. return;
  2160. if ( m_fDangerousTimer != 0.0f )
  2161. {
  2162. if ( g_ServerGameDLL.m_fAutoSaveDangerousTime != 0.0f && g_ServerGameDLL.m_fAutoSaveDangerousTime >= gpGlobals->curtime )
  2163. {
  2164. // A previous dangerous auto save was waiting to become safe
  2165. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  2166. if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
  2167. {
  2168. // The player isn't dead, so make the dangerous auto save safe
  2169. engine->ServerCommand( "autosavedangerousissafe\n" );
  2170. }
  2171. }
  2172. }
  2173. // this is a one-way transition - there is no way to return to the previous map.
  2174. if ( m_bForceNewLevelUnit )
  2175. {
  2176. engine->ClearSaveDir();
  2177. }
  2178. UTIL_Remove( this );
  2179. if ( m_fDangerousTimer != 0.0f )
  2180. {
  2181. // There's a dangerous timer. Save if we have enough hitpoints.
  2182. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  2183. if (pPlayer && pPlayer->GetHealth() >= m_minHitPoints)
  2184. {
  2185. engine->ServerCommand( "autosavedangerous\n" );
  2186. g_ServerGameDLL.m_fAutoSaveDangerousTime = gpGlobals->curtime + m_fDangerousTimer;
  2187. }
  2188. }
  2189. else
  2190. {
  2191. engine->ServerCommand( "autosave\n" );
  2192. }
  2193. }
  2194. class CTriggerGravity : public CBaseTrigger
  2195. {
  2196. public:
  2197. DECLARE_CLASS( CTriggerGravity, CBaseTrigger );
  2198. DECLARE_DATADESC();
  2199. void Spawn( void );
  2200. void GravityTouch( CBaseEntity *pOther );
  2201. };
  2202. LINK_ENTITY_TO_CLASS( trigger_gravity, CTriggerGravity );
  2203. BEGIN_DATADESC( CTriggerGravity )
  2204. // Function Pointers
  2205. DEFINE_FUNCTION(GravityTouch),
  2206. END_DATADESC()
  2207. void CTriggerGravity::Spawn( void )
  2208. {
  2209. BaseClass::Spawn();
  2210. InitTrigger();
  2211. SetTouch( &CTriggerGravity::GravityTouch );
  2212. }
  2213. void CTriggerGravity::GravityTouch( CBaseEntity *pOther )
  2214. {
  2215. // Only save on clients
  2216. if ( !pOther->IsPlayer() )
  2217. return;
  2218. pOther->SetGravity( GetGravity() );
  2219. }
  2220. // this is a really bad idea.
  2221. class CAI_ChangeTarget : public CBaseEntity
  2222. {
  2223. public:
  2224. DECLARE_CLASS( CAI_ChangeTarget, CBaseEntity );
  2225. // Input handlers.
  2226. void InputActivate( inputdata_t &inputdata );
  2227. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  2228. DECLARE_DATADESC();
  2229. private:
  2230. string_t m_iszNewTarget;
  2231. };
  2232. LINK_ENTITY_TO_CLASS( ai_changetarget, CAI_ChangeTarget );
  2233. BEGIN_DATADESC( CAI_ChangeTarget )
  2234. DEFINE_KEYFIELD( m_iszNewTarget, FIELD_STRING, "m_iszNewTarget" ),
  2235. // Inputs
  2236. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  2237. END_DATADESC()
  2238. void CAI_ChangeTarget::InputActivate( inputdata_t &inputdata )
  2239. {
  2240. CBaseEntity *pTarget = NULL;
  2241. while ((pTarget = gEntList.FindEntityByName( pTarget, m_target, NULL, inputdata.pActivator, inputdata.pCaller )) != NULL)
  2242. {
  2243. pTarget->m_target = m_iszNewTarget;
  2244. CAI_BaseNPC *pNPC = pTarget->MyNPCPointer( );
  2245. if (pNPC)
  2246. {
  2247. pNPC->SetGoalEnt( NULL );
  2248. }
  2249. }
  2250. }
  2251. //-----------------------------------------------------------------------------
  2252. // Purpose: Change an NPC's hint group to something new
  2253. //-----------------------------------------------------------------------------
  2254. class CAI_ChangeHintGroup : public CBaseEntity
  2255. {
  2256. public:
  2257. DECLARE_CLASS( CAI_ChangeHintGroup, CBaseEntity );
  2258. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  2259. // Input handlers.
  2260. void InputActivate( inputdata_t &inputdata );
  2261. DECLARE_DATADESC();
  2262. private:
  2263. CAI_BaseNPC *FindQualifiedNPC( CAI_BaseNPC *pPrev, CBaseEntity *pActivator, CBaseEntity *pCaller );
  2264. int m_iSearchType;
  2265. string_t m_strSearchName;
  2266. string_t m_strNewHintGroup;
  2267. float m_flRadius;
  2268. bool m_bHintGroupNavLimiting;
  2269. };
  2270. LINK_ENTITY_TO_CLASS( ai_changehintgroup, CAI_ChangeHintGroup );
  2271. BEGIN_DATADESC( CAI_ChangeHintGroup )
  2272. DEFINE_KEYFIELD( m_iSearchType, FIELD_INTEGER, "SearchType" ),
  2273. DEFINE_KEYFIELD( m_strSearchName, FIELD_STRING, "SearchName" ),
  2274. DEFINE_KEYFIELD( m_strNewHintGroup, FIELD_STRING, "NewHintGroup" ),
  2275. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "Radius" ),
  2276. DEFINE_KEYFIELD( m_bHintGroupNavLimiting, FIELD_BOOLEAN, "hintlimiting" ),
  2277. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  2278. END_DATADESC()
  2279. CAI_BaseNPC *CAI_ChangeHintGroup::FindQualifiedNPC( CAI_BaseNPC *pPrev, CBaseEntity *pActivator, CBaseEntity *pCaller )
  2280. {
  2281. CBaseEntity *pEntity = pPrev;
  2282. CAI_BaseNPC *pResult = NULL;
  2283. const char *pszSearchName = STRING(m_strSearchName);
  2284. while ( !pResult )
  2285. {
  2286. // Find a candidate
  2287. switch ( m_iSearchType )
  2288. {
  2289. case 0:
  2290. {
  2291. pEntity = gEntList.FindEntityByNameWithin( pEntity, pszSearchName, GetLocalOrigin(), m_flRadius, NULL, pActivator, pCaller );
  2292. break;
  2293. }
  2294. case 1:
  2295. {
  2296. pEntity = gEntList.FindEntityByClassnameWithin( pEntity, pszSearchName, GetLocalOrigin(), m_flRadius );
  2297. break;
  2298. }
  2299. case 2:
  2300. {
  2301. pEntity = gEntList.FindEntityInSphere( pEntity, GetLocalOrigin(), ( m_flRadius != 0.0 ) ? m_flRadius : FLT_MAX );
  2302. break;
  2303. }
  2304. }
  2305. if ( !pEntity )
  2306. return NULL;
  2307. // Qualify
  2308. pResult = pEntity->MyNPCPointer();
  2309. if ( pResult && m_iSearchType == 2 && (!FStrEq( STRING(pResult->GetHintGroup()), pszSearchName ) ) )
  2310. {
  2311. pResult = NULL;
  2312. }
  2313. }
  2314. return pResult;
  2315. }
  2316. void CAI_ChangeHintGroup::InputActivate( inputdata_t &inputdata )
  2317. {
  2318. CAI_BaseNPC *pTarget = NULL;
  2319. while((pTarget = FindQualifiedNPC( pTarget, inputdata.pActivator, inputdata.pCaller )) != NULL)
  2320. {
  2321. pTarget->SetHintGroup( m_strNewHintGroup, m_bHintGroupNavLimiting );
  2322. }
  2323. }
  2324. #define SF_CAMERA_PLAYER_POSITION 1
  2325. #define SF_CAMERA_PLAYER_TARGET 2
  2326. #define SF_CAMERA_PLAYER_TAKECONTROL 4
  2327. #define SF_CAMERA_PLAYER_INFINITE_WAIT 8
  2328. #define SF_CAMERA_PLAYER_SNAP_TO 16
  2329. #define SF_CAMERA_PLAYER_NOT_SOLID 32
  2330. #define SF_CAMERA_PLAYER_INTERRUPT 64
  2331. //-----------------------------------------------------------------------------
  2332. // Purpose:
  2333. //-----------------------------------------------------------------------------
  2334. class CTriggerCamera : public CBaseEntity
  2335. {
  2336. public:
  2337. DECLARE_CLASS( CTriggerCamera, CBaseEntity );
  2338. void Spawn( void );
  2339. bool KeyValue( const char *szKeyName, const char *szValue );
  2340. void Enable( void );
  2341. void Disable( void );
  2342. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  2343. void FollowTarget( void );
  2344. void Move(void);
  2345. // Always transmit to clients so they know where to move the view to
  2346. virtual int UpdateTransmitState();
  2347. DECLARE_DATADESC();
  2348. // Input handlers
  2349. void InputEnable( inputdata_t &inputdata );
  2350. void InputDisable( inputdata_t &inputdata );
  2351. private:
  2352. EHANDLE m_hPlayer;
  2353. EHANDLE m_hTarget;
  2354. // used for moving the camera along a path (rail rides)
  2355. CBaseEntity *m_pPath;
  2356. string_t m_sPath;
  2357. float m_flWait;
  2358. float m_flReturnTime;
  2359. float m_flStopTime;
  2360. float m_moveDistance;
  2361. float m_targetSpeed;
  2362. float m_initialSpeed;
  2363. float m_acceleration;
  2364. float m_deceleration;
  2365. int m_state;
  2366. Vector m_vecMoveDir;
  2367. string_t m_iszTargetAttachment;
  2368. int m_iAttachmentIndex;
  2369. bool m_bSnapToGoal;
  2370. #if HL2_EPISODIC
  2371. bool m_bInterpolatePosition;
  2372. // these are interpolation vars used for interpolating the camera over time
  2373. Vector m_vStartPos, m_vEndPos;
  2374. float m_flInterpStartTime;
  2375. const static float kflPosInterpTime; // seconds
  2376. #endif
  2377. int m_nPlayerButtons;
  2378. int m_nOldTakeDamage;
  2379. private:
  2380. COutputEvent m_OnEndFollow;
  2381. };
  2382. #if HL2_EPISODIC
  2383. const float CTriggerCamera::kflPosInterpTime = 2.0f;
  2384. #endif
  2385. LINK_ENTITY_TO_CLASS( point_viewcontrol, CTriggerCamera );
  2386. BEGIN_DATADESC( CTriggerCamera )
  2387. DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
  2388. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  2389. DEFINE_FIELD( m_pPath, FIELD_CLASSPTR ),
  2390. DEFINE_FIELD( m_sPath, FIELD_STRING ),
  2391. DEFINE_FIELD( m_flWait, FIELD_FLOAT ),
  2392. DEFINE_FIELD( m_flReturnTime, FIELD_TIME ),
  2393. DEFINE_FIELD( m_flStopTime, FIELD_TIME ),
  2394. DEFINE_FIELD( m_moveDistance, FIELD_FLOAT ),
  2395. DEFINE_FIELD( m_targetSpeed, FIELD_FLOAT ),
  2396. DEFINE_FIELD( m_initialSpeed, FIELD_FLOAT ),
  2397. DEFINE_FIELD( m_acceleration, FIELD_FLOAT ),
  2398. DEFINE_FIELD( m_deceleration, FIELD_FLOAT ),
  2399. DEFINE_FIELD( m_state, FIELD_INTEGER ),
  2400. DEFINE_FIELD( m_vecMoveDir, FIELD_VECTOR ),
  2401. DEFINE_KEYFIELD( m_iszTargetAttachment, FIELD_STRING, "targetattachment" ),
  2402. DEFINE_FIELD( m_iAttachmentIndex, FIELD_INTEGER ),
  2403. DEFINE_FIELD( m_bSnapToGoal, FIELD_BOOLEAN ),
  2404. #if HL2_EPISODIC
  2405. DEFINE_KEYFIELD( m_bInterpolatePosition, FIELD_BOOLEAN, "interpolatepositiontoplayer" ),
  2406. DEFINE_FIELD( m_vStartPos, FIELD_VECTOR ),
  2407. DEFINE_FIELD( m_vEndPos, FIELD_VECTOR ),
  2408. DEFINE_FIELD( m_flInterpStartTime, FIELD_TIME ),
  2409. #endif
  2410. DEFINE_FIELD( m_nPlayerButtons, FIELD_INTEGER ),
  2411. DEFINE_FIELD( m_nOldTakeDamage, FIELD_INTEGER ),
  2412. // Inputs
  2413. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  2414. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  2415. // Function Pointers
  2416. DEFINE_FUNCTION( FollowTarget ),
  2417. DEFINE_OUTPUT( m_OnEndFollow, "OnEndFollow" ),
  2418. END_DATADESC()
  2419. //-----------------------------------------------------------------------------
  2420. // Purpose:
  2421. //-----------------------------------------------------------------------------
  2422. void CTriggerCamera::Spawn( void )
  2423. {
  2424. BaseClass::Spawn();
  2425. SetMoveType( MOVETYPE_NOCLIP );
  2426. SetSolid( SOLID_NONE ); // Remove model & collisions
  2427. SetRenderColorA( 0 ); // The engine won't draw this model if this is set to 0 and blending is on
  2428. m_nRenderMode = kRenderTransTexture;
  2429. m_state = USE_OFF;
  2430. m_initialSpeed = m_flSpeed;
  2431. if ( m_acceleration == 0 )
  2432. m_acceleration = 500;
  2433. if ( m_deceleration == 0 )
  2434. m_deceleration = 500;
  2435. DispatchUpdateTransmitState();
  2436. }
  2437. int CTriggerCamera::UpdateTransmitState()
  2438. {
  2439. // always tranmit if currently used by a monitor
  2440. if ( m_state == USE_ON )
  2441. {
  2442. return SetTransmitState( FL_EDICT_ALWAYS );
  2443. }
  2444. else
  2445. {
  2446. return SetTransmitState( FL_EDICT_DONTSEND );
  2447. }
  2448. }
  2449. //-----------------------------------------------------------------------------
  2450. // Purpose:
  2451. //-----------------------------------------------------------------------------
  2452. bool CTriggerCamera::KeyValue( const char *szKeyName, const char *szValue )
  2453. {
  2454. if (FStrEq(szKeyName, "wait"))
  2455. {
  2456. m_flWait = atof(szValue);
  2457. }
  2458. else if (FStrEq(szKeyName, "moveto"))
  2459. {
  2460. m_sPath = AllocPooledString( szValue );
  2461. }
  2462. else if (FStrEq(szKeyName, "acceleration"))
  2463. {
  2464. m_acceleration = atof( szValue );
  2465. }
  2466. else if (FStrEq(szKeyName, "deceleration"))
  2467. {
  2468. m_deceleration = atof( szValue );
  2469. }
  2470. else
  2471. return BaseClass::KeyValue( szKeyName, szValue );
  2472. return true;
  2473. }
  2474. //------------------------------------------------------------------------------
  2475. // Purpose: Input handler to turn on this trigger.
  2476. //------------------------------------------------------------------------------
  2477. void CTriggerCamera::InputEnable( inputdata_t &inputdata )
  2478. {
  2479. m_hPlayer = inputdata.pActivator;
  2480. Enable();
  2481. }
  2482. //------------------------------------------------------------------------------
  2483. // Purpose: Input handler to turn off this trigger.
  2484. //------------------------------------------------------------------------------
  2485. void CTriggerCamera::InputDisable( inputdata_t &inputdata )
  2486. {
  2487. Disable();
  2488. }
  2489. //-----------------------------------------------------------------------------
  2490. // Purpose:
  2491. //-----------------------------------------------------------------------------
  2492. void CTriggerCamera::Enable( void )
  2493. {
  2494. m_state = USE_ON;
  2495. if ( !m_hPlayer || !m_hPlayer->IsPlayer() )
  2496. {
  2497. m_hPlayer = UTIL_GetLocalPlayer();
  2498. }
  2499. if ( !m_hPlayer )
  2500. {
  2501. DispatchUpdateTransmitState();
  2502. return;
  2503. }
  2504. Assert( m_hPlayer->IsPlayer() );
  2505. CBasePlayer *pPlayer = NULL;
  2506. if ( m_hPlayer->IsPlayer() )
  2507. {
  2508. pPlayer = ((CBasePlayer*)m_hPlayer.Get());
  2509. }
  2510. else
  2511. {
  2512. Warning("CTriggerCamera could not find a player!\n");
  2513. return;
  2514. }
  2515. // if the player was already under control of a similar trigger, disable the previous trigger.
  2516. {
  2517. CBaseEntity *pPrevViewControl = pPlayer->GetViewEntity();
  2518. if (pPrevViewControl && pPrevViewControl != pPlayer)
  2519. {
  2520. CTriggerCamera *pOtherCamera = dynamic_cast<CTriggerCamera *>(pPrevViewControl);
  2521. if ( pOtherCamera )
  2522. {
  2523. if ( pOtherCamera == this )
  2524. {
  2525. // what the hell do you think you are doing?
  2526. Warning("Viewcontrol %s was enabled twice in a row!\n", GetDebugName());
  2527. return;
  2528. }
  2529. else
  2530. {
  2531. pOtherCamera->Disable();
  2532. }
  2533. }
  2534. }
  2535. }
  2536. m_nPlayerButtons = pPlayer->m_nButtons;
  2537. // Make the player invulnerable while under control of the camera. This will prevent situations where the player dies while under camera control but cannot restart their game due to disabled player inputs.
  2538. m_nOldTakeDamage = m_hPlayer->m_takedamage;
  2539. m_hPlayer->m_takedamage = DAMAGE_NO;
  2540. if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) )
  2541. {
  2542. m_hPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
  2543. }
  2544. m_flReturnTime = gpGlobals->curtime + m_flWait;
  2545. m_flSpeed = m_initialSpeed;
  2546. m_targetSpeed = m_initialSpeed;
  2547. // this pertains to view angles, not translation.
  2548. if ( HasSpawnFlags( SF_CAMERA_PLAYER_SNAP_TO ) )
  2549. {
  2550. m_bSnapToGoal = true;
  2551. }
  2552. if ( HasSpawnFlags(SF_CAMERA_PLAYER_TARGET ) )
  2553. {
  2554. m_hTarget = m_hPlayer;
  2555. }
  2556. else
  2557. {
  2558. m_hTarget = GetNextTarget();
  2559. }
  2560. // If we don't have a target, ignore the attachment / etc
  2561. if ( m_hTarget )
  2562. {
  2563. m_iAttachmentIndex = 0;
  2564. if ( m_iszTargetAttachment != NULL_STRING )
  2565. {
  2566. if ( !m_hTarget->GetBaseAnimating() )
  2567. {
  2568. Warning("%s tried to target an attachment (%s) on target %s, which has no model.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) );
  2569. }
  2570. else
  2571. {
  2572. m_iAttachmentIndex = m_hTarget->GetBaseAnimating()->LookupAttachment( STRING(m_iszTargetAttachment) );
  2573. if ( m_iAttachmentIndex <= 0 )
  2574. {
  2575. Warning("%s could not find attachment %s on target %s.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) );
  2576. }
  2577. }
  2578. }
  2579. }
  2580. if (HasSpawnFlags(SF_CAMERA_PLAYER_TAKECONTROL ) )
  2581. {
  2582. ((CBasePlayer*)m_hPlayer.Get())->EnableControl(FALSE);
  2583. }
  2584. if ( m_sPath != NULL_STRING )
  2585. {
  2586. m_pPath = gEntList.FindEntityByName( NULL, m_sPath, NULL, m_hPlayer );
  2587. }
  2588. else
  2589. {
  2590. m_pPath = NULL;
  2591. }
  2592. m_flStopTime = gpGlobals->curtime;
  2593. if ( m_pPath )
  2594. {
  2595. if ( m_pPath->m_flSpeed != 0 )
  2596. m_targetSpeed = m_pPath->m_flSpeed;
  2597. m_flStopTime += m_pPath->GetDelay();
  2598. }
  2599. // copy over player information. If we're interpolating from
  2600. // the player position, do something more elaborate.
  2601. #if HL2_EPISODIC
  2602. if (m_bInterpolatePosition)
  2603. {
  2604. // initialize the values we'll spline between
  2605. m_vStartPos = m_hPlayer->EyePosition();
  2606. m_vEndPos = GetAbsOrigin();
  2607. m_flInterpStartTime = gpGlobals->curtime;
  2608. UTIL_SetOrigin( this, m_hPlayer->EyePosition() );
  2609. SetLocalAngles( QAngle( m_hPlayer->GetLocalAngles().x, m_hPlayer->GetLocalAngles().y, 0 ) );
  2610. SetAbsVelocity( vec3_origin );
  2611. }
  2612. else
  2613. #endif
  2614. if (HasSpawnFlags(SF_CAMERA_PLAYER_POSITION ) )
  2615. {
  2616. UTIL_SetOrigin( this, m_hPlayer->EyePosition() );
  2617. SetLocalAngles( QAngle( m_hPlayer->GetLocalAngles().x, m_hPlayer->GetLocalAngles().y, 0 ) );
  2618. SetAbsVelocity( m_hPlayer->GetAbsVelocity() );
  2619. }
  2620. else
  2621. {
  2622. SetAbsVelocity( vec3_origin );
  2623. }
  2624. pPlayer->SetViewEntity( this );
  2625. // Hide the player's viewmodel
  2626. if ( pPlayer->GetActiveWeapon() )
  2627. {
  2628. pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW );
  2629. }
  2630. // Only track if we have a target
  2631. if ( m_hTarget )
  2632. {
  2633. // follow the player down
  2634. SetThink( &CTriggerCamera::FollowTarget );
  2635. SetNextThink( gpGlobals->curtime );
  2636. }
  2637. m_moveDistance = 0;
  2638. Move();
  2639. DispatchUpdateTransmitState();
  2640. }
  2641. //-----------------------------------------------------------------------------
  2642. // Purpose:
  2643. //-----------------------------------------------------------------------------
  2644. void CTriggerCamera::Disable( void )
  2645. {
  2646. if ( m_hPlayer && m_hPlayer->IsAlive() )
  2647. {
  2648. if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) )
  2649. {
  2650. m_hPlayer->RemoveSolidFlags( FSOLID_NOT_SOLID );
  2651. }
  2652. ((CBasePlayer*)m_hPlayer.Get())->SetViewEntity( m_hPlayer );
  2653. ((CBasePlayer*)m_hPlayer.Get())->EnableControl(TRUE);
  2654. // Restore the player's viewmodel
  2655. if ( ((CBasePlayer*)m_hPlayer.Get())->GetActiveWeapon() )
  2656. {
  2657. ((CBasePlayer*)m_hPlayer.Get())->GetActiveWeapon()->RemoveEffects( EF_NODRAW );
  2658. }
  2659. //return the player to previous takedamage state
  2660. m_hPlayer->m_takedamage = m_nOldTakeDamage;
  2661. }
  2662. m_state = USE_OFF;
  2663. m_flReturnTime = gpGlobals->curtime;
  2664. SetThink( NULL );
  2665. m_OnEndFollow.FireOutput(this, this); // dvsents2: what is the best name for this output?
  2666. SetLocalAngularVelocity( vec3_angle );
  2667. DispatchUpdateTransmitState();
  2668. }
  2669. //-----------------------------------------------------------------------------
  2670. // Purpose:
  2671. //-----------------------------------------------------------------------------
  2672. void CTriggerCamera::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  2673. {
  2674. if ( !ShouldToggle( useType, m_state ) )
  2675. return;
  2676. // Toggle state
  2677. if ( m_state != USE_OFF )
  2678. {
  2679. Disable();
  2680. }
  2681. else
  2682. {
  2683. m_hPlayer = pActivator;
  2684. Enable();
  2685. }
  2686. }
  2687. //-----------------------------------------------------------------------------
  2688. // Purpose:
  2689. //-----------------------------------------------------------------------------
  2690. void CTriggerCamera::FollowTarget( )
  2691. {
  2692. if (m_hPlayer == NULL)
  2693. return;
  2694. if ( m_hTarget == NULL )
  2695. {
  2696. Disable();
  2697. return;
  2698. }
  2699. if ( !HasSpawnFlags(SF_CAMERA_PLAYER_INFINITE_WAIT) && (!m_hTarget || m_flReturnTime < gpGlobals->curtime) )
  2700. {
  2701. Disable();
  2702. return;
  2703. }
  2704. QAngle vecGoal;
  2705. if ( m_iAttachmentIndex )
  2706. {
  2707. Vector vecOrigin;
  2708. m_hTarget->GetBaseAnimating()->GetAttachment( m_iAttachmentIndex, vecOrigin );
  2709. VectorAngles( vecOrigin - GetAbsOrigin(), vecGoal );
  2710. }
  2711. else
  2712. {
  2713. if ( m_hTarget )
  2714. {
  2715. VectorAngles( m_hTarget->GetAbsOrigin() - GetAbsOrigin(), vecGoal );
  2716. }
  2717. else
  2718. {
  2719. // Use the viewcontroller's angles
  2720. vecGoal = GetAbsAngles();
  2721. }
  2722. }
  2723. // Should we just snap to the goal angles?
  2724. if ( m_bSnapToGoal )
  2725. {
  2726. SetAbsAngles( vecGoal );
  2727. m_bSnapToGoal = false;
  2728. }
  2729. else
  2730. {
  2731. // UNDONE: Can't we just use UTIL_AngleDiff here?
  2732. QAngle angles = GetLocalAngles();
  2733. if (angles.y > 360)
  2734. angles.y -= 360;
  2735. if (angles.y < 0)
  2736. angles.y += 360;
  2737. SetLocalAngles( angles );
  2738. float dx = vecGoal.x - GetLocalAngles().x;
  2739. float dy = vecGoal.y - GetLocalAngles().y;
  2740. if (dx < -180)
  2741. dx += 360;
  2742. if (dx > 180)
  2743. dx = dx - 360;
  2744. if (dy < -180)
  2745. dy += 360;
  2746. if (dy > 180)
  2747. dy = dy - 360;
  2748. QAngle vecAngVel;
  2749. vecAngVel.Init( dx * 40 * gpGlobals->frametime, dy * 40 * gpGlobals->frametime, GetLocalAngularVelocity().z );
  2750. SetLocalAngularVelocity(vecAngVel);
  2751. }
  2752. if (!HasSpawnFlags(SF_CAMERA_PLAYER_TAKECONTROL))
  2753. {
  2754. SetAbsVelocity( GetAbsVelocity() * 0.8 );
  2755. if (GetAbsVelocity().Length( ) < 10.0)
  2756. {
  2757. SetAbsVelocity( vec3_origin );
  2758. }
  2759. }
  2760. SetNextThink( gpGlobals->curtime );
  2761. Move();
  2762. }
  2763. void CTriggerCamera::Move()
  2764. {
  2765. if ( HasSpawnFlags( SF_CAMERA_PLAYER_INTERRUPT ) )
  2766. {
  2767. if ( m_hPlayer )
  2768. {
  2769. CBasePlayer *pPlayer = ToBasePlayer( m_hPlayer );
  2770. if ( pPlayer )
  2771. {
  2772. int buttonsChanged = m_nPlayerButtons ^ pPlayer->m_nButtons;
  2773. if ( buttonsChanged && pPlayer->m_nButtons )
  2774. {
  2775. Disable();
  2776. return;
  2777. }
  2778. m_nPlayerButtons = pPlayer->m_nButtons;
  2779. }
  2780. }
  2781. }
  2782. // In vanilla HL2, the camera is either on a path, or doesn't move. In episodic
  2783. // we add the capacity for interpolation to the start point.
  2784. #if HL2_EPISODIC
  2785. if (m_pPath)
  2786. #else
  2787. // Not moving on a path, return
  2788. if (!m_pPath)
  2789. return;
  2790. #endif
  2791. {
  2792. // Subtract movement from the previous frame
  2793. m_moveDistance -= m_flSpeed * gpGlobals->frametime;
  2794. // Have we moved enough to reach the target?
  2795. if ( m_moveDistance <= 0 )
  2796. {
  2797. variant_t emptyVariant;
  2798. m_pPath->AcceptInput( "InPass", this, this, emptyVariant, 0 );
  2799. // Time to go to the next target
  2800. m_pPath = m_pPath->GetNextTarget();
  2801. // Set up next corner
  2802. if ( !m_pPath )
  2803. {
  2804. SetAbsVelocity( vec3_origin );
  2805. }
  2806. else
  2807. {
  2808. if ( m_pPath->m_flSpeed != 0 )
  2809. m_targetSpeed = m_pPath->m_flSpeed;
  2810. m_vecMoveDir = m_pPath->GetLocalOrigin() - GetLocalOrigin();
  2811. m_moveDistance = VectorNormalize( m_vecMoveDir );
  2812. m_flStopTime = gpGlobals->curtime + m_pPath->GetDelay();
  2813. }
  2814. }
  2815. if ( m_flStopTime > gpGlobals->curtime )
  2816. m_flSpeed = UTIL_Approach( 0, m_flSpeed, m_deceleration * gpGlobals->frametime );
  2817. else
  2818. m_flSpeed = UTIL_Approach( m_targetSpeed, m_flSpeed, m_acceleration * gpGlobals->frametime );
  2819. float fraction = 2 * gpGlobals->frametime;
  2820. SetAbsVelocity( ((m_vecMoveDir * m_flSpeed) * fraction) + (GetAbsVelocity() * (1-fraction)) );
  2821. }
  2822. #if HL2_EPISODIC
  2823. else if (m_bInterpolatePosition)
  2824. {
  2825. // get the interpolation parameter [0..1]
  2826. float tt = (gpGlobals->curtime - m_flInterpStartTime) / kflPosInterpTime;
  2827. if (tt >= 1.0f)
  2828. {
  2829. // we're there, we're done
  2830. UTIL_SetOrigin( this, m_vEndPos );
  2831. SetAbsVelocity( vec3_origin );
  2832. m_bInterpolatePosition = false;
  2833. }
  2834. else
  2835. {
  2836. Assert(tt >= 0);
  2837. Vector nextPos = ( (m_vEndPos - m_vStartPos) * SimpleSpline(tt) ) + m_vStartPos;
  2838. // rather than stomping origin, set the velocity so that we get there in the proper time
  2839. Vector desiredVel = (nextPos - GetAbsOrigin()) * (1.0f / gpGlobals->frametime);
  2840. SetAbsVelocity( desiredVel );
  2841. }
  2842. }
  2843. #endif
  2844. }
  2845. //-----------------------------------------------------------------------------
  2846. // Purpose: Starts/stops cd audio tracks
  2847. //-----------------------------------------------------------------------------
  2848. class CTriggerCDAudio : public CBaseTrigger
  2849. {
  2850. public:
  2851. DECLARE_CLASS( CTriggerCDAudio, CBaseTrigger );
  2852. void Spawn( void );
  2853. virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  2854. void PlayTrack( void );
  2855. void Touch ( CBaseEntity *pOther );
  2856. };
  2857. LINK_ENTITY_TO_CLASS( trigger_cdaudio, CTriggerCDAudio );
  2858. //-----------------------------------------------------------------------------
  2859. // Purpose: Changes tracks or stops CD when player touches
  2860. // Input : pOther - The entity that touched us.
  2861. //-----------------------------------------------------------------------------
  2862. void CTriggerCDAudio::Touch ( CBaseEntity *pOther )
  2863. {
  2864. if ( !pOther->IsPlayer() )
  2865. {
  2866. return;
  2867. }
  2868. PlayTrack();
  2869. }
  2870. //-----------------------------------------------------------------------------
  2871. // Purpose:
  2872. //-----------------------------------------------------------------------------
  2873. void CTriggerCDAudio::Spawn( void )
  2874. {
  2875. BaseClass::Spawn();
  2876. InitTrigger();
  2877. }
  2878. void CTriggerCDAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  2879. {
  2880. PlayTrack();
  2881. }
  2882. //-----------------------------------------------------------------------------
  2883. // Purpose: Issues a client command to play a given CD track. Called from
  2884. // trigger_cdaudio and target_cdaudio.
  2885. // Input : iTrack - Track number to play.
  2886. //-----------------------------------------------------------------------------
  2887. static void PlayCDTrack( int iTrack )
  2888. {
  2889. edict_t *pClient;
  2890. // manually find the single player.
  2891. pClient = engine->PEntityOfEntIndex( 1 );
  2892. Assert(gpGlobals->maxClients == 1);
  2893. // Can't play if the client is not connected!
  2894. if ( !pClient )
  2895. return;
  2896. // UNDONE: Move this to engine sound
  2897. if ( iTrack < -1 || iTrack > 30 )
  2898. {
  2899. Warning( "TriggerCDAudio - Track %d out of range\n", iTrack );
  2900. return;
  2901. }
  2902. if ( iTrack == -1 )
  2903. {
  2904. engine->ClientCommand ( pClient, "cd pause\n");
  2905. }
  2906. else
  2907. {
  2908. engine->ClientCommand ( pClient, "cd play %3d\n", iTrack);
  2909. }
  2910. }
  2911. // only plays for ONE client, so only use in single play!
  2912. void CTriggerCDAudio::PlayTrack( void )
  2913. {
  2914. PlayCDTrack( (int)m_iHealth );
  2915. SetTouch( NULL );
  2916. UTIL_Remove( this );
  2917. }
  2918. //-----------------------------------------------------------------------------
  2919. // Purpose: Measures the proximity to a specified entity of any entities within
  2920. // the trigger, provided they are within a given radius of the specified
  2921. // entity. The nearest entity distance is output as a number from [0 - 1].
  2922. //-----------------------------------------------------------------------------
  2923. class CTriggerProximity : public CBaseTrigger
  2924. {
  2925. public:
  2926. DECLARE_CLASS( CTriggerProximity, CBaseTrigger );
  2927. virtual void Spawn(void);
  2928. virtual void Activate(void);
  2929. virtual void StartTouch(CBaseEntity *pOther);
  2930. virtual void EndTouch(CBaseEntity *pOther);
  2931. void MeasureThink(void);
  2932. protected:
  2933. EHANDLE m_hMeasureTarget;
  2934. string_t m_iszMeasureTarget; // The entity from which we measure proximities.
  2935. float m_fRadius; // The radius around the measure target that we measure within.
  2936. int m_nTouchers; // Number of entities touching us.
  2937. // Outputs
  2938. COutputFloat m_NearestEntityDistance;
  2939. DECLARE_DATADESC();
  2940. };
  2941. BEGIN_DATADESC( CTriggerProximity )
  2942. // Functions
  2943. DEFINE_FUNCTION(MeasureThink),
  2944. // Keys
  2945. DEFINE_KEYFIELD(m_iszMeasureTarget, FIELD_STRING, "measuretarget"),
  2946. DEFINE_FIELD( m_hMeasureTarget, FIELD_EHANDLE ),
  2947. DEFINE_KEYFIELD(m_fRadius, FIELD_FLOAT, "radius"),
  2948. DEFINE_FIELD( m_nTouchers, FIELD_INTEGER ),
  2949. // Outputs
  2950. DEFINE_OUTPUT(m_NearestEntityDistance, "NearestEntityDistance"),
  2951. END_DATADESC()
  2952. LINK_ENTITY_TO_CLASS(trigger_proximity, CTriggerProximity);
  2953. LINK_ENTITY_TO_CLASS(logic_proximity, CPointEntity);
  2954. //-----------------------------------------------------------------------------
  2955. // Purpose: Called when spawning, after keyvalues have been handled.
  2956. //-----------------------------------------------------------------------------
  2957. void CTriggerProximity::Spawn(void)
  2958. {
  2959. // Avoid divide by zero in MeasureThink!
  2960. if (m_fRadius == 0)
  2961. {
  2962. m_fRadius = 32;
  2963. }
  2964. InitTrigger();
  2965. }
  2966. //-----------------------------------------------------------------------------
  2967. // Purpose: Called after all entities have spawned and after a load game.
  2968. // Finds the reference point from which to measure.
  2969. //-----------------------------------------------------------------------------
  2970. void CTriggerProximity::Activate(void)
  2971. {
  2972. BaseClass::Activate();
  2973. m_hMeasureTarget = gEntList.FindEntityByName(NULL, m_iszMeasureTarget );
  2974. //
  2975. // Disable our Touch function if we were given a bad measure target.
  2976. //
  2977. if ((m_hMeasureTarget == NULL) || (m_hMeasureTarget->edict() == NULL))
  2978. {
  2979. Warning( "TriggerProximity - Missing measure target or measure target with no origin!\n");
  2980. }
  2981. }
  2982. //-----------------------------------------------------------------------------
  2983. // Purpose: Decrements the touch count and cancels the think if the count reaches
  2984. // zero.
  2985. // Input : pOther -
  2986. //-----------------------------------------------------------------------------
  2987. void CTriggerProximity::StartTouch(CBaseEntity *pOther)
  2988. {
  2989. BaseClass::StartTouch( pOther );
  2990. if ( PassesTriggerFilters( pOther ) )
  2991. {
  2992. m_nTouchers++;
  2993. SetThink( &CTriggerProximity::MeasureThink );
  2994. SetNextThink( gpGlobals->curtime );
  2995. }
  2996. }
  2997. //-----------------------------------------------------------------------------
  2998. // Purpose: Decrements the touch count and cancels the think if the count reaches
  2999. // zero.
  3000. // Input : pOther -
  3001. //-----------------------------------------------------------------------------
  3002. void CTriggerProximity::EndTouch(CBaseEntity *pOther)
  3003. {
  3004. BaseClass::EndTouch( pOther );
  3005. if ( PassesTriggerFilters( pOther ) )
  3006. {
  3007. m_nTouchers--;
  3008. if ( m_nTouchers == 0 )
  3009. {
  3010. SetThink( NULL );
  3011. SetNextThink( TICK_NEVER_THINK );
  3012. }
  3013. }
  3014. }
  3015. //-----------------------------------------------------------------------------
  3016. // Purpose: Think function called every frame as long as we have entities touching
  3017. // us that we care about. Finds the closest entity to the measure
  3018. // target and outputs the distance as a normalized value from [0..1].
  3019. //-----------------------------------------------------------------------------
  3020. void CTriggerProximity::MeasureThink( void )
  3021. {
  3022. if ( ( m_hMeasureTarget == NULL ) || ( m_hMeasureTarget->edict() == NULL ) )
  3023. {
  3024. SetThink(NULL);
  3025. SetNextThink( TICK_NEVER_THINK );
  3026. return;
  3027. }
  3028. //
  3029. // Traverse our list of touchers and find the entity that is closest to the
  3030. // measure target.
  3031. //
  3032. float fMinDistance = m_fRadius + 100;
  3033. CBaseEntity *pNearestEntity = NULL;
  3034. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  3035. if ( root )
  3036. {
  3037. touchlink_t *pLink = root->nextLink;
  3038. while ( pLink != root )
  3039. {
  3040. CBaseEntity *pEntity = pLink->entityTouched;
  3041. // If this is an entity that we care about, check its distance.
  3042. if ( ( pEntity != NULL ) && PassesTriggerFilters( pEntity ) )
  3043. {
  3044. float flDistance = (pEntity->GetLocalOrigin() - m_hMeasureTarget->GetLocalOrigin()).Length();
  3045. if (flDistance < fMinDistance)
  3046. {
  3047. fMinDistance = flDistance;
  3048. pNearestEntity = pEntity;
  3049. }
  3050. }
  3051. pLink = pLink->nextLink;
  3052. }
  3053. }
  3054. // Update our output with the nearest entity distance, normalized to [0..1].
  3055. if ( fMinDistance <= m_fRadius )
  3056. {
  3057. fMinDistance /= m_fRadius;
  3058. if ( fMinDistance != m_NearestEntityDistance.Get() )
  3059. {
  3060. m_NearestEntityDistance.Set( fMinDistance, pNearestEntity, this );
  3061. }
  3062. }
  3063. SetNextThink( gpGlobals->curtime );
  3064. }
  3065. // ##################################################################################
  3066. // >> TriggerWind
  3067. //
  3068. // Blows physics objects in the trigger
  3069. //
  3070. // ##################################################################################
  3071. #define MAX_WIND_CHANGE 5.0f
  3072. //------------------------------------------------------------------------------
  3073. // Purpose :
  3074. // Input :
  3075. // Output :
  3076. //------------------------------------------------------------------------------
  3077. class CPhysicsWind : public IMotionEvent
  3078. {
  3079. DECLARE_SIMPLE_DATADESC();
  3080. public:
  3081. simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  3082. {
  3083. // If we have no windspeed, we're not doing anything
  3084. if ( !m_flWindSpeed )
  3085. return IMotionEvent::SIM_NOTHING;
  3086. // Get a cosine modulated noise between 5 and 20 that is object specific
  3087. int nNoiseMod = 5+(int)pObject%15; //
  3088. // Turn wind yaw direction into a vector and add noise
  3089. QAngle vWindAngle = vec3_angle;
  3090. vWindAngle[1] = m_nWindYaw+(30*cos(nNoiseMod * gpGlobals->curtime + nNoiseMod));
  3091. Vector vWind;
  3092. AngleVectors(vWindAngle,&vWind);
  3093. // Add lift with noise
  3094. vWind.z = 1.1 + (1.0 * sin(nNoiseMod * gpGlobals->curtime + nNoiseMod));
  3095. linear = 3*vWind*m_flWindSpeed;
  3096. angular = vec3_origin;
  3097. return IMotionEvent::SIM_GLOBAL_FORCE;
  3098. }
  3099. int m_nWindYaw;
  3100. float m_flWindSpeed;
  3101. };
  3102. BEGIN_SIMPLE_DATADESC( CPhysicsWind )
  3103. DEFINE_FIELD( m_nWindYaw, FIELD_INTEGER ),
  3104. DEFINE_FIELD( m_flWindSpeed, FIELD_FLOAT ),
  3105. END_DATADESC()
  3106. extern short g_sModelIndexSmoke;
  3107. extern float GetFloorZ(const Vector &origin);
  3108. #define WIND_THINK_CONTEXT "WindThinkContext"
  3109. //-----------------------------------------------------------------------------
  3110. // Purpose:
  3111. //-----------------------------------------------------------------------------
  3112. class CTriggerWind : public CBaseVPhysicsTrigger
  3113. {
  3114. DECLARE_CLASS( CTriggerWind, CBaseVPhysicsTrigger );
  3115. public:
  3116. DECLARE_DATADESC();
  3117. void Spawn( void );
  3118. bool KeyValue( const char *szKeyName, const char *szValue );
  3119. void OnRestore();
  3120. void UpdateOnRemove();
  3121. bool CreateVPhysics();
  3122. void StartTouch( CBaseEntity *pOther );
  3123. void EndTouch( CBaseEntity *pOther );
  3124. void WindThink( void );
  3125. int DrawDebugTextOverlays( void );
  3126. // Input handlers
  3127. void InputEnable( inputdata_t &inputdata );
  3128. void InputSetSpeed( inputdata_t &inputdata );
  3129. private:
  3130. int m_nSpeedBase; // base line for how hard the wind blows
  3131. int m_nSpeedNoise; // noise added to wind speed +/-
  3132. int m_nSpeedCurrent;// current wind speed
  3133. int m_nSpeedTarget; // wind speed I'm approaching
  3134. int m_nDirBase; // base line for direction the wind blows (yaw)
  3135. int m_nDirNoise; // noise added to wind direction
  3136. int m_nDirCurrent; // the current wind direction
  3137. int m_nDirTarget; // wind direction I'm approaching
  3138. int m_nHoldBase; // base line for how long to wait before changing wind
  3139. int m_nHoldNoise; // noise added to how long to wait before changing wind
  3140. bool m_bSwitch; // when does wind change
  3141. IPhysicsMotionController* m_pWindController;
  3142. CPhysicsWind m_WindCallback;
  3143. };
  3144. LINK_ENTITY_TO_CLASS( trigger_wind, CTriggerWind );
  3145. BEGIN_DATADESC( CTriggerWind )
  3146. DEFINE_FIELD( m_nSpeedCurrent, FIELD_INTEGER),
  3147. DEFINE_FIELD( m_nSpeedTarget, FIELD_INTEGER),
  3148. DEFINE_FIELD( m_nDirBase, FIELD_INTEGER),
  3149. DEFINE_FIELD( m_nDirCurrent, FIELD_INTEGER),
  3150. DEFINE_FIELD( m_nDirTarget, FIELD_INTEGER),
  3151. DEFINE_FIELD( m_bSwitch, FIELD_BOOLEAN),
  3152. DEFINE_FIELD( m_nSpeedBase, FIELD_INTEGER ),
  3153. DEFINE_KEYFIELD( m_nSpeedNoise, FIELD_INTEGER, "SpeedNoise"),
  3154. DEFINE_KEYFIELD( m_nDirNoise, FIELD_INTEGER, "DirectionNoise"),
  3155. DEFINE_KEYFIELD( m_nHoldBase, FIELD_INTEGER, "HoldTime"),
  3156. DEFINE_KEYFIELD( m_nHoldNoise, FIELD_INTEGER, "HoldNoise"),
  3157. DEFINE_PHYSPTR( m_pWindController ),
  3158. DEFINE_EMBEDDED( m_WindCallback ),
  3159. DEFINE_FUNCTION( WindThink ),
  3160. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeed", InputSetSpeed ),
  3161. END_DATADESC()
  3162. //------------------------------------------------------------------------------
  3163. // Purpose:
  3164. //------------------------------------------------------------------------------
  3165. void CTriggerWind::Spawn( void )
  3166. {
  3167. m_bSwitch = true;
  3168. m_nDirBase = GetLocalAngles().y;
  3169. BaseClass::Spawn();
  3170. m_nSpeedCurrent = m_nSpeedBase;
  3171. m_nDirCurrent = m_nDirBase;
  3172. SetContextThink( &CTriggerWind::WindThink, gpGlobals->curtime, WIND_THINK_CONTEXT );
  3173. }
  3174. //-----------------------------------------------------------------------------
  3175. // Purpose:
  3176. //-----------------------------------------------------------------------------
  3177. bool CTriggerWind::KeyValue( const char *szKeyName, const char *szValue )
  3178. {
  3179. // Done here to avoid collision with CBaseEntity's speed key
  3180. if ( FStrEq(szKeyName, "Speed") )
  3181. {
  3182. m_nSpeedBase = atoi( szValue );
  3183. }
  3184. else
  3185. return BaseClass::KeyValue( szKeyName, szValue );
  3186. return true;
  3187. }
  3188. //------------------------------------------------------------------------------
  3189. // Create VPhysics
  3190. //------------------------------------------------------------------------------
  3191. bool CTriggerWind::CreateVPhysics()
  3192. {
  3193. BaseClass::CreateVPhysics();
  3194. m_pWindController = physenv->CreateMotionController( &m_WindCallback );
  3195. return true;
  3196. }
  3197. //------------------------------------------------------------------------------
  3198. // Cleanup
  3199. //------------------------------------------------------------------------------
  3200. void CTriggerWind::UpdateOnRemove()
  3201. {
  3202. if ( m_pWindController )
  3203. {
  3204. physenv->DestroyMotionController( m_pWindController );
  3205. m_pWindController = NULL;
  3206. }
  3207. BaseClass::UpdateOnRemove();
  3208. }
  3209. //------------------------------------------------------------------------------
  3210. // Purpose:
  3211. //------------------------------------------------------------------------------
  3212. void CTriggerWind::OnRestore()
  3213. {
  3214. BaseClass::OnRestore();
  3215. if ( m_pWindController )
  3216. {
  3217. m_pWindController->SetEventHandler( &m_WindCallback );
  3218. }
  3219. }
  3220. //------------------------------------------------------------------------------
  3221. // Purpose:
  3222. //------------------------------------------------------------------------------
  3223. void CTriggerWind::StartTouch(CBaseEntity *pOther)
  3224. {
  3225. if ( !PassesTriggerFilters(pOther) )
  3226. return;
  3227. if ( pOther->IsPlayer() )
  3228. return;
  3229. IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
  3230. if ( pPhys)
  3231. {
  3232. m_pWindController->AttachObject( pPhys, false );
  3233. pPhys->Wake();
  3234. }
  3235. }
  3236. //------------------------------------------------------------------------------
  3237. // Purpose:
  3238. //------------------------------------------------------------------------------
  3239. void CTriggerWind::EndTouch(CBaseEntity *pOther)
  3240. {
  3241. if ( !PassesTriggerFilters(pOther) )
  3242. return;
  3243. if ( pOther->IsPlayer() )
  3244. return;
  3245. IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
  3246. if ( pPhys && m_pWindController )
  3247. {
  3248. m_pWindController->DetachObject( pPhys );
  3249. }
  3250. }
  3251. //------------------------------------------------------------------------------
  3252. // Purpose:
  3253. //------------------------------------------------------------------------------
  3254. void CTriggerWind::InputEnable( inputdata_t &inputdata )
  3255. {
  3256. BaseClass::InputEnable( inputdata );
  3257. SetContextThink( &CTriggerWind::WindThink, gpGlobals->curtime + 0.1f, WIND_THINK_CONTEXT );
  3258. }
  3259. //------------------------------------------------------------------------------
  3260. // Purpose:
  3261. //------------------------------------------------------------------------------
  3262. void CTriggerWind::WindThink( void )
  3263. {
  3264. // By default...
  3265. SetContextThink( &CTriggerWind::WindThink, gpGlobals->curtime + 0.1, WIND_THINK_CONTEXT );
  3266. // Is it time to change the wind?
  3267. if (m_bSwitch)
  3268. {
  3269. m_bSwitch = false;
  3270. // Set new target direction and speed
  3271. m_nSpeedTarget = m_nSpeedBase + random->RandomInt( -m_nSpeedNoise, m_nSpeedNoise );
  3272. m_nDirTarget = UTIL_AngleMod( m_nDirBase + random->RandomInt(-m_nDirNoise, m_nDirNoise) );
  3273. }
  3274. else
  3275. {
  3276. bool bDone = true;
  3277. // either ramp up, or sleep till change
  3278. if (abs(m_nSpeedTarget - m_nSpeedCurrent) > MAX_WIND_CHANGE)
  3279. {
  3280. m_nSpeedCurrent += (m_nSpeedTarget > m_nSpeedCurrent) ? MAX_WIND_CHANGE : -MAX_WIND_CHANGE;
  3281. bDone = false;
  3282. }
  3283. if (abs(m_nDirTarget - m_nDirCurrent) > MAX_WIND_CHANGE)
  3284. {
  3285. m_nDirCurrent = UTIL_ApproachAngle( m_nDirTarget, m_nDirCurrent, MAX_WIND_CHANGE );
  3286. bDone = false;
  3287. }
  3288. if (bDone)
  3289. {
  3290. m_nSpeedCurrent = m_nSpeedTarget;
  3291. SetContextThink( &CTriggerWind::WindThink, m_nHoldBase + random->RandomFloat(-m_nHoldNoise,m_nHoldNoise), WIND_THINK_CONTEXT );
  3292. m_bSwitch = true;
  3293. }
  3294. }
  3295. // If we're starting to blow, where we weren't before, wake up all our objects
  3296. if ( m_nSpeedCurrent )
  3297. {
  3298. m_pWindController->WakeObjects();
  3299. }
  3300. // store the wind data in the controller callback
  3301. m_WindCallback.m_nWindYaw = m_nDirCurrent;
  3302. if ( m_bDisabled )
  3303. {
  3304. m_WindCallback.m_flWindSpeed = 0;
  3305. }
  3306. else
  3307. {
  3308. m_WindCallback.m_flWindSpeed = m_nSpeedCurrent;
  3309. }
  3310. }
  3311. //------------------------------------------------------------------------------
  3312. // Purpose:
  3313. //------------------------------------------------------------------------------
  3314. void CTriggerWind::InputSetSpeed( inputdata_t &inputdata )
  3315. {
  3316. // Set new speed and mark to switch
  3317. m_nSpeedBase = inputdata.value.Int();
  3318. m_bSwitch = true;
  3319. }
  3320. //-----------------------------------------------------------------------------
  3321. // Purpose: Draw any debug text overlays
  3322. // Output : Current text offset from the top
  3323. //-----------------------------------------------------------------------------
  3324. int CTriggerWind::DrawDebugTextOverlays(void)
  3325. {
  3326. int text_offset = BaseClass::DrawDebugTextOverlays();
  3327. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  3328. {
  3329. // --------------
  3330. // Print Target
  3331. // --------------
  3332. char tempstr[255];
  3333. Q_snprintf(tempstr,sizeof(tempstr),"Dir: %i (%i)",m_nDirCurrent,m_nDirTarget);
  3334. EntityText(text_offset,tempstr,0);
  3335. text_offset++;
  3336. Q_snprintf(tempstr,sizeof(tempstr),"Speed: %i (%i)",m_nSpeedCurrent,m_nSpeedTarget);
  3337. EntityText(text_offset,tempstr,0);
  3338. text_offset++;
  3339. }
  3340. return text_offset;
  3341. }
  3342. // ##################################################################################
  3343. // >> TriggerImpact
  3344. //
  3345. // Blows physics objects in the trigger
  3346. //
  3347. // ##################################################################################
  3348. #define TRIGGERIMPACT_VIEWKICK_SCALE 0.1
  3349. class CTriggerImpact : public CTriggerMultiple
  3350. {
  3351. DECLARE_CLASS( CTriggerImpact, CTriggerMultiple );
  3352. public:
  3353. DECLARE_DATADESC();
  3354. float m_flMagnitude;
  3355. float m_flNoise;
  3356. float m_flViewkick;
  3357. void Spawn( void );
  3358. void StartTouch( CBaseEntity *pOther );
  3359. // Inputs
  3360. void InputSetMagnitude( inputdata_t &inputdata );
  3361. void InputImpact( inputdata_t &inputdata );
  3362. // Outputs
  3363. COutputVector m_pOutputForce; // Output force in case anyone else wants to use it
  3364. // Debug
  3365. int DrawDebugTextOverlays(void);
  3366. };
  3367. LINK_ENTITY_TO_CLASS( trigger_impact, CTriggerImpact );
  3368. BEGIN_DATADESC( CTriggerImpact )
  3369. DEFINE_KEYFIELD( m_flMagnitude, FIELD_FLOAT, "Magnitude"),
  3370. DEFINE_KEYFIELD( m_flNoise, FIELD_FLOAT, "Noise"),
  3371. DEFINE_KEYFIELD( m_flViewkick, FIELD_FLOAT, "Viewkick"),
  3372. // Inputs
  3373. DEFINE_INPUTFUNC( FIELD_VOID, "Impact", InputImpact ),
  3374. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMagnitude", InputSetMagnitude ),
  3375. // Outputs
  3376. DEFINE_OUTPUT(m_pOutputForce, "ImpactForce"),
  3377. // Function Pointers
  3378. DEFINE_FUNCTION( Disable ),
  3379. END_DATADESC()
  3380. //------------------------------------------------------------------------------
  3381. // Purpose:
  3382. //------------------------------------------------------------------------------
  3383. void CTriggerImpact::Spawn( void )
  3384. {
  3385. // Clamp date in case user made an error
  3386. m_flNoise = clamp(m_flNoise,0.f,1.f);
  3387. m_flViewkick = clamp(m_flViewkick,0.f,1.f);
  3388. // Always start disabled
  3389. m_bDisabled = true;
  3390. BaseClass::Spawn();
  3391. }
  3392. //------------------------------------------------------------------------------
  3393. // Purpose:
  3394. //------------------------------------------------------------------------------
  3395. void CTriggerImpact::InputImpact( inputdata_t &inputdata )
  3396. {
  3397. // Output the force vector in case anyone else wants to use it
  3398. Vector vDir;
  3399. AngleVectors( GetLocalAngles(),&vDir );
  3400. m_pOutputForce.Set( m_flMagnitude * vDir, inputdata.pActivator, inputdata.pCaller);
  3401. // Enable long enough to throw objects inside me
  3402. Enable();
  3403. SetNextThink( gpGlobals->curtime + 0.1f );
  3404. SetThink(&CTriggerImpact::Disable);
  3405. }
  3406. //------------------------------------------------------------------------------
  3407. // Purpose:
  3408. //------------------------------------------------------------------------------
  3409. void CTriggerImpact::StartTouch(CBaseEntity *pOther)
  3410. {
  3411. //If the entity is valid and has physics, hit it
  3412. if ( ( pOther != NULL ) && ( pOther->VPhysicsGetObject() != NULL ) )
  3413. {
  3414. Vector vDir;
  3415. AngleVectors( GetLocalAngles(),&vDir );
  3416. vDir += RandomVector(-m_flNoise,m_flNoise);
  3417. pOther->VPhysicsGetObject()->ApplyForceCenter( m_flMagnitude * vDir );
  3418. }
  3419. // If the player, so a view kick
  3420. if (pOther->IsPlayer() && fabs(m_flMagnitude)>0 )
  3421. {
  3422. Vector vDir;
  3423. AngleVectors( GetLocalAngles(),&vDir );
  3424. float flPunch = -m_flViewkick*m_flMagnitude*TRIGGERIMPACT_VIEWKICK_SCALE;
  3425. pOther->ViewPunch( QAngle( vDir.y * flPunch, 0, vDir.x * flPunch ) );
  3426. }
  3427. }
  3428. //------------------------------------------------------------------------------
  3429. // Purpose:
  3430. //------------------------------------------------------------------------------
  3431. void CTriggerImpact::InputSetMagnitude( inputdata_t &inputdata )
  3432. {
  3433. m_flMagnitude = inputdata.value.Float();
  3434. }
  3435. //-----------------------------------------------------------------------------
  3436. // Purpose: Draw any debug text overlays
  3437. // Output : Current text offset from the top
  3438. //-----------------------------------------------------------------------------
  3439. int CTriggerImpact::DrawDebugTextOverlays(void)
  3440. {
  3441. int text_offset = BaseClass::DrawDebugTextOverlays();
  3442. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  3443. {
  3444. char tempstr[255];
  3445. Q_snprintf(tempstr,sizeof(tempstr),"Magnitude: %3.2f",m_flMagnitude);
  3446. EntityText(text_offset,tempstr,0);
  3447. text_offset++;
  3448. }
  3449. return text_offset;
  3450. }
  3451. //-----------------------------------------------------------------------------
  3452. // Purpose: Disables auto movement on players that touch it
  3453. //-----------------------------------------------------------------------------
  3454. const int SF_TRIGGER_MOVE_AUTODISABLE = 0x80; // disable auto movement
  3455. const int SF_TRIGGER_AUTO_DUCK = 0x800; // Duck automatically
  3456. class CTriggerPlayerMovement : public CBaseTrigger
  3457. {
  3458. DECLARE_CLASS( CTriggerPlayerMovement, CBaseTrigger );
  3459. public:
  3460. void Spawn( void );
  3461. void StartTouch( CBaseEntity *pOther );
  3462. void EndTouch( CBaseEntity *pOther );
  3463. DECLARE_DATADESC();
  3464. };
  3465. BEGIN_DATADESC( CTriggerPlayerMovement )
  3466. END_DATADESC()
  3467. LINK_ENTITY_TO_CLASS( trigger_playermovement, CTriggerPlayerMovement );
  3468. //-----------------------------------------------------------------------------
  3469. // Purpose: Called when spawning, after keyvalues have been handled.
  3470. //-----------------------------------------------------------------------------
  3471. void CTriggerPlayerMovement::Spawn( void )
  3472. {
  3473. if( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) )
  3474. {
  3475. // @Note (toml 01-07-04): fix up spawn flag collision coding error. Remove at some point once all maps fixed up please!
  3476. DevMsg("*** trigger_playermovement using obsolete spawnflag. Remove and reset with new value for \"Disable auto player movement\"\n" );
  3477. RemoveSpawnFlags(SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS);
  3478. AddSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE);
  3479. }
  3480. BaseClass::Spawn();
  3481. InitTrigger();
  3482. }
  3483. // UNDONE: This will not support a player touching more than one of these
  3484. // UNDONE: Do we care? If so, ref count automovement in the player?
  3485. void CTriggerPlayerMovement::StartTouch( CBaseEntity *pOther )
  3486. {
  3487. if (!PassesTriggerFilters(pOther))
  3488. return;
  3489. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  3490. if ( !pPlayer )
  3491. return;
  3492. if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) )
  3493. {
  3494. pPlayer->ForceButtons( IN_DUCK );
  3495. }
  3496. // UNDONE: Currently this is the only operation this trigger can do
  3497. if ( HasSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE) )
  3498. {
  3499. pPlayer->m_Local.m_bAllowAutoMovement = false;
  3500. }
  3501. }
  3502. void CTriggerPlayerMovement::EndTouch( CBaseEntity *pOther )
  3503. {
  3504. if (!PassesTriggerFilters(pOther))
  3505. return;
  3506. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  3507. if ( !pPlayer )
  3508. return;
  3509. if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) )
  3510. {
  3511. pPlayer->UnforceButtons( IN_DUCK );
  3512. }
  3513. if ( HasSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE) )
  3514. {
  3515. pPlayer->m_Local.m_bAllowAutoMovement = true;
  3516. }
  3517. }
  3518. //------------------------------------------------------------------------------
  3519. // Base VPhysics trigger implementation
  3520. //------------------------------------------------------------------------------
  3521. //------------------------------------------------------------------------------
  3522. // Save/load
  3523. //------------------------------------------------------------------------------
  3524. BEGIN_DATADESC( CBaseVPhysicsTrigger )
  3525. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  3526. DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
  3527. DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
  3528. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  3529. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  3530. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  3531. END_DATADESC()
  3532. //------------------------------------------------------------------------------
  3533. // Spawn
  3534. //------------------------------------------------------------------------------
  3535. void CBaseVPhysicsTrigger::Spawn()
  3536. {
  3537. Precache();
  3538. SetSolid( SOLID_VPHYSICS );
  3539. AddSolidFlags( FSOLID_NOT_SOLID );
  3540. // NOTE: Don't make yourself FSOLID_TRIGGER here or you'll get game
  3541. // collisions AND vphysics collisions. You don't want any game collisions
  3542. // so just use FSOLID_NOT_SOLID
  3543. SetMoveType( MOVETYPE_NONE );
  3544. SetModel( STRING( GetModelName() ) ); // set size and link into world
  3545. if ( showtriggers.GetInt() == 0 )
  3546. {
  3547. AddEffects( EF_NODRAW );
  3548. }
  3549. CreateVPhysics();
  3550. }
  3551. //------------------------------------------------------------------------------
  3552. // Create VPhysics
  3553. //------------------------------------------------------------------------------
  3554. bool CBaseVPhysicsTrigger::CreateVPhysics()
  3555. {
  3556. IPhysicsObject *pPhysics;
  3557. if ( !HasSpawnFlags( SF_VPHYSICS_MOTION_MOVEABLE ) )
  3558. {
  3559. pPhysics = VPhysicsInitStatic();
  3560. }
  3561. else
  3562. {
  3563. pPhysics = VPhysicsInitShadow( false, false );
  3564. }
  3565. pPhysics->BecomeTrigger();
  3566. return true;
  3567. }
  3568. //------------------------------------------------------------------------------
  3569. // Cleanup
  3570. //------------------------------------------------------------------------------
  3571. void CBaseVPhysicsTrigger::UpdateOnRemove()
  3572. {
  3573. if ( VPhysicsGetObject())
  3574. {
  3575. VPhysicsGetObject()->RemoveTrigger();
  3576. }
  3577. BaseClass::UpdateOnRemove();
  3578. }
  3579. //------------------------------------------------------------------------------
  3580. // Activate
  3581. //------------------------------------------------------------------------------
  3582. void CBaseVPhysicsTrigger::Activate( void )
  3583. {
  3584. // Get a handle to my filter entity if there is one
  3585. if (m_iFilterName != NULL_STRING)
  3586. {
  3587. m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
  3588. }
  3589. BaseClass::Activate();
  3590. }
  3591. //------------------------------------------------------------------------------
  3592. // Inputs
  3593. //------------------------------------------------------------------------------
  3594. void CBaseVPhysicsTrigger::InputToggle( inputdata_t &inputdata )
  3595. {
  3596. if ( m_bDisabled )
  3597. {
  3598. InputEnable( inputdata );
  3599. }
  3600. else
  3601. {
  3602. InputDisable( inputdata );
  3603. }
  3604. }
  3605. //-----------------------------------------------------------------------------
  3606. // Purpose:
  3607. //-----------------------------------------------------------------------------
  3608. void CBaseVPhysicsTrigger::InputEnable( inputdata_t &inputdata )
  3609. {
  3610. if ( m_bDisabled )
  3611. {
  3612. m_bDisabled = false;
  3613. if ( VPhysicsGetObject())
  3614. {
  3615. VPhysicsGetObject()->EnableCollisions( true );
  3616. }
  3617. }
  3618. }
  3619. //-----------------------------------------------------------------------------
  3620. // Purpose:
  3621. //-----------------------------------------------------------------------------
  3622. void CBaseVPhysicsTrigger::InputDisable( inputdata_t &inputdata )
  3623. {
  3624. if ( !m_bDisabled )
  3625. {
  3626. m_bDisabled = true;
  3627. if ( VPhysicsGetObject())
  3628. {
  3629. VPhysicsGetObject()->EnableCollisions( false );
  3630. }
  3631. }
  3632. }
  3633. //-----------------------------------------------------------------------------
  3634. // Purpose:
  3635. //-----------------------------------------------------------------------------
  3636. void CBaseVPhysicsTrigger::StartTouch( CBaseEntity *pOther )
  3637. {
  3638. }
  3639. //-----------------------------------------------------------------------------
  3640. // Purpose:
  3641. //-----------------------------------------------------------------------------
  3642. void CBaseVPhysicsTrigger::EndTouch( CBaseEntity *pOther )
  3643. {
  3644. }
  3645. //-----------------------------------------------------------------------------
  3646. // Purpose:
  3647. //-----------------------------------------------------------------------------
  3648. bool CBaseVPhysicsTrigger::PassesTriggerFilters( CBaseEntity *pOther )
  3649. {
  3650. if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS && !pOther->IsPlayer() )
  3651. return false;
  3652. // First test spawn flag filters
  3653. if ( HasSpawnFlags(SF_TRIGGER_ALLOW_ALL) ||
  3654. (HasSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS) && (pOther->GetFlags() & FL_CLIENT)) ||
  3655. (HasSpawnFlags(SF_TRIGGER_ALLOW_NPCS) && (pOther->GetFlags() & FL_NPC)) ||
  3656. (HasSpawnFlags(SF_TRIGGER_ALLOW_PUSHABLES) && FClassnameIs(pOther, "func_pushable")) ||
  3657. (HasSpawnFlags(SF_TRIGGER_ALLOW_PHYSICS) && pOther->GetMoveType() == MOVETYPE_VPHYSICS))
  3658. {
  3659. bool bOtherIsPlayer = pOther->IsPlayer();
  3660. if( HasSpawnFlags(SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS) && !bOtherIsPlayer )
  3661. {
  3662. CAI_BaseNPC *pNPC = pOther->MyNPCPointer();
  3663. if( !pNPC || !pNPC->IsPlayerAlly() )
  3664. {
  3665. return false;
  3666. }
  3667. }
  3668. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES) && bOtherIsPlayer )
  3669. {
  3670. if ( !((CBasePlayer*)pOther)->IsInAVehicle() )
  3671. return false;
  3672. }
  3673. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES) && bOtherIsPlayer )
  3674. {
  3675. if ( ((CBasePlayer*)pOther)->IsInAVehicle() )
  3676. return false;
  3677. }
  3678. CBaseFilter *pFilter = m_hFilter.Get();
  3679. return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
  3680. }
  3681. return false;
  3682. }
  3683. //=====================================================================================================================
  3684. //-----------------------------------------------------------------------------
  3685. // Purpose: VPhysics trigger that changes the motion of vphysics objects that touch it
  3686. //-----------------------------------------------------------------------------
  3687. class CTriggerVPhysicsMotion : public CBaseVPhysicsTrigger, public IMotionEvent
  3688. {
  3689. DECLARE_CLASS( CTriggerVPhysicsMotion, CBaseVPhysicsTrigger );
  3690. public:
  3691. void Spawn();
  3692. void Precache();
  3693. virtual void UpdateOnRemove();
  3694. bool CreateVPhysics();
  3695. void OnRestore();
  3696. // UNDONE: Pass trigger event in or change Start/EndTouch. Add ITriggerVPhysics perhaps?
  3697. // BUGBUG: If a player touches two of these, his movement will screw up.
  3698. // BUGBUG: If a player uses crouch/uncrouch it will generate touch events and clear the motioncontroller flag
  3699. void StartTouch( CBaseEntity *pOther );
  3700. void EndTouch( CBaseEntity *pOther );
  3701. void InputSetVelocityLimitTime( inputdata_t &inputdata );
  3702. float LinearLimit();
  3703. inline bool HasGravityScale() { return m_gravityScale != 1.0 ? true : false; }
  3704. inline bool HasAirDensity() { return m_addAirDensity != 0 ? true : false; }
  3705. inline bool HasLinearLimit() { return LinearLimit() != 0.0f; }
  3706. inline bool HasLinearScale() { return m_linearScale != 1.0 ? true : false; }
  3707. inline bool HasAngularLimit() { return m_angularLimit != 0 ? true : false; }
  3708. inline bool HasAngularScale() { return m_angularScale != 1.0 ? true : false; }
  3709. inline bool HasLinearForce() { return m_linearForce != 0.0 ? true : false; }
  3710. DECLARE_DATADESC();
  3711. virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  3712. private:
  3713. IPhysicsMotionController *m_pController;
  3714. #ifndef _XBOX
  3715. EntityParticleTrailInfo_t m_ParticleTrail;
  3716. #endif //!_XBOX
  3717. float m_gravityScale;
  3718. float m_addAirDensity;
  3719. float m_linearLimit;
  3720. float m_linearLimitDelta;
  3721. float m_linearLimitTime;
  3722. float m_linearLimitStart;
  3723. float m_linearLimitStartTime;
  3724. float m_linearScale;
  3725. float m_angularLimit;
  3726. float m_angularScale;
  3727. float m_linearForce;
  3728. QAngle m_linearForceAngles;
  3729. };
  3730. //------------------------------------------------------------------------------
  3731. // Save/load
  3732. //------------------------------------------------------------------------------
  3733. BEGIN_DATADESC( CTriggerVPhysicsMotion )
  3734. DEFINE_PHYSPTR( m_pController ),
  3735. #ifndef _XBOX
  3736. DEFINE_EMBEDDED( m_ParticleTrail ),
  3737. #endif //!_XBOX
  3738. DEFINE_INPUT( m_gravityScale, FIELD_FLOAT, "SetGravityScale" ),
  3739. DEFINE_INPUT( m_addAirDensity, FIELD_FLOAT, "SetAdditionalAirDensity" ),
  3740. DEFINE_INPUT( m_linearLimit, FIELD_FLOAT, "SetVelocityLimit" ),
  3741. DEFINE_INPUT( m_linearLimitDelta, FIELD_FLOAT, "SetVelocityLimitDelta" ),
  3742. DEFINE_FIELD( m_linearLimitTime, FIELD_FLOAT ),
  3743. DEFINE_FIELD( m_linearLimitStart, FIELD_TIME ),
  3744. DEFINE_FIELD( m_linearLimitStartTime, FIELD_TIME ),
  3745. DEFINE_INPUT( m_linearScale, FIELD_FLOAT, "SetVelocityScale" ),
  3746. DEFINE_INPUT( m_angularLimit, FIELD_FLOAT, "SetAngVelocityLimit" ),
  3747. DEFINE_INPUT( m_angularScale, FIELD_FLOAT, "SetAngVelocityScale" ),
  3748. DEFINE_INPUT( m_linearForce, FIELD_FLOAT, "SetLinearForce" ),
  3749. DEFINE_INPUT( m_linearForceAngles, FIELD_VECTOR, "SetLinearForceAngles" ),
  3750. DEFINE_INPUTFUNC( FIELD_STRING, "SetVelocityLimitTime", InputSetVelocityLimitTime ),
  3751. END_DATADESC()
  3752. LINK_ENTITY_TO_CLASS( trigger_vphysics_motion, CTriggerVPhysicsMotion );
  3753. //------------------------------------------------------------------------------
  3754. // Spawn
  3755. //------------------------------------------------------------------------------
  3756. void CTriggerVPhysicsMotion::Spawn()
  3757. {
  3758. Precache();
  3759. BaseClass::Spawn();
  3760. }
  3761. //------------------------------------------------------------------------------
  3762. // Precache
  3763. //------------------------------------------------------------------------------
  3764. void CTriggerVPhysicsMotion::Precache()
  3765. {
  3766. #ifndef _XBOX
  3767. if ( m_ParticleTrail.m_strMaterialName != NULL_STRING )
  3768. {
  3769. PrecacheMaterial( STRING(m_ParticleTrail.m_strMaterialName) );
  3770. }
  3771. #endif //!_XBOX
  3772. }
  3773. //------------------------------------------------------------------------------
  3774. // Create VPhysics
  3775. //------------------------------------------------------------------------------
  3776. float CTriggerVPhysicsMotion::LinearLimit()
  3777. {
  3778. if ( m_linearLimitTime == 0.0f )
  3779. return m_linearLimit;
  3780. float dt = gpGlobals->curtime - m_linearLimitStartTime;
  3781. if ( dt >= m_linearLimitTime )
  3782. {
  3783. m_linearLimitTime = 0.0;
  3784. return m_linearLimit;
  3785. }
  3786. dt /= m_linearLimitTime;
  3787. float flLimit = RemapVal( dt, 0.0f, 1.0f, m_linearLimitStart, m_linearLimit );
  3788. return flLimit;
  3789. }
  3790. //------------------------------------------------------------------------------
  3791. // Create VPhysics
  3792. //------------------------------------------------------------------------------
  3793. bool CTriggerVPhysicsMotion::CreateVPhysics()
  3794. {
  3795. m_pController = physenv->CreateMotionController( this );
  3796. BaseClass::CreateVPhysics();
  3797. return true;
  3798. }
  3799. //------------------------------------------------------------------------------
  3800. // Cleanup
  3801. //------------------------------------------------------------------------------
  3802. void CTriggerVPhysicsMotion::UpdateOnRemove()
  3803. {
  3804. if ( m_pController )
  3805. {
  3806. physenv->DestroyMotionController( m_pController );
  3807. m_pController = NULL;
  3808. }
  3809. BaseClass::UpdateOnRemove();
  3810. }
  3811. //------------------------------------------------------------------------------
  3812. // Restore
  3813. //------------------------------------------------------------------------------
  3814. void CTriggerVPhysicsMotion::OnRestore()
  3815. {
  3816. BaseClass::OnRestore();
  3817. if ( m_pController )
  3818. {
  3819. m_pController->SetEventHandler( this );
  3820. }
  3821. }
  3822. //------------------------------------------------------------------------------
  3823. // Start/End Touch
  3824. //------------------------------------------------------------------------------
  3825. // UNDONE: Pass trigger event in or change Start/EndTouch. Add ITriggerVPhysics perhaps?
  3826. // BUGBUG: If a player touches two of these, his movement will screw up.
  3827. // BUGBUG: If a player uses crouch/uncrouch it will generate touch events and clear the motioncontroller flag
  3828. void CTriggerVPhysicsMotion::StartTouch( CBaseEntity *pOther )
  3829. {
  3830. BaseClass::StartTouch( pOther );
  3831. if ( !PassesTriggerFilters(pOther) )
  3832. return;
  3833. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  3834. if ( pPlayer )
  3835. {
  3836. pPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );
  3837. pPlayer->m_Local.m_bSlowMovement = true;
  3838. }
  3839. triggerevent_t event;
  3840. PhysGetTriggerEvent( &event, this );
  3841. if ( event.pObject )
  3842. {
  3843. // these all get done again on save/load, so check
  3844. m_pController->AttachObject( event.pObject, true );
  3845. }
  3846. // Don't show these particles on the XBox
  3847. #ifndef _XBOX
  3848. if ( m_ParticleTrail.m_strMaterialName != NULL_STRING )
  3849. {
  3850. CEntityParticleTrail::Create( pOther, m_ParticleTrail, this );
  3851. }
  3852. #endif
  3853. if ( pOther->GetBaseAnimating() && pOther->GetBaseAnimating()->IsRagdoll() )
  3854. {
  3855. CRagdollBoogie::IncrementSuppressionCount( pOther );
  3856. }
  3857. }
  3858. //-----------------------------------------------------------------------------
  3859. // Purpose:
  3860. //-----------------------------------------------------------------------------
  3861. void CTriggerVPhysicsMotion::EndTouch( CBaseEntity *pOther )
  3862. {
  3863. BaseClass::EndTouch( pOther );
  3864. if ( !PassesTriggerFilters(pOther) )
  3865. return;
  3866. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  3867. if ( pPlayer )
  3868. {
  3869. pPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
  3870. pPlayer->m_Local.m_bSlowMovement = false;
  3871. }
  3872. triggerevent_t event;
  3873. PhysGetTriggerEvent( &event, this );
  3874. if ( event.pObject && m_pController )
  3875. {
  3876. m_pController->DetachObject( event.pObject );
  3877. }
  3878. #ifndef _XBOX
  3879. if ( m_ParticleTrail.m_strMaterialName != NULL_STRING )
  3880. {
  3881. CEntityParticleTrail::Destroy( pOther, m_ParticleTrail );
  3882. }
  3883. #endif //!_XBOX
  3884. if ( pOther->GetBaseAnimating() && pOther->GetBaseAnimating()->IsRagdoll() )
  3885. {
  3886. CRagdollBoogie::DecrementSuppressionCount( pOther );
  3887. }
  3888. }
  3889. //------------------------------------------------------------------------------
  3890. // Inputs
  3891. //------------------------------------------------------------------------------
  3892. void CTriggerVPhysicsMotion::InputSetVelocityLimitTime( inputdata_t &inputdata )
  3893. {
  3894. m_linearLimitStart = LinearLimit();
  3895. m_linearLimitStartTime = gpGlobals->curtime;
  3896. float args[2];
  3897. UTIL_StringToFloatArray( args, 2, inputdata.value.String() );
  3898. m_linearLimit = args[0];
  3899. m_linearLimitTime = args[1];
  3900. }
  3901. //------------------------------------------------------------------------------
  3902. // Apply the forces to the entity
  3903. //------------------------------------------------------------------------------
  3904. IMotionEvent::simresult_e CTriggerVPhysicsMotion::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  3905. {
  3906. if ( m_bDisabled )
  3907. return SIM_NOTHING;
  3908. linear.Init();
  3909. angular.Init();
  3910. if ( HasGravityScale() )
  3911. {
  3912. // assume object already has 1.0 gravities applied to it, so apply the additional amount
  3913. linear.z -= (m_gravityScale-1) * GetCurrentGravity();
  3914. }
  3915. if ( HasLinearForce() )
  3916. {
  3917. Vector vecForceDir;
  3918. AngleVectors( m_linearForceAngles, &vecForceDir );
  3919. VectorMA( linear, m_linearForce, vecForceDir, linear );
  3920. }
  3921. if ( HasAirDensity() || HasLinearLimit() || HasLinearScale() || HasAngularLimit() || HasAngularScale() )
  3922. {
  3923. Vector vel;
  3924. AngularImpulse angVel;
  3925. pObject->GetVelocity( &vel, &angVel );
  3926. vel += linear * deltaTime; // account for gravity scale
  3927. Vector unitVel = vel;
  3928. Vector unitAngVel = angVel;
  3929. float speed = VectorNormalize( unitVel );
  3930. float angSpeed = VectorNormalize( unitAngVel );
  3931. float speedScale = 0.0;
  3932. float angSpeedScale = 0.0;
  3933. if ( HasAirDensity() )
  3934. {
  3935. float linearDrag = -0.5 * m_addAirDensity * pObject->CalculateLinearDrag( unitVel ) * deltaTime;
  3936. if ( linearDrag < -1 )
  3937. {
  3938. linearDrag = -1;
  3939. }
  3940. speedScale += linearDrag / deltaTime;
  3941. float angDrag = -0.5 * m_addAirDensity * pObject->CalculateAngularDrag( unitAngVel ) * deltaTime;
  3942. if ( angDrag < -1 )
  3943. {
  3944. angDrag = -1;
  3945. }
  3946. angSpeedScale += angDrag / deltaTime;
  3947. }
  3948. if ( HasLinearLimit() && speed > m_linearLimit )
  3949. {
  3950. float flDeltaVel = (LinearLimit() - speed) / deltaTime;
  3951. if ( m_linearLimitDelta != 0.0f )
  3952. {
  3953. float flMaxDeltaVel = -m_linearLimitDelta / deltaTime;
  3954. if ( flDeltaVel < flMaxDeltaVel )
  3955. {
  3956. flDeltaVel = flMaxDeltaVel;
  3957. }
  3958. }
  3959. VectorMA( linear, flDeltaVel, unitVel, linear );
  3960. }
  3961. if ( HasAngularLimit() && angSpeed > m_angularLimit )
  3962. {
  3963. angular += ((m_angularLimit - angSpeed)/deltaTime) * unitAngVel;
  3964. }
  3965. if ( HasLinearScale() )
  3966. {
  3967. speedScale = ( (speedScale+1) * m_linearScale ) - 1;
  3968. }
  3969. if ( HasAngularScale() )
  3970. {
  3971. angSpeedScale = ( (angSpeedScale+1) * m_angularScale ) - 1;
  3972. }
  3973. linear += vel * speedScale;
  3974. angular += angVel * angSpeedScale;
  3975. }
  3976. return SIM_GLOBAL_ACCELERATION;
  3977. }
  3978. class CServerRagdollTrigger : public CBaseTrigger
  3979. {
  3980. DECLARE_CLASS( CServerRagdollTrigger, CBaseTrigger );
  3981. public:
  3982. virtual void StartTouch( CBaseEntity *pOther );
  3983. virtual void EndTouch( CBaseEntity *pOther );
  3984. virtual void Spawn( void );
  3985. };
  3986. LINK_ENTITY_TO_CLASS( trigger_serverragdoll, CServerRagdollTrigger );
  3987. void CServerRagdollTrigger::Spawn( void )
  3988. {
  3989. BaseClass::Spawn();
  3990. InitTrigger();
  3991. }
  3992. void CServerRagdollTrigger::StartTouch(CBaseEntity *pOther)
  3993. {
  3994. BaseClass::StartTouch( pOther );
  3995. if ( pOther->IsPlayer() )
  3996. return;
  3997. CBaseCombatCharacter *pCombatChar = pOther->MyCombatCharacterPointer();
  3998. if ( pCombatChar )
  3999. {
  4000. pCombatChar->m_bForceServerRagdoll = true;
  4001. }
  4002. }
  4003. void CServerRagdollTrigger::EndTouch(CBaseEntity *pOther)
  4004. {
  4005. BaseClass::EndTouch( pOther );
  4006. if ( pOther->IsPlayer() )
  4007. return;
  4008. CBaseCombatCharacter *pCombatChar = pOther->MyCombatCharacterPointer();
  4009. if ( pCombatChar )
  4010. {
  4011. pCombatChar->m_bForceServerRagdoll = false;
  4012. }
  4013. }
  4014. //-----------------------------------------------------------------------------
  4015. // Purpose: A trigger that adds impulse to touching entities
  4016. //-----------------------------------------------------------------------------
  4017. class CTriggerApplyImpulse : public CBaseTrigger
  4018. {
  4019. public:
  4020. DECLARE_CLASS( CTriggerApplyImpulse, CBaseTrigger );
  4021. DECLARE_DATADESC();
  4022. CTriggerApplyImpulse();
  4023. void Spawn( void );
  4024. void InputApplyImpulse( inputdata_t& );
  4025. private:
  4026. Vector m_vecImpulseDir;
  4027. float m_flForce;
  4028. };
  4029. BEGIN_DATADESC( CTriggerApplyImpulse )
  4030. DEFINE_KEYFIELD( m_vecImpulseDir, FIELD_VECTOR, "impulse_dir" ),
  4031. DEFINE_KEYFIELD( m_flForce, FIELD_FLOAT, "force" ),
  4032. DEFINE_INPUTFUNC( FIELD_VOID, "ApplyImpulse", InputApplyImpulse ),
  4033. END_DATADESC()
  4034. LINK_ENTITY_TO_CLASS( trigger_apply_impulse, CTriggerApplyImpulse );
  4035. CTriggerApplyImpulse::CTriggerApplyImpulse()
  4036. {
  4037. m_flForce = 300.f;
  4038. }
  4039. //-----------------------------------------------------------------------------
  4040. // Purpose:
  4041. //-----------------------------------------------------------------------------
  4042. void CTriggerApplyImpulse::Spawn()
  4043. {
  4044. // Convert pushdir from angles to a vector
  4045. Vector vecAbsDir;
  4046. QAngle angPushDir = QAngle(m_vecImpulseDir.x, m_vecImpulseDir.y, m_vecImpulseDir.z);
  4047. AngleVectors(angPushDir, &vecAbsDir);
  4048. // Transform the vector into entity space
  4049. VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecImpulseDir );
  4050. BaseClass::Spawn();
  4051. InitTrigger();
  4052. }
  4053. //-----------------------------------------------------------------------------
  4054. // Purpose:
  4055. //-----------------------------------------------------------------------------
  4056. void CTriggerApplyImpulse::InputApplyImpulse( inputdata_t& )
  4057. {
  4058. Vector vecImpulse = m_flForce * m_vecImpulseDir;
  4059. FOR_EACH_VEC( m_hTouchingEntities, i )
  4060. {
  4061. if ( m_hTouchingEntities[i] )
  4062. {
  4063. m_hTouchingEntities[i]->ApplyAbsVelocityImpulse( vecImpulse );
  4064. }
  4065. }
  4066. }
  4067. #ifdef HL1_DLL
  4068. //----------------------------------------------------------------------------------
  4069. // func_friction
  4070. //----------------------------------------------------------------------------------
  4071. class CFrictionModifier : public CBaseTrigger
  4072. {
  4073. DECLARE_CLASS( CFrictionModifier, CBaseTrigger );
  4074. public:
  4075. void Spawn( void );
  4076. bool KeyValue( const char *szKeyName, const char *szValue );
  4077. virtual void StartTouch(CBaseEntity *pOther);
  4078. virtual void EndTouch(CBaseEntity *pOther);
  4079. virtual int ObjectCaps( void ) { return CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  4080. float m_frictionFraction;
  4081. DECLARE_DATADESC();
  4082. };
  4083. LINK_ENTITY_TO_CLASS( func_friction, CFrictionModifier );
  4084. BEGIN_DATADESC( CFrictionModifier )
  4085. DEFINE_FIELD( m_frictionFraction, FIELD_FLOAT ),
  4086. END_DATADESC()
  4087. // Modify an entity's friction
  4088. void CFrictionModifier::Spawn( void )
  4089. {
  4090. BaseClass::Spawn();
  4091. InitTrigger();
  4092. }
  4093. // Sets toucher's friction to m_frictionFraction (1.0 = normal friction)
  4094. bool CFrictionModifier::KeyValue( const char *szKeyName, const char *szValue )
  4095. {
  4096. if (FStrEq(szKeyName, "modifier"))
  4097. {
  4098. m_frictionFraction = atof(szValue) / 100.0;
  4099. }
  4100. else
  4101. {
  4102. BaseClass::KeyValue( szKeyName, szValue );
  4103. }
  4104. return true;
  4105. }
  4106. void CFrictionModifier::StartTouch( CBaseEntity *pOther )
  4107. {
  4108. if ( !pOther->IsPlayer() ) // ignore player
  4109. {
  4110. pOther->SetFriction( m_frictionFraction );
  4111. }
  4112. }
  4113. void CFrictionModifier::EndTouch( CBaseEntity *pOther )
  4114. {
  4115. if ( !pOther->IsPlayer() ) // ignore player
  4116. {
  4117. pOther->SetFriction( 1.0f );
  4118. }
  4119. }
  4120. #endif //HL1_DLL
  4121. bool IsTriggerClass( CBaseEntity *pEntity )
  4122. {
  4123. if ( NULL != dynamic_cast<CBaseTrigger *>(pEntity) )
  4124. return true;
  4125. if ( NULL != dynamic_cast<CTriggerVPhysicsMotion *>(pEntity) )
  4126. return true;
  4127. if ( NULL != dynamic_cast<CTriggerVolume *>(pEntity) )
  4128. return true;
  4129. return false;
  4130. }