Counter Strike : Global Offensive Source Code
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.

6650 lines
184 KiB

  1. //===== Copyright � 1996-2005, 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 "fmtstr.h"
  36. #if defined ( PORTAL2 )
  37. #include "portal_player.h"
  38. #endif
  39. #include "weapon_c4.h"
  40. #ifdef HL2_DLL
  41. #include "hl2_player.h"
  42. #endif
  43. #ifdef PORTAL2
  44. extern const char *ChangeLevel_DestinationMapName( void );
  45. extern const char *ChangeLevel_OriginMapName( void );
  46. extern const char *ChangeLevel_GetLandmarkName( void );
  47. #endif // PORTAL2
  48. // memdbgon must be the last include file in a .cpp file!!!
  49. #include "tier0/memdbgon.h"
  50. #define DEBUG_TRANSITIONS_VERBOSE 2
  51. 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." );
  52. // Global list of triggers that care about weapon fire
  53. // Doesn't need saving, the triggers re-add themselves on restore.
  54. CUtlVector< CHandle<CTriggerMultiple> > g_hWeaponFireTriggers;
  55. extern CServerGameDLL g_ServerGameDLL;
  56. extern bool g_fGameOver;
  57. ConVar showtriggers( "showtriggers", "0", FCVAR_CHEAT, "Shows trigger brushes" );
  58. bool IsTriggerClass( CBaseEntity *pEntity );
  59. // Command to dynamically toggle trigger visibility
  60. void Cmd_ShowtriggersToggle_f( const CCommand &args )
  61. {
  62. // Loop through the entities in the game and make visible anything derived from CBaseTrigger
  63. CBaseEntity *pEntity = gEntList.FirstEnt();
  64. while ( pEntity )
  65. {
  66. if ( IsTriggerClass(pEntity) )
  67. {
  68. // If a classname is specified, only show triggers of that type
  69. if ( args.ArgC() > 1 )
  70. {
  71. const char *sClassname = args[1];
  72. if ( sClassname && sClassname[0] )
  73. {
  74. if ( !FClassnameIs( pEntity, sClassname ) )
  75. {
  76. pEntity = gEntList.NextEnt( pEntity );
  77. continue;
  78. }
  79. }
  80. }
  81. if ( pEntity->IsEffectActive( EF_NODRAW ) )
  82. {
  83. pEntity->RemoveEffects( EF_NODRAW );
  84. }
  85. else
  86. {
  87. pEntity->AddEffects( EF_NODRAW );
  88. }
  89. }
  90. pEntity = gEntList.NextEnt( pEntity );
  91. }
  92. }
  93. static ConCommand showtriggers_toggle( "showtriggers_toggle", Cmd_ShowtriggersToggle_f, "Toggle show triggers", FCVAR_CHEAT );
  94. // Global Savedata for base trigger
  95. BEGIN_DATADESC( CBaseTrigger )
  96. // Keyfields
  97. DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
  98. DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
  99. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  100. DEFINE_UTLVECTOR( m_hTouchingEntities, FIELD_EHANDLE ),
  101. // Inputs
  102. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  103. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  104. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  105. DEFINE_INPUTFUNC( FIELD_VOID, "TouchTest", InputTouchTest ),
  106. DEFINE_INPUTFUNC( FIELD_VOID, "StartTouch", InputStartTouch ),
  107. DEFINE_INPUTFUNC( FIELD_VOID, "EndTouch", InputEndTouch ),
  108. // Outputs
  109. DEFINE_OUTPUT( m_OnStartTouch, "OnStartTouch"),
  110. DEFINE_OUTPUT( m_OnStartTouchAll, "OnStartTouchAll"),
  111. DEFINE_OUTPUT( m_OnEndTouch, "OnEndTouch"),
  112. DEFINE_OUTPUT( m_OnEndTouchAll, "OnEndTouchAll"),
  113. DEFINE_OUTPUT( m_OnTouching, "OnTouching" ),
  114. DEFINE_OUTPUT( m_OnNotTouching, "OnNotTouching" ),
  115. END_DATADESC()
  116. LINK_ENTITY_TO_CLASS( trigger, CBaseTrigger );
  117. IMPLEMENT_SERVERCLASS_ST( CBaseTrigger, DT_BaseTrigger )
  118. SendPropBool( SENDINFO( m_bClientSidePredicted ) ),
  119. SendPropInt( SENDINFO(m_spawnflags), -1, SPROP_NOSCALE )
  120. END_SEND_TABLE()
  121. CBaseTrigger::CBaseTrigger()
  122. {
  123. AddEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
  124. m_bClientSidePredicted = false;
  125. }
  126. //------------------------------------------------------------------------------
  127. // Purpose: Input handler to turn on this trigger.
  128. //------------------------------------------------------------------------------
  129. void CBaseTrigger::InputEnable( inputdata_t &inputdata )
  130. {
  131. Enable();
  132. }
  133. //------------------------------------------------------------------------------
  134. // Purpose: Input handler to turn off this trigger.
  135. //------------------------------------------------------------------------------
  136. void CBaseTrigger::InputDisable( inputdata_t &inputdata )
  137. {
  138. Disable();
  139. }
  140. void CBaseTrigger::InputTouchTest( inputdata_t &inputdata )
  141. {
  142. TouchTest();
  143. }
  144. //------------------------------------------------------------------------------
  145. //------------------------------------------------------------------------------
  146. void CBaseTrigger::Spawn()
  147. {
  148. if ( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) || HasSpawnFlags( SF_TRIGGER_ONLY_NPCS_IN_VEHICLES ) )
  149. {
  150. // Automatically set this trigger to work with NPC's.
  151. AddSpawnFlags( SF_TRIGGER_ALLOW_NPCS );
  152. }
  153. if ( HasSpawnFlags( SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES ) )
  154. {
  155. AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
  156. }
  157. if ( HasSpawnFlags( SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES ) )
  158. {
  159. AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS );
  160. }
  161. BaseClass::Spawn();
  162. }
  163. //------------------------------------------------------------------------------
  164. // Cleanup
  165. //------------------------------------------------------------------------------
  166. void CBaseTrigger::UpdateOnRemove( void )
  167. {
  168. if ( VPhysicsGetObject())
  169. {
  170. VPhysicsGetObject()->RemoveTrigger();
  171. }
  172. BaseClass::UpdateOnRemove();
  173. }
  174. //------------------------------------------------------------------------------
  175. // Purpose: Turns on this trigger.
  176. //------------------------------------------------------------------------------
  177. void CBaseTrigger::Enable( void )
  178. {
  179. m_bDisabled = false;
  180. if ( VPhysicsGetObject())
  181. {
  182. VPhysicsGetObject()->EnableCollisions( true );
  183. }
  184. if (!IsSolidFlagSet( FSOLID_TRIGGER ))
  185. {
  186. AddSolidFlags( FSOLID_TRIGGER );
  187. PhysicsTouchTriggers();
  188. }
  189. }
  190. //------------------------------------------------------------------------------
  191. // Purpose :
  192. //------------------------------------------------------------------------------
  193. void CBaseTrigger::Activate( void )
  194. {
  195. // Get a handle to my filter entity if there is one
  196. if (m_iFilterName != NULL_STRING)
  197. {
  198. m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
  199. }
  200. BaseClass::Activate();
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose: Called after player becomes active in the game
  204. //-----------------------------------------------------------------------------
  205. void CBaseTrigger::PostClientActive( void )
  206. {
  207. BaseClass::PostClientActive();
  208. if ( !m_bDisabled )
  209. {
  210. PhysicsTouchTriggers();
  211. }
  212. }
  213. //------------------------------------------------------------------------------
  214. // Purpose: Turns off this trigger.
  215. //------------------------------------------------------------------------------
  216. void CBaseTrigger::Disable( void )
  217. {
  218. m_bDisabled = true;
  219. if ( VPhysicsGetObject())
  220. {
  221. VPhysicsGetObject()->EnableCollisions( false );
  222. }
  223. if (IsSolidFlagSet(FSOLID_TRIGGER))
  224. {
  225. RemoveSolidFlags( FSOLID_TRIGGER );
  226. PhysicsTouchTriggers();
  227. }
  228. }
  229. //------------------------------------------------------------------------------
  230. // Purpose: Tests to see if anything is touching this trigger.
  231. //------------------------------------------------------------------------------
  232. void CBaseTrigger::TouchTest( void )
  233. {
  234. // If the trigger is disabled don't test to see if anything is touching it.
  235. if ( !m_bDisabled )
  236. {
  237. if ( m_hTouchingEntities.Count() !=0 )
  238. {
  239. m_OnTouching.FireOutput( this, this );
  240. }
  241. else
  242. {
  243. m_OnNotTouching.FireOutput( this, this );
  244. }
  245. }
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose: Draw any debug text overlays
  249. // Output : Current text offset from the top
  250. //-----------------------------------------------------------------------------
  251. int CBaseTrigger::DrawDebugTextOverlays(void)
  252. {
  253. int text_offset = BaseClass::DrawDebugTextOverlays();
  254. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  255. {
  256. // --------------
  257. // Print Target
  258. // --------------
  259. char tempstr[255];
  260. if (IsSolidFlagSet(FSOLID_TRIGGER))
  261. {
  262. Q_strncpy(tempstr,"State: Enabled",sizeof(tempstr));
  263. }
  264. else
  265. {
  266. Q_strncpy(tempstr,"State: Disabled",sizeof(tempstr));
  267. }
  268. EntityText(text_offset,tempstr,0);
  269. text_offset++;
  270. }
  271. return text_offset;
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Purpose:
  275. //-----------------------------------------------------------------------------
  276. void CBaseTrigger::InitTrigger( )
  277. {
  278. SetSolid( GetParent() ? SOLID_VPHYSICS : SOLID_BSP );
  279. AddSolidFlags( FSOLID_NOT_SOLID );
  280. if (m_bDisabled)
  281. {
  282. RemoveSolidFlags( FSOLID_TRIGGER );
  283. }
  284. else
  285. {
  286. AddSolidFlags( FSOLID_TRIGGER );
  287. }
  288. SetMoveType( MOVETYPE_NONE );
  289. SetModel( STRING( GetModelName() ) ); // set size and link into world
  290. if ( showtriggers.GetInt() == 0 )
  291. {
  292. AddEffects( EF_NODRAW );
  293. }
  294. m_hTouchingEntities.Purge();
  295. if ( HasSpawnFlags( SF_TRIG_TOUCH_DEBRIS ) )
  296. {
  297. CollisionProp()->AddSolidFlags( FSOLID_TRIGGER_TOUCH_DEBRIS );
  298. }
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Returns true if this entity passes the filter criteria, false if not.
  302. // Input : pOther - The entity to be filtered.
  303. //-----------------------------------------------------------------------------
  304. bool CBaseTrigger::PassesTriggerFilters(CBaseEntity *pOther)
  305. {
  306. // First test spawn flag filters
  307. if ( HasSpawnFlags(SF_TRIGGER_ALLOW_ALL) ||
  308. (HasSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS) && (pOther->GetFlags() & FL_CLIENT)) ||
  309. (HasSpawnFlags(SF_TRIGGER_ALLOW_NPCS) && (pOther->GetFlags() & FL_NPC)) ||
  310. (HasSpawnFlags(SF_TRIGGER_ALLOW_PUSHABLES) && FClassnameIs(pOther, "func_pushable")) ||
  311. (HasSpawnFlags(SF_TRIGGER_ALLOW_PHYSICS) && pOther->GetMoveType() == MOVETYPE_VPHYSICS)
  312. #if defined( HL2_EPISODIC ) || defined( TF_DLL )
  313. ||
  314. ( HasSpawnFlags(SF_TRIG_TOUCH_DEBRIS) &&
  315. (pOther->GetCollisionGroup() == COLLISION_GROUP_DEBRIS ||
  316. pOther->GetCollisionGroup() == COLLISION_GROUP_DEBRIS_TRIGGER ||
  317. pOther->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS)
  318. )
  319. #endif
  320. )
  321. {
  322. if ( pOther->GetFlags() & FL_NPC )
  323. {
  324. CAI_BaseNPC *pNPC = pOther->MyNPCPointer();
  325. if ( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) )
  326. {
  327. if ( !pNPC || !pNPC->IsPlayerAlly() )
  328. {
  329. return false;
  330. }
  331. }
  332. if ( HasSpawnFlags( SF_TRIGGER_ONLY_NPCS_IN_VEHICLES ) )
  333. {
  334. if ( !pNPC || !pNPC->IsInAVehicle() )
  335. return false;
  336. }
  337. }
  338. bool bOtherIsPlayer = pOther->IsPlayer();
  339. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES) && bOtherIsPlayer )
  340. {
  341. if ( !((CBasePlayer*)pOther)->IsInAVehicle() )
  342. return false;
  343. // Make sure we're also not exiting the vehicle at the moment
  344. IServerVehicle *pVehicleServer = ((CBasePlayer*)pOther)->GetVehicle();
  345. if ( pVehicleServer == NULL )
  346. return false;
  347. if ( pVehicleServer->IsPassengerExiting() )
  348. return false;
  349. }
  350. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES) && bOtherIsPlayer )
  351. {
  352. if ( ((CBasePlayer*)pOther)->IsInAVehicle() )
  353. return false;
  354. }
  355. CBaseFilter *pFilter = m_hFilter.Get();
  356. return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
  357. }
  358. return false;
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Purpose: Called to simulate what happens when an entity touches the trigger.
  362. // Input : pOther - The entity that is touching us.
  363. //-----------------------------------------------------------------------------
  364. void CBaseTrigger::InputStartTouch( inputdata_t &inputdata )
  365. {
  366. //Pretend we just touched the trigger.
  367. StartTouch( inputdata.pCaller );
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose: Called to simulate what happens when an entity leaves the trigger.
  371. // Input : pOther - The entity that is touching us.
  372. //-----------------------------------------------------------------------------
  373. void CBaseTrigger::InputEndTouch( inputdata_t &inputdata )
  374. {
  375. //And... pretend we left the trigger.
  376. EndTouch( inputdata.pCaller );
  377. }
  378. //-----------------------------------------------------------------------------
  379. // Purpose: Called when the first entity that passes filters starts touching us.
  380. // Input : pOther - The entity that is touching us.
  381. //-----------------------------------------------------------------------------
  382. void CBaseTrigger::OnStartTouchAll(CBaseEntity *pOther)
  383. {
  384. m_OnStartTouchAll.FireOutput( pOther, this );
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose: Called when the last entity stops touching us
  388. // Input : pOther - The entity that is touching us.
  389. //-----------------------------------------------------------------------------
  390. void CBaseTrigger::OnEndTouchAll(CBaseEntity *pOther)
  391. {
  392. m_OnEndTouchAll.FireOutput(pOther, this);
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose: Called when an entity starts touching us.
  396. // Input : pOther - The entity that is touching us.
  397. //-----------------------------------------------------------------------------
  398. void CBaseTrigger::StartTouch(CBaseEntity *pOther)
  399. {
  400. if (PassesTriggerFilters(pOther) )
  401. {
  402. EHANDLE hOther;
  403. hOther = pOther;
  404. bool bAdded = false;
  405. if ( m_hTouchingEntities.Find( hOther ) == m_hTouchingEntities.InvalidIndex() )
  406. {
  407. m_hTouchingEntities.AddToTail( hOther );
  408. bAdded = true;
  409. }
  410. m_OnStartTouch.FireOutput(pOther, this);
  411. if ( bAdded && ( m_hTouchingEntities.Count() == 1 ) )
  412. {
  413. // First entity to touch us that passes our filters
  414. OnStartTouchAll( pOther );
  415. }
  416. }
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Purpose: Called when an entity stops touching us.
  420. // Input : pOther - The entity that was touching us.
  421. //-----------------------------------------------------------------------------
  422. void CBaseTrigger::EndTouch(CBaseEntity *pOther)
  423. {
  424. if ( IsTouching( pOther ) )
  425. {
  426. EHANDLE hOther;
  427. hOther = pOther;
  428. m_hTouchingEntities.FindAndRemove( hOther );
  429. //FIXME: Without this, triggers fire their EndTouch outputs when they are disabled!
  430. //if ( !m_bDisabled )
  431. //{
  432. m_OnEndTouch.FireOutput(pOther, this);
  433. //}
  434. // If there are no more entities touching this trigger, fire the lost all touches
  435. // Loop through the touching entities backwards. Clean out old ones, and look for existing
  436. bool bFoundOtherTouchee = false;
  437. int iSize = m_hTouchingEntities.Count();
  438. for ( int i = iSize-1; i >= 0; i-- )
  439. {
  440. EHANDLE hOther;
  441. hOther = m_hTouchingEntities[i];
  442. if ( !hOther )
  443. {
  444. m_hTouchingEntities.Remove( i );
  445. }
  446. else
  447. {
  448. bFoundOtherTouchee = true;
  449. }
  450. }
  451. //FIXME: Without this, triggers fire their EndTouch outputs when they are disabled!
  452. // Didn't find one?
  453. if ( !bFoundOtherTouchee /*&& !m_bDisabled*/ )
  454. {
  455. OnEndTouchAll( pOther );
  456. }
  457. }
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Purpose: Return true if the specified entity is touching us
  461. //-----------------------------------------------------------------------------
  462. bool CBaseTrigger::IsTouching( CBaseEntity *pOther )
  463. {
  464. EHANDLE hOther;
  465. hOther = pOther;
  466. return ( m_hTouchingEntities.Find( hOther ) != m_hTouchingEntities.InvalidIndex() );
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Purpose: Return a pointer to the first entity of the specified type being touched by this trigger
  470. //-----------------------------------------------------------------------------
  471. CBaseEntity *CBaseTrigger::GetTouchedEntityOfType( const char *sClassName )
  472. {
  473. int iCount = m_hTouchingEntities.Count();
  474. for ( int i = 0; i < iCount; i++ )
  475. {
  476. CBaseEntity *pEntity = m_hTouchingEntities[i];
  477. if ( FClassnameIs( pEntity, sClassName ) )
  478. return pEntity;
  479. }
  480. return NULL;
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Purpose: Toggles this trigger between enabled and disabled.
  484. //-----------------------------------------------------------------------------
  485. void CBaseTrigger::InputToggle( inputdata_t &inputdata )
  486. {
  487. if (IsSolidFlagSet( FSOLID_TRIGGER ))
  488. {
  489. RemoveSolidFlags(FSOLID_TRIGGER);
  490. }
  491. else
  492. {
  493. AddSolidFlags(FSOLID_TRIGGER);
  494. }
  495. PhysicsTouchTriggers();
  496. }
  497. //-----------------------------------------------------------------------------
  498. // Purpose: Removes anything that touches it. If the trigger has a targetname,
  499. // firing it will toggle state.
  500. //-----------------------------------------------------------------------------
  501. class CTriggerRemove : public CBaseTrigger
  502. {
  503. public:
  504. DECLARE_CLASS( CTriggerRemove, CBaseTrigger );
  505. void Spawn( void );
  506. void Touch( CBaseEntity *pOther );
  507. DECLARE_DATADESC();
  508. // Outputs
  509. COutputEvent m_OnRemove;
  510. };
  511. BEGIN_DATADESC( CTriggerRemove )
  512. // Outputs
  513. DEFINE_OUTPUT( m_OnRemove, "OnRemove" ),
  514. END_DATADESC()
  515. LINK_ENTITY_TO_CLASS( trigger_remove, CTriggerRemove );
  516. //-----------------------------------------------------------------------------
  517. // Purpose:
  518. //-----------------------------------------------------------------------------
  519. void CTriggerRemove::Spawn( void )
  520. {
  521. BaseClass::Spawn();
  522. InitTrigger();
  523. }
  524. //-----------------------------------------------------------------------------
  525. // Purpose: Trigger hurt that causes radiation will do a radius check and set
  526. // the player's geiger counter level according to distance from center
  527. // of trigger.
  528. //-----------------------------------------------------------------------------
  529. void CTriggerRemove::Touch( CBaseEntity *pOther )
  530. {
  531. if (!PassesTriggerFilters(pOther))
  532. return;
  533. UTIL_Remove( pOther );
  534. }
  535. BEGIN_DATADESC( CTriggerHurt )
  536. // Function Pointers
  537. DEFINE_FUNCTION( RadiationThink ),
  538. DEFINE_FUNCTION( HurtThink ),
  539. DEFINE_FUNCTION( NavThink ),
  540. // Fields
  541. DEFINE_FIELD( m_flOriginalDamage, FIELD_FLOAT ),
  542. DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "damage" ),
  543. DEFINE_KEYFIELD( m_flDamageCap, FIELD_FLOAT, "damagecap" ),
  544. DEFINE_KEYFIELD( m_bitsDamageInflict, FIELD_INTEGER, "damagetype" ),
  545. DEFINE_KEYFIELD( m_damageModel, FIELD_INTEGER, "damagemodel" ),
  546. DEFINE_KEYFIELD( m_bNoDmgForce, FIELD_BOOLEAN, "nodmgforce" ),
  547. DEFINE_FIELD( m_flLastDmgTime, FIELD_TIME ),
  548. DEFINE_FIELD( m_flDmgResetTime, FIELD_TIME ),
  549. DEFINE_UTLVECTOR( m_hurtEntities, FIELD_EHANDLE ),
  550. // Inputs
  551. DEFINE_INPUT( m_flDamage, FIELD_FLOAT, "SetDamage" ),
  552. // Outputs
  553. DEFINE_OUTPUT( m_OnHurt, "OnHurt" ),
  554. DEFINE_OUTPUT( m_OnHurtPlayer, "OnHurtPlayer" ),
  555. END_DATADESC()
  556. LINK_ENTITY_TO_CLASS( trigger_hurt, CTriggerHurt );
  557. //-----------------------------------------------------------------------------
  558. // Purpose: Called when spawning, after keyvalues have been handled.
  559. //-----------------------------------------------------------------------------
  560. void CTriggerHurt::Spawn( void )
  561. {
  562. BaseClass::Spawn();
  563. InitTrigger();
  564. m_flOriginalDamage = m_flDamage;
  565. SetNextThink( TICK_NEVER_THINK );
  566. SetThink( NULL );
  567. if (m_bitsDamageInflict & DMG_RADIATION)
  568. {
  569. SetThink ( &CTriggerHurt::RadiationThink );
  570. SetNextThink( gpGlobals->curtime + random->RandomFloat(0.0, 0.5) );
  571. }
  572. if ( TheNavMesh )
  573. {
  574. SetContextThink( &CTriggerHurt::NavThink, gpGlobals->curtime, "NavContext" );
  575. }
  576. }
  577. //-----------------------------------------------------------------------------
  578. // Purpose: Marks nav areas as damaging.
  579. //-----------------------------------------------------------------------------
  580. void CTriggerHurt::NavThink( void )
  581. {
  582. const float deltaT = 5.0f;
  583. SetContextThink( &CTriggerHurt::NavThink, gpGlobals->curtime + deltaT, "NavContext" );
  584. if ( !TheNavMesh->IsLoaded() )
  585. return;
  586. if ( m_bDisabled )
  587. return;
  588. // mark overlapping nav areas as "damaging"
  589. NavAreaCollector overlap;
  590. Extent extent;
  591. CollisionProp()->WorldSpaceAABB( &extent.lo, &extent.hi );
  592. extent.lo.z -= HumanHeight;
  593. if ( m_bitsDamageInflict & DMG_BURN )
  594. {
  595. const float DangerBloat = HalfHumanWidth;
  596. Vector dangerBloat( DangerBloat, DangerBloat, 0 );
  597. extent.lo -= dangerBloat;
  598. extent.hi += dangerBloat;
  599. }
  600. TheNavMesh->ForAllAreasOverlappingExtent( overlap, extent );
  601. FOR_EACH_VEC( overlap.m_area, it )
  602. {
  603. CNavArea *area = overlap.m_area[ it ];
  604. area->MarkAsDamaging( deltaT + 1.0f );
  605. }
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose: Trigger hurt that causes radiation will do a radius check and set
  609. // the player's geiger counter level according to distance from center
  610. // of trigger.
  611. //-----------------------------------------------------------------------------
  612. void CTriggerHurt::RadiationThink( void )
  613. {
  614. // check to see if a player is in pvs
  615. // if not, continue
  616. Vector vecSurroundMins, vecSurroundMaxs;
  617. CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  618. CBasePlayer *pPlayer = static_cast<CBasePlayer *>(UTIL_FindClientInPVS( vecSurroundMins, vecSurroundMaxs ));
  619. if (pPlayer)
  620. {
  621. // get range to player;
  622. float flRange = CollisionProp()->CalcDistanceFromPoint( pPlayer->WorldSpaceCenter() );
  623. flRange *= 3.0f;
  624. pPlayer->NotifyNearbyRadiationSource(flRange);
  625. }
  626. float dt = gpGlobals->curtime - m_flLastDmgTime;
  627. if ( dt >= 0.5 )
  628. {
  629. HurtAllTouchers( dt );
  630. }
  631. SetNextThink( gpGlobals->curtime + 0.25 );
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose: When touched, a hurt trigger does m_flDamage points of damage each half-second.
  635. // Input : pOther - The entity that is touching us.
  636. //-----------------------------------------------------------------------------
  637. bool CTriggerHurt::HurtEntity( CBaseEntity *pOther, float damage )
  638. {
  639. if ( !pOther->m_takedamage || !PassesTriggerFilters(pOther) )
  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( 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( &CTriggerHurt::HurtThink );
  766. SetNextThink( gpGlobals->curtime );
  767. }
  768. }
  769. // ##################################################################################
  770. // >> TriggerMultiple
  771. // ##################################################################################
  772. LINK_ENTITY_TO_CLASS( trigger_multiple, CTriggerMultiple );
  773. BEGIN_DATADESC( CTriggerMultiple )
  774. // Function Pointers
  775. DEFINE_FUNCTION(MultiTouch),
  776. DEFINE_FUNCTION(MultiWaitOver ),
  777. // Outputs
  778. DEFINE_OUTPUT(m_OnTrigger, "OnTrigger")
  779. END_DATADESC()
  780. //-----------------------------------------------------------------------------
  781. // Purpose: Called when spawning, after keyvalues have been handled.
  782. //-----------------------------------------------------------------------------
  783. void CTriggerMultiple::Spawn( void )
  784. {
  785. BaseClass::Spawn();
  786. InitTrigger();
  787. if (m_flWait == 0)
  788. {
  789. m_flWait = 0.2;
  790. }
  791. ASSERTSZ(m_iHealth == 0, "trigger_multiple with health");
  792. SetTouch( &CTriggerMultiple::MultiTouch );
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Purpose: Touch function. Activates the trigger.
  796. // Input : pOther - The thing that touched us.
  797. //-----------------------------------------------------------------------------
  798. void CTriggerMultiple::MultiTouch(CBaseEntity *pOther)
  799. {
  800. if (PassesTriggerFilters(pOther))
  801. {
  802. ActivateMultiTrigger( pOther );
  803. }
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Purpose:
  807. // Input : pActivator -
  808. //-----------------------------------------------------------------------------
  809. void CTriggerMultiple::ActivateMultiTrigger(CBaseEntity *pActivator)
  810. {
  811. if (GetNextThink() > gpGlobals->curtime)
  812. return; // still waiting for reset time
  813. m_hActivator = pActivator;
  814. m_OnTrigger.FireOutput(m_hActivator, this);
  815. if (m_flWait > 0)
  816. {
  817. SetThink( &CTriggerMultiple::MultiWaitOver );
  818. SetNextThink( gpGlobals->curtime + m_flWait );
  819. }
  820. else
  821. {
  822. // we can't just remove (self) here, because this is a touch function
  823. // called while C code is looping through area links...
  824. SetTouch( NULL );
  825. SetNextThink( gpGlobals->curtime + 0.1f );
  826. SetThink( &CTriggerMultiple::SUB_Remove );
  827. }
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Purpose: The wait time has passed, so set back up for another activation
  831. //-----------------------------------------------------------------------------
  832. void CTriggerMultiple::MultiWaitOver( void )
  833. {
  834. SetThink( NULL );
  835. }
  836. // ##################################################################################
  837. // >> TriggerOnce
  838. // ##################################################################################
  839. class CTriggerOnce : public CTriggerMultiple
  840. {
  841. DECLARE_CLASS( CTriggerOnce, CTriggerMultiple );
  842. public:
  843. void Spawn( void );
  844. };
  845. LINK_ENTITY_TO_CLASS( trigger_once, CTriggerOnce );
  846. void CTriggerOnce::Spawn( void )
  847. {
  848. BaseClass::Spawn();
  849. m_flWait = -1;
  850. }
  851. // ##################################################################################
  852. // >> TriggerLook
  853. //
  854. // Triggers once when player is looking at m_target
  855. //
  856. // ##################################################################################
  857. #define SF_TRIGGERLOOK_FIREONCE 128
  858. #define SF_TRIGGERLOOK_USEVELOCITY 256
  859. class CTriggerLook : public CTriggerOnce
  860. {
  861. DECLARE_CLASS( CTriggerLook, CTriggerOnce );
  862. public:
  863. EHANDLE m_hLookTarget;
  864. float m_flFieldOfView;
  865. float m_flLookTime; // How long must I look for
  866. float m_flLookTimeTotal; // How long have I looked
  867. float m_flLookTimeLast; // When did I last look
  868. float m_flTimeoutDuration; // Number of seconds after start touch to fire anyway
  869. bool m_bTimeoutFired; // True if the OnTimeout output fired since the last StartTouch.
  870. EHANDLE m_hActivator; // The entity that triggered us.
  871. void Spawn( void );
  872. void Activate( void );
  873. void Touch( CBaseEntity *pOther );
  874. void StartTouch(CBaseEntity *pOther);
  875. virtual void EndTouch( CBaseEntity *pOther );
  876. virtual void OnEndTouchAll(CBaseEntity *pOther);
  877. int DrawDebugTextOverlays(void);
  878. DECLARE_DATADESC();
  879. private:
  880. void Trigger(CBaseEntity *pActivator, bool bTimeout);
  881. void TimeoutThink();
  882. COutputEvent m_OnTimeout;
  883. };
  884. LINK_ENTITY_TO_CLASS( trigger_look, CTriggerLook );
  885. BEGIN_DATADESC( CTriggerLook )
  886. DEFINE_FIELD( m_hLookTarget, FIELD_EHANDLE ),
  887. DEFINE_FIELD( m_flLookTimeTotal, FIELD_FLOAT ),
  888. DEFINE_FIELD( m_flLookTimeLast, FIELD_TIME ),
  889. DEFINE_KEYFIELD( m_flTimeoutDuration, FIELD_FLOAT, "timeout" ),
  890. DEFINE_FIELD( m_bTimeoutFired, FIELD_BOOLEAN ),
  891. DEFINE_FIELD( m_hActivator, FIELD_EHANDLE ),
  892. DEFINE_OUTPUT( m_OnTimeout, "OnTimeout" ),
  893. DEFINE_FUNCTION( TimeoutThink ),
  894. // Inputs
  895. DEFINE_INPUT( m_flFieldOfView, FIELD_FLOAT, "FieldOfView" ),
  896. DEFINE_INPUT( m_flLookTime, FIELD_FLOAT, "LookTime" ),
  897. END_DATADESC()
  898. //------------------------------------------------------------------------------
  899. // Purpose:
  900. //------------------------------------------------------------------------------
  901. void CTriggerLook::Spawn( void )
  902. {
  903. m_hLookTarget = NULL;
  904. m_flLookTimeTotal = -1;
  905. m_bTimeoutFired = false;
  906. BaseClass::Spawn();
  907. }
  908. //------------------------------------------------------------------------------
  909. // Purpose:
  910. //------------------------------------------------------------------------------
  911. void CTriggerLook::Activate( void )
  912. {
  913. BaseClass::Activate();
  914. m_hLookTarget = gEntList.FindEntityByName( NULL, STRING( m_target ) );
  915. if (m_hLookTarget == NULL)
  916. {
  917. Warning ( "CTriggerLook '%s' has a bad look target (%s)\n", GetDebugName(), ( STRING( m_target ) != NULL )?( STRING(m_target)):("<unnamed>") );
  918. }
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose:
  922. // Input : pOther -
  923. //-----------------------------------------------------------------------------
  924. void CTriggerLook::StartTouch(CBaseEntity *pOther)
  925. {
  926. BaseClass::StartTouch(pOther);
  927. if (pOther->IsPlayer() && PassesTriggerFilters( pOther ) && m_flTimeoutDuration)
  928. {
  929. m_bTimeoutFired = false;
  930. m_hActivator = pOther;
  931. SetThink(&CTriggerLook::TimeoutThink);
  932. SetNextThink(gpGlobals->curtime + m_flTimeoutDuration);
  933. }
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Purpose:
  937. //-----------------------------------------------------------------------------
  938. void CTriggerLook::TimeoutThink(void)
  939. {
  940. Trigger(m_hActivator, true);
  941. }
  942. //------------------------------------------------------------------------------
  943. // Purpose:
  944. //------------------------------------------------------------------------------
  945. void CTriggerLook::EndTouch(CBaseEntity *pOther)
  946. {
  947. BaseClass::EndTouch(pOther);
  948. }
  949. void CTriggerLook::OnEndTouchAll( CBaseEntity *pOther )
  950. {
  951. BaseClass::OnEndTouchAll(pOther);
  952. SetThink( NULL );
  953. SetNextThink( TICK_NEVER_THINK );
  954. m_flLookTimeTotal = -1;
  955. }
  956. //------------------------------------------------------------------------------
  957. // Purpose:
  958. //------------------------------------------------------------------------------
  959. void CTriggerLook::Touch(CBaseEntity *pOther)
  960. {
  961. // Don't fire the OnTrigger if we've already fired the OnTimeout. This will be
  962. // reset in OnEndTouch.
  963. if (m_bTimeoutFired)
  964. return;
  965. // --------------------------------
  966. // Make sure we have a look target
  967. // --------------------------------
  968. if (m_hLookTarget == NULL)
  969. {
  970. return;
  971. }
  972. // This is designed for single player only
  973. // so we'll always have the same player
  974. if (pOther->IsPlayer() && PassesTriggerFilters( pOther ))
  975. {
  976. // ----------------------------------------
  977. // Check that toucher is facing the target
  978. // ----------------------------------------
  979. Vector vLookDir;
  980. if ( HasSpawnFlags( SF_TRIGGERLOOK_USEVELOCITY ) )
  981. {
  982. vLookDir = pOther->GetAbsVelocity();
  983. if ( vLookDir == vec3_origin )
  984. {
  985. // See if they're in a vehicle
  986. CBasePlayer *pPlayer = (CBasePlayer *)pOther;
  987. if ( pPlayer->IsInAVehicle() )
  988. {
  989. vLookDir = pPlayer->GetVehicle()->GetVehicleEnt()->GetSmoothedVelocity();
  990. }
  991. }
  992. VectorNormalize( vLookDir );
  993. }
  994. else
  995. {
  996. vLookDir = ((CBaseCombatCharacter*)pOther)->EyeDirection3D( );
  997. }
  998. Vector vTargetDir = m_hLookTarget->GetAbsOrigin() - pOther->EyePosition();
  999. VectorNormalize(vTargetDir);
  1000. float fDotPr = DotProduct(vLookDir,vTargetDir);
  1001. if (fDotPr > m_flFieldOfView)
  1002. {
  1003. // Is it the first time I'm looking?
  1004. if (m_flLookTimeTotal == -1)
  1005. {
  1006. m_flLookTimeLast = gpGlobals->curtime;
  1007. m_flLookTimeTotal = 0;
  1008. }
  1009. else
  1010. {
  1011. m_flLookTimeTotal += gpGlobals->curtime - m_flLookTimeLast;
  1012. m_flLookTimeLast = gpGlobals->curtime;
  1013. }
  1014. if (m_flLookTimeTotal >= m_flLookTime)
  1015. {
  1016. Trigger(pOther, false);
  1017. }
  1018. }
  1019. else
  1020. {
  1021. m_flLookTimeTotal = -1;
  1022. }
  1023. }
  1024. }
  1025. //-----------------------------------------------------------------------------
  1026. // Purpose: Called when the trigger is fired by look logic or timeout.
  1027. //-----------------------------------------------------------------------------
  1028. void CTriggerLook::Trigger(CBaseEntity *pActivator, bool bTimeout)
  1029. {
  1030. if (bTimeout)
  1031. {
  1032. // Fired due to timeout (player never looked at the target).
  1033. m_OnTimeout.FireOutput(pActivator, this);
  1034. // Don't fire the OnTrigger for this toucher.
  1035. m_bTimeoutFired = true;
  1036. }
  1037. else
  1038. {
  1039. // Fire because the player looked at the target.
  1040. m_OnTrigger.FireOutput(pActivator, this);
  1041. m_flLookTimeTotal = -1;
  1042. // Cancel the timeout think.
  1043. SetThink(NULL);
  1044. SetNextThink( TICK_NEVER_THINK );
  1045. }
  1046. if (HasSpawnFlags(SF_TRIGGERLOOK_FIREONCE))
  1047. {
  1048. SetThink(&CTriggerLook::SUB_Remove);
  1049. SetNextThink(gpGlobals->curtime);
  1050. }
  1051. }
  1052. //-----------------------------------------------------------------------------
  1053. // Purpose: Draw any debug text overlays
  1054. // Output : Current text offset from the top
  1055. //-----------------------------------------------------------------------------
  1056. int CTriggerLook::DrawDebugTextOverlays(void)
  1057. {
  1058. int text_offset = BaseClass::DrawDebugTextOverlays();
  1059. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1060. {
  1061. // ----------------
  1062. // Print Look time
  1063. // ----------------
  1064. char tempstr[255];
  1065. Q_snprintf(tempstr,sizeof(tempstr),"Time: %3.2f",m_flLookTime - MAX(0,m_flLookTimeTotal));
  1066. EntityText(text_offset,tempstr,0);
  1067. text_offset++;
  1068. }
  1069. return text_offset;
  1070. }
  1071. // ##################################################################################
  1072. // >> TriggerVolume
  1073. // ##################################################################################
  1074. class CTriggerVolume : public CPointEntity // Derive from point entity so this doesn't move across levels
  1075. {
  1076. public:
  1077. DECLARE_CLASS( CTriggerVolume, CPointEntity );
  1078. virtual void Spawn( void );
  1079. virtual void Activate( void );
  1080. };
  1081. LINK_ENTITY_TO_CLASS( trigger_transition, CTriggerVolume );
  1082. // Define space that travels across a level transition
  1083. void CTriggerVolume::Spawn( void )
  1084. {
  1085. SetSolid( SOLID_BSP );
  1086. AddSolidFlags( FSOLID_NOT_SOLID );
  1087. SetMoveType( MOVETYPE_NONE );
  1088. SetModel( STRING( GetModelName() ) ); // set size and link into world
  1089. if ( showtriggers.GetInt() == 0 )
  1090. {
  1091. AddEffects( EF_NODRAW );
  1092. }
  1093. }
  1094. // Make a special naming exception for Portal
  1095. void CTriggerVolume::Activate( void )
  1096. {
  1097. BaseClass::Activate();
  1098. #ifdef PORTAL2
  1099. // For simplicity, we always share the name of the landmarks
  1100. if ( m_iName.Get() == NULL_STRING )
  1101. {
  1102. SetName( AllocPooledString( ChangeLevel_GetLandmarkName() ) );
  1103. }
  1104. #endif // PORTAL2
  1105. }
  1106. #define SF_CHANGELEVEL_NOTOUCH 0x0002
  1107. #define SF_CHANGELEVEL_CHAPTER 0x0004
  1108. #define cchMapNameMost 32
  1109. enum
  1110. {
  1111. TRANSITION_VOLUME_SCREENED_OUT = 0,
  1112. TRANSITION_VOLUME_NOT_FOUND = 1,
  1113. TRANSITION_VOLUME_PASSED = 2,
  1114. };
  1115. //------------------------------------------------------------------------------
  1116. // Reesponsible for changing levels when the player touches it
  1117. //------------------------------------------------------------------------------
  1118. class CChangeLevel : public CBaseTrigger
  1119. {
  1120. DECLARE_DATADESC();
  1121. public:
  1122. DECLARE_CLASS( CChangeLevel, CBaseTrigger );
  1123. void Spawn( void );
  1124. void Activate( void );
  1125. bool KeyValue( const char *szKeyName, const char *szValue );
  1126. static int ChangeList( levellist_t *pLevelList, int maxList );
  1127. private:
  1128. void TouchChangeLevel( CBaseEntity *pOther );
  1129. void ChangeLevelNow( CBaseEntity *pActivator );
  1130. void InputChangeLevel( inputdata_t &inputdata );
  1131. bool IsEntityInTransition( CBaseEntity *pEntity );
  1132. void NotifyEntitiesOutOfTransition();
  1133. void WarnAboutActiveLead( void );
  1134. static CBaseEntity *FindLandmark( const char *pLandmarkName );
  1135. static int AddTransitionToList( levellist_t *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark );
  1136. static int InTransitionVolume( CBaseEntity *pEntity, const char *pVolumeName );
  1137. // Builds the list of entities to save when moving across a transition
  1138. static int BuildChangeLevelList( levellist_t *pLevelList, int maxList );
  1139. // Builds the list of entities to bring across a particular transition
  1140. static int BuildEntityTransitionList( CBaseEntity *pLandmarkEntity, const char *pLandmarkName, CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList );
  1141. // Adds a single entity to the transition list, if appropriate. Returns the new count
  1142. static int AddEntityToTransitionList( CBaseEntity *pEntity, int flags, int nCount, CBaseEntity **ppEntList, int *pEntityFlags );
  1143. // Adds in all entities depended on by entities near the transition
  1144. static int AddDependentEntities( int nCount, CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList );
  1145. // Figures out save flags for the entity
  1146. static int ComputeEntitySaveFlags( CBaseEntity *pEntity );
  1147. private:
  1148. char m_szMapName[cchMapNameMost]; // trigger_changelevel only: next map
  1149. char m_szLandmarkName[cchMapNameMost]; // trigger_changelevel only: landmark on next map
  1150. bool m_bTouched;
  1151. // Outputs
  1152. COutputEvent m_OnChangeLevel;
  1153. };
  1154. LINK_ENTITY_TO_CLASS( trigger_changelevel, CChangeLevel );
  1155. // Global Savedata for changelevel trigger
  1156. BEGIN_DATADESC( CChangeLevel )
  1157. DEFINE_AUTO_ARRAY( m_szMapName, FIELD_CHARACTER ),
  1158. DEFINE_AUTO_ARRAY( m_szLandmarkName, FIELD_CHARACTER ),
  1159. // DEFINE_FIELD( m_touchTime, FIELD_TIME ), // don't save
  1160. // DEFINE_FIELD( m_bTouched, FIELD_BOOLEAN ),
  1161. // Function Pointers
  1162. DEFINE_FUNCTION( TouchChangeLevel ),
  1163. DEFINE_INPUTFUNC( FIELD_VOID, "ChangeLevel", InputChangeLevel ),
  1164. // Outputs
  1165. DEFINE_OUTPUT( m_OnChangeLevel, "OnChangeLevel"),
  1166. END_DATADESC()
  1167. //
  1168. // Cache user-entity-field values until spawn is called.
  1169. //
  1170. bool CChangeLevel::KeyValue( const char *szKeyName, const char *szValue )
  1171. {
  1172. if (FStrEq(szKeyName, "map"))
  1173. {
  1174. if (strlen(szValue) >= cchMapNameMost)
  1175. {
  1176. Warning( "Map name '%s' too long (32 chars)\n", szValue );
  1177. Assert(0);
  1178. }
  1179. Q_strncpy(m_szMapName, szValue, sizeof(m_szMapName));
  1180. }
  1181. else if (FStrEq(szKeyName, "landmark"))
  1182. {
  1183. if (strlen(szValue) >= cchMapNameMost)
  1184. {
  1185. Warning( "Landmark name '%s' too long (32 chars)\n", szValue );
  1186. Assert(0);
  1187. }
  1188. Q_strncpy(m_szLandmarkName, szValue, sizeof( m_szLandmarkName ));
  1189. }
  1190. else
  1191. return BaseClass::KeyValue( szKeyName, szValue );
  1192. return true;
  1193. }
  1194. void CChangeLevel::Spawn( void )
  1195. {
  1196. if ( FStrEq( m_szMapName, "" ) )
  1197. {
  1198. Msg( "a trigger_changelevel doesn't have a map" );
  1199. }
  1200. if ( FStrEq( m_szLandmarkName, "" ) )
  1201. {
  1202. Msg( "trigger_changelevel to %s doesn't have a landmark", m_szMapName );
  1203. }
  1204. InitTrigger();
  1205. if ( !HasSpawnFlags(SF_CHANGELEVEL_NOTOUCH) )
  1206. {
  1207. SetTouch( &CChangeLevel::TouchChangeLevel );
  1208. }
  1209. // Msg( "TRANSITION: %s (%s)\n", m_szMapName, m_szLandmarkName );
  1210. }
  1211. void CChangeLevel::Activate( void )
  1212. {
  1213. BaseClass::Activate();
  1214. if ( gpGlobals->eLoadType == MapLoad_NewGame )
  1215. {
  1216. if ( HasSpawnFlags( SF_CHANGELEVEL_CHAPTER ) )
  1217. {
  1218. VPhysicsInitStatic();
  1219. RemoveSolidFlags( FSOLID_NOT_SOLID | FSOLID_TRIGGER );
  1220. SetTouch( NULL );
  1221. return;
  1222. }
  1223. }
  1224. // Level transitions will bust if they are in solid
  1225. CBaseEntity *pLandmark = FindLandmark( m_szLandmarkName );
  1226. if ( pLandmark )
  1227. {
  1228. int clusterIndex = engine->GetClusterForOrigin( pLandmark->GetAbsOrigin() );
  1229. if ( clusterIndex < 0 )
  1230. {
  1231. Warning( "trigger_changelevel to map %s has a landmark embedded in solid!\n"
  1232. "This will break level transitions!\n", m_szMapName );
  1233. }
  1234. if ( g_debug_transitions.GetInt() )
  1235. {
  1236. if ( !gEntList.FindEntityByClassname( NULL, "trigger_transition" ) )
  1237. {
  1238. Warning( "Map has no trigger_transition volumes for landmark %s\n", m_szLandmarkName );
  1239. }
  1240. }
  1241. }
  1242. m_bTouched = false;
  1243. }
  1244. static char st_szNextMap[cchMapNameMost];
  1245. static char st_szNextSpot[cchMapNameMost];
  1246. // Used to show debug for only the transition volume we're currently in
  1247. static int g_iDebuggingTransition = 0;
  1248. CBaseEntity *CChangeLevel::FindLandmark( const char *pLandmarkName )
  1249. {
  1250. CBaseEntity *pentLandmark;
  1251. pentLandmark = gEntList.FindEntityByName( NULL, pLandmarkName );
  1252. while ( pentLandmark )
  1253. {
  1254. // Found the landmark
  1255. if ( FClassnameIs( pentLandmark, "info_landmark" ) )
  1256. return pentLandmark;
  1257. else
  1258. pentLandmark = gEntList.FindEntityByName( pentLandmark, pLandmarkName );
  1259. }
  1260. Warning( "Can't find landmark %s\n", pLandmarkName );
  1261. return NULL;
  1262. }
  1263. //-----------------------------------------------------------------------------
  1264. // Purpose: Allows level transitions to be triggered by buttons, etc.
  1265. //-----------------------------------------------------------------------------
  1266. void CChangeLevel::InputChangeLevel( inputdata_t &inputdata )
  1267. {
  1268. // Ignore changelevel transitions if the player's dead or attempting a challenge
  1269. if ( gpGlobals->maxClients == 1 )
  1270. {
  1271. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  1272. if ( pPlayer && ( !pPlayer->IsAlive() || pPlayer->GetBonusChallenge() > 0 ) )
  1273. return;
  1274. }
  1275. ChangeLevelNow( inputdata.pActivator );
  1276. }
  1277. //-----------------------------------------------------------------------------
  1278. // Purpose: Performs the level change and fires targets.
  1279. // Input : pActivator -
  1280. //-----------------------------------------------------------------------------
  1281. bool CChangeLevel::IsEntityInTransition( CBaseEntity *pEntity )
  1282. {
  1283. int transitionState = InTransitionVolume(pEntity, m_szLandmarkName);
  1284. if ( transitionState == TRANSITION_VOLUME_SCREENED_OUT )
  1285. {
  1286. return false;
  1287. }
  1288. // look for a landmark entity
  1289. CBaseEntity *pLandmark = FindLandmark( m_szLandmarkName );
  1290. if ( !pLandmark )
  1291. return false;
  1292. // Check to make sure it's also in the PVS of landmark
  1293. byte pvs[MAX_MAP_CLUSTERS/8];
  1294. int clusterIndex = engine->GetClusterForOrigin( pLandmark->GetAbsOrigin() );
  1295. engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs );
  1296. Vector vecSurroundMins, vecSurroundMaxs;
  1297. pEntity->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  1298. return engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) );
  1299. }
  1300. void CChangeLevel::NotifyEntitiesOutOfTransition()
  1301. {
  1302. CBaseEntity *pEnt = gEntList.FirstEnt();
  1303. while ( pEnt )
  1304. {
  1305. // Found the landmark
  1306. if ( pEnt->ObjectCaps() & FCAP_NOTIFY_ON_TRANSITION )
  1307. {
  1308. variant_t emptyVariant;
  1309. if ( !(pEnt->ObjectCaps() & (FCAP_ACROSS_TRANSITION|FCAP_FORCE_TRANSITION)) || !IsEntityInTransition( pEnt ) )
  1310. {
  1311. pEnt->AcceptInput( "OutsideTransition", this, this, emptyVariant, 0 );
  1312. }
  1313. else
  1314. {
  1315. pEnt->AcceptInput( "InsideTransition", this, this, emptyVariant, 0 );
  1316. }
  1317. }
  1318. pEnt = gEntList.NextEnt( pEnt );
  1319. }
  1320. }
  1321. //------------------------------------------------------------------------------
  1322. // Purpose : Checks all spawned AIs and prints a warning if any are actively leading
  1323. // Input :
  1324. // Output :
  1325. //------------------------------------------------------------------------------
  1326. void CChangeLevel::WarnAboutActiveLead( void )
  1327. {
  1328. int i;
  1329. CAI_BaseNPC * ai;
  1330. CAI_BehaviorBase * behavior;
  1331. for ( i = 0; i < g_AI_Manager.NumAIs(); i++ )
  1332. {
  1333. ai = g_AI_Manager.AccessAIs()[i];
  1334. behavior = ai->GetPrimaryBehavior();
  1335. if ( behavior )
  1336. {
  1337. if ( dynamic_cast<CAI_LeadBehavior *>( behavior ) )
  1338. {
  1339. Warning( "Entity '%s' is still actively leading\n", STRING( ai->GetEntityName() ) );
  1340. }
  1341. }
  1342. }
  1343. }
  1344. void CChangeLevel::ChangeLevelNow( CBaseEntity *pActivator )
  1345. {
  1346. CBaseEntity *pLandmark;
  1347. levellist_t levels[16];
  1348. Assert(!FStrEq(m_szMapName, ""));
  1349. // Don't work in deathmatch
  1350. if ( g_pGameRules->IsDeathmatch() )
  1351. return;
  1352. // Some people are firing these multiple times in a frame, disable
  1353. if ( m_bTouched )
  1354. return;
  1355. m_bTouched = true;
  1356. CBaseEntity *pPlayer = (pActivator && pActivator->IsPlayer()) ? pActivator : UTIL_GetLocalPlayer();
  1357. int transitionState = InTransitionVolume(pPlayer, m_szLandmarkName);
  1358. if ( transitionState == TRANSITION_VOLUME_SCREENED_OUT )
  1359. {
  1360. DevMsg( 2, "Player isn't in the transition volume %s, aborting\n", m_szLandmarkName );
  1361. return;
  1362. }
  1363. // look for a landmark entity
  1364. pLandmark = FindLandmark( m_szLandmarkName );
  1365. if ( !pLandmark )
  1366. return;
  1367. // no transition volumes, check PVS of landmark
  1368. if ( transitionState == TRANSITION_VOLUME_NOT_FOUND )
  1369. {
  1370. byte pvs[MAX_MAP_CLUSTERS/8];
  1371. int clusterIndex = engine->GetClusterForOrigin( pLandmark->GetAbsOrigin() );
  1372. engine->GetPVSForCluster( clusterIndex, sizeof(pvs), pvs );
  1373. if ( pPlayer )
  1374. {
  1375. Vector vecSurroundMins, vecSurroundMaxs;
  1376. pPlayer->CollisionProp()->WorldSpaceSurroundingBounds( &vecSurroundMins, &vecSurroundMaxs );
  1377. bool playerInPVS = engine->CheckBoxInPVS( vecSurroundMins, vecSurroundMaxs, pvs, sizeof( pvs ) );
  1378. //Assert( playerInPVS );
  1379. if ( !playerInPVS )
  1380. {
  1381. Warning( "Player isn't in the landmark's (%s) PVS, aborting\n", m_szLandmarkName );
  1382. return;
  1383. }
  1384. }
  1385. }
  1386. WarnAboutActiveLead();
  1387. g_iDebuggingTransition = 0;
  1388. st_szNextSpot[0] = 0; // Init landmark to NULL
  1389. Q_strncpy(st_szNextSpot, m_szLandmarkName,sizeof(st_szNextSpot));
  1390. // This object will get removed in the call to engine->ChangeLevel, copy the params into "safe" memory
  1391. Q_strncpy(st_szNextMap, m_szMapName, sizeof(st_szNextMap));
  1392. m_hActivator = pActivator;
  1393. m_OnChangeLevel.FireOutput(pActivator, this);
  1394. NotifyEntitiesOutOfTransition();
  1395. //// Msg( "Level touches %d levels\n", ChangeList( levels, 16 ) );
  1396. if ( g_debug_transitions.GetInt() )
  1397. {
  1398. Msg( "CHANGE LEVEL: %s %s\n", st_szNextMap, st_szNextSpot );
  1399. }
  1400. // If we're debugging, don't actually change level
  1401. if ( g_debug_transitions.GetInt() == 0 )
  1402. {
  1403. g_pGameRules->OnBeginChangeLevel( st_szNextMap, NULL );
  1404. engine->ChangeLevel( st_szNextMap, st_szNextSpot );
  1405. }
  1406. else
  1407. {
  1408. // Build a change list so we can see what would be transitioning
  1409. CSaveRestoreData *pSaveData = SaveInit( 0 );
  1410. if ( pSaveData )
  1411. {
  1412. g_pGameSaveRestoreBlockSet->PreSave( pSaveData );
  1413. pSaveData->levelInfo.connectionCount = BuildChangeList( pSaveData->levelInfo.levelList, MAX_LEVEL_CONNECTIONS );
  1414. g_pGameSaveRestoreBlockSet->PostSave();
  1415. }
  1416. SetTouch( NULL );
  1417. }
  1418. }
  1419. //
  1420. // GLOBALS ASSUMED SET: st_szNextMap
  1421. //
  1422. void CChangeLevel::TouchChangeLevel( CBaseEntity *pOther )
  1423. {
  1424. CBasePlayer *pPlayer = ToBasePlayer(pOther);
  1425. if ( !pPlayer )
  1426. return;
  1427. if( pPlayer->IsSinglePlayerGameEnding() )
  1428. {
  1429. // Some semblance of deceleration, but allow player to fall normally.
  1430. // Also, disable controls.
  1431. Vector vecVelocity = pPlayer->GetAbsVelocity();
  1432. vecVelocity.x *= 0.5f;
  1433. vecVelocity.y *= 0.5f;
  1434. pPlayer->SetAbsVelocity( vecVelocity );
  1435. pPlayer->AddFlag( FL_FROZEN );
  1436. return;
  1437. }
  1438. if ( !pPlayer->IsInAVehicle() && pPlayer->GetMoveType() == MOVETYPE_NOCLIP )
  1439. {
  1440. DevMsg("In level transition: %s %s\n", st_szNextMap, st_szNextSpot );
  1441. return;
  1442. }
  1443. ChangeLevelNow( pOther );
  1444. }
  1445. // Add a transition to the list, but ignore duplicates
  1446. // (a designer may have placed multiple trigger_changelevels with the same landmark)
  1447. int CChangeLevel::AddTransitionToList( levellist_t *pLevelList, int listCount, const char *pMapName, const char *pLandmarkName, edict_t *pentLandmark )
  1448. {
  1449. int i;
  1450. if ( !pLevelList || !pMapName || !pLandmarkName || !pentLandmark )
  1451. return 0;
  1452. // Ignore changelevels to the level we're ready in. Mapmakers love to do this!
  1453. if ( stricmp( pMapName, STRING(gpGlobals->mapname) ) == 0 )
  1454. return 0;
  1455. for ( i = 0; i < listCount; i++ )
  1456. {
  1457. if ( pLevelList[i].pentLandmark == pentLandmark && stricmp( pLevelList[i].mapName, pMapName ) == 0 )
  1458. return 0;
  1459. }
  1460. Q_strncpy( pLevelList[listCount].mapName, pMapName, sizeof(pLevelList[listCount].mapName) );
  1461. Q_strncpy( pLevelList[listCount].landmarkName, pLandmarkName, sizeof(pLevelList[listCount].landmarkName) );
  1462. pLevelList[listCount].pentLandmark = pentLandmark;
  1463. CBaseEntity *ent = CBaseEntity::Instance( pentLandmark );
  1464. Assert( ent );
  1465. pLevelList[listCount].vecLandmarkOrigin = ent->GetAbsOrigin();
  1466. return 1;
  1467. }
  1468. int BuildChangeList( levellist_t *pLevelList, int maxList )
  1469. {
  1470. return CChangeLevel::ChangeList( pLevelList, maxList );
  1471. }
  1472. struct collidelist_t
  1473. {
  1474. const CPhysCollide *pCollide;
  1475. Vector origin;
  1476. QAngle angles;
  1477. };
  1478. // NOTE: This routine is relatively slow. If you need to use it for per-frame work, consider that fact.
  1479. // UNDONE: Expand this to the full matrix of solid types on each side and move into enginetrace
  1480. static bool TestEntityTriggerIntersection_Accurate( CBaseEntity *pTrigger, CBaseEntity *pEntity )
  1481. {
  1482. Assert( pTrigger->GetSolid() == SOLID_BSP );
  1483. if ( pTrigger->Intersects( pEntity ) ) // It touches one, it's in the volume
  1484. {
  1485. switch ( pEntity->GetSolid() )
  1486. {
  1487. case SOLID_BBOX:
  1488. {
  1489. ICollideable *pCollide = pTrigger->CollisionProp();
  1490. Ray_t ray;
  1491. trace_t tr;
  1492. ray.Init( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin(), pEntity->WorldAlignMins(), pEntity->WorldAlignMaxs() );
  1493. enginetrace->ClipRayToCollideable( ray, MASK_ALL, pCollide, &tr );
  1494. if ( tr.startsolid )
  1495. return true;
  1496. }
  1497. break;
  1498. case SOLID_BSP:
  1499. case SOLID_VPHYSICS:
  1500. {
  1501. CPhysCollide *pTriggerCollide = modelinfo->GetVCollide( pTrigger->GetModelIndex() )->solids[0];
  1502. Assert( pTriggerCollide );
  1503. CUtlVector<collidelist_t> collideList;
  1504. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  1505. int physicsCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  1506. if ( physicsCount )
  1507. {
  1508. for ( int i = 0; i < physicsCount; i++ )
  1509. {
  1510. const CPhysCollide *pCollide = pList[i]->GetCollide();
  1511. if ( pCollide )
  1512. {
  1513. collidelist_t element;
  1514. element.pCollide = pCollide;
  1515. pList[i]->GetPosition( &element.origin, &element.angles );
  1516. collideList.AddToTail( element );
  1517. }
  1518. }
  1519. }
  1520. else
  1521. {
  1522. vcollide_t *pVCollide = modelinfo->GetVCollide( pEntity->GetModelIndex() );
  1523. if ( pVCollide && pVCollide->solidCount )
  1524. {
  1525. collidelist_t element;
  1526. element.pCollide = pVCollide->solids[0];
  1527. element.origin = pEntity->GetAbsOrigin();
  1528. element.angles = pEntity->GetAbsAngles();
  1529. collideList.AddToTail( element );
  1530. }
  1531. }
  1532. for ( int i = collideList.Count()-1; i >= 0; --i )
  1533. {
  1534. const collidelist_t &element = collideList[i];
  1535. trace_t tr;
  1536. physcollision->TraceCollide( element.origin, element.origin, element.pCollide, element.angles, pTriggerCollide, pTrigger->GetAbsOrigin(), pTrigger->GetAbsAngles(), &tr );
  1537. if ( tr.startsolid )
  1538. return true;
  1539. }
  1540. }
  1541. break;
  1542. default:
  1543. return true;
  1544. }
  1545. }
  1546. return false;
  1547. }
  1548. int CChangeLevel::InTransitionVolume( CBaseEntity *pEntity, const char *pVolumeName )
  1549. {
  1550. CBaseEntity *pVolume;
  1551. if ( pEntity->ObjectCaps() & FCAP_FORCE_TRANSITION )
  1552. return TRANSITION_VOLUME_PASSED;
  1553. // If you're following another entity, follow it through the transition (weapons follow the player)
  1554. pEntity = pEntity->GetRootMoveParent();
  1555. int inVolume = TRANSITION_VOLUME_NOT_FOUND; // Unless we find a trigger_transition, everything is in the volume
  1556. pVolume = gEntList.FindEntityByName( NULL, pVolumeName );
  1557. while ( pVolume )
  1558. {
  1559. if ( pVolume && FClassnameIs( pVolume, "trigger_transition" ) )
  1560. {
  1561. if ( TestEntityTriggerIntersection_Accurate(pVolume, pEntity ) ) // It touches one, it's in the volume
  1562. return TRANSITION_VOLUME_PASSED;
  1563. inVolume = TRANSITION_VOLUME_SCREENED_OUT; // Found a trigger_transition, but I don't intersect it -- if I don't find another, don't go!
  1564. }
  1565. pVolume = gEntList.FindEntityByName( pVolume, pVolumeName );
  1566. }
  1567. return inVolume;
  1568. }
  1569. //------------------------------------------------------------------------------
  1570. // Builds the list of entities to save when moving across a transition
  1571. //------------------------------------------------------------------------------
  1572. int CChangeLevel::BuildChangeLevelList( levellist_t *pLevelList, int maxList )
  1573. {
  1574. #ifdef PORTAL2
  1575. // Get our origin and destination names
  1576. const char *lpszDestMapName = ChangeLevel_DestinationMapName();
  1577. const char *lpszOriginMapName = ChangeLevel_OriginMapName();
  1578. // If these names are valid, we're opting into the streamlined level transition system
  1579. if ( lpszOriginMapName != NULL && lpszOriginMapName[0] != NULL && lpszDestMapName != NULL && lpszDestMapName[0] != NULL )
  1580. {
  1581. // Because this is called symmetrically on both sides of the transition, we need to infer from the names which side we're on
  1582. bool bAtDestination = !Q_stricmp( STRING(gpGlobals->mapname), lpszDestMapName );
  1583. // Find a landmark on this end of the transition to refer to
  1584. CBaseEntity *pentLandmark = gEntList.FindEntityByClassname( NULL, (bAtDestination) ? "info_landmark_entry" : "info_landmark_exit" );
  1585. if ( pentLandmark )
  1586. {
  1587. // Landmarks are all named the same thing, we only allow one entry and one exit per level
  1588. const char *lpszLandmarkName = ChangeLevel_GetLandmarkName();
  1589. if ( AddTransitionToList( pLevelList, 0, (bAtDestination) ? lpszOriginMapName : lpszDestMapName, lpszLandmarkName, pentLandmark->edict() ) )
  1590. return 1; // Only one way to go
  1591. }
  1592. }
  1593. #endif
  1594. int nCount = 0;
  1595. CBaseEntity *pentChangelevel = gEntList.FindEntityByClassname( NULL, "trigger_changelevel" );
  1596. while ( pentChangelevel )
  1597. {
  1598. CChangeLevel *pTrigger = dynamic_cast<CChangeLevel *>(pentChangelevel);
  1599. if ( pTrigger )
  1600. {
  1601. // Find the corresponding landmark
  1602. CBaseEntity *pentLandmark = FindLandmark( pTrigger->m_szLandmarkName );
  1603. if ( pentLandmark )
  1604. {
  1605. // Build a list of unique transitions
  1606. if ( AddTransitionToList( pLevelList, nCount, pTrigger->m_szMapName, pTrigger->m_szLandmarkName, pentLandmark->edict() ) )
  1607. {
  1608. ++nCount;
  1609. if ( nCount >= maxList ) // FULL!!
  1610. break;
  1611. }
  1612. }
  1613. }
  1614. pentChangelevel = gEntList.FindEntityByClassname( pentChangelevel, "trigger_changelevel" );
  1615. }
  1616. return nCount;
  1617. }
  1618. //------------------------------------------------------------------------------
  1619. // Adds a single entity to the transition list, if appropriate. Returns the new count
  1620. //------------------------------------------------------------------------------
  1621. int CChangeLevel::ComputeEntitySaveFlags( CBaseEntity *pEntity )
  1622. {
  1623. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1624. {
  1625. Msg( "Trying %s (%s): ", pEntity->GetClassname(), pEntity->GetDebugName() );
  1626. }
  1627. int caps = pEntity->ObjectCaps();
  1628. if ( caps & FCAP_DONT_SAVE )
  1629. {
  1630. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1631. {
  1632. Msg( "IGNORED due to being marked \"Don't save\".\n" );
  1633. }
  1634. return 0;
  1635. }
  1636. // If this entity can be moved or is global, mark it
  1637. int flags = 0;
  1638. if ( caps & FCAP_ACROSS_TRANSITION )
  1639. {
  1640. flags |= FENTTABLE_MOVEABLE;
  1641. }
  1642. if ( pEntity->m_iGlobalname != NULL_STRING && !pEntity->IsDormant() )
  1643. {
  1644. flags |= FENTTABLE_GLOBAL;
  1645. }
  1646. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE && !flags )
  1647. {
  1648. Msg( "IGNORED, no across_transition flag & no globalname\n" );
  1649. }
  1650. return flags;
  1651. }
  1652. //------------------------------------------------------------------------------
  1653. // Adds a single entity to the transition list, if appropriate. Returns the new count
  1654. //------------------------------------------------------------------------------
  1655. inline int CChangeLevel::AddEntityToTransitionList( CBaseEntity *pEntity, int flags, int nCount, CBaseEntity **ppEntList, int *pEntityFlags )
  1656. {
  1657. ppEntList[ nCount ] = pEntity;
  1658. pEntityFlags[ nCount ] = flags;
  1659. ++nCount;
  1660. // If we're debugging, make it visible
  1661. if ( g_iDebuggingTransition )
  1662. {
  1663. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1664. {
  1665. // In verbose mode we've already printed out what the entity is
  1666. Msg("ADDED.\n");
  1667. }
  1668. else
  1669. {
  1670. // In non-verbose mode, we just print this line
  1671. Msg( "ADDED %s (%s) to transition.\n", pEntity->GetClassname(), pEntity->GetDebugName() );
  1672. }
  1673. pEntity->m_debugOverlays |= (OVERLAY_BBOX_BIT | OVERLAY_NAME_BIT);
  1674. }
  1675. return nCount;
  1676. }
  1677. //------------------------------------------------------------------------------
  1678. // Builds the list of entities to bring across a particular transition
  1679. //------------------------------------------------------------------------------
  1680. int CChangeLevel::BuildEntityTransitionList( CBaseEntity *pLandmarkEntity, const char *pLandmarkName,
  1681. CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList )
  1682. {
  1683. int iEntity = 0;
  1684. // Only show debug for the transition to the level we're going to
  1685. if ( g_debug_transitions.GetInt() && pLandmarkEntity->NameMatches(st_szNextSpot) )
  1686. {
  1687. g_iDebuggingTransition = g_debug_transitions.GetInt();
  1688. // Show us where the landmark entity is
  1689. pLandmarkEntity->m_debugOverlays |= (OVERLAY_PIVOT_BIT | OVERLAY_BBOX_BIT | OVERLAY_NAME_BIT);
  1690. }
  1691. else
  1692. {
  1693. g_iDebuggingTransition = 0;
  1694. }
  1695. // Follow the linked list of entities in the PVS of the transition landmark
  1696. CBaseEntity *pEntity = NULL;
  1697. while ( (pEntity = UTIL_EntitiesInPVS( pLandmarkEntity, pEntity)) != NULL )
  1698. {
  1699. int flags = ComputeEntitySaveFlags( pEntity );
  1700. if ( !flags )
  1701. continue;
  1702. // Check to make sure the entity isn't screened out by a trigger_transition
  1703. if ( !InTransitionVolume( pEntity, pLandmarkName ) )
  1704. {
  1705. if ( g_iDebuggingTransition == DEBUG_TRANSITIONS_VERBOSE )
  1706. {
  1707. Msg( "IGNORED, outside transition volume.\n" );
  1708. }
  1709. continue;
  1710. }
  1711. if ( iEntity >= nMaxList )
  1712. {
  1713. Warning( "Too many entities across a transition!\n" );
  1714. Assert( 0 );
  1715. return iEntity;
  1716. }
  1717. iEntity = AddEntityToTransitionList( pEntity, flags, iEntity, ppEntList, pEntityFlags );
  1718. }
  1719. return iEntity;
  1720. }
  1721. //------------------------------------------------------------------------------
  1722. // Tests bits in a bitfield
  1723. //------------------------------------------------------------------------------
  1724. static inline bool IsBitSet( char *pBuf, int nBit )
  1725. {
  1726. return (pBuf[ nBit >> 3 ] & ( 1 << (nBit & 0x7) )) != 0;
  1727. }
  1728. static inline void Set( char *pBuf, int nBit )
  1729. {
  1730. pBuf[ nBit >> 3 ] |= 1 << (nBit & 0x7);
  1731. }
  1732. //------------------------------------------------------------------------------
  1733. // Adds in all entities depended on by entities near the transition
  1734. //------------------------------------------------------------------------------
  1735. #define MAX_ENTITY_BYTE_COUNT (NUM_ENT_ENTRIES >> 3)
  1736. int CChangeLevel::AddDependentEntities( int nCount, CBaseEntity **ppEntList, int *pEntityFlags, int nMaxList )
  1737. {
  1738. char pEntitiesSaved[MAX_ENTITY_BYTE_COUNT];
  1739. memset( pEntitiesSaved, 0, MAX_ENTITY_BYTE_COUNT * sizeof(char) );
  1740. // Populate the initial bitfield
  1741. int i;
  1742. for ( i = 0; i < nCount; ++i )
  1743. {
  1744. // NOTE: Must use GetEntryIndex because we're saving non-networked entities
  1745. int nEntIndex = ppEntList[i]->GetRefEHandle().GetEntryIndex();
  1746. // We shouldn't already have this entity in the list!
  1747. Assert( !IsBitSet( pEntitiesSaved, nEntIndex ) );
  1748. // Mark the entity as being in the list
  1749. Set( pEntitiesSaved, nEntIndex );
  1750. }
  1751. IEntitySaveUtils *pSaveUtils = GetEntitySaveUtils();
  1752. // Iterate over entities whose dependencies we've not yet processed
  1753. // NOTE: nCount will change value during this loop in AddEntityToTransitionList
  1754. for ( i = 0; i < nCount; ++i )
  1755. {
  1756. CBaseEntity *pEntity = ppEntList[i];
  1757. // Find dependencies in the hash.
  1758. int nDepCount = pSaveUtils->GetEntityDependencyCount( pEntity );
  1759. if ( !nDepCount )
  1760. continue;
  1761. CBaseEntity **ppDependentEntities = (CBaseEntity**)stackalloc( nDepCount * sizeof(CBaseEntity*) );
  1762. pSaveUtils->GetEntityDependencies( pEntity, nDepCount, ppDependentEntities );
  1763. for ( int j = 0; j < nDepCount; ++j )
  1764. {
  1765. CBaseEntity *pDependent = ppDependentEntities[j];
  1766. if ( !pDependent )
  1767. continue;
  1768. // NOTE: Must use GetEntryIndex because we're saving non-networked entities
  1769. int nEntIndex = pDependent->GetRefEHandle().GetEntryIndex();
  1770. // Don't re-add it if it's already in the list
  1771. if ( IsBitSet( pEntitiesSaved, nEntIndex ) )
  1772. continue;
  1773. // Mark the entity as being in the list
  1774. Set( pEntitiesSaved, nEntIndex );
  1775. int flags = ComputeEntitySaveFlags( pEntity );
  1776. if ( flags )
  1777. {
  1778. if ( nCount >= nMaxList )
  1779. {
  1780. Warning( "Too many entities across a transition!\n" );
  1781. Assert( 0 );
  1782. return false;
  1783. }
  1784. if ( g_debug_transitions.GetInt() )
  1785. {
  1786. Msg( "ADDED DEPENDANCY: %s (%s)\n", pEntity->GetClassname(), pEntity->GetDebugName() );
  1787. }
  1788. nCount = AddEntityToTransitionList( pEntity, flags, nCount, ppEntList, pEntityFlags );
  1789. }
  1790. else
  1791. {
  1792. Warning("Warning!! Save dependency is linked to an entity that doesn't want to be saved!\n");
  1793. }
  1794. }
  1795. }
  1796. return nCount;
  1797. }
  1798. //------------------------------------------------------------------------------
  1799. // This builds the list of all transitions on this level and which entities
  1800. // are in their PVS's and can / should be moved across.
  1801. //------------------------------------------------------------------------------
  1802. // We can only ever move 512 entities across a transition
  1803. #define MAX_ENTITY 512
  1804. // FIXME: This has grown into a complicated beast. Can we make this more elegant?
  1805. int CChangeLevel::ChangeList( levellist_t *pLevelList, int maxList )
  1806. {
  1807. // Find all of the possible level changes on this BSP
  1808. int count = BuildChangeLevelList( pLevelList, maxList );
  1809. if ( !gpGlobals->pSaveData || ( static_cast<CSaveRestoreData *>(gpGlobals->pSaveData)->NumEntities() == 0 ) )
  1810. return count;
  1811. CSave saveHelper( static_cast<CSaveRestoreData *>(gpGlobals->pSaveData) );
  1812. // For each level change, find nearby entities and save them
  1813. int i;
  1814. for ( i = 0; i < count; i++ )
  1815. {
  1816. CBaseEntity *pEntList[ MAX_ENTITY ];
  1817. int entityFlags[ MAX_ENTITY ];
  1818. // First, figure out which entities are near the transition
  1819. CBaseEntity *pLandmarkEntity = CBaseEntity::Instance( pLevelList[i].pentLandmark );
  1820. int iEntity = BuildEntityTransitionList( pLandmarkEntity, pLevelList[i].landmarkName, pEntList, entityFlags, MAX_ENTITY );
  1821. // FIXME: Activate if we have a dependency problem on level transition
  1822. // Next, add in all entities depended on by entities near the transition
  1823. // iEntity = AddDependentEntities( iEntity, pEntList, entityFlags, MAX_ENTITY );
  1824. int j;
  1825. for ( j = 0; j < iEntity; j++ )
  1826. {
  1827. // Mark entity table with 1<<i
  1828. int index = saveHelper.EntityIndex( pEntList[j] );
  1829. // Flag it with the level number
  1830. saveHelper.EntityFlagsSet( index, entityFlags[j] | (1<<i) );
  1831. }
  1832. }
  1833. return count;
  1834. }
  1835. //-----------------------------------------------------------------------------
  1836. // Purpose: A trigger volume that gently slows players traveling in a particular direction.
  1837. // Useful for 'softly' stopping players without them feeling like they've hit an invisible wall.
  1838. //-----------------------------------------------------------------------------
  1839. struct softbarrierplane_t
  1840. {
  1841. cplane_t m_plane;
  1842. Vector m_vecWorldPointOnPlane;
  1843. };
  1844. class CTriggerSoftBarrier : public CBaseTrigger
  1845. {
  1846. public:
  1847. DECLARE_CLASS( CTriggerSoftBarrier, CBaseTrigger );
  1848. void Spawn( void );
  1849. void Activate( void );
  1850. void Touch( CBaseEntity *pOther );
  1851. void Untouch( CBaseEntity *pOther );
  1852. void InputSetPushDirection( inputdata_t &inputdata );
  1853. Vector m_vecPushDir;
  1854. DECLARE_DATADESC();
  1855. void SetupFrontAndBackPlanes( void );
  1856. float GetDistanceToPlane( int nPlaneIdx, Vector vecWorldPos );
  1857. softbarrierplane_t m_planes[2];
  1858. bool m_bInitialized;
  1859. };
  1860. BEGIN_DATADESC( CTriggerSoftBarrier )
  1861. DEFINE_KEYFIELD( m_vecPushDir, FIELD_VECTOR, "pushdir" ),
  1862. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetPushDirection", InputSetPushDirection ),
  1863. END_DATADESC()
  1864. LINK_ENTITY_TO_CLASS( trigger_softbarrier, CTriggerSoftBarrier );
  1865. #define SOFTPLANE_FRONT 0
  1866. #define SOFTPLANE_BACK 1
  1867. void CTriggerSoftBarrier::SetupFrontAndBackPlanes()
  1868. {
  1869. // find and save the brush faces that are most in line and opposite the push vector
  1870. m_bInitialized = false;
  1871. if ( IsBSPModel() )
  1872. {
  1873. // Transform the push dir into global space
  1874. Vector vecAbsDir;
  1875. VectorRotate( m_vecPushDir, EntityToWorldTransform(), vecAbsDir );
  1876. float flFaceIdxFrontDot = 0;
  1877. float flFaceIdxBackDot = 0;
  1878. bool bFoundFront = false;
  1879. bool bFoundBack = false;
  1880. const model_t *pModel = GetModel();
  1881. int nCount = modelinfo->GetBrushModelPlaneCount( pModel );
  1882. for ( int i = 0; i < nCount; ++i )
  1883. {
  1884. cplane_t localPlane;
  1885. Vector vecTemp;
  1886. modelinfo->GetBrushModelPlane( pModel, i, localPlane, &vecTemp );
  1887. cplane_t worldPlane;
  1888. MatrixTransformPlane( EntityToWorldTransform(), localPlane, worldPlane );
  1889. float flDot = DotProduct( worldPlane.normal, vecAbsDir );
  1890. if ( flDot > flFaceIdxFrontDot )
  1891. {
  1892. flFaceIdxFrontDot = flDot;
  1893. m_planes[SOFTPLANE_FRONT].m_plane = worldPlane;
  1894. m_planes[SOFTPLANE_FRONT].m_plane.normal = -m_planes[SOFTPLANE_FRONT].m_plane.normal;
  1895. VectorTransform( vecTemp, EntityToWorldTransform(), m_planes[SOFTPLANE_FRONT].m_vecWorldPointOnPlane );
  1896. bFoundFront = true;
  1897. continue;
  1898. }
  1899. if ( flDot < flFaceIdxBackDot && flDot != flFaceIdxFrontDot )
  1900. {
  1901. flFaceIdxBackDot = flDot;
  1902. m_planes[SOFTPLANE_BACK].m_plane = worldPlane;
  1903. m_planes[SOFTPLANE_BACK].m_plane.normal = -m_planes[SOFTPLANE_BACK].m_plane.normal;
  1904. VectorTransform( vecTemp, EntityToWorldTransform(), m_planes[SOFTPLANE_BACK].m_vecWorldPointOnPlane );
  1905. bFoundBack = true;
  1906. continue;
  1907. }
  1908. }
  1909. if ( bFoundBack == false || bFoundFront == false )
  1910. {
  1911. Warning( "Warning: Softbarrier trigger couldn't find front or back planes. Removing!\n" );
  1912. UTIL_Remove( this );
  1913. }
  1914. m_bInitialized = true;
  1915. }
  1916. else
  1917. {
  1918. Warning( "Warning: Softbarrier trigger is NOT a bsp model. Removing!\n" );
  1919. UTIL_Remove( this );
  1920. }
  1921. }
  1922. //-----------------------------------------------------------------------------
  1923. // Purpose: Called when spawning, after keyvalues have been handled.
  1924. //-----------------------------------------------------------------------------
  1925. void CTriggerSoftBarrier::Spawn()
  1926. {
  1927. // Convert pushdir from angles to a vector
  1928. Vector vecAbsDir;
  1929. QAngle angPushDir = QAngle(m_vecPushDir.x, m_vecPushDir.y, m_vecPushDir.z);
  1930. AngleVectors(angPushDir, &vecAbsDir);
  1931. // Transform the vector into entity space
  1932. VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecPushDir );
  1933. BaseClass::Spawn();
  1934. InitTrigger();
  1935. SetupFrontAndBackPlanes();
  1936. }
  1937. //-----------------------------------------------------------------------------
  1938. //-----------------------------------------------------------------------------
  1939. void CTriggerSoftBarrier::Activate()
  1940. {
  1941. BaseClass::Activate();
  1942. }
  1943. float CTriggerSoftBarrier::GetDistanceToPlane( int nPlaneIdx, Vector vecWorldPos )
  1944. {
  1945. Vector vToPlane = ( vecWorldPos - m_planes[nPlaneIdx].m_vecWorldPointOnPlane );
  1946. float flDistToPlane = DotProduct( m_planes[nPlaneIdx].m_plane.normal, vToPlane );
  1947. //// draw debug arrows
  1948. //Vector vTouchPos = vecWorldPos + flDistToPlane * -m_planes[nPlaneIdx].m_plane.normal;
  1949. //if ( nPlaneIdx == 0 )
  1950. //{
  1951. // NDebugOverlay::HorzArrow( vTouchPos, vTouchPos - m_planes[nPlaneIdx].m_plane.normal * -100, 10, 0, 255, 0, 0, true, 0.1 );
  1952. //}
  1953. //else
  1954. //{
  1955. // NDebugOverlay::HorzArrow( vTouchPos, vTouchPos - m_planes[nPlaneIdx].m_plane.normal * -100, 10, 255, 0, 0, 0, true, 0.1 );
  1956. //}
  1957. return flDistToPlane;
  1958. }
  1959. //-----------------------------------------------------------------------------
  1960. // Purpose:
  1961. // Input : *pOther -
  1962. //-----------------------------------------------------------------------------
  1963. void CTriggerSoftBarrier::Touch( CBaseEntity *pOther )
  1964. {
  1965. if ( !pOther->IsSolid() || (pOther->GetMoveType() == MOVETYPE_PUSH || pOther->GetMoveType() == MOVETYPE_NONE ) )
  1966. return;
  1967. if (!PassesTriggerFilters(pOther))
  1968. return;
  1969. // FIXME: If something is hierarchically attached, should we try to push the parent?
  1970. if (pOther->GetMoveParent())
  1971. return;
  1972. // only for players for the time being
  1973. if ( pOther->IsPlayer() && pOther->IsAlive() && m_bInitialized )
  1974. {
  1975. MoveType_t playerMoveType = pOther->GetMoveType();
  1976. if ( playerMoveType == MOVETYPE_NONE || playerMoveType == MOVETYPE_PUSH || playerMoveType == MOVETYPE_NOCLIP )
  1977. return;
  1978. // get the player distance to the front plane
  1979. float flDistToFront = GetDistanceToPlane( SOFTPLANE_FRONT, pOther->GetAbsOrigin() );
  1980. // get the player distance to the back plane
  1981. float flDistToBack = GetDistanceToPlane( SOFTPLANE_BACK, pOther->GetAbsOrigin() );
  1982. // get how far between the planes we are (and apply some nonlinear bias)
  1983. float flDistRatio = Bias( clamp( flDistToFront / MAX( flDistToFront + flDistToBack, 0.0001f ), 0, 1 ), 0.2f );
  1984. Vector vecPushNormal = Lerp( flDistRatio, -m_planes[SOFTPLANE_FRONT].m_plane.normal, m_planes[SOFTPLANE_BACK].m_plane.normal ).Normalized();
  1985. Vector vecPlayerVel = pOther->GetAbsVelocity();
  1986. float flVelDot = DotProduct( vecPlayerVel.Normalized(), vecPushNormal );
  1987. if ( flVelDot < 0 )
  1988. {
  1989. // get the component of velocity that's pointing into the soft barrier
  1990. float flNormal = DotProduct( vecPlayerVel, vecPushNormal );
  1991. Vector vecCross;
  1992. VectorScale( vecPushNormal, flNormal, vecCross );
  1993. // this is the player's velocity if they were on the back plane
  1994. Vector vecLateral;
  1995. VectorSubtract( vecPlayerVel, vecCross, vecLateral );
  1996. // lerp the velocity to the lateral velocity, depending on how close we are to the back plane
  1997. pOther->SetAbsVelocity( Lerp( flDistRatio, vecPlayerVel, vecLateral ) );
  1998. }
  1999. }
  2000. }
  2001. void CTriggerSoftBarrier::InputSetPushDirection( inputdata_t &inputdata )
  2002. {
  2003. inputdata.value.Vector3D( m_vecPushDir );
  2004. // Convert pushdir from angles to a vector
  2005. Vector vecAbsDir;
  2006. QAngle angPushDir = QAngle(m_vecPushDir.x, m_vecPushDir.y, m_vecPushDir.z);
  2007. AngleVectors(angPushDir, &vecAbsDir);
  2008. // Transform the vector into entity space
  2009. VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecPushDir );
  2010. SetupFrontAndBackPlanes();
  2011. }
  2012. //-----------------------------------------------------------------------------
  2013. // Purpose: A trigger that pushes the player, NPCs, or objects.
  2014. //-----------------------------------------------------------------------------
  2015. class CTriggerPush : public CBaseTrigger
  2016. {
  2017. public:
  2018. DECLARE_CLASS( CTriggerPush, CBaseTrigger );
  2019. void Spawn( void );
  2020. void Activate( void );
  2021. void Touch( CBaseEntity *pOther );
  2022. void Untouch( CBaseEntity *pOther );
  2023. void DrawDebugGeometryOverlays();
  2024. void InputSetPushDirection( inputdata_t &inputdata );
  2025. Vector m_vecPushDir;
  2026. DECLARE_DATADESC();
  2027. float m_flAlternateTicksFix; // Scale factor to apply to the push speed when running with alternate ticks
  2028. float m_flPushSpeed;
  2029. };
  2030. BEGIN_DATADESC( CTriggerPush )
  2031. DEFINE_KEYFIELD( m_vecPushDir, FIELD_VECTOR, "pushdir" ),
  2032. DEFINE_KEYFIELD( m_flAlternateTicksFix, FIELD_FLOAT, "alternateticksfix" ),
  2033. #ifdef PORTAL2
  2034. DEFINE_KEYFIELD( m_flSpeed, FIELD_FLOAT, "pushspeed" ),
  2035. #endif //PORTAL2
  2036. //DEFINE_FIELD( m_flPushSpeed, FIELD_FLOAT ),
  2037. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetPushDirection", InputSetPushDirection ),
  2038. END_DATADESC()
  2039. LINK_ENTITY_TO_CLASS( trigger_push, CTriggerPush );
  2040. //-----------------------------------------------------------------------------
  2041. // Purpose: Called when spawning, after keyvalues have been handled.
  2042. //-----------------------------------------------------------------------------
  2043. void CTriggerPush::Spawn()
  2044. {
  2045. // Convert pushdir from angles to a vector
  2046. Vector vecAbsDir;
  2047. QAngle angPushDir = QAngle(m_vecPushDir.x, m_vecPushDir.y, m_vecPushDir.z);
  2048. AngleVectors(angPushDir, &vecAbsDir);
  2049. // Transform the vector into entity space
  2050. VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecPushDir );
  2051. BaseClass::Spawn();
  2052. InitTrigger();
  2053. if (m_flSpeed == 0)
  2054. {
  2055. m_flSpeed = 100;
  2056. }
  2057. }
  2058. //-----------------------------------------------------------------------------
  2059. //-----------------------------------------------------------------------------
  2060. void CTriggerPush::Activate()
  2061. {
  2062. // Fix problems with triggers pushing too hard under sv_alternateticks.
  2063. // This is somewhat hacky, but it's simple and we're really close to shipping.
  2064. if ( ( m_flAlternateTicksFix != 0 ) && IsSimulatingOnAlternateTicks() )
  2065. {
  2066. m_flPushSpeed = m_flSpeed * m_flAlternateTicksFix;
  2067. }
  2068. else
  2069. {
  2070. m_flPushSpeed = m_flSpeed;
  2071. #ifdef PORTAL2
  2072. //
  2073. // DIRTY HACK TO FOLLOW
  2074. //
  2075. // If alternateticks is disabled, our trigger_push values are too low
  2076. // because the single player game was tuned with alternateticks on for the most part.
  2077. //
  2078. if ( gpGlobals->maxClients == 1 && !IsSimulatingOnAlternateTicks() )
  2079. {
  2080. m_flPushSpeed *= 2.0f;
  2081. }
  2082. #endif // PORTAL 2
  2083. }
  2084. BaseClass::Activate();
  2085. }
  2086. //-----------------------------------------------------------------------------
  2087. // Purpose:
  2088. // Input : *pOther -
  2089. //-----------------------------------------------------------------------------
  2090. void CTriggerPush::Touch( CBaseEntity *pOther )
  2091. {
  2092. if ( !pOther->IsSolid() || (pOther->GetMoveType() == MOVETYPE_PUSH || pOther->GetMoveType() == MOVETYPE_NONE ) )
  2093. return;
  2094. if (!PassesTriggerFilters(pOther))
  2095. return;
  2096. // FIXME: If something is hierarchically attached, should we try to push the parent?
  2097. if (pOther->GetMoveParent())
  2098. return;
  2099. // Transform the push dir into global space
  2100. Vector vecAbsDir;
  2101. VectorRotate( m_vecPushDir, EntityToWorldTransform(), vecAbsDir );
  2102. // Instant trigger, just transfer velocity and remove
  2103. if (HasSpawnFlags(SF_TRIG_PUSH_ONCE))
  2104. {
  2105. pOther->ApplyAbsVelocityImpulse( m_flPushSpeed * vecAbsDir );
  2106. if ( vecAbsDir.z > 0 )
  2107. {
  2108. pOther->SetGroundEntity( NULL );
  2109. }
  2110. UTIL_Remove( this );
  2111. return;
  2112. }
  2113. switch( pOther->GetMoveType() )
  2114. {
  2115. case MOVETYPE_NONE:
  2116. case MOVETYPE_PUSH:
  2117. case MOVETYPE_NOCLIP:
  2118. break;
  2119. case MOVETYPE_VPHYSICS:
  2120. {
  2121. const float DEFAULT_MASS = 100.0f;
  2122. if ( HasSpawnFlags( SF_TRIGGER_PUSH_USE_MASS ) )
  2123. {
  2124. // New-style code (Ep3 and beyond), affects all physobjs and accounts for mass
  2125. IPhysicsObject *ppPhysObjs[ VPHYSICS_MAX_OBJECT_LIST_COUNT ];
  2126. int nNumPhysObjs = pOther->VPhysicsGetObjectList( ppPhysObjs, VPHYSICS_MAX_OBJECT_LIST_COUNT );
  2127. for ( int i = 0; i < nNumPhysObjs; i++ )
  2128. {
  2129. Vector force = m_flPushSpeed * vecAbsDir * DEFAULT_MASS * gpGlobals->frametime;
  2130. force *= ppPhysObjs[ i ]->GetMass() / DEFAULT_MASS;
  2131. ppPhysObjs[ i ]->ApplyForceCenter( force );
  2132. }
  2133. }
  2134. else
  2135. {
  2136. // Old-style code (Ep2 and before), affects the first physobj and assumes the mass is 100kg
  2137. IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
  2138. if ( pPhys )
  2139. {
  2140. pPhys->ApplyForceCenter( m_flPushSpeed * vecAbsDir * DEFAULT_MASS * gpGlobals->frametime );
  2141. return;
  2142. }
  2143. }
  2144. }
  2145. break;
  2146. default:
  2147. {
  2148. #if defined( HL2_DLL )
  2149. // HACK HACK HL2 players on ladders will only be disengaged if the sf is set, otherwise no push occurs.
  2150. if ( pOther->IsPlayer() &&
  2151. pOther->GetMoveType() == MOVETYPE_LADDER )
  2152. {
  2153. if ( !HasSpawnFlags(SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER) )
  2154. {
  2155. // Ignore the push
  2156. return;
  2157. }
  2158. }
  2159. #endif
  2160. Vector vecPush = (m_flPushSpeed * vecAbsDir);
  2161. if ( pOther->GetFlags() & FL_BASEVELOCITY )
  2162. {
  2163. vecPush = vecPush + pOther->GetBaseVelocity();
  2164. }
  2165. if ( vecPush.z > 0 && (pOther->GetFlags() & FL_ONGROUND) )
  2166. {
  2167. pOther->SetGroundEntity( NULL );
  2168. Vector origin = pOther->GetAbsOrigin();
  2169. origin.z += 1.0f;
  2170. pOther->SetAbsOrigin( origin );
  2171. }
  2172. pOther->SetBaseVelocity( vecPush );
  2173. pOther->AddFlag( FL_BASEVELOCITY );
  2174. }
  2175. break;
  2176. }
  2177. }
  2178. void CTriggerPush::InputSetPushDirection( inputdata_t &inputdata )
  2179. {
  2180. inputdata.value.Vector3D( m_vecPushDir );
  2181. // Convert pushdir from angles to a vector
  2182. Vector vecAbsDir;
  2183. QAngle angPushDir = QAngle(m_vecPushDir.x, m_vecPushDir.y, m_vecPushDir.z);
  2184. AngleVectors(angPushDir, &vecAbsDir);
  2185. // Transform the vector into entity space
  2186. VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecPushDir );
  2187. }
  2188. //-----------------------------------------------------------------------------
  2189. //-----------------------------------------------------------------------------
  2190. void CTriggerPush::DrawDebugGeometryOverlays()
  2191. {
  2192. BaseClass::DrawDebugGeometryOverlays();
  2193. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  2194. {
  2195. // Transform the push dir into global space
  2196. Vector vecAbsDir;
  2197. VectorRotate( m_vecPushDir, EntityToWorldTransform(), vecAbsDir );
  2198. NDebugOverlay::VertArrow( WorldSpaceCenter(), WorldSpaceCenter() + vecAbsDir * 100, 10, 255, 0, 255, 0, true, 0.1 );
  2199. }
  2200. }
  2201. //-----------------------------------------------------------------------------
  2202. // Bomb reset trigger - places dropped bomb at the last valid player-held location
  2203. //-----------------------------------------------------------------------------
  2204. class CTriggerBombReset : public CBaseTrigger
  2205. {
  2206. public:
  2207. DECLARE_CLASS(CTriggerBombReset, CBaseTrigger);
  2208. void Spawn(void);
  2209. void Touch(CBaseEntity *pOther);
  2210. DECLARE_DATADESC();
  2211. };
  2212. LINK_ENTITY_TO_CLASS(trigger_bomb_reset, CTriggerBombReset);
  2213. BEGIN_DATADESC(CTriggerBombReset)
  2214. END_DATADESC()
  2215. void CTriggerBombReset::Spawn(void)
  2216. {
  2217. InitTrigger();
  2218. //the bomb is technically 'debris' so always set this flag
  2219. CollisionProp()->AddSolidFlags(FSOLID_TRIGGER_TOUCH_DEBRIS);
  2220. }
  2221. void CTriggerBombReset::Touch(CBaseEntity *pOther)
  2222. {
  2223. // If the bomb touches this trigger, tell it to reset to its last known valid position.
  2224. CC4 *pC4 = dynamic_cast< CC4* > (pOther);
  2225. if (pC4)
  2226. {
  2227. pC4->ResetToLastValidPlayerHeldPosition();
  2228. }
  2229. }
  2230. //-----------------------------------------------------------------------------
  2231. // Teleport trigger
  2232. //-----------------------------------------------------------------------------
  2233. const int SF_TELEPORT_PRESERVE_ANGLES = 0x20; // Preserve angles even when a local landmark is not specified
  2234. class CTriggerTeleport : public CBaseTrigger
  2235. {
  2236. public:
  2237. DECLARE_CLASS( CTriggerTeleport, CBaseTrigger );
  2238. void Spawn( void );
  2239. void Touch( CBaseEntity *pOther );
  2240. void InputSetTarget( inputdata_t &inputdata );
  2241. string_t m_iLandmark;
  2242. private:
  2243. bool m_bUseLandmarkAngles;
  2244. bool m_bCheckDestIfClearForPlayer;
  2245. CBaseEntity *m_pentTarget;
  2246. DECLARE_DATADESC();
  2247. };
  2248. LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport );
  2249. BEGIN_DATADESC( CTriggerTeleport )
  2250. DEFINE_KEYFIELD( m_iLandmark, FIELD_STRING, "landmark" ),
  2251. DEFINE_KEYFIELD( m_bUseLandmarkAngles, FIELD_BOOLEAN, "UseLandmarkAngles" ),
  2252. DEFINE_KEYFIELD( m_bCheckDestIfClearForPlayer, FIELD_BOOLEAN, "CheckDestIfClearForPlayer" ),
  2253. DEFINE_INPUTFUNC( FIELD_STRING, "SetRemoteDestination", InputSetTarget ),
  2254. END_DATADESC()
  2255. void CTriggerTeleport::Spawn( void )
  2256. {
  2257. InitTrigger();
  2258. m_pentTarget = NULL;
  2259. }
  2260. //------------------------------------------------------------------------------
  2261. // Purpose: Input handler to set a new target
  2262. //------------------------------------------------------------------------------
  2263. void CTriggerTeleport::InputSetTarget( inputdata_t &inputdata )
  2264. {
  2265. m_target = MAKE_STRING( inputdata.value.String() );
  2266. }
  2267. //-----------------------------------------------------------------------------
  2268. // Purpose: Teleports the entity that touched us to the location of our target,
  2269. // setting the toucher's angles to our target's angles if they are a
  2270. // player.
  2271. //
  2272. // If a landmark was specified, the toucher is offset from the target
  2273. // by their initial offset from the landmark and their angles are
  2274. // left alone.
  2275. //
  2276. // Input : pOther - The entity that touched us.
  2277. //-----------------------------------------------------------------------------
  2278. void CTriggerTeleport::Touch( CBaseEntity *pOther )
  2279. {
  2280. if (!PassesTriggerFilters(pOther))
  2281. {
  2282. return;
  2283. }
  2284. m_pentTarget = gEntList.FindEntityByName( m_pentTarget, m_target, NULL, pOther, pOther );
  2285. // the first NULL may signify the end of the list. Search one more time to confirm that there are no valid targets.
  2286. if (!m_pentTarget &&
  2287. ( ( m_pentTarget = gEntList.FindEntityByName( m_pentTarget, m_target, NULL, pOther, pOther ) ) == NULL ) )
  2288. {
  2289. Warning("Teleport trigger '%s' cannot find destination named '%s'!\n", this->GetEntityName().ToCStr(), m_target.ToCStr() );
  2290. return;
  2291. }
  2292. if ( pOther->IsPlayer() && m_bCheckDestIfClearForPlayer )
  2293. {
  2294. CBasePlayer * pPlayer = ( CBasePlayer * )pOther;
  2295. while ( m_pentTarget && !g_pGameRules->IsSpawnPointValid( m_pentTarget, pPlayer ) )
  2296. {
  2297. m_pentTarget = gEntList.FindEntityByName( m_pentTarget, m_target, NULL, pOther, pOther );
  2298. }
  2299. }
  2300. if ( !m_pentTarget )
  2301. {
  2302. Warning( "Teleport trigger '%s' cannot find a clear player position\n", this->GetEntityName().ToCStr() );
  2303. return;
  2304. }
  2305. //
  2306. // If a landmark was specified, offset the player relative to the landmark.
  2307. //
  2308. CBaseEntity *pentLandmark = NULL;
  2309. Vector vecLandmarkOffset(0, 0, 0);
  2310. if (m_iLandmark != NULL_STRING)
  2311. {
  2312. // The activator and caller are the same
  2313. pentLandmark = gEntList.FindEntityByName(pentLandmark, m_iLandmark, NULL, pOther, pOther );
  2314. if (pentLandmark)
  2315. {
  2316. vecLandmarkOffset = pOther->GetAbsOrigin() - pentLandmark->GetAbsOrigin();
  2317. }
  2318. }
  2319. pOther->SetGroundEntity( NULL );
  2320. // collect the teleporting object's angles, origin, and velocity
  2321. QAngle qActivatorEyeAngles = pOther->GetAbsAngles();
  2322. if ( pOther->IsPlayer() )
  2323. {
  2324. qActivatorEyeAngles = pOther->EyeAngles();
  2325. }
  2326. QAngle qNewActivatorEyeAngles = qActivatorEyeAngles;
  2327. Vector vecActivatorOrigin = pOther->GetAbsOrigin();
  2328. Vector vecNewActivatorOrigin = vecActivatorOrigin;
  2329. Vector vecActivatorVelocity;
  2330. pOther->GetVelocity( &vecActivatorVelocity );
  2331. Vector vecNewActivatorVelocity = vecActivatorVelocity;
  2332. Vector vecPentTargetOrigin = m_pentTarget->GetAbsOrigin();
  2333. if ( pentLandmark )
  2334. {
  2335. // transform the activator origin, angles, and velocity into the world space of the destination landmark
  2336. matrix3x4_t pTransformMatrix;
  2337. matrix3x4_t pLocalLandmarkMatrix;
  2338. matrix3x4_t pRemoteLandmarkMatrix = m_pentTarget->EntityToWorldTransform();
  2339. MatrixInvert( pentLandmark->EntityToWorldTransform(), pLocalLandmarkMatrix );
  2340. ConcatTransforms( pRemoteLandmarkMatrix, pLocalLandmarkMatrix, pTransformMatrix );
  2341. qNewActivatorEyeAngles = TransformAnglesToWorldSpace( qActivatorEyeAngles, pTransformMatrix );
  2342. VectorTransform( vecActivatorOrigin, pTransformMatrix, vecNewActivatorOrigin );
  2343. VectorRotate( vecActivatorVelocity, pTransformMatrix, vecNewActivatorVelocity );
  2344. }
  2345. else
  2346. {
  2347. // there isn't a local landmark specified so just set the origin to the origin of the destination landmark
  2348. if ( pOther->IsPlayer())
  2349. {
  2350. // make origin adjustments in case the teleportee is a player. (origin in center, not at feet)
  2351. vecPentTargetOrigin.z -= pOther->WorldAlignMins().z;
  2352. }
  2353. // apply the offset
  2354. vecNewActivatorOrigin = vecPentTargetOrigin + vecLandmarkOffset;
  2355. }
  2356. // force the object to use the angles of the teleport destination entity
  2357. if ( m_bUseLandmarkAngles )
  2358. {
  2359. qNewActivatorEyeAngles = m_pentTarget->GetAbsAngles();
  2360. }
  2361. pOther->Teleport( &vecNewActivatorOrigin, &qNewActivatorEyeAngles, &vecNewActivatorVelocity );
  2362. }
  2363. LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity );
  2364. //-----------------------------------------------------------------------------
  2365. // Purpose: Saves the game when the player touches the trigger. Can be enabled or disabled
  2366. //-----------------------------------------------------------------------------
  2367. class CTriggerToggleSave : public CBaseTrigger
  2368. {
  2369. public:
  2370. DECLARE_CLASS( CTriggerToggleSave, CBaseTrigger );
  2371. void Spawn( void );
  2372. void Touch( CBaseEntity *pOther );
  2373. void InputEnable( inputdata_t &inputdata )
  2374. {
  2375. m_bDisabled = false;
  2376. }
  2377. void InputDisable( inputdata_t &inputdata )
  2378. {
  2379. m_bDisabled = true;
  2380. }
  2381. bool m_bDisabled; // Initial state
  2382. DECLARE_DATADESC();
  2383. };
  2384. BEGIN_DATADESC( CTriggerToggleSave )
  2385. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  2386. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  2387. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  2388. END_DATADESC()
  2389. LINK_ENTITY_TO_CLASS( trigger_togglesave, CTriggerToggleSave );
  2390. //-----------------------------------------------------------------------------
  2391. // Purpose: Called when spawning, after keyvalues have been set.
  2392. //-----------------------------------------------------------------------------
  2393. void CTriggerToggleSave::Spawn( void )
  2394. {
  2395. if ( g_pGameRules->IsDeathmatch() )
  2396. {
  2397. UTIL_Remove( this );
  2398. return;
  2399. }
  2400. InitTrigger();
  2401. }
  2402. //-----------------------------------------------------------------------------
  2403. // Purpose: Performs the autosave when the player touches us.
  2404. // Input : pOther -
  2405. //-----------------------------------------------------------------------------
  2406. void CTriggerToggleSave::Touch( CBaseEntity *pOther )
  2407. {
  2408. if( m_bDisabled )
  2409. return;
  2410. // Only save on clients
  2411. if ( !pOther->IsPlayer() )
  2412. return;
  2413. // Can be re-enabled
  2414. m_bDisabled = true;
  2415. engine->ServerCommand( "autosave\n" );
  2416. }
  2417. //-----------------------------------------------------------------------------
  2418. // Purpose: Saves the game when the player touches the trigger.
  2419. //-----------------------------------------------------------------------------
  2420. class CTriggerSave : public CBaseTrigger
  2421. {
  2422. public:
  2423. DECLARE_CLASS( CTriggerSave, CBaseTrigger );
  2424. void Spawn( void );
  2425. void Touch( CBaseEntity *pOther );
  2426. DECLARE_DATADESC();
  2427. bool m_bForceNewLevelUnit;
  2428. float m_fDangerousTimer;
  2429. int m_minHitPoints;
  2430. };
  2431. BEGIN_DATADESC( CTriggerSave )
  2432. DEFINE_KEYFIELD( m_bForceNewLevelUnit, FIELD_BOOLEAN, "NewLevelUnit" ),
  2433. DEFINE_KEYFIELD( m_minHitPoints, FIELD_INTEGER, "MinimumHitPoints" ),
  2434. DEFINE_KEYFIELD( m_fDangerousTimer, FIELD_FLOAT, "DangerousTimer" ),
  2435. END_DATADESC()
  2436. LINK_ENTITY_TO_CLASS( trigger_autosave, CTriggerSave );
  2437. //-----------------------------------------------------------------------------
  2438. // Purpose: Called when spawning, after keyvalues have been set.
  2439. //-----------------------------------------------------------------------------
  2440. void CTriggerSave::Spawn( void )
  2441. {
  2442. if ( g_pGameRules->IsDeathmatch() )
  2443. {
  2444. UTIL_Remove( this );
  2445. return;
  2446. }
  2447. InitTrigger();
  2448. }
  2449. //-----------------------------------------------------------------------------
  2450. // Purpose: Performs the autosave when the player touches us.
  2451. // Input : pOther -
  2452. //-----------------------------------------------------------------------------
  2453. void CTriggerSave::Touch( CBaseEntity *pOther )
  2454. {
  2455. // Only save on clients
  2456. if ( !pOther->IsPlayer() )
  2457. return;
  2458. if ( m_fDangerousTimer != 0.0f )
  2459. {
  2460. if ( g_ServerGameDLL.m_fAutoSaveDangerousTime != 0.0f && g_ServerGameDLL.m_fAutoSaveDangerousTime >= gpGlobals->curtime )
  2461. {
  2462. // A previous dangerous auto save was waiting to become safe
  2463. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  2464. if ( pPlayer->GetDeathTime() == 0.0f || pPlayer->GetDeathTime() > gpGlobals->curtime )
  2465. {
  2466. // The player isn't dead, so make the dangerous auto save safe
  2467. engine->ServerCommand( "autosavedangerousissafe\n" );
  2468. }
  2469. }
  2470. }
  2471. // this is a one-way transition - there is no way to return to the previous map.
  2472. if ( m_bForceNewLevelUnit )
  2473. {
  2474. engine->ClearSaveDir();
  2475. }
  2476. UTIL_Remove( this );
  2477. if ( m_fDangerousTimer != 0.0f )
  2478. {
  2479. // There's a dangerous timer. Save if we have enough hitpoints.
  2480. CBasePlayer *pPlayer = UTIL_PlayerByIndex( 1 );
  2481. if (pPlayer && pPlayer->GetHealth() >= m_minHitPoints)
  2482. {
  2483. engine->ServerCommand( "autosavedangerous\n" );
  2484. g_ServerGameDLL.m_fAutoSaveDangerousTime = gpGlobals->curtime + m_fDangerousTimer;
  2485. }
  2486. }
  2487. else
  2488. {
  2489. engine->ServerCommand( "autosave\n" );
  2490. }
  2491. }
  2492. class CTriggerGravity : public CBaseTrigger
  2493. {
  2494. public:
  2495. DECLARE_CLASS( CTriggerGravity, CBaseTrigger );
  2496. DECLARE_DATADESC();
  2497. void Spawn( void );
  2498. void GravityTouch( CBaseEntity *pOther );
  2499. virtual void EndTouch(CBaseEntity *pOther);
  2500. };
  2501. LINK_ENTITY_TO_CLASS( trigger_gravity, CTriggerGravity );
  2502. BEGIN_DATADESC( CTriggerGravity )
  2503. // Function Pointers
  2504. DEFINE_FUNCTION(GravityTouch),
  2505. END_DATADESC()
  2506. void CTriggerGravity::Spawn( void )
  2507. {
  2508. BaseClass::Spawn();
  2509. InitTrigger();
  2510. SetTouch( &CTriggerGravity::GravityTouch );
  2511. }
  2512. void CTriggerGravity::GravityTouch( CBaseEntity *pOther )
  2513. {
  2514. // Only save on clients
  2515. if ( !pOther->IsPlayer() )
  2516. return;
  2517. pOther->SetGravity( GetGravity() );
  2518. }
  2519. //-----------------------------------------------------------------------------
  2520. // Purpose: Removes the added gravity for this entity
  2521. // Input : pOther -
  2522. //-----------------------------------------------------------------------------
  2523. void CTriggerGravity::EndTouch(CBaseEntity *pOther)
  2524. {
  2525. BaseClass::EndTouch( pOther );
  2526. // Only save on clients
  2527. if ( !pOther->IsPlayer() )
  2528. return;
  2529. pOther->SetGravity( 1.0f );
  2530. }
  2531. // this is a really bad idea.
  2532. class CAI_ChangeTarget : public CBaseEntity
  2533. {
  2534. public:
  2535. DECLARE_CLASS( CAI_ChangeTarget, CBaseEntity );
  2536. // Input handlers.
  2537. void InputActivate( inputdata_t &inputdata );
  2538. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  2539. DECLARE_DATADESC();
  2540. private:
  2541. string_t m_iszNewTarget;
  2542. };
  2543. LINK_ENTITY_TO_CLASS( ai_changetarget, CAI_ChangeTarget );
  2544. BEGIN_DATADESC( CAI_ChangeTarget )
  2545. DEFINE_KEYFIELD( m_iszNewTarget, FIELD_STRING, "m_iszNewTarget" ),
  2546. // Inputs
  2547. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  2548. END_DATADESC()
  2549. void CAI_ChangeTarget::InputActivate( inputdata_t &inputdata )
  2550. {
  2551. CBaseEntity *pTarget = NULL;
  2552. while ((pTarget = gEntList.FindEntityByName( pTarget, m_target, NULL, inputdata.pActivator, inputdata.pCaller )) != NULL)
  2553. {
  2554. pTarget->m_target = m_iszNewTarget;
  2555. CAI_BaseNPC *pNPC = pTarget->MyNPCPointer( );
  2556. if (pNPC)
  2557. {
  2558. pNPC->SetGoalEnt( NULL );
  2559. }
  2560. }
  2561. }
  2562. //-----------------------------------------------------------------------------
  2563. // Purpose: Change an NPC's hint group to something new
  2564. //-----------------------------------------------------------------------------
  2565. class CAI_ChangeHintGroup : public CBaseEntity
  2566. {
  2567. public:
  2568. DECLARE_CLASS( CAI_ChangeHintGroup, CBaseEntity );
  2569. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  2570. // Input handlers.
  2571. void InputActivate( inputdata_t &inputdata );
  2572. DECLARE_DATADESC();
  2573. private:
  2574. CAI_BaseNPC *FindQualifiedNPC( CAI_BaseNPC *pPrev, CBaseEntity *pActivator, CBaseEntity *pCaller );
  2575. int m_iSearchType;
  2576. string_t m_strSearchName;
  2577. string_t m_strNewHintGroup;
  2578. float m_flRadius;
  2579. bool m_bHintGroupNavLimiting;
  2580. };
  2581. LINK_ENTITY_TO_CLASS( ai_changehintgroup, CAI_ChangeHintGroup );
  2582. BEGIN_DATADESC( CAI_ChangeHintGroup )
  2583. DEFINE_KEYFIELD( m_iSearchType, FIELD_INTEGER, "SearchType" ),
  2584. DEFINE_KEYFIELD( m_strSearchName, FIELD_STRING, "SearchName" ),
  2585. DEFINE_KEYFIELD( m_strNewHintGroup, FIELD_STRING, "NewHintGroup" ),
  2586. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "Radius" ),
  2587. DEFINE_KEYFIELD( m_bHintGroupNavLimiting, FIELD_BOOLEAN, "hintlimiting" ),
  2588. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate ),
  2589. END_DATADESC()
  2590. CAI_BaseNPC *CAI_ChangeHintGroup::FindQualifiedNPC( CAI_BaseNPC *pPrev, CBaseEntity *pActivator, CBaseEntity *pCaller )
  2591. {
  2592. CBaseEntity *pEntity = pPrev;
  2593. CAI_BaseNPC *pResult = NULL;
  2594. const char *pszSearchName = STRING(m_strSearchName);
  2595. while ( !pResult )
  2596. {
  2597. // Find a candidate
  2598. switch ( m_iSearchType )
  2599. {
  2600. case 0:
  2601. {
  2602. pEntity = gEntList.FindEntityByNameWithin( pEntity, pszSearchName, GetLocalOrigin(), m_flRadius, NULL, pActivator, pCaller );
  2603. break;
  2604. }
  2605. case 1:
  2606. {
  2607. pEntity = gEntList.FindEntityByClassnameWithin( pEntity, pszSearchName, GetLocalOrigin(), m_flRadius );
  2608. break;
  2609. }
  2610. case 2:
  2611. {
  2612. pEntity = gEntList.FindEntityInSphere( pEntity, GetLocalOrigin(), ( m_flRadius != 0.0 ) ? m_flRadius : FLT_MAX );
  2613. break;
  2614. }
  2615. }
  2616. if ( !pEntity )
  2617. return NULL;
  2618. // Qualify
  2619. pResult = pEntity->MyNPCPointer();
  2620. if ( pResult && m_iSearchType == 2 && (!FStrEq( STRING(pResult->GetHintGroup()), pszSearchName ) ) )
  2621. {
  2622. pResult = NULL;
  2623. }
  2624. }
  2625. return pResult;
  2626. }
  2627. void CAI_ChangeHintGroup::InputActivate( inputdata_t &inputdata )
  2628. {
  2629. CAI_BaseNPC *pTarget = NULL;
  2630. while((pTarget = FindQualifiedNPC( pTarget, inputdata.pActivator, inputdata.pCaller )) != NULL)
  2631. {
  2632. pTarget->SetHintGroup( m_strNewHintGroup, m_bHintGroupNavLimiting );
  2633. }
  2634. }
  2635. #define SF_CAMERA_PLAYER_POSITION 1
  2636. #define SF_CAMERA_PLAYER_TARGET 2
  2637. #define SF_CAMERA_PLAYER_TAKECONTROL 4
  2638. #define SF_CAMERA_PLAYER_INFINITE_WAIT 8
  2639. #define SF_CAMERA_PLAYER_SNAP_TO 16
  2640. #define SF_CAMERA_PLAYER_NOT_SOLID 32
  2641. #define SF_CAMERA_PLAYER_INTERRUPT 64
  2642. #define SF_CAMERA_PLAYER_SETFOV 128
  2643. #if HL2_EPISODIC
  2644. const float CTriggerCamera::kflPosInterpTime = 2.0f;
  2645. #endif
  2646. LINK_ENTITY_TO_CLASS( point_viewcontrol, CTriggerCamera );
  2647. BEGIN_DATADESC( CTriggerCamera )
  2648. DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
  2649. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  2650. DEFINE_FIELD( m_pPath, FIELD_CLASSPTR ),
  2651. DEFINE_FIELD( m_sPath, FIELD_STRING ),
  2652. DEFINE_FIELD( m_flWait, FIELD_FLOAT ),
  2653. DEFINE_FIELD( m_flReturnTime, FIELD_TIME ),
  2654. DEFINE_FIELD( m_flStopTime, FIELD_TIME ),
  2655. DEFINE_FIELD( m_moveDistance, FIELD_FLOAT ),
  2656. DEFINE_FIELD( m_targetSpeed, FIELD_FLOAT ),
  2657. DEFINE_FIELD( m_initialSpeed, FIELD_FLOAT ),
  2658. DEFINE_FIELD( m_acceleration, FIELD_FLOAT ),
  2659. DEFINE_FIELD( m_deceleration, FIELD_FLOAT ),
  2660. DEFINE_FIELD( m_state, FIELD_INTEGER ),
  2661. DEFINE_FIELD( m_vecMoveDir, FIELD_VECTOR ),
  2662. DEFINE_KEYFIELD( m_iszTargetAttachment, FIELD_STRING, "targetattachment" ),
  2663. DEFINE_FIELD( m_iAttachmentIndex, FIELD_INTEGER ),
  2664. DEFINE_FIELD( m_bSnapToGoal, FIELD_BOOLEAN ),
  2665. #if HL2_EPISODIC
  2666. DEFINE_KEYFIELD( m_bInterpolatePosition, FIELD_BOOLEAN, "interpolatepositiontoplayer" ),
  2667. DEFINE_FIELD( m_vStartPos, FIELD_VECTOR ),
  2668. DEFINE_FIELD( m_vEndPos, FIELD_VECTOR ),
  2669. DEFINE_FIELD( m_flInterpStartTime, FIELD_TIME ),
  2670. #endif
  2671. DEFINE_FIELD( m_nPlayerButtons, FIELD_INTEGER ),
  2672. DEFINE_FIELD( m_nOldTakeDamage, FIELD_INTEGER ),
  2673. DEFINE_KEYFIELD( m_trackSpeed, FIELD_FLOAT, "trackspeed" ),
  2674. DEFINE_KEYFIELD( m_fov, FIELD_FLOAT, "fov" ),
  2675. DEFINE_KEYFIELD( m_fovSpeed, FIELD_FLOAT, "fov_rate" ),
  2676. // Inputs
  2677. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  2678. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  2679. DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ),
  2680. DEFINE_INPUTFUNC( FIELD_STRING, "SetTargetAttachment", InputSetTargetAttachment ),
  2681. DEFINE_INPUTFUNC( FIELD_VOID, "ReturnToEyes", InputReturnToEyes ),
  2682. DEFINE_INPUTFUNC( FIELD_VOID, "TeleportToView", InputTeleportToView ),
  2683. DEFINE_INPUTFUNC( FIELD_STRING, "SetPath", InputSetPath ),
  2684. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTrackSpeed", InputSetTrackSpeed ),
  2685. // Function Pointers
  2686. DEFINE_FUNCTION( FollowTarget ),
  2687. DEFINE_FUNCTION( ReturnToEyes ),
  2688. DEFINE_OUTPUT( m_OnEndFollow, "OnEndFollow" ),
  2689. END_DATADESC()
  2690. // VScript: publish class and select members to script language
  2691. BEGIN_ENT_SCRIPTDESC( CTriggerCamera, CBaseEntity, "Server-side camera entity" )
  2692. DEFINE_SCRIPTFUNC_NAMED( ScriptGetFov, "GetFov", "get camera's current fov setting as integer" )
  2693. DEFINE_SCRIPTFUNC_NAMED( ScriptSetFov, "SetFov", "set camera's current fov in integer degrees and fov change rate as float" )
  2694. END_SCRIPTDESC();
  2695. //-----------------------------------------------------------------------------
  2696. // Purpose:
  2697. //-----------------------------------------------------------------------------
  2698. CTriggerCamera::CTriggerCamera()
  2699. {
  2700. m_fov = 90;
  2701. m_fovSpeed = 1;
  2702. m_trackSpeed = 40;
  2703. }
  2704. //-----------------------------------------------------------------------------
  2705. // Purpose:
  2706. //-----------------------------------------------------------------------------
  2707. void CTriggerCamera::Spawn( void )
  2708. {
  2709. BaseClass::Spawn();
  2710. SetMoveType( MOVETYPE_NOCLIP );
  2711. SetSolid( SOLID_NONE ); // Remove model & collisions
  2712. SetRenderAlpha( 0 ); // The engine won't draw this model if this is set to 0 and blending is on
  2713. m_nRenderMode = kRenderTransTexture;
  2714. m_state = USE_OFF;
  2715. m_initialSpeed = m_flSpeed;
  2716. if ( m_acceleration == 0 )
  2717. m_acceleration = 500;
  2718. if ( m_deceleration == 0 )
  2719. m_deceleration = 500;
  2720. DispatchUpdateTransmitState();
  2721. }
  2722. int CTriggerCamera::UpdateTransmitState()
  2723. {
  2724. // always tranmit if currently used by a monitor
  2725. if ( m_state == USE_ON )
  2726. {
  2727. return SetTransmitState( FL_EDICT_ALWAYS );
  2728. }
  2729. else
  2730. {
  2731. return SetTransmitState( FL_EDICT_DONTSEND );
  2732. }
  2733. }
  2734. //-----------------------------------------------------------------------------
  2735. // Purpose:
  2736. //-----------------------------------------------------------------------------
  2737. bool CTriggerCamera::KeyValue( const char *szKeyName, const char *szValue )
  2738. {
  2739. if (FStrEq(szKeyName, "wait"))
  2740. {
  2741. m_flWait = atof(szValue);
  2742. }
  2743. else if (FStrEq(szKeyName, "moveto"))
  2744. {
  2745. m_sPath = AllocPooledString( szValue );
  2746. }
  2747. else if (FStrEq(szKeyName, "acceleration"))
  2748. {
  2749. m_acceleration = atof( szValue );
  2750. }
  2751. else if (FStrEq(szKeyName, "deceleration"))
  2752. {
  2753. m_deceleration = atof( szValue );
  2754. }
  2755. else
  2756. return BaseClass::KeyValue( szKeyName, szValue );
  2757. return true;
  2758. }
  2759. //------------------------------------------------------------------------------
  2760. // Purpose: Input handler to turn on this trigger.
  2761. //------------------------------------------------------------------------------
  2762. void CTriggerCamera::InputEnable( inputdata_t &inputdata )
  2763. {
  2764. m_hPlayer = inputdata.pActivator;
  2765. Enable();
  2766. }
  2767. //------------------------------------------------------------------------------
  2768. // Purpose: Input handler to turn off this trigger.
  2769. //------------------------------------------------------------------------------
  2770. void CTriggerCamera::InputDisable( inputdata_t &inputdata )
  2771. {
  2772. Disable();
  2773. }
  2774. //------------------------------------------------------------------------------
  2775. // Purpose: Input handler to return the view to the player eyes
  2776. //------------------------------------------------------------------------------
  2777. void CTriggerCamera::InputReturnToEyes( inputdata_t &inputdata )
  2778. {
  2779. if ( m_hPlayer && HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) )
  2780. {
  2781. CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get();
  2782. pBasePlayer->SetFOV( this, 0, m_fovSpeed );
  2783. }
  2784. SetThink( &CTriggerCamera::ReturnToEyes );
  2785. SetNextThink( gpGlobals->curtime );
  2786. }
  2787. //------------------------------------------------------------------------------
  2788. // Purpose: Input handler to return the view to the player eyes
  2789. //------------------------------------------------------------------------------
  2790. void CTriggerCamera::InputSetTrackSpeed( inputdata_t &inputdata )
  2791. {
  2792. m_trackSpeed = inputdata.value.Float();
  2793. }
  2794. //------------------------------------------------------------------------------
  2795. // Purpose: Input handler to set a new target
  2796. //------------------------------------------------------------------------------
  2797. void CTriggerCamera::InputSetTarget( inputdata_t &inputdata )
  2798. {
  2799. m_target = MAKE_STRING( inputdata.value.String() );
  2800. m_hTarget = gEntList.FindEntityByName( NULL, inputdata.value.String() );
  2801. m_iAttachmentIndex = 0;
  2802. // Only track if we have a target
  2803. if ( m_hTarget )
  2804. {
  2805. // follow the player down
  2806. SetThink( &CTriggerCamera::FollowTarget );
  2807. SetNextThink( gpGlobals->curtime );
  2808. }
  2809. }
  2810. //------------------------------------------------------------------------------
  2811. // Purpose: Input handler to look at a new attachment
  2812. //------------------------------------------------------------------------------
  2813. void CTriggerCamera::InputSetTargetAttachment( inputdata_t &inputdata )
  2814. {
  2815. m_iszTargetAttachment = MAKE_STRING( inputdata.value.String() );
  2816. FindAttachment();
  2817. }
  2818. //------------------------------------------------------------------------------
  2819. // Purpose: Input handler to set a new target
  2820. //------------------------------------------------------------------------------
  2821. void CTriggerCamera::InputSetPath( inputdata_t &inputdata )
  2822. {
  2823. m_pPath = gEntList.FindEntityByName( NULL, inputdata.value.String() );
  2824. m_flStopTime = gpGlobals->curtime;
  2825. if ( m_pPath )
  2826. {
  2827. if ( m_pPath->m_flSpeed != 0 )
  2828. m_targetSpeed = m_pPath->m_flSpeed;
  2829. m_vecMoveDir = m_pPath->GetLocalOrigin() - GetLocalOrigin();
  2830. m_moveDistance = VectorNormalize( m_vecMoveDir );
  2831. m_flStopTime = gpGlobals->curtime + m_pPath->GetDelay();
  2832. }
  2833. }
  2834. //------------------------------------------------------------------------------
  2835. // Purpose: Input handler to turn off this trigger.
  2836. //------------------------------------------------------------------------------
  2837. void CTriggerCamera::FindAttachment()
  2838. {
  2839. // If we don't have a target, ignore the attachment / etc
  2840. if ( m_hTarget )
  2841. {
  2842. m_iAttachmentIndex = 0;
  2843. if ( m_iszTargetAttachment != NULL_STRING )
  2844. {
  2845. if ( !m_hTarget->GetBaseAnimating() )
  2846. {
  2847. Warning("%s tried to target an attachment (%s) on target %s, which has no model.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) );
  2848. }
  2849. else
  2850. {
  2851. m_iAttachmentIndex = m_hTarget->GetBaseAnimating()->LookupAttachment( STRING(m_iszTargetAttachment) );
  2852. if ( !m_iAttachmentIndex )
  2853. {
  2854. Warning("%s could not find attachment %s on target %s.\n", GetClassname(), STRING(m_iszTargetAttachment), STRING(m_hTarget->GetEntityName()) );
  2855. }
  2856. }
  2857. }
  2858. }
  2859. }
  2860. //-----------------------------------------------------------------------------
  2861. // Purpose:
  2862. //-----------------------------------------------------------------------------
  2863. void CTriggerCamera::Enable( void )
  2864. {
  2865. m_state = USE_ON;
  2866. if ( !m_hPlayer || !m_hPlayer->IsPlayer() )
  2867. {
  2868. m_hPlayer = UTIL_GetLocalPlayer();
  2869. }
  2870. if ( !m_hPlayer )
  2871. {
  2872. DispatchUpdateTransmitState();
  2873. return;
  2874. }
  2875. Assert( m_hPlayer->IsPlayer() );
  2876. CBasePlayer *pPlayer = NULL;
  2877. if ( m_hPlayer->IsPlayer() )
  2878. {
  2879. pPlayer = ((CBasePlayer*)m_hPlayer.Get());
  2880. }
  2881. else
  2882. {
  2883. Warning("CTriggerCamera could not find a player!\n");
  2884. return;
  2885. }
  2886. // if the player was already under control of a similar trigger, disable the previous trigger.
  2887. {
  2888. CBaseEntity *pPrevViewControl = pPlayer->GetViewEntity();
  2889. if (pPrevViewControl && pPrevViewControl != pPlayer)
  2890. {
  2891. CTriggerCamera *pOtherCamera = dynamic_cast<CTriggerCamera *>(pPrevViewControl);
  2892. if ( pOtherCamera )
  2893. {
  2894. if ( pOtherCamera == this )
  2895. {
  2896. // what the hell do you think you are doing?
  2897. Warning("Viewcontrol %s was enabled twice in a row!\n", GetDebugName());
  2898. return;
  2899. }
  2900. else
  2901. {
  2902. pOtherCamera->Disable();
  2903. }
  2904. }
  2905. }
  2906. }
  2907. #if defined ( PORTAL2 )
  2908. CPortal_Player* pPortalPlayer = ToPortalPlayer( pPlayer );
  2909. if ( pPortalPlayer && pPortalPlayer->IsZoomed() )
  2910. {
  2911. pPortalPlayer->ZoomOut();
  2912. }
  2913. #endif
  2914. m_nPlayerButtons = pPlayer->m_nButtons;
  2915. // 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.
  2916. m_nOldTakeDamage = m_hPlayer->m_takedamage;
  2917. m_hPlayer->m_takedamage = DAMAGE_NO;
  2918. if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) )
  2919. {
  2920. m_hPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
  2921. }
  2922. m_flReturnTime = gpGlobals->curtime + m_flWait;
  2923. m_flSpeed = m_initialSpeed;
  2924. m_targetSpeed = m_initialSpeed;
  2925. // this pertains to view angles, not translation.
  2926. if ( HasSpawnFlags( SF_CAMERA_PLAYER_SNAP_TO ) )
  2927. {
  2928. m_bSnapToGoal = true;
  2929. }
  2930. if ( HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) )
  2931. {
  2932. if ( pPlayer )
  2933. {
  2934. if ( pPlayer->GetFOVOwner() && (FClassnameIs( pPlayer->GetFOVOwner(), "point_viewcontrol_multiplayer" ) || FClassnameIs( pPlayer->GetFOVOwner(), "point_viewcontrol" )) )
  2935. {
  2936. pPlayer->ClearZoomOwner();
  2937. }
  2938. pPlayer->SetFOV( this, m_fov, m_fovSpeed );
  2939. }
  2940. }
  2941. if ( HasSpawnFlags(SF_CAMERA_PLAYER_TARGET ) )
  2942. {
  2943. m_hTarget = m_hPlayer;
  2944. }
  2945. else
  2946. {
  2947. m_hTarget = GetNextTarget();
  2948. }
  2949. FindAttachment();
  2950. if (HasSpawnFlags(SF_CAMERA_PLAYER_TAKECONTROL ) )
  2951. {
  2952. ((CBasePlayer*)m_hPlayer.Get())->EnableControl(FALSE);
  2953. }
  2954. if ( m_sPath != NULL_STRING )
  2955. {
  2956. m_pPath = gEntList.FindEntityByName( NULL, m_sPath, NULL, m_hPlayer );
  2957. }
  2958. else
  2959. {
  2960. m_pPath = NULL;
  2961. }
  2962. m_flStopTime = gpGlobals->curtime;
  2963. if ( m_pPath )
  2964. {
  2965. if ( m_pPath->m_flSpeed != 0 )
  2966. m_targetSpeed = m_pPath->m_flSpeed;
  2967. m_flStopTime += m_pPath->GetDelay();
  2968. }
  2969. // copy over player information. If we're interpolating from
  2970. // the player position, do something more elaborate.
  2971. #if HL2_EPISODIC
  2972. if (m_bInterpolatePosition)
  2973. {
  2974. // initialize the values we'll spline between
  2975. m_vStartPos = m_hPlayer->EyePosition();
  2976. m_vEndPos = GetAbsOrigin();
  2977. m_flInterpStartTime = gpGlobals->curtime;
  2978. UTIL_SetOrigin( this, m_hPlayer->EyePosition() );
  2979. SetLocalAngles( QAngle( m_hPlayer->GetLocalAngles().x, m_hPlayer->GetLocalAngles().y, 0 ) );
  2980. SetAbsVelocity( vec3_origin );
  2981. }
  2982. else
  2983. #endif
  2984. if (HasSpawnFlags(SF_CAMERA_PLAYER_POSITION ) )
  2985. {
  2986. UTIL_SetOrigin( this, m_hPlayer->EyePosition() );
  2987. SetLocalAngles( QAngle( m_hPlayer->LocalEyeAngles().x, m_hPlayer->LocalEyeAngles().y, 0 ) );
  2988. SetAbsVelocity( m_hPlayer->GetAbsVelocity() );
  2989. }
  2990. else
  2991. {
  2992. SetAbsVelocity( vec3_origin );
  2993. }
  2994. pPlayer->SetViewEntity( this, false ); //HACK: drawing the local player should probably be an entity option.
  2995. // Hide the player's viewmodel
  2996. if ( pPlayer->GetActiveWeapon() )
  2997. {
  2998. pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW );
  2999. }
  3000. // Only track if we have a target
  3001. if ( m_hTarget )
  3002. {
  3003. // follow the player down
  3004. SetThink( &CTriggerCamera::FollowTarget );
  3005. SetNextThink( gpGlobals->curtime );
  3006. }
  3007. m_moveDistance = 0;
  3008. Move();
  3009. DispatchUpdateTransmitState();
  3010. }
  3011. //-----------------------------------------------------------------------------
  3012. // Purpose: vscript callback to get the player's fov
  3013. //-----------------------------------------------------------------------------
  3014. int CTriggerCamera::ScriptGetFov( void )
  3015. {
  3016. if ( m_hPlayer)
  3017. {
  3018. CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get();
  3019. int iFOV = pBasePlayer->GetFOV( );
  3020. return iFOV;
  3021. }
  3022. return 0;
  3023. }
  3024. //-----------------------------------------------------------------------------
  3025. // Purpose: vscript callback to slam the player's fov
  3026. //-----------------------------------------------------------------------------
  3027. void CTriggerCamera::ScriptSetFov( int iFOV, float fovSpeed )
  3028. {
  3029. if ( m_hPlayer)
  3030. {
  3031. m_fov = iFOV;
  3032. m_fovSpeed = fovSpeed;
  3033. CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get();
  3034. pBasePlayer->SetFOV( this, iFOV, fovSpeed );
  3035. }
  3036. }
  3037. //-----------------------------------------------------------------------------
  3038. // Purpose:
  3039. //-----------------------------------------------------------------------------
  3040. void CTriggerCamera::Disable( void )
  3041. {
  3042. if ( m_hPlayer )
  3043. {
  3044. CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get();
  3045. if ( pBasePlayer->IsAlive() )
  3046. {
  3047. if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) )
  3048. {
  3049. pBasePlayer->RemoveSolidFlags( FSOLID_NOT_SOLID );
  3050. }
  3051. pBasePlayer->SetViewEntity( NULL );
  3052. pBasePlayer->EnableControl(TRUE);
  3053. pBasePlayer->m_Local.m_bDrawViewmodel = true;
  3054. }
  3055. if ( HasSpawnFlags( SF_CAMERA_PLAYER_SETFOV ) )
  3056. {
  3057. pBasePlayer->SetFOV( this, 0, m_fovSpeed );
  3058. }
  3059. //return the player to previous takedamage state
  3060. m_hPlayer->m_takedamage = m_nOldTakeDamage;
  3061. }
  3062. m_state = USE_OFF;
  3063. m_flReturnTime = gpGlobals->curtime;
  3064. SetThink( NULL );
  3065. m_OnEndFollow.FireOutput(this, this); // dvsents2: what is the best name for this output?
  3066. SetLocalAngularVelocity( vec3_angle );
  3067. DispatchUpdateTransmitState();
  3068. }
  3069. //-----------------------------------------------------------------------------
  3070. // Purpose:
  3071. //-----------------------------------------------------------------------------
  3072. void CTriggerCamera::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  3073. {
  3074. if ( !ShouldToggle( useType, m_state ) )
  3075. return;
  3076. // Toggle state
  3077. if ( m_state != USE_OFF )
  3078. {
  3079. Disable();
  3080. }
  3081. else
  3082. {
  3083. m_hPlayer = pActivator;
  3084. Enable();
  3085. }
  3086. }
  3087. //-----------------------------------------------------------------------------
  3088. // Purpose:
  3089. //-----------------------------------------------------------------------------
  3090. void CTriggerCamera::MoveViewTo( QAngle vecGoalView )
  3091. {
  3092. if (m_hPlayer == NULL)
  3093. return;
  3094. QAngle angles = GetLocalAngles();
  3095. if (angles.y > 360)
  3096. angles.y -= 360;
  3097. if (angles.y < 0)
  3098. angles.y += 360;
  3099. SetLocalAngles( angles );
  3100. float dx = vecGoalView.x - GetLocalAngles().x;
  3101. float dy = vecGoalView.y - GetLocalAngles().y;
  3102. if (dx < -180)
  3103. dx += 360;
  3104. if (dx > 180)
  3105. dx = dx - 360;
  3106. if (dy < -180)
  3107. dy += 360;
  3108. if (dy > 180)
  3109. dy = dy - 360;
  3110. QAngle vecAngVel;
  3111. vecAngVel.Init( dx * m_trackSpeed * gpGlobals->frametime, dy * m_trackSpeed * gpGlobals->frametime, GetLocalAngularVelocity().z );
  3112. SetLocalAngularVelocity(vecAngVel);
  3113. }
  3114. //-----------------------------------------------------------------------------
  3115. // Purpose:
  3116. //-----------------------------------------------------------------------------
  3117. void CTriggerCamera::InputTeleportToView( inputdata_t &inputdata )
  3118. {
  3119. if( m_hPlayer )
  3120. {
  3121. CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get();
  3122. QAngle vecPlayerView = GetAbsAngles();
  3123. Vector vecEyeOffset = pBasePlayer->EyePosition() - pBasePlayer->GetAbsOrigin(); // is there better way to get this? - just want standing eye height
  3124. Vector vecTeleportPosition = GetAbsOrigin() - vecEyeOffset;
  3125. // try to find a position on the ground - this will prevent a pop
  3126. trace_t tr;
  3127. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - 1.02f*vecEyeOffset, MASK_SOLID, pBasePlayer, COLLISION_GROUP_NONE, &tr );
  3128. if( tr.fraction != 1.0 )
  3129. {
  3130. vecTeleportPosition = tr.endpos;
  3131. }
  3132. pBasePlayer->SetGroundEntity( NULL );
  3133. pBasePlayer->Teleport( &vecTeleportPosition, &vecPlayerView, NULL );
  3134. }
  3135. Disable();
  3136. }
  3137. //-----------------------------------------------------------------------------
  3138. // Purpose:
  3139. //-----------------------------------------------------------------------------
  3140. void CTriggerCamera::ReturnToEyes( void )
  3141. {
  3142. if (m_hPlayer == NULL)
  3143. return;
  3144. // get back to our original position
  3145. Vector vecPositionDiff = m_hPlayer->EyePosition() - GetAbsOrigin();
  3146. UTIL_SetOrigin( this, GetAbsOrigin() + 0.1f * vecPositionDiff );
  3147. // and our eye angles
  3148. QAngle vecPlayerView = QAngle( m_hPlayer->LocalEyeAngles().x, m_hPlayer->LocalEyeAngles().y, 0 );
  3149. MoveViewTo( vecPlayerView );
  3150. const float epsilon = 1.0f;
  3151. float distance = fabs((vecPlayerView - QAngle( GetLocalAngles().x, GetLocalAngles().y, 0 )).Length());
  3152. if( distance > 180.f )
  3153. {
  3154. distance -= 360.f;
  3155. }
  3156. if( fabs(distance) < epsilon )
  3157. {
  3158. Disable();
  3159. }
  3160. else
  3161. {
  3162. SetNextThink( gpGlobals->curtime );
  3163. }
  3164. }
  3165. //-----------------------------------------------------------------------------
  3166. // Purpose:
  3167. //-----------------------------------------------------------------------------
  3168. void CTriggerCamera::FollowTarget( )
  3169. {
  3170. if (m_hPlayer == NULL)
  3171. return;
  3172. if ( m_hTarget == NULL )
  3173. {
  3174. Disable();
  3175. return;
  3176. }
  3177. if ( !HasSpawnFlags(SF_CAMERA_PLAYER_INFINITE_WAIT) && (!m_hTarget || m_flReturnTime < gpGlobals->curtime) )
  3178. {
  3179. Disable();
  3180. return;
  3181. }
  3182. QAngle vecGoal;
  3183. if ( m_iAttachmentIndex )
  3184. {
  3185. Vector vecOrigin;
  3186. m_hTarget->GetBaseAnimating()->GetAttachment( m_iAttachmentIndex, vecOrigin );
  3187. VectorAngles( vecOrigin - GetAbsOrigin(), vecGoal );
  3188. }
  3189. else
  3190. {
  3191. if ( m_hTarget )
  3192. {
  3193. VectorAngles( m_hTarget->GetAbsOrigin() - GetAbsOrigin(), vecGoal );
  3194. }
  3195. else
  3196. {
  3197. // Use the viewcontroller's angles
  3198. vecGoal = GetAbsAngles();
  3199. }
  3200. }
  3201. // Should we just snap to the goal angles?
  3202. if ( m_bSnapToGoal )
  3203. {
  3204. SetAbsAngles( vecGoal );
  3205. m_bSnapToGoal = false;
  3206. }
  3207. else
  3208. {
  3209. MoveViewTo( vecGoal );
  3210. }
  3211. if (!HasSpawnFlags(SF_CAMERA_PLAYER_TAKECONTROL))
  3212. {
  3213. SetAbsVelocity( GetAbsVelocity() * 0.8 );
  3214. if (GetAbsVelocity().Length( ) < 10.0)
  3215. {
  3216. SetAbsVelocity( vec3_origin );
  3217. }
  3218. }
  3219. SetNextThink( gpGlobals->curtime );
  3220. Move();
  3221. }
  3222. void CTriggerCamera::StartCameraShot( const char *pszShotType, CBaseEntity *pSceneEntity, CBaseEntity *pActor1, CBaseEntity *pActor2, float duration )
  3223. {
  3224. // called from SceneEntity in response to a CChoreoEvent::CAMERA sent from a VCD.
  3225. // talk to vscript, start a camera move
  3226. HSCRIPT hStartCameraShot = NULL;
  3227. // switch to this camera
  3228. // Enable();
  3229. // get script module associated with this ent, lookup function in module
  3230. if( m_iszVScripts != NULL_STRING )
  3231. {
  3232. hStartCameraShot = m_ScriptScope.LookupFunction( "ScriptStartCameraShot" );
  3233. }
  3234. // call the script function to begin the camera move
  3235. if ( hStartCameraShot )
  3236. {
  3237. g_pScriptVM->Call( hStartCameraShot, m_ScriptScope, true, NULL, pszShotType, ToHScript(pSceneEntity), ToHScript(pActor1), ToHScript(pActor2), duration );
  3238. g_pScriptVM->ReleaseFunction( hStartCameraShot );
  3239. }
  3240. }
  3241. void CTriggerCamera::Move()
  3242. {
  3243. if ( HasSpawnFlags( SF_CAMERA_PLAYER_INTERRUPT ) )
  3244. {
  3245. if ( m_hPlayer )
  3246. {
  3247. CBasePlayer *pPlayer = ToBasePlayer( m_hPlayer );
  3248. if ( pPlayer )
  3249. {
  3250. int buttonsChanged = m_nPlayerButtons ^ pPlayer->m_nButtons;
  3251. if ( buttonsChanged && pPlayer->m_nButtons )
  3252. {
  3253. Disable();
  3254. return;
  3255. }
  3256. m_nPlayerButtons = pPlayer->m_nButtons;
  3257. }
  3258. }
  3259. }
  3260. // In vanilla HL2, the camera is either on a path, or doesn't move. In episodic
  3261. // we add the capacity for interpolation to the start point.
  3262. #if HL2_EPISODIC
  3263. if (m_pPath)
  3264. #else
  3265. // Not moving on a path, return
  3266. if (!m_pPath)
  3267. {
  3268. SetAbsVelocity( vec3_origin );
  3269. return;
  3270. }
  3271. #endif
  3272. {
  3273. // Subtract movement from the previous frame
  3274. m_moveDistance -= m_flSpeed * gpGlobals->frametime;
  3275. // Have we moved enough to reach the target?
  3276. if ( m_moveDistance <= 0 )
  3277. {
  3278. variant_t emptyVariant;
  3279. m_pPath->AcceptInput( "InPass", this, this, emptyVariant, 0 );
  3280. // Time to go to the next target
  3281. m_pPath = m_pPath->GetNextTarget();
  3282. // Set up next corner
  3283. if ( !m_pPath )
  3284. {
  3285. SetAbsVelocity( vec3_origin );
  3286. }
  3287. else
  3288. {
  3289. if ( m_pPath->m_flSpeed != 0 )
  3290. m_targetSpeed = m_pPath->m_flSpeed;
  3291. m_vecMoveDir = m_pPath->GetLocalOrigin() - GetLocalOrigin();
  3292. m_moveDistance = VectorNormalize( m_vecMoveDir );
  3293. m_flStopTime = gpGlobals->curtime + m_pPath->GetDelay();
  3294. }
  3295. }
  3296. if ( m_flStopTime > gpGlobals->curtime )
  3297. m_flSpeed = UTIL_Approach( 0, m_flSpeed, m_deceleration * gpGlobals->frametime );
  3298. else
  3299. m_flSpeed = UTIL_Approach( m_targetSpeed, m_flSpeed, m_acceleration * gpGlobals->frametime );
  3300. float fraction = 2 * gpGlobals->frametime;
  3301. SetAbsVelocity( ((m_vecMoveDir * m_flSpeed) * fraction) + (GetAbsVelocity() * (1-fraction)) );
  3302. }
  3303. #if HL2_EPISODIC
  3304. else if (m_bInterpolatePosition)
  3305. {
  3306. // get the interpolation parameter [0..1]
  3307. float tt = (gpGlobals->curtime - m_flInterpStartTime) / kflPosInterpTime;
  3308. if (tt >= 1.0f)
  3309. {
  3310. // we're there, we're done
  3311. UTIL_SetOrigin( this, m_vEndPos );
  3312. SetAbsVelocity( vec3_origin );
  3313. m_bInterpolatePosition = false;
  3314. }
  3315. else
  3316. {
  3317. Assert(tt >= 0);
  3318. Vector nextPos = ( (m_vEndPos - m_vStartPos) * SimpleSpline(tt) ) + m_vStartPos;
  3319. // rather than stomping origin, set the velocity so that we get there in the proper time
  3320. Vector desiredVel = (nextPos - GetAbsOrigin()) * (1.0f / gpGlobals->frametime);
  3321. SetAbsVelocity( desiredVel );
  3322. }
  3323. }
  3324. #endif
  3325. }
  3326. #define SF_CAMERA_MULTIPLAYER_DISABLE_ON_MOVEEND 1
  3327. #define SF_CAMERA_MULTIPLAYER_PLAYER_SETFOV 2
  3328. //-----------------------------------------------------------------------------
  3329. class CMoveableCamera : public CBaseEntity
  3330. {
  3331. public:
  3332. DECLARE_CLASS( CMoveableCamera, CBaseEntity );
  3333. CMoveableCamera();
  3334. virtual void Spawn( void );
  3335. virtual void Enable( void );
  3336. virtual void Disable( void );
  3337. virtual Vector GetEndPos( EHANDLE hTarget );
  3338. virtual float MoveTime( float flTime );
  3339. void SetTarget( EHANDLE hTarget );
  3340. void StartMovement( void );
  3341. void FollowTarget( void );
  3342. void Move( void );
  3343. bool m_bEnabled;
  3344. bool m_bDisableOnMoveEnd;
  3345. bool m_bMovementStarted;
  3346. // these are interpolation vars used for interpolating the camera over time
  3347. Vector m_vStartPos;
  3348. QAngle m_vStartAngles;
  3349. float m_flInterpStartTime;
  3350. float m_flInterpTime;
  3351. EHANDLE m_hTargetEnt;
  3352. };
  3353. //-----------------------------------------------------------------------------
  3354. CMoveableCamera::CMoveableCamera()
  3355. {
  3356. m_flInterpTime = 1.f;
  3357. }
  3358. //-----------------------------------------------------------------------------
  3359. void CMoveableCamera::Spawn( void )
  3360. {
  3361. BaseClass::Spawn();
  3362. SetMoveType( MOVETYPE_NOCLIP );
  3363. SetSolid( SOLID_NONE ); // Remove model & collisions
  3364. SetRenderAlpha( 0 ); // The engine won't draw this model if this is set to 0 and blending is on
  3365. m_nRenderMode = kRenderTransTexture;
  3366. m_bEnabled = false;
  3367. m_bMovementStarted = false;
  3368. m_bDisableOnMoveEnd = HasSpawnFlags( SF_CAMERA_MULTIPLAYER_DISABLE_ON_MOVEEND );
  3369. DispatchUpdateTransmitState();
  3370. }
  3371. //------------------------------------------------------------------------------
  3372. void CMoveableCamera::SetTarget( EHANDLE hTarget )
  3373. {
  3374. m_hTargetEnt = hTarget;
  3375. }
  3376. //------------------------------------------------------------------------------
  3377. Vector CMoveableCamera::GetEndPos( EHANDLE hTarget )
  3378. {
  3379. return m_hTargetEnt->GetAbsOrigin();
  3380. }
  3381. //------------------------------------------------------------------------------
  3382. void CMoveableCamera::StartMovement( void )
  3383. {
  3384. if ( m_hTargetEnt == NULL )
  3385. {
  3386. Disable();
  3387. return;
  3388. }
  3389. // Detach us before moving
  3390. SetParent( NULL );
  3391. SetMoveType( MOVETYPE_NOCLIP );
  3392. // initialize the values we'll spline between
  3393. m_vStartPos = GetAbsOrigin();
  3394. m_vStartAngles = GetLocalAngles();
  3395. m_bMovementStarted = true;
  3396. SetThink( &CMoveableCamera::FollowTarget );
  3397. m_flInterpStartTime = gpGlobals->curtime;
  3398. FollowTarget();
  3399. }
  3400. //-----------------------------------------------------------------------------
  3401. void CMoveableCamera::Enable( void )
  3402. {
  3403. m_bEnabled = true;
  3404. SetAbsVelocity( vec3_origin );
  3405. if ( m_bMovementStarted )
  3406. {
  3407. SetThink( &CMoveableCamera::FollowTarget );
  3408. }
  3409. }
  3410. //-----------------------------------------------------------------------------
  3411. void CMoveableCamera::Disable( void )
  3412. {
  3413. m_bEnabled = false;
  3414. SetThink( NULL );
  3415. SetLocalAngularVelocity( vec3_angle );
  3416. }
  3417. //-----------------------------------------------------------------------------
  3418. float CMoveableCamera::MoveTime( float flTime )
  3419. {
  3420. // default is no change
  3421. return flTime;
  3422. }
  3423. //-----------------------------------------------------------------------------
  3424. void CMoveableCamera::FollowTarget( void )
  3425. {
  3426. QAngle vecGoal = m_hTargetEnt->GetAbsAngles();
  3427. QAngle angles = m_vStartAngles;
  3428. float dx = vecGoal.x - angles.x;
  3429. float dy = vecGoal.y - angles.y;
  3430. float dz = vecGoal.z - angles.z;
  3431. if (dx < -180)
  3432. dx += 360;
  3433. if (dx > 180)
  3434. dx = dx - 360;
  3435. if (dy < -180)
  3436. dy += 360;
  3437. if (dy > 180)
  3438. dy = dy - 360;
  3439. if (dz < -180)
  3440. dz += 360;
  3441. if (dz > 180)
  3442. dz = dz - 360;
  3443. float tt = (gpGlobals->curtime - m_flInterpStartTime) / m_flInterpTime;
  3444. QAngle nextAngle = Lerp( tt, angles, vecGoal );
  3445. SetAbsAngles( nextAngle );
  3446. SetNextThink( gpGlobals->curtime );
  3447. Move();
  3448. }
  3449. //-----------------------------------------------------------------------------
  3450. void CMoveableCamera::Move( void )
  3451. {
  3452. Vector vEndPos = GetEndPos( m_hTargetEnt );
  3453. // get the interpolation parameter [0..1]
  3454. float tt = (gpGlobals->curtime - m_flInterpStartTime) / m_flInterpTime;
  3455. if ( tt >= 1.0f )
  3456. {
  3457. // we're there, we're done
  3458. UTIL_SetOrigin( this, vEndPos );
  3459. SetAbsAngles( m_hTargetEnt->GetLocalAngles() );
  3460. SetAbsVelocity( vec3_origin );
  3461. if ( m_bDisableOnMoveEnd )
  3462. {
  3463. Disable();
  3464. }
  3465. m_bMovementStarted = false;
  3466. }
  3467. else
  3468. {
  3469. Assert( tt >= 0 );
  3470. Vector nextPos = ( (vEndPos - m_vStartPos) * MoveTime(tt) ) + m_vStartPos;
  3471. // rather than stomping origin, set the velocity so that we get there in the proper time
  3472. Vector desiredVel = (nextPos - GetAbsOrigin()) * (1.0f / gpGlobals->frametime);
  3473. SetAbsVelocity( desiredVel );
  3474. }
  3475. }
  3476. //--------------------------------------------------------------------------------------------------------
  3477. class CTriggerCameraMultiplayer : public CMoveableCamera
  3478. {
  3479. public:
  3480. DECLARE_CLASS( CTriggerCameraMultiplayer, CMoveableCamera );
  3481. CTriggerCameraMultiplayer()
  3482. {
  3483. m_fov = 90;
  3484. m_fovSpeed = 1;
  3485. m_nTeamNum = -1;
  3486. }
  3487. void Spawn( void );
  3488. void Enable( void );
  3489. void Disable( void );
  3490. void AddPlayer( CBasePlayer *player );
  3491. void RemovePlayer( CBasePlayer *player );
  3492. float MoveTime( float flTime );
  3493. // Always transmit to clients so they know where to move the view to
  3494. virtual int UpdateTransmitState();
  3495. DECLARE_DATADESC();
  3496. // Input handlers
  3497. void InputEnable( inputdata_t &inputdata );
  3498. void InputDisable( inputdata_t &inputdata );
  3499. void InputAddPlayer( inputdata_t &inputdata );
  3500. void InputRemovePlayer( inputdata_t &inputdata );
  3501. void InputStartMovement( inputdata_t &inputdata );
  3502. private:
  3503. CUtlVector< EHANDLE > m_players;
  3504. float m_fov;
  3505. float m_fovSpeed;
  3506. float m_fMoveTime;
  3507. string_t m_targetEntName;
  3508. int m_nTeamNum;
  3509. };
  3510. //--------------------------------------------------------------------------------------------------------
  3511. LINK_ENTITY_TO_CLASS( point_viewcontrol_multiplayer, CTriggerCameraMultiplayer );
  3512. //--------------------------------------------------------------------------------------------------------
  3513. BEGIN_DATADESC( CTriggerCameraMultiplayer )
  3514. // Inputs
  3515. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  3516. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  3517. DEFINE_INPUTFUNC( FIELD_VOID, "AddPlayer", InputAddPlayer ),
  3518. DEFINE_INPUTFUNC( FIELD_VOID, "RemovePlayer", InputRemovePlayer ),
  3519. DEFINE_INPUTFUNC( FIELD_VOID, "StartMovement", InputStartMovement ),
  3520. DEFINE_KEYFIELD( m_fov, FIELD_FLOAT, "fov" ),
  3521. DEFINE_KEYFIELD( m_fovSpeed, FIELD_FLOAT, "fov_rate" ),
  3522. DEFINE_KEYFIELD( m_targetEntName, FIELD_STRING, "target_entity"),
  3523. DEFINE_KEYFIELD( m_flInterpTime, FIELD_FLOAT, "interp_time"),
  3524. DEFINE_KEYFIELD( m_nTeamNum, FIELD_INTEGER, "target_team"),
  3525. END_DATADESC()
  3526. //--------------------------------------------------------------------------------------------------------
  3527. void CTriggerCameraMultiplayer::Spawn( void )
  3528. {
  3529. BaseClass::Spawn();
  3530. DispatchUpdateTransmitState();
  3531. }
  3532. //--------------------------------------------------------------------------------------------------------
  3533. int CTriggerCameraMultiplayer::UpdateTransmitState( void )
  3534. {
  3535. // always transmit if currently used by a monitor
  3536. if ( m_bEnabled )
  3537. {
  3538. return SetTransmitState( FL_EDICT_ALWAYS );
  3539. }
  3540. else
  3541. {
  3542. return SetTransmitState( FL_EDICT_DONTSEND );
  3543. }
  3544. }
  3545. //--------------------------------------------------------------------------------------------------------
  3546. void CTriggerCameraMultiplayer::InputEnable( inputdata_t &inputdata )
  3547. {
  3548. CBaseEntity *pTargetEnt = gEntList.FindEntityByName( NULL, m_targetEntName );
  3549. SetTarget( pTargetEnt );
  3550. Enable();
  3551. }
  3552. //--------------------------------------------------------------------------------------------------------
  3553. void CTriggerCameraMultiplayer::InputDisable( inputdata_t &inputdata )
  3554. {
  3555. Disable();
  3556. }
  3557. //------------------------------------------------------------------------------
  3558. // Purpose: Input handler to starting camera movement.
  3559. //------------------------------------------------------------------------------
  3560. void CTriggerCameraMultiplayer::InputStartMovement( inputdata_t &inputdata )
  3561. {
  3562. StartMovement();
  3563. }
  3564. //--------------------------------------------------------------------------------------------------------
  3565. void CTriggerCameraMultiplayer::InputAddPlayer( inputdata_t &inputdata )
  3566. {
  3567. CBasePlayer *pPlayer = ToBasePlayer( inputdata.pActivator );
  3568. if ( pPlayer )
  3569. {
  3570. AddPlayer( pPlayer );
  3571. }
  3572. }
  3573. //--------------------------------------------------------------------------------------------------------
  3574. void CTriggerCameraMultiplayer::InputRemovePlayer( inputdata_t &inputdata )
  3575. {
  3576. CBasePlayer *pPlayer = ToBasePlayer( inputdata.pActivator );
  3577. if ( pPlayer )
  3578. {
  3579. RemovePlayer( pPlayer );
  3580. m_players.FindAndFastRemove( pPlayer );
  3581. }
  3582. }
  3583. //--------------------------------------------------------------------------------------------------------
  3584. void CTriggerCameraMultiplayer::AddPlayer( CBasePlayer *player )
  3585. {
  3586. if ( m_players.HasElement( player ) )
  3587. return;
  3588. m_players.AddToTail( player );
  3589. player->EnableControl( false );
  3590. player->m_Local.m_bDrawViewmodel = false;
  3591. player->SetViewEntity( this );
  3592. CBaseEntity *pFOVOwner = player->GetFOVOwner();
  3593. if ( pFOVOwner && ( ( pFOVOwner == player ) ||
  3594. FClassnameIs( pFOVOwner, "point_viewcontrol_multiplayer" ) ||
  3595. FClassnameIs( pFOVOwner, "point_viewcontrol_survivor" ) ) )
  3596. {
  3597. player->ClearZoomOwner();
  3598. }
  3599. player->SetFOV( this, m_fov, m_fovSpeed );
  3600. }
  3601. //--------------------------------------------------------------------------------------------------------
  3602. void CTriggerCameraMultiplayer::RemovePlayer( CBasePlayer *player )
  3603. {
  3604. player->EnableControl( true );
  3605. player->m_Local.m_bDrawViewmodel = true;
  3606. player->SetViewEntity( NULL );
  3607. player->SetFOV( this, 0, m_fovSpeed );
  3608. player->ClearZoomOwner();
  3609. }
  3610. //--------------------------------------------------------------------------------------------------------
  3611. void CTriggerCameraMultiplayer::Enable( void )
  3612. {
  3613. BaseClass::Enable();
  3614. #if defined( PORTAL2 ) || defined( CSTRIKE15 )
  3615. for( int i = 1; i <= gpGlobals->maxClients; ++i )
  3616. {
  3617. CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  3618. if( pPlayer == NULL )
  3619. continue;
  3620. if( !pPlayer->IsConnected() )
  3621. continue;
  3622. if ( m_nTeamNum == -1 || m_nTeamNum == pPlayer->GetTeamNumber() )
  3623. AddPlayer( pPlayer );
  3624. }
  3625. #endif // PORTAL2
  3626. DispatchUpdateTransmitState();
  3627. }
  3628. //--------------------------------------------------------------------------------------------------------
  3629. void CTriggerCameraMultiplayer::Disable( void )
  3630. {
  3631. BaseClass::Disable();
  3632. for ( int i = 0; i < m_players.Count(); ++i )
  3633. {
  3634. CBasePlayer *player = ToBasePlayer( m_players[i] );
  3635. if ( !player )
  3636. continue;
  3637. RemovePlayer( player );
  3638. }
  3639. m_players.RemoveAll();
  3640. DispatchUpdateTransmitState();
  3641. }
  3642. //-----------------------------------------------------------------------------
  3643. float CTriggerCameraMultiplayer::MoveTime( float flTime )
  3644. {
  3645. return SmoothCurve( flTime );
  3646. }
  3647. enum ViewProxyOffsetType_e
  3648. {
  3649. VIEW_PROXY_SNAP_TO_CAMERA = 0,
  3650. VIEW_PROXY_EASE_TO_CAMERA,
  3651. VIEW_PROXY_KEEP_OFFSET,
  3652. };
  3653. LINK_ENTITY_TO_CLASS( point_viewproxy, CTriggerViewProxy );
  3654. BEGIN_DATADESC( CTriggerViewProxy )
  3655. DEFINE_FIELD( m_hPlayer, FIELD_EHANDLE ),
  3656. DEFINE_FIELD( m_pProxy, FIELD_CLASSPTR ),
  3657. DEFINE_KEYFIELD(m_nOffsetType, FIELD_INTEGER, "offsettype"),
  3658. DEFINE_FIELD( m_vecInitialOffset, FIELD_VECTOR ),
  3659. DEFINE_FIELD( m_flStartTime, FIELD_FLOAT ),
  3660. DEFINE_KEYFIELD( m_sProxy, FIELD_STRING, "proxy" ),
  3661. DEFINE_KEYFIELD( m_sProxyAttachment, FIELD_STRING, "proxyattachment" ),
  3662. DEFINE_KEYFIELD( m_flTiltFraction, FIELD_FLOAT, "tiltfraction" ),
  3663. DEFINE_KEYFIELD( m_bUseFakeAcceleration, FIELD_BOOLEAN, "usefakeacceleration" ),
  3664. DEFINE_KEYFIELD( m_bSkewAccelerationForward, FIELD_BOOLEAN, "skewaccelerationforward" ),
  3665. DEFINE_KEYFIELD( m_flAccelerationScalar, FIELD_FLOAT, "accelerationscalar" ),
  3666. DEFINE_KEYFIELD( m_bEaseAnglesToCamera, FIELD_BOOLEAN, "easeanglestocamera" ),
  3667. DEFINE_FIELD( m_nParentAttachment, FIELD_INTEGER ),
  3668. DEFINE_FIELD( m_state, FIELD_INTEGER ),
  3669. DEFINE_FIELD( m_vecInitialPosition, FIELD_VECTOR ),
  3670. DEFINE_FIELD( m_nPlayerButtons, FIELD_INTEGER ),
  3671. DEFINE_FIELD( m_nOldTakeDamage, FIELD_INTEGER ),
  3672. // Inputs
  3673. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  3674. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  3675. DEFINE_INPUTFUNC( FIELD_VOID, "TeleportPlayerToProxy", InputTeleportPlayerToProxy ),
  3676. DEFINE_FUNCTION( TranslateViewToProxy ),
  3677. END_DATADESC()
  3678. //-----------------------------------------------------------------------------
  3679. // Purpose:
  3680. //-----------------------------------------------------------------------------
  3681. CTriggerViewProxy::CTriggerViewProxy()
  3682. {
  3683. }
  3684. //-----------------------------------------------------------------------------
  3685. // Purpose:
  3686. //-----------------------------------------------------------------------------
  3687. void CTriggerViewProxy::Spawn( void )
  3688. {
  3689. BaseClass::Spawn();
  3690. SetMoveType( MOVETYPE_NOCLIP );
  3691. SetSolid( SOLID_NONE ); // Remove model & collisions
  3692. SetRenderAlpha( 0 ); // The engine won't draw this model if this is set to 0 and blending is on
  3693. m_nRenderMode = kRenderTransTexture;
  3694. m_vecInitialPosition = GetAbsOrigin();
  3695. m_pProxy = gEntList.FindEntityByName( NULL, m_sProxy );
  3696. m_state = USE_OFF;
  3697. m_flTiltFraction = clamp( m_flTiltFraction, 0.f, 1.f );
  3698. DispatchUpdateTransmitState();
  3699. m_vecInitialOffset = vec3_origin;
  3700. if( m_pProxy )
  3701. {
  3702. m_vecLastPosition = m_pProxy->GetAbsOrigin();
  3703. }
  3704. }
  3705. int CTriggerViewProxy::UpdateTransmitState()
  3706. {
  3707. // always tranmit if currently used by a monitor
  3708. if ( m_state == USE_ON )
  3709. {
  3710. return SetTransmitState( FL_EDICT_ALWAYS );
  3711. }
  3712. else
  3713. {
  3714. return SetTransmitState( FL_EDICT_DONTSEND );
  3715. }
  3716. }
  3717. //-----------------------------------------------------------------------------
  3718. // Purpose:
  3719. //-----------------------------------------------------------------------------
  3720. bool CTriggerViewProxy::KeyValue( const char *szKeyName, const char *szValue )
  3721. {
  3722. return BaseClass::KeyValue( szKeyName, szValue );
  3723. }
  3724. //------------------------------------------------------------------------------
  3725. // Purpose: Input handler to turn on this trigger.
  3726. //------------------------------------------------------------------------------
  3727. void CTriggerViewProxy::InputEnable( inputdata_t &inputdata )
  3728. {
  3729. m_hPlayer = UTIL_GetLocalPlayer();
  3730. m_flStartTime = gpGlobals->curtime;
  3731. if ( m_pProxy &&
  3732. m_hPlayer &&
  3733. m_nOffsetType != VIEW_PROXY_KEEP_OFFSET )
  3734. {
  3735. m_vecInitialOffset = m_hPlayer->EyePosition() - m_pProxy->GetAbsOrigin();
  3736. }
  3737. Enable();
  3738. }
  3739. //------------------------------------------------------------------------------
  3740. // Purpose: Input handler to turn off this trigger.
  3741. //------------------------------------------------------------------------------
  3742. void CTriggerViewProxy::InputDisable( inputdata_t &inputdata )
  3743. {
  3744. Disable();
  3745. }
  3746. //------------------------------------------------------------------------------
  3747. // Purpose: Input handler to turn off the proxy and teleport the player to the end position
  3748. //------------------------------------------------------------------------------
  3749. void CTriggerViewProxy::InputTeleportPlayerToProxy( inputdata_t &inputdata )
  3750. {
  3751. #if defined ( PORTAL2 )
  3752. if( m_hPlayer )
  3753. {
  3754. CPortal_Player *pBasePlayer = (CPortal_Player *)m_hPlayer.Get();
  3755. QAngle vecPlayerView = GetAbsAngles();
  3756. Vector vecEyeOffset = pBasePlayer->EyePosition() - pBasePlayer->GetAbsOrigin();
  3757. Vector vecTeleportPosition = GetAbsOrigin() - vecEyeOffset;
  3758. // try to find a position on the ground - this will prevent a pop
  3759. trace_t tr;
  3760. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - 1.02f*vecEyeOffset, MASK_SOLID, pBasePlayer, COLLISION_GROUP_NONE, &tr );
  3761. if( tr.fraction != 1.0 )
  3762. {
  3763. vecTeleportPosition = tr.endpos;
  3764. }
  3765. pBasePlayer->SetGroundEntity( NULL );
  3766. pBasePlayer->Teleport( &vecTeleportPosition, &vecPlayerView, NULL );
  3767. }
  3768. Disable();
  3769. #endif // defined ( PORTAL2 )
  3770. }
  3771. //-----------------------------------------------------------------------------
  3772. // Purpose:
  3773. //-----------------------------------------------------------------------------
  3774. void CTriggerViewProxy::Enable( void )
  3775. {
  3776. if( m_pProxy )
  3777. {
  3778. m_nParentAttachment = -1;
  3779. if( m_sProxyAttachment != NULL_STRING && m_pProxy->GetParent() )
  3780. {
  3781. CBaseAnimating *pAnimating = (CBaseAnimating *) m_pProxy->GetParent();
  3782. if( pAnimating )
  3783. {
  3784. m_nParentAttachment = pAnimating->LookupAttachment( STRING( m_sProxyAttachment ) );
  3785. }
  3786. }
  3787. }
  3788. m_state = USE_ON;
  3789. if ( !m_hPlayer || !m_hPlayer->IsPlayer() )
  3790. {
  3791. m_hPlayer = UTIL_GetLocalPlayer();
  3792. }
  3793. if ( !m_hPlayer )
  3794. {
  3795. DispatchUpdateTransmitState();
  3796. return;
  3797. }
  3798. Assert( m_hPlayer->IsPlayer() );
  3799. CBasePlayer *pPlayer = NULL;
  3800. if ( m_hPlayer->IsPlayer() )
  3801. {
  3802. pPlayer = ((CBasePlayer*)m_hPlayer.Get());
  3803. }
  3804. else
  3805. {
  3806. Warning("CTriggerViewProxy could not find a player!\n");
  3807. return;
  3808. }
  3809. m_nPlayerButtons = pPlayer->m_nButtons;
  3810. pPlayer->SetDuckEnabled( false );
  3811. #if defined ( PORTAL2 )
  3812. CPortal_Player* pPortalPlayer = ToPortalPlayer( pPlayer );
  3813. if ( pPortalPlayer && pPortalPlayer->IsZoomed() )
  3814. {
  3815. pPortalPlayer->ZoomOut();
  3816. }
  3817. #endif
  3818. // 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.
  3819. m_nOldTakeDamage = m_hPlayer->m_takedamage;
  3820. m_hPlayer->m_takedamage = DAMAGE_NO;
  3821. if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) )
  3822. {
  3823. m_hPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
  3824. }
  3825. if (HasSpawnFlags(SF_CAMERA_PLAYER_TAKECONTROL ) )
  3826. {
  3827. ((CBasePlayer*)m_hPlayer.Get())->AddFlag( FL_ATCONTROLS );//EnableControl(FALSE);
  3828. }
  3829. SetAbsVelocity( vec3_origin );
  3830. pPlayer->SetViewEntity( this, false ); //HACK: drawing the local player should probably be an entity option.
  3831. // Hide the player's viewmodel
  3832. if ( pPlayer->GetActiveWeapon() )
  3833. {
  3834. pPlayer->GetActiveWeapon()->AddEffects( EF_NODRAW );
  3835. }
  3836. // follow the player down
  3837. SetThink( &CTriggerViewProxy::TranslateViewToProxy );
  3838. SetNextThink( gpGlobals->curtime );
  3839. // do this once to get things set up correctly.
  3840. Move();
  3841. TranslateViewToProxy();
  3842. m_hPlayer->SetBaseVelocity( vec3_origin );
  3843. DispatchUpdateTransmitState();
  3844. }
  3845. //-----------------------------------------------------------------------------
  3846. // Purpose:
  3847. //-----------------------------------------------------------------------------
  3848. void CTriggerViewProxy::Disable( void )
  3849. {
  3850. if ( m_hPlayer )
  3851. {
  3852. CBasePlayer *pBasePlayer = (CBasePlayer*)m_hPlayer.Get();
  3853. if ( pBasePlayer->IsAlive() )
  3854. {
  3855. if ( HasSpawnFlags( SF_CAMERA_PLAYER_NOT_SOLID ) )
  3856. {
  3857. pBasePlayer->RemoveSolidFlags( FSOLID_NOT_SOLID );
  3858. }
  3859. pBasePlayer->SetViewEntity( NULL );
  3860. pBasePlayer->EnableControl(TRUE);
  3861. ((CBasePlayer*)m_hPlayer.Get())->RemoveFlag( FL_ATCONTROLS );
  3862. pBasePlayer->m_Local.m_bDrawViewmodel = true;
  3863. }
  3864. //return the player to previous takedamage state
  3865. m_hPlayer->m_takedamage = m_nOldTakeDamage;
  3866. pBasePlayer->SetDuckEnabled( true );
  3867. }
  3868. m_state = USE_OFF;
  3869. SetThink( NULL );
  3870. SetLocalAngularVelocity( vec3_angle );
  3871. DispatchUpdateTransmitState();
  3872. }
  3873. //-----------------------------------------------------------------------------
  3874. // Purpose:
  3875. //-----------------------------------------------------------------------------
  3876. void CTriggerViewProxy::TranslateViewToProxy( )
  3877. {
  3878. if (m_hPlayer == NULL || m_pProxy == NULL )
  3879. return;
  3880. QAngle qGoal, qProxyAngles;
  3881. qProxyAngles = m_pProxy->GetAbsAngles();
  3882. if( m_nParentAttachment != -1 )
  3883. {
  3884. CBaseAnimating *pAnimating = (CBaseAnimating *) m_pProxy->GetParent();
  3885. if( pAnimating )
  3886. {
  3887. Vector vOrigin;
  3888. pAnimating->StudioFrameAdvance();
  3889. pAnimating->InvalidateBoneCache();
  3890. pAnimating->GetAttachment( m_nParentAttachment, vOrigin, qProxyAngles );
  3891. }
  3892. }
  3893. #ifdef PORTAL2
  3894. CPortal_Player *pOtherAsPlayer = (CPortal_Player *)(m_hPlayer.Get());
  3895. if ( m_bEaseAnglesToCamera )
  3896. {
  3897. QAngle playerAngles = pOtherAsPlayer->pl.v_angle;
  3898. float dx = qProxyAngles.x - playerAngles.x;
  3899. float dy = qProxyAngles.y - playerAngles.y;
  3900. if (dx < -180)
  3901. dx += 360;
  3902. if (dx > 180)
  3903. dx = dx - 360;
  3904. if (dy < -180)
  3905. dy += 360;
  3906. if (dy > 180)
  3907. dy = dy - 360;
  3908. dx *= 0.15f;
  3909. dy *= 0.15f;
  3910. playerAngles.x += dx;
  3911. playerAngles.y += dy;
  3912. pOtherAsPlayer->Teleport( NULL, &playerAngles, NULL );
  3913. qProxyAngles = QAngle(0, 0, 0);
  3914. }
  3915. qProxyAngles[PITCH] *= m_flTiltFraction;
  3916. qProxyAngles[ROLL] *= m_flTiltFraction;
  3917. VMatrix matRotate;
  3918. matRotate.SetupMatrixOrgAngles( vec3_origin, qProxyAngles );
  3919. QAngle qTransformedEyeAngles = TransformAnglesToWorldSpace( pOtherAsPlayer->pl.v_angle, matRotate.As3x4() );
  3920. qGoal = qTransformedEyeAngles;
  3921. SetAbsAngles( qGoal );
  3922. #endif // PORTAL2
  3923. SetNextThink( gpGlobals->curtime );
  3924. Move();
  3925. }
  3926. Vector CTriggerViewProxy::GetPlayerOffset()
  3927. {
  3928. Vector vecOffset = vec3_origin;
  3929. if ( m_hPlayer && m_pProxy )
  3930. {
  3931. vecOffset = m_hPlayer->GetAbsOrigin() + Vector( 0,0,64.f) - m_vecInitialPosition;
  3932. QAngle qProxyAngles = m_pProxy->GetAbsAngles();
  3933. if( m_nParentAttachment != -1 )
  3934. {
  3935. CBaseAnimating *pAnimating = (CBaseAnimating *) m_pProxy->GetParent();
  3936. Vector vOrigin;
  3937. pAnimating->GetAttachment( m_nParentAttachment, vOrigin, qProxyAngles );
  3938. }
  3939. VMatrix matRotate;
  3940. matRotate.SetupMatrixOrgAngles( vec3_origin, qProxyAngles );
  3941. // move out player's offset into the space of the proxy
  3942. vecOffset = matRotate * vecOffset;
  3943. }
  3944. return vecOffset;
  3945. }
  3946. void CTriggerViewProxy::Move()
  3947. {
  3948. if ( m_hPlayer && m_pProxy )
  3949. {
  3950. Vector vecPlayerAdd = vec3_origin;
  3951. if ( m_nOffsetType == VIEW_PROXY_EASE_TO_CAMERA )
  3952. {
  3953. float flRatio = clamp( gpGlobals->curtime - m_flStartTime, 0.0f, 1.0f );
  3954. flRatio = SimpleSpline( flRatio );
  3955. vecPlayerAdd = (1.0f - flRatio)*m_vecInitialOffset;
  3956. }
  3957. else if ( m_nOffsetType == VIEW_PROXY_KEEP_OFFSET )
  3958. {
  3959. vecPlayerAdd = GetPlayerOffset();
  3960. }
  3961. Vector vOrigin = m_pProxy->GetAbsOrigin();//vec3_origin;
  3962. UTIL_SetOrigin( this, vOrigin + vecPlayerAdd );
  3963. if( m_bUseFakeAcceleration && gpGlobals->frametime > 0.0f )
  3964. {
  3965. Vector vecNewVelocity = (vOrigin - m_vecLastPosition)/gpGlobals->frametime;
  3966. Vector vecAccel = vecNewVelocity - m_vecLastVelocity;
  3967. m_vecLastVelocity = vecNewVelocity;
  3968. m_vecLastPosition = vOrigin;
  3969. SetAbsVelocity( vecNewVelocity );
  3970. // decay our movement based on our time - this is a slow decay - most is taken off linearly
  3971. float decay = 1.0f - 0.25f*gpGlobals->frametime;
  3972. if( decay < 0.f )
  3973. {
  3974. decay = 0.f;
  3975. }
  3976. if( vecAccel.LengthSqr() < 20.0f || vecAccel.LengthSqr() > 10000.f )
  3977. {
  3978. vecAccel = vec3_origin;
  3979. }
  3980. if( m_bSkewAccelerationForward )
  3981. {
  3982. Vector vecForward, vecNormalizedAccel;
  3983. AngleVectors( m_pProxy->GetAbsAngles(), &vecForward );
  3984. vecNormalizedAccel = vecAccel.Normalized();
  3985. vecAccel *= (0.75f - 0.35f*vecForward.Dot( vecNormalizedAccel ) );
  3986. }
  3987. if( gpGlobals->frametime > 0.0f )
  3988. {
  3989. Vector vecBase = m_hPlayer->GetBaseVelocity();
  3990. float flLength = vecBase.Length();
  3991. // this is going to be the bulk of our decay - we want small scale movement to get
  3992. float flLinearDecrease = 100.0f*gpGlobals->frametime;
  3993. if( flLength < flLinearDecrease )
  3994. {
  3995. vecBase = vec3_origin;
  3996. }
  3997. else
  3998. {
  3999. vecBase = vecBase*((flLength - flLinearDecrease)/flLength);
  4000. }
  4001. m_hPlayer->SetBaseVelocity( decay*vecBase - 3.0f*vecAccel*m_flAccelerationScalar );
  4002. m_hPlayer->AddFlag( FL_BASEVELOCITY );
  4003. }
  4004. }
  4005. }
  4006. return;
  4007. }
  4008. //-----------------------------------------------------------------------------
  4009. // Purpose: Starts/stops cd audio tracks
  4010. //-----------------------------------------------------------------------------
  4011. class CTriggerCDAudio : public CBaseTrigger
  4012. {
  4013. public:
  4014. DECLARE_CLASS( CTriggerCDAudio, CBaseTrigger );
  4015. void Spawn( void );
  4016. virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  4017. void PlayTrack( void );
  4018. void Touch ( CBaseEntity *pOther );
  4019. };
  4020. LINK_ENTITY_TO_CLASS( trigger_cdaudio, CTriggerCDAudio );
  4021. //-----------------------------------------------------------------------------
  4022. // Purpose: Changes tracks or stops CD when player touches
  4023. // Input : pOther - The entity that touched us.
  4024. //-----------------------------------------------------------------------------
  4025. void CTriggerCDAudio::Touch ( CBaseEntity *pOther )
  4026. {
  4027. if ( !pOther->IsPlayer() )
  4028. {
  4029. return;
  4030. }
  4031. PlayTrack();
  4032. }
  4033. //-----------------------------------------------------------------------------
  4034. // Purpose:
  4035. //-----------------------------------------------------------------------------
  4036. void CTriggerCDAudio::Spawn( void )
  4037. {
  4038. BaseClass::Spawn();
  4039. InitTrigger();
  4040. }
  4041. void CTriggerCDAudio::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  4042. {
  4043. PlayTrack();
  4044. }
  4045. //-----------------------------------------------------------------------------
  4046. // Purpose: Issues a client command to play a given CD track. Called from
  4047. // trigger_cdaudio and target_cdaudio.
  4048. // Input : iTrack - Track number to play.
  4049. //-----------------------------------------------------------------------------
  4050. static void PlayCDTrack( int iTrack )
  4051. {
  4052. edict_t *pClient;
  4053. // manually find the single player.
  4054. pClient = INDEXENT( 1 );
  4055. Assert(gpGlobals->maxClients == 1);
  4056. // Can't play if the client is not connected!
  4057. if ( !pClient )
  4058. return;
  4059. // UNDONE: Move this to engine sound
  4060. if ( iTrack < -1 || iTrack > 30 )
  4061. {
  4062. Warning( "TriggerCDAudio - Track %d out of range\n", iTrack );
  4063. return;
  4064. }
  4065. if ( iTrack == -1 )
  4066. {
  4067. engine->ClientCommand ( pClient, "cd pause\n");
  4068. }
  4069. else
  4070. {
  4071. engine->ClientCommand ( pClient, "cd play %3d\n", iTrack );
  4072. }
  4073. }
  4074. // only plays for ONE client, so only use in single play!
  4075. void CTriggerCDAudio::PlayTrack( void )
  4076. {
  4077. PlayCDTrack( (int)m_iHealth );
  4078. SetTouch( NULL );
  4079. UTIL_Remove( this );
  4080. }
  4081. //-----------------------------------------------------------------------------
  4082. // Purpose: Measures the proximity to a specified entity of any entities within
  4083. // the trigger, provided they are within a given radius of the specified
  4084. // entity. The nearest entity distance is output as a number from [0 - 1].
  4085. //-----------------------------------------------------------------------------
  4086. class CTriggerProximity : public CBaseTrigger
  4087. {
  4088. public:
  4089. DECLARE_CLASS( CTriggerProximity, CBaseTrigger );
  4090. virtual void Spawn(void);
  4091. virtual void Activate(void);
  4092. virtual void StartTouch(CBaseEntity *pOther);
  4093. virtual void EndTouch(CBaseEntity *pOther);
  4094. void MeasureThink(void);
  4095. protected:
  4096. EHANDLE m_hMeasureTarget;
  4097. string_t m_iszMeasureTarget; // The entity from which we measure proximities.
  4098. float m_fRadius; // The radius around the measure target that we measure within.
  4099. int m_nTouchers; // Number of entities touching us.
  4100. // Outputs
  4101. COutputFloat m_NearestEntityDistance;
  4102. DECLARE_DATADESC();
  4103. };
  4104. BEGIN_DATADESC( CTriggerProximity )
  4105. // Functions
  4106. DEFINE_FUNCTION(MeasureThink),
  4107. // Keys
  4108. DEFINE_KEYFIELD(m_iszMeasureTarget, FIELD_STRING, "measuretarget"),
  4109. DEFINE_FIELD( m_hMeasureTarget, FIELD_EHANDLE ),
  4110. DEFINE_KEYFIELD(m_fRadius, FIELD_FLOAT, "radius"),
  4111. DEFINE_FIELD( m_nTouchers, FIELD_INTEGER ),
  4112. // Outputs
  4113. DEFINE_OUTPUT(m_NearestEntityDistance, "NearestEntityDistance"),
  4114. END_DATADESC()
  4115. LINK_ENTITY_TO_CLASS(trigger_proximity, CTriggerProximity);
  4116. LINK_ENTITY_TO_CLASS(logic_proximity, CPointEntity);
  4117. //-----------------------------------------------------------------------------
  4118. // Purpose: Called when spawning, after keyvalues have been handled.
  4119. //-----------------------------------------------------------------------------
  4120. void CTriggerProximity::Spawn(void)
  4121. {
  4122. // Avoid divide by zero in MeasureThink!
  4123. if (m_fRadius == 0)
  4124. {
  4125. m_fRadius = 32;
  4126. }
  4127. InitTrigger();
  4128. }
  4129. //-----------------------------------------------------------------------------
  4130. // Purpose: Called after all entities have spawned and after a load game.
  4131. // Finds the reference point from which to measure.
  4132. //-----------------------------------------------------------------------------
  4133. void CTriggerProximity::Activate(void)
  4134. {
  4135. BaseClass::Activate();
  4136. m_hMeasureTarget = gEntList.FindEntityByName(NULL, m_iszMeasureTarget );
  4137. //
  4138. // Disable our Touch function if we were given a bad measure target.
  4139. //
  4140. if ((m_hMeasureTarget == NULL) || (m_hMeasureTarget->edict() == NULL))
  4141. {
  4142. Warning( "TriggerProximity - Missing measure target or measure target with no origin!\n");
  4143. }
  4144. }
  4145. //-----------------------------------------------------------------------------
  4146. // Purpose: Decrements the touch count and cancels the think if the count reaches
  4147. // zero.
  4148. // Input : pOther -
  4149. //-----------------------------------------------------------------------------
  4150. void CTriggerProximity::StartTouch(CBaseEntity *pOther)
  4151. {
  4152. BaseClass::StartTouch( pOther );
  4153. if ( PassesTriggerFilters( pOther ) )
  4154. {
  4155. m_nTouchers++;
  4156. SetThink( &CTriggerProximity::MeasureThink );
  4157. SetNextThink( gpGlobals->curtime );
  4158. }
  4159. }
  4160. //-----------------------------------------------------------------------------
  4161. // Purpose: Decrements the touch count and cancels the think if the count reaches
  4162. // zero.
  4163. // Input : pOther -
  4164. //-----------------------------------------------------------------------------
  4165. void CTriggerProximity::EndTouch(CBaseEntity *pOther)
  4166. {
  4167. BaseClass::EndTouch( pOther );
  4168. if ( PassesTriggerFilters( pOther ) )
  4169. {
  4170. m_nTouchers--;
  4171. if ( m_nTouchers == 0 )
  4172. {
  4173. SetThink( NULL );
  4174. SetNextThink( TICK_NEVER_THINK );
  4175. }
  4176. }
  4177. }
  4178. //-----------------------------------------------------------------------------
  4179. // Purpose: Think function called every frame as long as we have entities touching
  4180. // us that we care about. Finds the closest entity to the measure
  4181. // target and outputs the distance as a normalized value from [0..1].
  4182. //-----------------------------------------------------------------------------
  4183. void CTriggerProximity::MeasureThink( void )
  4184. {
  4185. if ( ( m_hMeasureTarget == NULL ) || ( m_hMeasureTarget->edict() == NULL ) )
  4186. {
  4187. SetThink(NULL);
  4188. SetNextThink( TICK_NEVER_THINK );
  4189. return;
  4190. }
  4191. //
  4192. // Traverse our list of touchers and find the entity that is closest to the
  4193. // measure target.
  4194. //
  4195. float fMinDistance = m_fRadius + 100;
  4196. CBaseEntity *pNearestEntity = NULL;
  4197. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  4198. if ( root )
  4199. {
  4200. touchlink_t *pLink = root->nextLink;
  4201. while ( pLink != root )
  4202. {
  4203. CBaseEntity *pEntity = pLink->entityTouched;
  4204. // If this is an entity that we care about, check its distance.
  4205. if ( ( pEntity != NULL ) && PassesTriggerFilters( pEntity ) )
  4206. {
  4207. float flDistance = (pEntity->GetLocalOrigin() - m_hMeasureTarget->GetLocalOrigin()).Length();
  4208. if (flDistance < fMinDistance)
  4209. {
  4210. fMinDistance = flDistance;
  4211. pNearestEntity = pEntity;
  4212. }
  4213. }
  4214. pLink = pLink->nextLink;
  4215. }
  4216. }
  4217. // Update our output with the nearest entity distance, normalized to [0..1].
  4218. if ( fMinDistance <= m_fRadius )
  4219. {
  4220. fMinDistance /= m_fRadius;
  4221. if ( fMinDistance != m_NearestEntityDistance.Get() )
  4222. {
  4223. m_NearestEntityDistance.Set( fMinDistance, pNearestEntity, this );
  4224. }
  4225. }
  4226. SetNextThink( gpGlobals->curtime );
  4227. }
  4228. // ##################################################################################
  4229. // >> TriggerWind
  4230. //
  4231. // Blows physics objects in the trigger
  4232. //
  4233. // ##################################################################################
  4234. #define MAX_WIND_CHANGE 5.0f
  4235. //------------------------------------------------------------------------------
  4236. // Purpose :
  4237. // Input :
  4238. // Output :
  4239. //------------------------------------------------------------------------------
  4240. class CPhysicsWind : public IMotionEvent
  4241. {
  4242. DECLARE_SIMPLE_DATADESC();
  4243. public:
  4244. simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  4245. {
  4246. // If we have no windspeed, we're not doing anything
  4247. if ( !m_flWindSpeed )
  4248. return IMotionEvent::SIM_NOTHING;
  4249. // Get a cosine modulated noise between 5 and 20 that is object specific
  4250. int nNoiseMod = 5+(int)(intp)pObject%15; //
  4251. // Turn wind yaw direction into a vector and add noise
  4252. QAngle vWindAngle = vec3_angle;
  4253. vWindAngle[1] = m_nWindYaw+(30*cos(nNoiseMod * gpGlobals->curtime + nNoiseMod));
  4254. Vector vWind;
  4255. AngleVectors(vWindAngle,&vWind);
  4256. // Add lift with noise
  4257. vWind.z = 1.1 + (1.0 * sin(nNoiseMod * gpGlobals->curtime + nNoiseMod));
  4258. linear = 3*vWind*m_flWindSpeed;
  4259. angular = vec3_origin;
  4260. return IMotionEvent::SIM_GLOBAL_FORCE;
  4261. }
  4262. int m_nWindYaw;
  4263. float m_flWindSpeed;
  4264. };
  4265. BEGIN_SIMPLE_DATADESC( CPhysicsWind )
  4266. DEFINE_FIELD( m_nWindYaw, FIELD_INTEGER ),
  4267. DEFINE_FIELD( m_flWindSpeed, FIELD_FLOAT ),
  4268. END_DATADESC()
  4269. extern int g_sModelIndexSmoke;
  4270. extern float GetFloorZ(const Vector &origin);
  4271. #define WIND_THINK_CONTEXT "WindThinkContext"
  4272. //-----------------------------------------------------------------------------
  4273. // Purpose:
  4274. //-----------------------------------------------------------------------------
  4275. class CTriggerWind : public CBaseVPhysicsTrigger
  4276. {
  4277. DECLARE_CLASS( CTriggerWind, CBaseVPhysicsTrigger );
  4278. public:
  4279. DECLARE_DATADESC();
  4280. void Spawn( void );
  4281. bool KeyValue( const char *szKeyName, const char *szValue );
  4282. void OnRestore();
  4283. void UpdateOnRemove();
  4284. bool CreateVPhysics();
  4285. void StartTouch( CBaseEntity *pOther );
  4286. void EndTouch( CBaseEntity *pOther );
  4287. void WindThink( void );
  4288. int DrawDebugTextOverlays( void );
  4289. // Input handlers
  4290. void InputEnable( inputdata_t &inputdata );
  4291. void InputSetSpeed( inputdata_t &inputdata );
  4292. private:
  4293. int m_nSpeedBase; // base line for how hard the wind blows
  4294. int m_nSpeedNoise; // noise added to wind speed +/-
  4295. int m_nSpeedCurrent;// current wind speed
  4296. int m_nSpeedTarget; // wind speed I'm approaching
  4297. int m_nDirBase; // base line for direction the wind blows (yaw)
  4298. int m_nDirNoise; // noise added to wind direction
  4299. int m_nDirCurrent; // the current wind direction
  4300. int m_nDirTarget; // wind direction I'm approaching
  4301. int m_nHoldBase; // base line for how long to wait before changing wind
  4302. int m_nHoldNoise; // noise added to how long to wait before changing wind
  4303. bool m_bSwitch; // when does wind change
  4304. IPhysicsMotionController* m_pWindController;
  4305. CPhysicsWind m_WindCallback;
  4306. };
  4307. LINK_ENTITY_TO_CLASS( trigger_wind, CTriggerWind );
  4308. BEGIN_DATADESC( CTriggerWind )
  4309. DEFINE_FIELD( m_nSpeedCurrent, FIELD_INTEGER),
  4310. DEFINE_FIELD( m_nSpeedTarget, FIELD_INTEGER),
  4311. DEFINE_FIELD( m_nDirBase, FIELD_INTEGER),
  4312. DEFINE_FIELD( m_nDirCurrent, FIELD_INTEGER),
  4313. DEFINE_FIELD( m_nDirTarget, FIELD_INTEGER),
  4314. DEFINE_FIELD( m_bSwitch, FIELD_BOOLEAN),
  4315. DEFINE_FIELD( m_nSpeedBase, FIELD_INTEGER ),
  4316. DEFINE_KEYFIELD( m_nSpeedNoise, FIELD_INTEGER, "SpeedNoise"),
  4317. DEFINE_KEYFIELD( m_nDirNoise, FIELD_INTEGER, "DirectionNoise"),
  4318. DEFINE_KEYFIELD( m_nHoldBase, FIELD_INTEGER, "HoldTime"),
  4319. DEFINE_KEYFIELD( m_nHoldNoise, FIELD_INTEGER, "HoldNoise"),
  4320. DEFINE_PHYSPTR( m_pWindController ),
  4321. DEFINE_EMBEDDED( m_WindCallback ),
  4322. DEFINE_FUNCTION( WindThink ),
  4323. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetSpeed", InputSetSpeed ),
  4324. END_DATADESC()
  4325. //------------------------------------------------------------------------------
  4326. // Purpose:
  4327. //------------------------------------------------------------------------------
  4328. void CTriggerWind::Spawn( void )
  4329. {
  4330. m_bSwitch = true;
  4331. m_nDirBase = GetLocalAngles().y;
  4332. BaseClass::Spawn();
  4333. m_nSpeedCurrent = m_nSpeedBase;
  4334. m_nDirCurrent = m_nDirBase;
  4335. SetContextThink( &CTriggerWind::WindThink, gpGlobals->curtime, WIND_THINK_CONTEXT );
  4336. }
  4337. //-----------------------------------------------------------------------------
  4338. // Purpose:
  4339. //-----------------------------------------------------------------------------
  4340. bool CTriggerWind::KeyValue( const char *szKeyName, const char *szValue )
  4341. {
  4342. // Done here to avoid collision with CBaseEntity's speed key
  4343. if ( FStrEq(szKeyName, "Speed") )
  4344. {
  4345. m_nSpeedBase = atoi( szValue );
  4346. }
  4347. else
  4348. return BaseClass::KeyValue( szKeyName, szValue );
  4349. return true;
  4350. }
  4351. //------------------------------------------------------------------------------
  4352. // Create VPhysics
  4353. //------------------------------------------------------------------------------
  4354. bool CTriggerWind::CreateVPhysics()
  4355. {
  4356. BaseClass::CreateVPhysics();
  4357. m_pWindController = physenv->CreateMotionController( &m_WindCallback );
  4358. return true;
  4359. }
  4360. //------------------------------------------------------------------------------
  4361. // Cleanup
  4362. //------------------------------------------------------------------------------
  4363. void CTriggerWind::UpdateOnRemove()
  4364. {
  4365. if ( m_pWindController )
  4366. {
  4367. physenv->DestroyMotionController( m_pWindController );
  4368. m_pWindController = NULL;
  4369. }
  4370. BaseClass::UpdateOnRemove();
  4371. }
  4372. //------------------------------------------------------------------------------
  4373. // Purpose:
  4374. //------------------------------------------------------------------------------
  4375. void CTriggerWind::OnRestore()
  4376. {
  4377. BaseClass::OnRestore();
  4378. if ( m_pWindController )
  4379. {
  4380. m_pWindController->SetEventHandler( &m_WindCallback );
  4381. }
  4382. }
  4383. //------------------------------------------------------------------------------
  4384. // Purpose:
  4385. //------------------------------------------------------------------------------
  4386. void CTriggerWind::StartTouch(CBaseEntity *pOther)
  4387. {
  4388. if ( !PassesTriggerFilters(pOther) )
  4389. return;
  4390. if ( pOther->IsPlayer() )
  4391. return;
  4392. IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
  4393. if ( pPhys)
  4394. {
  4395. m_pWindController->AttachObject( pPhys, false );
  4396. pPhys->Wake();
  4397. }
  4398. }
  4399. //------------------------------------------------------------------------------
  4400. // Purpose:
  4401. //------------------------------------------------------------------------------
  4402. void CTriggerWind::EndTouch(CBaseEntity *pOther)
  4403. {
  4404. if ( !PassesTriggerFilters(pOther) )
  4405. return;
  4406. if ( pOther->IsPlayer() )
  4407. return;
  4408. IPhysicsObject *pPhys = pOther->VPhysicsGetObject();
  4409. if ( pPhys && m_pWindController )
  4410. {
  4411. m_pWindController->DetachObject( pPhys );
  4412. }
  4413. }
  4414. //------------------------------------------------------------------------------
  4415. // Purpose:
  4416. //------------------------------------------------------------------------------
  4417. void CTriggerWind::InputEnable( inputdata_t &inputdata )
  4418. {
  4419. BaseClass::InputEnable( inputdata );
  4420. SetContextThink( &CTriggerWind::WindThink, gpGlobals->curtime + 0.1f, WIND_THINK_CONTEXT );
  4421. }
  4422. //------------------------------------------------------------------------------
  4423. // Purpose:
  4424. //------------------------------------------------------------------------------
  4425. void CTriggerWind::WindThink( void )
  4426. {
  4427. // By default...
  4428. SetContextThink( &CTriggerWind::WindThink, gpGlobals->curtime + 0.1, WIND_THINK_CONTEXT );
  4429. // Is it time to change the wind?
  4430. if (m_bSwitch)
  4431. {
  4432. m_bSwitch = false;
  4433. // Set new target direction and speed
  4434. m_nSpeedTarget = m_nSpeedBase + random->RandomInt( -m_nSpeedNoise, m_nSpeedNoise );
  4435. m_nDirTarget = UTIL_AngleMod( m_nDirBase + random->RandomInt(-m_nDirNoise, m_nDirNoise) );
  4436. }
  4437. else
  4438. {
  4439. bool bDone = true;
  4440. // either ramp up, or sleep till change
  4441. if (abs(m_nSpeedTarget - m_nSpeedCurrent) > MAX_WIND_CHANGE)
  4442. {
  4443. m_nSpeedCurrent += (m_nSpeedTarget > m_nSpeedCurrent) ? MAX_WIND_CHANGE : -MAX_WIND_CHANGE;
  4444. bDone = false;
  4445. }
  4446. if (abs(m_nDirTarget - m_nDirCurrent) > MAX_WIND_CHANGE)
  4447. {
  4448. m_nDirCurrent = UTIL_ApproachAngle( m_nDirTarget, m_nDirCurrent, MAX_WIND_CHANGE );
  4449. bDone = false;
  4450. }
  4451. if (bDone)
  4452. {
  4453. m_nSpeedCurrent = m_nSpeedTarget;
  4454. SetContextThink( &CTriggerWind::WindThink, m_nHoldBase + random->RandomFloat(-m_nHoldNoise,m_nHoldNoise), WIND_THINK_CONTEXT );
  4455. m_bSwitch = true;
  4456. }
  4457. }
  4458. // If we're starting to blow, where we weren't before, wake up all our objects
  4459. if ( m_nSpeedCurrent )
  4460. {
  4461. m_pWindController->WakeObjects();
  4462. }
  4463. // store the wind data in the controller callback
  4464. m_WindCallback.m_nWindYaw = m_nDirCurrent;
  4465. if ( m_bDisabled )
  4466. {
  4467. m_WindCallback.m_flWindSpeed = 0;
  4468. }
  4469. else
  4470. {
  4471. m_WindCallback.m_flWindSpeed = m_nSpeedCurrent;
  4472. }
  4473. }
  4474. //------------------------------------------------------------------------------
  4475. // Purpose:
  4476. //------------------------------------------------------------------------------
  4477. void CTriggerWind::InputSetSpeed( inputdata_t &inputdata )
  4478. {
  4479. // Set new speed and mark to switch
  4480. m_nSpeedBase = inputdata.value.Int();
  4481. m_bSwitch = true;
  4482. }
  4483. //-----------------------------------------------------------------------------
  4484. // Purpose: Draw any debug text overlays
  4485. // Output : Current text offset from the top
  4486. //-----------------------------------------------------------------------------
  4487. int CTriggerWind::DrawDebugTextOverlays(void)
  4488. {
  4489. int text_offset = BaseClass::DrawDebugTextOverlays();
  4490. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  4491. {
  4492. // --------------
  4493. // Print Target
  4494. // --------------
  4495. char tempstr[255];
  4496. Q_snprintf(tempstr,sizeof(tempstr),"Dir: %i (%i)",m_nDirCurrent,m_nDirTarget);
  4497. EntityText(text_offset,tempstr,0);
  4498. text_offset++;
  4499. Q_snprintf(tempstr,sizeof(tempstr),"Speed: %i (%i)",m_nSpeedCurrent,m_nSpeedTarget);
  4500. EntityText(text_offset,tempstr,0);
  4501. text_offset++;
  4502. }
  4503. return text_offset;
  4504. }
  4505. // ##################################################################################
  4506. // >> TriggerHierarchy
  4507. //
  4508. // Triggers when touching entities children that match a given filter
  4509. //
  4510. // ##################################################################################
  4511. class CTriggerHierarchy : public CTriggerMultiple
  4512. {
  4513. DECLARE_CLASS( CTriggerHierarchy, CTriggerMultiple );
  4514. public:
  4515. DECLARE_DATADESC();
  4516. string_t m_iChildFilterName;
  4517. CHandle<class CBaseFilter> m_hChildFilter;
  4518. virtual void Activate( void );
  4519. virtual bool PassesTriggerFilters( CBaseEntity *pOther );
  4520. bool HasChildThatPassesChildFilter( CBaseEntity *pEnt );
  4521. };
  4522. LINK_ENTITY_TO_CLASS( trigger_hierarchy, CTriggerHierarchy );
  4523. BEGIN_DATADESC( CTriggerHierarchy )
  4524. DEFINE_KEYFIELD( m_iChildFilterName, FIELD_STRING, "childfiltername" ),
  4525. END_DATADESC()
  4526. void CTriggerHierarchy::Activate( void )
  4527. {
  4528. BaseClass::Activate();
  4529. if ( m_iChildFilterName != NULL_STRING )
  4530. {
  4531. m_hChildFilter = dynamic_cast<CBaseFilter *>( gEntList.FindEntityByName( NULL, m_iChildFilterName ) );
  4532. }
  4533. }
  4534. bool CTriggerHierarchy::PassesTriggerFilters( CBaseEntity *pOther )
  4535. {
  4536. bool bPass = BaseClass::PassesTriggerFilters( pOther );
  4537. if ( !bPass )
  4538. return false;
  4539. bPass = HasChildThatPassesChildFilter( pOther );
  4540. return bPass;
  4541. }
  4542. bool CTriggerHierarchy::HasChildThatPassesChildFilter( CBaseEntity *pEnt )
  4543. {
  4544. CBaseFilter *pChildFilter = m_hChildFilter.Get();
  4545. // Loop through all children of this ent and check them for passing
  4546. for ( CBaseEntity *pChild = pEnt->FirstMoveChild(); pChild; pChild = pChild->NextMovePeer() )
  4547. {
  4548. if ( !pChildFilter || pChildFilter->PassesFilter( this, pChild ) )
  4549. {
  4550. // Found one!
  4551. return true;
  4552. }
  4553. // This didn't pass, maybe its children will... recurse!
  4554. if ( HasChildThatPassesChildFilter( pChild ) )
  4555. {
  4556. return true;
  4557. }
  4558. }
  4559. // None of these children or their children pass
  4560. return false;
  4561. }
  4562. // ##################################################################################
  4563. // >> TriggerImpact
  4564. //
  4565. // Blows physics objects in the trigger
  4566. //
  4567. // ##################################################################################
  4568. #define TRIGGERIMPACT_VIEWKICK_SCALE 0.1
  4569. class CTriggerImpact : public CTriggerMultiple
  4570. {
  4571. DECLARE_CLASS( CTriggerImpact, CTriggerMultiple );
  4572. public:
  4573. DECLARE_DATADESC();
  4574. float m_flMagnitude;
  4575. float m_flNoise;
  4576. float m_flViewkick;
  4577. void Spawn( void );
  4578. void StartTouch( CBaseEntity *pOther );
  4579. // Inputs
  4580. void InputSetMagnitude( inputdata_t &inputdata );
  4581. void InputImpact( inputdata_t &inputdata );
  4582. // Outputs
  4583. COutputVector m_pOutputForce; // Output force in case anyone else wants to use it
  4584. // Debug
  4585. int DrawDebugTextOverlays(void);
  4586. };
  4587. LINK_ENTITY_TO_CLASS( trigger_impact, CTriggerImpact );
  4588. BEGIN_DATADESC( CTriggerImpact )
  4589. DEFINE_KEYFIELD( m_flMagnitude, FIELD_FLOAT, "Magnitude"),
  4590. DEFINE_KEYFIELD( m_flNoise, FIELD_FLOAT, "Noise"),
  4591. DEFINE_KEYFIELD( m_flViewkick, FIELD_FLOAT, "Viewkick"),
  4592. // Inputs
  4593. DEFINE_INPUTFUNC( FIELD_VOID, "Impact", InputImpact ),
  4594. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMagnitude", InputSetMagnitude ),
  4595. // Outputs
  4596. DEFINE_OUTPUT(m_pOutputForce, "ImpactForce"),
  4597. // Function Pointers
  4598. DEFINE_FUNCTION( Disable ),
  4599. END_DATADESC()
  4600. //------------------------------------------------------------------------------
  4601. // Purpose:
  4602. //------------------------------------------------------------------------------
  4603. void CTriggerImpact::Spawn( void )
  4604. {
  4605. // Clamp date in case user made an error
  4606. m_flNoise = clamp(m_flNoise,0,1);
  4607. m_flViewkick = clamp(m_flViewkick,0,1);
  4608. // Always start disabled
  4609. m_bDisabled = true;
  4610. BaseClass::Spawn();
  4611. }
  4612. //------------------------------------------------------------------------------
  4613. // Purpose:
  4614. //------------------------------------------------------------------------------
  4615. void CTriggerImpact::InputImpact( inputdata_t &inputdata )
  4616. {
  4617. // Output the force vector in case anyone else wants to use it
  4618. Vector vDir;
  4619. AngleVectors( GetLocalAngles(),&vDir );
  4620. m_pOutputForce.Set( m_flMagnitude * vDir, inputdata.pActivator, inputdata.pCaller);
  4621. // Enable long enough to throw objects inside me
  4622. Enable();
  4623. SetNextThink( gpGlobals->curtime + 0.1f );
  4624. SetThink(&CTriggerImpact::Disable);
  4625. }
  4626. //------------------------------------------------------------------------------
  4627. // Purpose:
  4628. //------------------------------------------------------------------------------
  4629. void CTriggerImpact::StartTouch(CBaseEntity *pOther)
  4630. {
  4631. //If the entity is valid and has physics, hit it
  4632. if ( ( pOther != NULL ) && ( pOther->VPhysicsGetObject() != NULL ) )
  4633. {
  4634. Vector vDir;
  4635. AngleVectors( GetLocalAngles(),&vDir );
  4636. vDir += RandomVector(-m_flNoise,m_flNoise);
  4637. pOther->VPhysicsGetObject()->ApplyForceCenter( m_flMagnitude * vDir );
  4638. }
  4639. // If the player, so a view kick
  4640. if (pOther->IsPlayer() && fabs(m_flMagnitude)>0 )
  4641. {
  4642. Vector vDir;
  4643. AngleVectors( GetLocalAngles(),&vDir );
  4644. float flPunch = -m_flViewkick*m_flMagnitude*TRIGGERIMPACT_VIEWKICK_SCALE;
  4645. pOther->ViewPunch( QAngle( vDir.y * flPunch, 0, vDir.x * flPunch ) );
  4646. }
  4647. }
  4648. //------------------------------------------------------------------------------
  4649. // Purpose:
  4650. //------------------------------------------------------------------------------
  4651. void CTriggerImpact::InputSetMagnitude( inputdata_t &inputdata )
  4652. {
  4653. m_flMagnitude = inputdata.value.Float();
  4654. }
  4655. //-----------------------------------------------------------------------------
  4656. // Purpose: Draw any debug text overlays
  4657. // Output : Current text offset from the top
  4658. //-----------------------------------------------------------------------------
  4659. int CTriggerImpact::DrawDebugTextOverlays(void)
  4660. {
  4661. int text_offset = BaseClass::DrawDebugTextOverlays();
  4662. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  4663. {
  4664. char tempstr[255];
  4665. Q_snprintf(tempstr,sizeof(tempstr),"Magnitude: %3.2f",m_flMagnitude);
  4666. EntityText(text_offset,tempstr,0);
  4667. text_offset++;
  4668. }
  4669. return text_offset;
  4670. }
  4671. //-----------------------------------------------------------------------------
  4672. // Purpose: Disables auto movement on players that touch it
  4673. //-----------------------------------------------------------------------------
  4674. class CTriggerPlayerMovement : public CBaseTrigger
  4675. {
  4676. DECLARE_CLASS( CTriggerPlayerMovement, CBaseTrigger );
  4677. DECLARE_SERVERCLASS();
  4678. public:
  4679. void Spawn( void );
  4680. void StartTouch( CBaseEntity *pOther );
  4681. void EndTouch( CBaseEntity *pOther );
  4682. DECLARE_DATADESC();
  4683. };
  4684. IMPLEMENT_SERVERCLASS_ST( CTriggerPlayerMovement, DT_TriggerPlayerMovement )
  4685. END_SEND_TABLE()
  4686. BEGIN_DATADESC( CTriggerPlayerMovement )
  4687. END_DATADESC()
  4688. LINK_ENTITY_TO_CLASS( trigger_playermovement, CTriggerPlayerMovement );
  4689. //-----------------------------------------------------------------------------
  4690. // Purpose: Called when spawning, after keyvalues have been handled.
  4691. //-----------------------------------------------------------------------------
  4692. void CTriggerPlayerMovement::Spawn( void )
  4693. {
  4694. if( HasSpawnFlags( SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS ) )
  4695. {
  4696. // @Note (toml 01-07-04): fix up spawn flag collision coding error. Remove at some point once all maps fixed up please!
  4697. DevMsg("*** trigger_playermovement using obsolete spawnflag. Remove and reset with new value for \"Disable auto player movement\"\n" );
  4698. RemoveSpawnFlags(SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS);
  4699. AddSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE);
  4700. }
  4701. BaseClass::Spawn();
  4702. InitTrigger();
  4703. // Bugbait 19571: Make CTriggerPlayerMovement for AutoDuck available on client for client side prediction
  4704. if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) ||
  4705. HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) )
  4706. {
  4707. m_bClientSidePredicted = true;
  4708. SetTransmitState( FL_EDICT_PVSCHECK );
  4709. }
  4710. }
  4711. // UNDONE: This will not support a player touching more than one of these
  4712. // UNDONE: Do we care? If so, ref count automovement in the player?
  4713. void CTriggerPlayerMovement::StartTouch( CBaseEntity *pOther )
  4714. {
  4715. if (!PassesTriggerFilters(pOther))
  4716. return;
  4717. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  4718. if ( !pPlayer )
  4719. return;
  4720. if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) )
  4721. {
  4722. pPlayer->ForceButtons( IN_DUCK );
  4723. }
  4724. if ( HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) )
  4725. {
  4726. pPlayer->ForceButtons( IN_SPEED );
  4727. }
  4728. // UNDONE: Currently this is the only operation this trigger can do
  4729. if ( HasSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE) )
  4730. {
  4731. pPlayer->m_Local.m_bAllowAutoMovement = false;
  4732. }
  4733. }
  4734. void CTriggerPlayerMovement::EndTouch( CBaseEntity *pOther )
  4735. {
  4736. if (!PassesTriggerFilters(pOther))
  4737. return;
  4738. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  4739. if ( !pPlayer )
  4740. return;
  4741. if ( HasSpawnFlags( SF_TRIGGER_AUTO_DUCK ) )
  4742. {
  4743. pPlayer->UnforceButtons( IN_DUCK );
  4744. }
  4745. if ( HasSpawnFlags( SF_TRIGGER_AUTO_WALK ) )
  4746. {
  4747. pPlayer->UnforceButtons( IN_SPEED );
  4748. }
  4749. if ( HasSpawnFlags(SF_TRIGGER_MOVE_AUTODISABLE) )
  4750. {
  4751. pPlayer->m_Local.m_bAllowAutoMovement = true;
  4752. }
  4753. }
  4754. //-----------------------------------------------------------------------------
  4755. // Purpose: Disables auto movement on players that touch it
  4756. //-----------------------------------------------------------------------------
  4757. class CTriggerSoundOperator : public CBaseTrigger
  4758. {
  4759. DECLARE_CLASS( CTriggerSoundOperator, CBaseTrigger );
  4760. DECLARE_SERVERCLASS();
  4761. public:
  4762. void Spawn( void );
  4763. void StartTouch( CBaseEntity *pOther );
  4764. void EndTouch( CBaseEntity *pOther );
  4765. DECLARE_DATADESC();
  4766. protected:
  4767. CNetworkVar( int, m_nSoundOperator );
  4768. };
  4769. IMPLEMENT_SERVERCLASS_ST( CTriggerSoundOperator, DT_TriggerSoundOperator )
  4770. SendPropInt( SENDINFO( m_nSoundOperator ), -1, SPROP_NOSCALE )
  4771. END_SEND_TABLE()
  4772. BEGIN_DATADESC( CTriggerSoundOperator )
  4773. DEFINE_KEYFIELD( m_nSoundOperator, FIELD_INTEGER, "sosvar" )
  4774. END_DATADESC()
  4775. LINK_ENTITY_TO_CLASS( trigger_soundoperator, CTriggerSoundOperator );
  4776. //-----------------------------------------------------------------------------
  4777. // Purpose: Called when spawning, after keyvalues have been handled.
  4778. //-----------------------------------------------------------------------------
  4779. void CTriggerSoundOperator::Spawn( void )
  4780. {
  4781. BaseClass::Spawn();
  4782. InitTrigger();
  4783. m_bClientSidePredicted = true;
  4784. SetTransmitState( FL_EDICT_PVSCHECK );
  4785. }
  4786. // UNDONE: This will not support a player touching more than one of these
  4787. // UNDONE: Do we care? If so, ref count automovement in the player?
  4788. void CTriggerSoundOperator::StartTouch( CBaseEntity *pOther )
  4789. {
  4790. if ( !PassesTriggerFilters( pOther ) )
  4791. return;
  4792. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  4793. if ( !pPlayer )
  4794. return;
  4795. }
  4796. void CTriggerSoundOperator::EndTouch( CBaseEntity *pOther )
  4797. {
  4798. if ( !PassesTriggerFilters( pOther ) )
  4799. return;
  4800. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  4801. if ( !pPlayer )
  4802. return;
  4803. }
  4804. //------------------------------------------------------------------------------
  4805. // Base VPhysics trigger implementation
  4806. //------------------------------------------------------------------------------
  4807. //------------------------------------------------------------------------------
  4808. // Save/load
  4809. //------------------------------------------------------------------------------
  4810. BEGIN_DATADESC( CBaseVPhysicsTrigger )
  4811. DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
  4812. DEFINE_KEYFIELD( m_iFilterName, FIELD_STRING, "filtername" ),
  4813. DEFINE_FIELD( m_hFilter, FIELD_EHANDLE ),
  4814. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  4815. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  4816. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  4817. END_DATADESC()
  4818. IMPLEMENT_SERVERCLASS_ST( CBaseVPhysicsTrigger, DT_BaseVPhysicsTrigger )
  4819. //SendPropBool ( SENDINFO( m_bDisabled ) ),
  4820. END_SEND_TABLE()
  4821. //------------------------------------------------------------------------------
  4822. // Spawn
  4823. //------------------------------------------------------------------------------
  4824. void CBaseVPhysicsTrigger::Spawn()
  4825. {
  4826. Precache();
  4827. SetSolid( SOLID_VPHYSICS );
  4828. AddSolidFlags( FSOLID_NOT_SOLID );
  4829. // NOTE: Don't make yourself FSOLID_TRIGGER here or you'll get game
  4830. // collisions AND vphysics collisions. You don't want any game collisions
  4831. // so just use FSOLID_NOT_SOLID
  4832. SetMoveType( MOVETYPE_NONE );
  4833. SetModel( STRING( GetModelName() ) ); // set size and link into world
  4834. if ( showtriggers.GetInt() == 0 )
  4835. {
  4836. AddEffects( EF_NODRAW );
  4837. }
  4838. CreateVPhysics();
  4839. }
  4840. //------------------------------------------------------------------------------
  4841. // Create VPhysics
  4842. //------------------------------------------------------------------------------
  4843. bool CBaseVPhysicsTrigger::CreateVPhysics()
  4844. {
  4845. IPhysicsObject *pPhysics;
  4846. if ( !HasSpawnFlags( SF_VPHYSICS_MOTION_MOVEABLE ) )
  4847. {
  4848. pPhysics = VPhysicsInitStatic();
  4849. }
  4850. else
  4851. {
  4852. pPhysics = VPhysicsInitShadow( false, false );
  4853. }
  4854. pPhysics->BecomeTrigger();
  4855. return true;
  4856. }
  4857. //------------------------------------------------------------------------------
  4858. // Cleanup
  4859. //------------------------------------------------------------------------------
  4860. void CBaseVPhysicsTrigger::UpdateOnRemove()
  4861. {
  4862. if ( VPhysicsGetObject())
  4863. {
  4864. VPhysicsGetObject()->RemoveTrigger();
  4865. }
  4866. BaseClass::UpdateOnRemove();
  4867. }
  4868. //------------------------------------------------------------------------------
  4869. // Activate
  4870. //------------------------------------------------------------------------------
  4871. void CBaseVPhysicsTrigger::Activate( void )
  4872. {
  4873. // Get a handle to my filter entity if there is one
  4874. if (m_iFilterName != NULL_STRING)
  4875. {
  4876. m_hFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iFilterName ));
  4877. }
  4878. BaseClass::Activate();
  4879. }
  4880. //------------------------------------------------------------------------------
  4881. // Inputs
  4882. //------------------------------------------------------------------------------
  4883. void CBaseVPhysicsTrigger::InputToggle( inputdata_t &inputdata )
  4884. {
  4885. if ( m_bDisabled )
  4886. {
  4887. InputEnable( inputdata );
  4888. }
  4889. else
  4890. {
  4891. InputDisable( inputdata );
  4892. }
  4893. }
  4894. //-----------------------------------------------------------------------------
  4895. // Purpose:
  4896. //-----------------------------------------------------------------------------
  4897. void CBaseVPhysicsTrigger::InputEnable( inputdata_t &inputdata )
  4898. {
  4899. if ( m_bDisabled )
  4900. {
  4901. m_bDisabled = false;
  4902. if ( VPhysicsGetObject())
  4903. {
  4904. VPhysicsGetObject()->EnableCollisions( true );
  4905. }
  4906. }
  4907. }
  4908. //-----------------------------------------------------------------------------
  4909. // Purpose:
  4910. //-----------------------------------------------------------------------------
  4911. void CBaseVPhysicsTrigger::InputDisable( inputdata_t &inputdata )
  4912. {
  4913. if ( !m_bDisabled )
  4914. {
  4915. m_bDisabled = true;
  4916. if ( VPhysicsGetObject())
  4917. {
  4918. VPhysicsGetObject()->EnableCollisions( false );
  4919. }
  4920. }
  4921. }
  4922. //-----------------------------------------------------------------------------
  4923. // Purpose:
  4924. //-----------------------------------------------------------------------------
  4925. void CBaseVPhysicsTrigger::StartTouch( CBaseEntity *pOther )
  4926. {
  4927. }
  4928. //-----------------------------------------------------------------------------
  4929. // Purpose:
  4930. //-----------------------------------------------------------------------------
  4931. void CBaseVPhysicsTrigger::EndTouch( CBaseEntity *pOther )
  4932. {
  4933. }
  4934. //-----------------------------------------------------------------------------
  4935. // Purpose:
  4936. //-----------------------------------------------------------------------------
  4937. bool CBaseVPhysicsTrigger::PassesTriggerFilters( CBaseEntity *pOther )
  4938. {
  4939. if ( pOther->GetMoveType() != MOVETYPE_VPHYSICS && !pOther->IsPlayer() )
  4940. return false;
  4941. // First test spawn flag filters
  4942. if ( HasSpawnFlags(SF_TRIGGER_ALLOW_ALL) ||
  4943. (HasSpawnFlags(SF_TRIGGER_ALLOW_CLIENTS) && (pOther->GetFlags() & FL_CLIENT)) ||
  4944. (HasSpawnFlags(SF_TRIGGER_ALLOW_NPCS) && (pOther->GetFlags() & FL_NPC)) ||
  4945. (HasSpawnFlags(SF_TRIGGER_ALLOW_PUSHABLES) && FClassnameIs(pOther, "func_pushable")) ||
  4946. (HasSpawnFlags(SF_TRIGGER_ALLOW_PHYSICS) && pOther->GetMoveType() == MOVETYPE_VPHYSICS))
  4947. {
  4948. bool bOtherIsPlayer = pOther->IsPlayer();
  4949. if( HasSpawnFlags(SF_TRIGGER_ONLY_PLAYER_ALLY_NPCS) && !bOtherIsPlayer )
  4950. {
  4951. CAI_BaseNPC *pNPC = pOther->MyNPCPointer();
  4952. if( !pNPC || !pNPC->IsPlayerAlly() )
  4953. {
  4954. return false;
  4955. }
  4956. }
  4957. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_IN_VEHICLES) && bOtherIsPlayer )
  4958. {
  4959. if ( !((CBasePlayer*)pOther)->IsInAVehicle() )
  4960. return false;
  4961. }
  4962. if ( HasSpawnFlags(SF_TRIGGER_ONLY_CLIENTS_OUT_OF_VEHICLES) && bOtherIsPlayer )
  4963. {
  4964. if ( ((CBasePlayer*)pOther)->IsInAVehicle() )
  4965. return false;
  4966. }
  4967. CBaseFilter *pFilter = m_hFilter.Get();
  4968. return (!pFilter) ? true : pFilter->PassesFilter( this, pOther );
  4969. }
  4970. return false;
  4971. }
  4972. //=====================================================================================================================
  4973. //-----------------------------------------------------------------------------
  4974. // Purpose: VPhysics trigger that changes the motion of vphysics objects that touch it
  4975. //-----------------------------------------------------------------------------
  4976. class CTriggerVPhysicsMotion : public CBaseVPhysicsTrigger, public IMotionEvent
  4977. {
  4978. DECLARE_CLASS( CTriggerVPhysicsMotion, CBaseVPhysicsTrigger );
  4979. public:
  4980. void Spawn();
  4981. void Precache();
  4982. virtual void UpdateOnRemove();
  4983. bool CreateVPhysics();
  4984. void OnRestore();
  4985. // UNDONE: Pass trigger event in or change Start/EndTouch. Add ITriggerVPhysics perhaps?
  4986. // BUGBUG: If a player touches two of these, his movement will screw up.
  4987. // BUGBUG: If a player uses crouch/uncrouch it will generate touch events and clear the motioncontroller flag
  4988. void StartTouch( CBaseEntity *pOther );
  4989. void EndTouch( CBaseEntity *pOther );
  4990. void InputSetVelocityLimitTime( inputdata_t &inputdata );
  4991. float LinearLimit();
  4992. inline bool HasGravityScale() { return m_gravityScale != 1.0 ? true : false; }
  4993. inline bool HasAirDensity() { return m_addAirDensity != 0 ? true : false; }
  4994. inline bool HasLinearLimit() { return LinearLimit() != 0.0f; }
  4995. inline bool HasLinearScale() { return m_linearScale != 1.0 ? true : false; }
  4996. inline bool HasAngularLimit() { return m_angularLimit != 0 ? true : false; }
  4997. inline bool HasAngularScale() { return m_angularScale != 1.0 ? true : false; }
  4998. inline bool HasLinearForce() { return m_linearForce != 0.0 ? true : false; }
  4999. DECLARE_DATADESC();
  5000. virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  5001. private:
  5002. IPhysicsMotionController *m_pController;
  5003. EntityParticleTrailInfo_t m_ParticleTrail;
  5004. float m_gravityScale;
  5005. float m_addAirDensity;
  5006. float m_linearLimit;
  5007. float m_linearLimitDelta;
  5008. float m_linearLimitTime;
  5009. float m_linearLimitStart;
  5010. float m_linearLimitStartTime;
  5011. float m_linearScale;
  5012. float m_angularLimit;
  5013. float m_angularScale;
  5014. float m_linearForce;
  5015. QAngle m_linearForceAngles;
  5016. };
  5017. //------------------------------------------------------------------------------
  5018. // Save/load
  5019. //------------------------------------------------------------------------------
  5020. BEGIN_DATADESC( CTriggerVPhysicsMotion )
  5021. DEFINE_PHYSPTR( m_pController ),
  5022. DEFINE_EMBEDDED( m_ParticleTrail ),
  5023. DEFINE_INPUT( m_gravityScale, FIELD_FLOAT, "SetGravityScale" ),
  5024. DEFINE_INPUT( m_addAirDensity, FIELD_FLOAT, "SetAdditionalAirDensity" ),
  5025. DEFINE_INPUT( m_linearLimit, FIELD_FLOAT, "SetVelocityLimit" ),
  5026. DEFINE_INPUT( m_linearLimitDelta, FIELD_FLOAT, "SetVelocityLimitDelta" ),
  5027. DEFINE_FIELD( m_linearLimitTime, FIELD_FLOAT ),
  5028. DEFINE_FIELD( m_linearLimitStart, FIELD_TIME ),
  5029. DEFINE_FIELD( m_linearLimitStartTime, FIELD_TIME ),
  5030. DEFINE_INPUT( m_linearScale, FIELD_FLOAT, "SetVelocityScale" ),
  5031. DEFINE_INPUT( m_angularLimit, FIELD_FLOAT, "SetAngVelocityLimit" ),
  5032. DEFINE_INPUT( m_angularScale, FIELD_FLOAT, "SetAngVelocityScale" ),
  5033. DEFINE_INPUT( m_linearForce, FIELD_FLOAT, "SetLinearForce" ),
  5034. DEFINE_INPUT( m_linearForceAngles, FIELD_VECTOR, "SetLinearForceAngles" ),
  5035. DEFINE_INPUTFUNC( FIELD_STRING, "SetVelocityLimitTime", InputSetVelocityLimitTime ),
  5036. END_DATADESC()
  5037. LINK_ENTITY_TO_CLASS( trigger_vphysics_motion, CTriggerVPhysicsMotion );
  5038. //------------------------------------------------------------------------------
  5039. // Spawn
  5040. //------------------------------------------------------------------------------
  5041. void CTriggerVPhysicsMotion::Spawn()
  5042. {
  5043. Precache();
  5044. BaseClass::Spawn();
  5045. }
  5046. //------------------------------------------------------------------------------
  5047. // Precache
  5048. //------------------------------------------------------------------------------
  5049. void CTriggerVPhysicsMotion::Precache()
  5050. {
  5051. if ( m_ParticleTrail.m_strMaterialName != NULL_STRING )
  5052. {
  5053. PrecacheMaterial( STRING(m_ParticleTrail.m_strMaterialName) );
  5054. }
  5055. }
  5056. //------------------------------------------------------------------------------
  5057. // Create VPhysics
  5058. //------------------------------------------------------------------------------
  5059. float CTriggerVPhysicsMotion::LinearLimit()
  5060. {
  5061. if ( m_linearLimitTime == 0.0f )
  5062. return m_linearLimit;
  5063. float dt = gpGlobals->curtime - m_linearLimitStartTime;
  5064. if ( dt >= m_linearLimitTime )
  5065. {
  5066. m_linearLimitTime = 0.0;
  5067. return m_linearLimit;
  5068. }
  5069. dt /= m_linearLimitTime;
  5070. float flLimit = RemapVal( dt, 0.0f, 1.0f, m_linearLimitStart, m_linearLimit );
  5071. return flLimit;
  5072. }
  5073. //------------------------------------------------------------------------------
  5074. // Create VPhysics
  5075. //------------------------------------------------------------------------------
  5076. bool CTriggerVPhysicsMotion::CreateVPhysics()
  5077. {
  5078. m_pController = physenv->CreateMotionController( this );
  5079. BaseClass::CreateVPhysics();
  5080. return true;
  5081. }
  5082. //------------------------------------------------------------------------------
  5083. // Cleanup
  5084. //------------------------------------------------------------------------------
  5085. void CTriggerVPhysicsMotion::UpdateOnRemove()
  5086. {
  5087. if ( m_pController )
  5088. {
  5089. physenv->DestroyMotionController( m_pController );
  5090. m_pController = NULL;
  5091. }
  5092. BaseClass::UpdateOnRemove();
  5093. }
  5094. //------------------------------------------------------------------------------
  5095. // Restore
  5096. //------------------------------------------------------------------------------
  5097. void CTriggerVPhysicsMotion::OnRestore()
  5098. {
  5099. BaseClass::OnRestore();
  5100. if ( m_pController )
  5101. {
  5102. m_pController->SetEventHandler( this );
  5103. }
  5104. }
  5105. //------------------------------------------------------------------------------
  5106. // Start/End Touch
  5107. //------------------------------------------------------------------------------
  5108. // UNDONE: Pass trigger event in or change Start/EndTouch. Add ITriggerVPhysics perhaps?
  5109. // BUGBUG: If a player touches two of these, his movement will screw up.
  5110. // BUGBUG: If a player uses crouch/uncrouch it will generate touch events and clear the motioncontroller flag
  5111. void CTriggerVPhysicsMotion::StartTouch( CBaseEntity *pOther )
  5112. {
  5113. BaseClass::StartTouch( pOther );
  5114. if ( !PassesTriggerFilters(pOther) )
  5115. return;
  5116. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  5117. if ( pPlayer )
  5118. {
  5119. pPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, true );
  5120. pPlayer->m_Local.m_bSlowMovement = true;
  5121. }
  5122. triggerevent_t event;
  5123. PhysGetTriggerEvent( &event, this );
  5124. if ( event.pObject )
  5125. {
  5126. // these all get done again on save/load, so check
  5127. m_pController->AttachObject( event.pObject, true );
  5128. }
  5129. // Don't show these particles on the XBox
  5130. if ( m_ParticleTrail.m_strMaterialName != NULL_STRING )
  5131. {
  5132. CEntityParticleTrail::Create( pOther, m_ParticleTrail, this );
  5133. }
  5134. if ( pOther->GetBaseAnimating() && pOther->GetBaseAnimating()->IsRagdoll() )
  5135. {
  5136. CRagdollBoogie::IncrementSuppressionCount( pOther );
  5137. }
  5138. }
  5139. //-----------------------------------------------------------------------------
  5140. // Purpose:
  5141. //-----------------------------------------------------------------------------
  5142. void CTriggerVPhysicsMotion::EndTouch( CBaseEntity *pOther )
  5143. {
  5144. BaseClass::EndTouch( pOther );
  5145. if ( !PassesTriggerFilters(pOther) )
  5146. return;
  5147. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  5148. if ( pPlayer )
  5149. {
  5150. pPlayer->SetPhysicsFlag( PFLAG_VPHYSICS_MOTIONCONTROLLER, false );
  5151. pPlayer->m_Local.m_bSlowMovement = false;
  5152. }
  5153. triggerevent_t event;
  5154. PhysGetTriggerEvent( &event, this );
  5155. if ( event.pObject && m_pController )
  5156. {
  5157. m_pController->DetachObject( event.pObject );
  5158. }
  5159. if ( m_ParticleTrail.m_strMaterialName != NULL_STRING )
  5160. {
  5161. CEntityParticleTrail::Destroy( pOther, m_ParticleTrail );
  5162. }
  5163. if ( pOther->GetBaseAnimating() && pOther->GetBaseAnimating()->IsRagdoll() )
  5164. {
  5165. CRagdollBoogie::DecrementSuppressionCount( pOther );
  5166. }
  5167. }
  5168. //------------------------------------------------------------------------------
  5169. // Inputs
  5170. //------------------------------------------------------------------------------
  5171. void CTriggerVPhysicsMotion::InputSetVelocityLimitTime( inputdata_t &inputdata )
  5172. {
  5173. m_linearLimitStart = LinearLimit();
  5174. m_linearLimitStartTime = gpGlobals->curtime;
  5175. float args[2];
  5176. UTIL_StringToFloatArray( args, 2, inputdata.value.String() );
  5177. m_linearLimit = args[0];
  5178. m_linearLimitTime = args[1];
  5179. }
  5180. //------------------------------------------------------------------------------
  5181. // Apply the forces to the entity
  5182. //------------------------------------------------------------------------------
  5183. IMotionEvent::simresult_e CTriggerVPhysicsMotion::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
  5184. {
  5185. if ( m_bDisabled )
  5186. return SIM_NOTHING;
  5187. linear.Init();
  5188. angular.Init();
  5189. if ( HasGravityScale() )
  5190. {
  5191. // assume object already has 1.0 gravities applied to it, so apply the additional amount
  5192. linear.z -= (m_gravityScale-1) * sv_gravity.GetFloat();
  5193. }
  5194. if ( HasLinearForce() )
  5195. {
  5196. Vector vecForceDir;
  5197. AngleVectors( m_linearForceAngles, &vecForceDir );
  5198. VectorMA( linear, m_linearForce, vecForceDir, linear );
  5199. }
  5200. if ( HasAirDensity() || HasLinearLimit() || HasLinearScale() || HasAngularLimit() || HasAngularScale() )
  5201. {
  5202. Vector vel;
  5203. AngularImpulse angVel;
  5204. pObject->GetVelocity( &vel, &angVel );
  5205. vel += linear * deltaTime; // account for gravity scale
  5206. Vector unitVel = vel;
  5207. Vector unitAngVel = angVel;
  5208. float speed = VectorNormalize( unitVel );
  5209. float angSpeed = VectorNormalize( unitAngVel );
  5210. float speedScale = 0.0;
  5211. float angSpeedScale = 0.0;
  5212. if ( HasAirDensity() )
  5213. {
  5214. float linearDrag = -0.5 * m_addAirDensity * pObject->CalculateLinearDrag( unitVel ) * deltaTime;
  5215. if ( linearDrag < -1 )
  5216. {
  5217. linearDrag = -1;
  5218. }
  5219. speedScale += linearDrag / deltaTime;
  5220. float angDrag = -0.5 * m_addAirDensity * pObject->CalculateAngularDrag( unitAngVel ) * deltaTime;
  5221. if ( angDrag < -1 )
  5222. {
  5223. angDrag = -1;
  5224. }
  5225. angSpeedScale += angDrag / deltaTime;
  5226. }
  5227. if ( HasLinearLimit() && speed > m_linearLimit )
  5228. {
  5229. float flDeltaVel = (LinearLimit() - speed) / deltaTime;
  5230. if ( m_linearLimitDelta != 0.0f )
  5231. {
  5232. float flMaxDeltaVel = -m_linearLimitDelta / deltaTime;
  5233. if ( flDeltaVel < flMaxDeltaVel )
  5234. {
  5235. flDeltaVel = flMaxDeltaVel;
  5236. }
  5237. }
  5238. VectorMA( linear, flDeltaVel, unitVel, linear );
  5239. }
  5240. if ( HasAngularLimit() && angSpeed > m_angularLimit )
  5241. {
  5242. angular += ((m_angularLimit - angSpeed)/deltaTime) * unitAngVel;
  5243. }
  5244. if ( HasLinearScale() )
  5245. {
  5246. speedScale = ( (speedScale+1) * m_linearScale ) - 1;
  5247. }
  5248. if ( HasAngularScale() )
  5249. {
  5250. angSpeedScale = ( (angSpeedScale+1) * m_angularScale ) - 1;
  5251. }
  5252. linear += vel * speedScale;
  5253. angular += angVel * angSpeedScale;
  5254. }
  5255. return SIM_GLOBAL_ACCELERATION;
  5256. }
  5257. class CServerRagdollTrigger : public CBaseTrigger
  5258. {
  5259. DECLARE_CLASS( CServerRagdollTrigger, CBaseTrigger );
  5260. public:
  5261. virtual void StartTouch( CBaseEntity *pOther );
  5262. virtual void EndTouch( CBaseEntity *pOther );
  5263. virtual void Spawn( void );
  5264. };
  5265. LINK_ENTITY_TO_CLASS( trigger_serverragdoll, CServerRagdollTrigger );
  5266. void CServerRagdollTrigger::Spawn( void )
  5267. {
  5268. BaseClass::Spawn();
  5269. InitTrigger();
  5270. }
  5271. void CServerRagdollTrigger::StartTouch(CBaseEntity *pOther)
  5272. {
  5273. BaseClass::StartTouch( pOther );
  5274. if ( pOther->IsPlayer() )
  5275. return;
  5276. CBaseCombatCharacter *pCombatChar = pOther->MyCombatCharacterPointer();
  5277. if ( pCombatChar )
  5278. {
  5279. pCombatChar->m_bForceServerRagdoll = true;
  5280. }
  5281. }
  5282. void CServerRagdollTrigger::EndTouch(CBaseEntity *pOther)
  5283. {
  5284. BaseClass::EndTouch( pOther );
  5285. if ( pOther->IsPlayer() )
  5286. return;
  5287. CBaseCombatCharacter *pCombatChar = pOther->MyCombatCharacterPointer();
  5288. if ( pCombatChar )
  5289. {
  5290. pCombatChar->m_bForceServerRagdoll = false;
  5291. }
  5292. }
  5293. //--------------------------------------------------------------------------------------------------------
  5294. // Allows players touching it to auto-crouch.
  5295. class CTriggerAutoCrouch : public CBaseTrigger
  5296. {
  5297. public:
  5298. DECLARE_CLASS( CTriggerAutoCrouch, CBaseTrigger );
  5299. void Spawn( void );
  5300. virtual bool PassesTriggerFilters( CBaseEntity *pOther )
  5301. {
  5302. return pOther && pOther->IsPlayer();
  5303. }
  5304. virtual void StartTouch( CBaseEntity *pOther );
  5305. virtual void EndTouch( CBaseEntity *pOther );
  5306. DECLARE_DATADESC();
  5307. };
  5308. BEGIN_DATADESC( CTriggerAutoCrouch )
  5309. END_DATADESC()
  5310. LINK_ENTITY_TO_CLASS( trigger_auto_crouch, CTriggerAutoCrouch );
  5311. //--------------------------------------------------------------------------------------------------------
  5312. void CTriggerAutoCrouch::Spawn( void )
  5313. {
  5314. BaseClass::Spawn();
  5315. InitTrigger();
  5316. }
  5317. //--------------------------------------------------------------------------------------------------------
  5318. void CTriggerAutoCrouch::StartTouch( CBaseEntity *pOther )
  5319. {
  5320. BaseClass::StartTouch( pOther );
  5321. if ( IsTouching( pOther ) )
  5322. {
  5323. CBasePlayer *player = ToBasePlayer( pOther );
  5324. if ( player )
  5325. {
  5326. // FIXMEL4DTOMAINMERGE
  5327. // player->SetAutoCrouchEnabled( true );
  5328. }
  5329. }
  5330. }
  5331. //--------------------------------------------------------------------------------------------------------
  5332. void CTriggerAutoCrouch::EndTouch( CBaseEntity *pOther )
  5333. {
  5334. BaseClass::EndTouch( pOther );
  5335. CBasePlayer *player = ToBasePlayer( pOther );
  5336. if ( player )
  5337. {
  5338. // FIXMEL4DTOMAINMERGE
  5339. // player->SetAutoCrouchEnabled( false );
  5340. }
  5341. }
  5342. //----------------------------------------------------------------------------------
  5343. // Purpose:
  5344. //----------------------------------------------------------------------------------
  5345. void CTriggerCallback::Spawn( void )
  5346. {
  5347. // Setup our basic attributes
  5348. SetMoveType( MOVETYPE_NONE );
  5349. SetSolid( SOLID_OBB );
  5350. SetSolidFlags( FSOLID_NOT_SOLID|FSOLID_TRIGGER );
  5351. AddSpawnFlags( SF_TRIGGER_ALLOW_CLIENTS|SF_TRIGGER_ALLOW_NPCS|SF_TRIGGER_ALLOW_PHYSICS );
  5352. }
  5353. //----------------------------------------------------------------------------------
  5354. // Purpose:
  5355. //----------------------------------------------------------------------------------
  5356. void CTriggerCallback::StartTouch( CBaseEntity *pOther )
  5357. {
  5358. // Don't touch things under certain circumstances
  5359. if( pOther->VPhysicsGetObject() )
  5360. {
  5361. if( pOther->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  5362. return;
  5363. }
  5364. if ( PassesTriggerFilters( pOther ) == false )
  5365. return;
  5366. Assert( m_pfnCallback );
  5367. if ( GetParent() && m_pfnCallback )
  5368. {
  5369. (GetParent()->*m_pfnCallback)( pOther );
  5370. }
  5371. BaseClass::StartTouch( pOther );
  5372. }
  5373. bool IsTriggerClass( CBaseEntity *pEntity )
  5374. {
  5375. if ( NULL != dynamic_cast<CBaseTrigger *>(pEntity) )
  5376. return true;
  5377. if ( NULL != dynamic_cast<CTriggerVPhysicsMotion *>(pEntity) )
  5378. return true;
  5379. if ( NULL != dynamic_cast<CTriggerVolume *>(pEntity) )
  5380. return true;
  5381. return false;
  5382. }