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.

2225 lines
58 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "engine/IEngineSound.h"
  9. #include "mempool.h"
  10. #include "movevars_shared.h"
  11. #include "utlrbtree.h"
  12. #include "tier0/vprof.h"
  13. #include "entitydatainstantiator.h"
  14. #include "positionwatcher.h"
  15. #include "movetype_push.h"
  16. #include "vphysicsupdateai.h"
  17. #include "igamesystem.h"
  18. #include "utlmultilist.h"
  19. #include "tier1/callqueue.h"
  20. #include "engine/ivdebugoverlay.h"
  21. #ifdef PORTAL
  22. #include "portal_util_shared.h"
  23. #endif
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. // memory pool for storing links between entities
  27. static CUtlMemoryPool g_EdictTouchLinks( sizeof(touchlink_t), MAX_EDICTS, CUtlMemoryPool::GROW_NONE, "g_EdictTouchLinks");
  28. static CUtlMemoryPool g_EntityGroundLinks( sizeof( groundlink_t ), MAX_EDICTS, CUtlMemoryPool::GROW_NONE, "g_EntityGroundLinks");
  29. struct watcher_t
  30. {
  31. EHANDLE hWatcher;
  32. IWatcherCallback *pWatcherCallback;
  33. };
  34. static CUtlMultiList<watcher_t, unsigned short> g_WatcherList;
  35. class CWatcherList
  36. {
  37. public:
  38. //CWatcherList(); NOTE: Dataobj doesn't support constructors - it zeros the memory
  39. ~CWatcherList(); // frees the positionwatcher_t's to the pool
  40. void Init();
  41. void NotifyPositionChanged( CBaseEntity *pEntity );
  42. void NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake );
  43. void AddToList( CBaseEntity *pWatcher );
  44. void RemoveWatcher( CBaseEntity *pWatcher );
  45. private:
  46. int GetCallbackObjects( IWatcherCallback **pList, int listMax );
  47. unsigned short Find( CBaseEntity *pEntity );
  48. unsigned short m_list;
  49. };
  50. int linksallocated = 0;
  51. int groundlinksallocated = 0;
  52. // Prints warnings if any entity think functions take longer than this many milliseconds
  53. #ifdef _DEBUG
  54. #define DEF_THINK_LIMIT "20"
  55. #else
  56. #define DEF_THINK_LIMIT "10"
  57. #endif
  58. ConVar think_limit( "think_limit", DEF_THINK_LIMIT, FCVAR_REPLICATED | FCVAR_RELEASE, "Maximum think time in milliseconds, warning is printed if this is exceeded." );
  59. #ifndef CLIENT_DLL
  60. ConVar debug_touchlinks( "debug_touchlinks", "0", 0, "Spew touch link activity" );
  61. #define DebugTouchlinks() debug_touchlinks.GetBool()
  62. #else
  63. #define DebugTouchlinks() false
  64. #endif
  65. ConVar sv_grenade_trajectory("sv_grenade_trajectory", "0", FCVAR_REPLICATED | FCVAR_RELEASE | FCVAR_CHEAT, "Shows grenade trajectory visualization in-game." );
  66. ConVar sv_grenade_trajectory_time("sv_grenade_trajectory_time", "20", FCVAR_REPLICATED | FCVAR_RELEASE, "Length of time grenade trajectory remains visible.", true, 0.1f, true, 20.0f );
  67. ConVar sv_grenade_trajectory_time_spectator("sv_grenade_trajectory_time_spectator", "4", FCVAR_REPLICATED | FCVAR_RELEASE, "Length of time grenade trajectory remains visible as a spectator.", true, 0.0f, true, 8.0f );
  68. ConVar sv_grenade_trajectory_thickness("sv_grenade_trajectory_thickness", "0.2", FCVAR_REPLICATED | FCVAR_RELEASE, "Visible thickness of grenade trajectory arc", true, 0.1f, true, 1.0f );
  69. ConVar sv_grenade_trajectory_dash("sv_grenade_trajectory_dash", "0", FCVAR_REPLICATED | FCVAR_RELEASE, "Dot-dash style grenade trajectory arc" );
  70. //-----------------------------------------------------------------------------
  71. // Portal-specific hack designed to eliminate re-entrancy in touch functions
  72. //-----------------------------------------------------------------------------
  73. class CPortalTouchScope
  74. {
  75. public:
  76. CPortalTouchScope();
  77. ~CPortalTouchScope();
  78. public:
  79. static int m_nDepth;
  80. static CCallQueue m_CallQueue;
  81. };
  82. int CPortalTouchScope::m_nDepth = 0;
  83. CCallQueue CPortalTouchScope::m_CallQueue;
  84. CCallQueue *GetPortalCallQueue()
  85. {
  86. return ( CPortalTouchScope::m_nDepth > 0 ) ? &CPortalTouchScope::m_CallQueue : NULL;
  87. }
  88. CPortalTouchScope::CPortalTouchScope()
  89. {
  90. ++m_nDepth;
  91. }
  92. CPortalTouchScope::~CPortalTouchScope()
  93. {
  94. Assert( m_nDepth >= 1 );
  95. if ( --m_nDepth == 0 )
  96. {
  97. m_CallQueue.CallQueued();
  98. }
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose: System for hanging objects off of CBaseEntity, etc.
  102. // Externalized data objects ( see sharreddefs.h for enum )
  103. //-----------------------------------------------------------------------------
  104. class CDataObjectAccessSystem : public CAutoGameSystem
  105. {
  106. public:
  107. enum
  108. {
  109. MAX_ACCESSORS = 32,
  110. };
  111. CDataObjectAccessSystem()
  112. {
  113. COMPILE_TIME_ASSERT( (int)NUM_DATAOBJECT_TYPES <= (int)MAX_ACCESSORS );
  114. Q_memset( m_Accessors, 0, sizeof( m_Accessors ) );
  115. }
  116. virtual bool Init()
  117. {
  118. AddDataAccessor( TOUCHLINK, new CEntityDataInstantiator< touchlink_t > );
  119. AddDataAccessor( GROUNDLINK, new CEntityDataInstantiator< groundlink_t > );
  120. AddDataAccessor( STEPSIMULATION, new CEntityDataInstantiator< StepSimulationData > );
  121. AddDataAccessor( MODELSCALE, new CEntityDataInstantiator< ModelScale > );
  122. AddDataAccessor( POSITIONWATCHER, new CEntityDataInstantiator< CWatcherList > );
  123. AddDataAccessor( PHYSICSPUSHLIST, new CEntityDataInstantiator< physicspushlist_t > );
  124. AddDataAccessor( VPHYSICSUPDATEAI, new CEntityDataInstantiator< vphysicsupdateai_t > );
  125. AddDataAccessor( VPHYSICSWATCHER, new CEntityDataInstantiator< CWatcherList > );
  126. return true;
  127. }
  128. virtual void Shutdown()
  129. {
  130. for ( int i = 0; i < MAX_ACCESSORS; i++ )
  131. {
  132. delete m_Accessors[ i ];
  133. m_Accessors[ i ] = 0;
  134. }
  135. }
  136. void *GetDataObject( int type, const CBaseEntity *instance )
  137. {
  138. if ( !IsValidType( type ) )
  139. {
  140. Assert( !"Bogus type" );
  141. return NULL;
  142. }
  143. return m_Accessors[ type ]->GetDataObject( instance );
  144. }
  145. void *CreateDataObject( int type, CBaseEntity *instance )
  146. {
  147. if ( !IsValidType( type ) )
  148. {
  149. Assert( !"Bogus type" );
  150. return NULL;
  151. }
  152. return m_Accessors[ type ]->CreateDataObject( instance );
  153. }
  154. void DestroyDataObject( int type, CBaseEntity *instance )
  155. {
  156. if ( !IsValidType( type ) )
  157. {
  158. Assert( !"Bogus type" );
  159. return;
  160. }
  161. m_Accessors[ type ]->DestroyDataObject( instance );
  162. }
  163. private:
  164. bool IsValidType( int type ) const
  165. {
  166. if ( type < 0 || type >= MAX_ACCESSORS )
  167. return false;
  168. if ( m_Accessors[ type ] == NULL )
  169. return false;
  170. return true;
  171. }
  172. void AddDataAccessor( int type, IEntityDataInstantiator *instantiator )
  173. {
  174. if ( type < 0 || type >= MAX_ACCESSORS )
  175. {
  176. Assert( !"AddDataAccessor with out of range type!!!\n" );
  177. return;
  178. }
  179. Assert( instantiator );
  180. if ( m_Accessors[ type ] != NULL )
  181. {
  182. Assert( !"AddDataAccessor, duplicate adds!!!\n" );
  183. return;
  184. }
  185. m_Accessors[ type ] = instantiator;
  186. }
  187. IEntityDataInstantiator *m_Accessors[ MAX_ACCESSORS ];
  188. };
  189. static CDataObjectAccessSystem g_DataObjectAccessSystem;
  190. bool CBaseEntity::HasDataObjectType( int type ) const
  191. {
  192. Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
  193. return ( m_fDataObjectTypes & (1<<type) ) ? true : false;
  194. }
  195. void CBaseEntity::AddDataObjectType( int type )
  196. {
  197. Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
  198. m_fDataObjectTypes |= (1<<type);
  199. }
  200. void CBaseEntity::RemoveDataObjectType( int type )
  201. {
  202. Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
  203. m_fDataObjectTypes &= ~(1<<type);
  204. }
  205. void *CBaseEntity::GetDataObject( int type )
  206. {
  207. Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
  208. if ( !HasDataObjectType( type ) )
  209. return NULL;
  210. return g_DataObjectAccessSystem.GetDataObject( type, this );
  211. }
  212. void *CBaseEntity::CreateDataObject( int type )
  213. {
  214. Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
  215. AddDataObjectType( type );
  216. return g_DataObjectAccessSystem.CreateDataObject( type, this );
  217. }
  218. void CBaseEntity::DestroyDataObject( int type )
  219. {
  220. Assert( type >= 0 && type < NUM_DATAOBJECT_TYPES );
  221. if ( !HasDataObjectType( type ) )
  222. return;
  223. g_DataObjectAccessSystem.DestroyDataObject( type, this );
  224. RemoveDataObjectType( type );
  225. }
  226. void CWatcherList::Init()
  227. {
  228. m_list = g_WatcherList.CreateList();
  229. }
  230. CWatcherList::~CWatcherList()
  231. {
  232. g_WatcherList.DestroyList( m_list );
  233. }
  234. int CWatcherList::GetCallbackObjects( IWatcherCallback **pList, int listMax )
  235. {
  236. int index = 0;
  237. unsigned short next = g_WatcherList.InvalidIndex();
  238. for ( unsigned short node = g_WatcherList.Head( m_list ); node != g_WatcherList.InvalidIndex(); node = next )
  239. {
  240. next = g_WatcherList.Next( node );
  241. watcher_t *pNode = &g_WatcherList.Element(node);
  242. if ( pNode->hWatcher.Get() )
  243. {
  244. pList[index] = pNode->pWatcherCallback;
  245. index++;
  246. if ( index >= listMax )
  247. {
  248. Assert(0);
  249. return index;
  250. }
  251. }
  252. else
  253. {
  254. g_WatcherList.Remove( m_list, node );
  255. }
  256. }
  257. return index;
  258. }
  259. void CWatcherList::NotifyPositionChanged( CBaseEntity *pEntity )
  260. {
  261. IWatcherCallback *pCallbacks[1024]; // HACKHACK: Assumes this list is big enough
  262. int count = GetCallbackObjects( pCallbacks, ARRAYSIZE(pCallbacks) );
  263. for ( int i = 0; i < count; i++ )
  264. {
  265. IPositionWatcher *pWatcher = assert_cast<IPositionWatcher *>(pCallbacks[i]);
  266. if ( pWatcher )
  267. {
  268. pWatcher->NotifyPositionChanged(pEntity);
  269. }
  270. }
  271. }
  272. void CWatcherList::NotifyVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
  273. {
  274. IWatcherCallback *pCallbacks[1024]; // HACKHACK: Assumes this list is big enough!
  275. int count = GetCallbackObjects( pCallbacks, ARRAYSIZE(pCallbacks) );
  276. for ( int i = 0; i < count; i++ )
  277. {
  278. IVPhysicsWatcher *pWatcher = assert_cast<IVPhysicsWatcher *>(pCallbacks[i]);
  279. if ( pWatcher )
  280. {
  281. pWatcher->NotifyVPhysicsStateChanged(pPhysics, pEntity, bAwake);
  282. }
  283. }
  284. }
  285. unsigned short CWatcherList::Find( CBaseEntity *pEntity )
  286. {
  287. unsigned short next = g_WatcherList.InvalidIndex();
  288. for ( unsigned short node = g_WatcherList.Head( m_list ); node != g_WatcherList.InvalidIndex(); node = next )
  289. {
  290. next = g_WatcherList.Next( node );
  291. watcher_t *pNode = &g_WatcherList.Element(node);
  292. if ( pNode->hWatcher.Get() == pEntity )
  293. {
  294. return node;
  295. }
  296. }
  297. return g_WatcherList.InvalidIndex();
  298. }
  299. void CWatcherList::RemoveWatcher( CBaseEntity *pEntity )
  300. {
  301. unsigned short node = Find( pEntity );
  302. if ( node != g_WatcherList.InvalidIndex() )
  303. {
  304. g_WatcherList.Remove( m_list, node );
  305. }
  306. }
  307. void CWatcherList::AddToList( CBaseEntity *pWatcher )
  308. {
  309. unsigned short node = Find( pWatcher );
  310. if ( node == g_WatcherList.InvalidIndex() )
  311. {
  312. watcher_t watcher;
  313. watcher.hWatcher = pWatcher;
  314. // save this separately so we can use the EHANDLE to test for deletion
  315. watcher.pWatcherCallback = dynamic_cast<IWatcherCallback *> (pWatcher);
  316. if ( watcher.pWatcherCallback )
  317. {
  318. g_WatcherList.AddToTail( m_list, watcher );
  319. }
  320. }
  321. }
  322. static void AddWatcherToEntity( CBaseEntity *pWatcher, CBaseEntity *pEntity, int watcherType )
  323. {
  324. CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(watcherType);
  325. if ( !pList )
  326. {
  327. pList = ( CWatcherList * )pEntity->CreateDataObject( watcherType );
  328. pList->Init();
  329. }
  330. pList->AddToList( pWatcher );
  331. }
  332. static void RemoveWatcherFromEntity( CBaseEntity *pWatcher, CBaseEntity *pEntity, int watcherType )
  333. {
  334. CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(watcherType);
  335. if ( pList )
  336. {
  337. pList->RemoveWatcher( pWatcher );
  338. }
  339. }
  340. void WatchPositionChanges( CBaseEntity *pWatcher, CBaseEntity *pMovingEntity )
  341. {
  342. AddWatcherToEntity( pWatcher, pMovingEntity, POSITIONWATCHER );
  343. }
  344. void RemovePositionWatcher( CBaseEntity *pWatcher, CBaseEntity *pMovingEntity )
  345. {
  346. RemoveWatcherFromEntity( pWatcher, pMovingEntity, POSITIONWATCHER );
  347. }
  348. void ReportPositionChanged( CBaseEntity *pMovedEntity )
  349. {
  350. CWatcherList *pList = (CWatcherList *)pMovedEntity->GetDataObject(POSITIONWATCHER);
  351. if ( pList )
  352. {
  353. pList->NotifyPositionChanged( pMovedEntity );
  354. }
  355. }
  356. void WatchVPhysicsStateChanges( CBaseEntity *pWatcher, CBaseEntity *pPhysicsEntity )
  357. {
  358. AddWatcherToEntity( pWatcher, pPhysicsEntity, VPHYSICSWATCHER );
  359. }
  360. void RemoveVPhysicsStateWatcher( CBaseEntity *pWatcher, CBaseEntity *pPhysicsEntity )
  361. {
  362. AddWatcherToEntity( pWatcher, pPhysicsEntity, VPHYSICSWATCHER );
  363. }
  364. void ReportVPhysicsStateChanged( IPhysicsObject *pPhysics, CBaseEntity *pEntity, bool bAwake )
  365. {
  366. CWatcherList *pList = (CWatcherList *)pEntity->GetDataObject(VPHYSICSWATCHER);
  367. if ( pList )
  368. {
  369. pList->NotifyVPhysicsStateChanged( pPhysics, pEntity, bAwake );
  370. }
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. void CBaseEntity::DestroyAllDataObjects( void )
  376. {
  377. int i;
  378. for ( i = 0; i < NUM_DATAOBJECT_TYPES; i++ )
  379. {
  380. if ( HasDataObjectType( i ) )
  381. {
  382. DestroyDataObject( i );
  383. }
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. // For debugging
  388. //-----------------------------------------------------------------------------
  389. #ifdef GAME_DLL
  390. void SpewLinks()
  391. {
  392. int nCount = 0;
  393. for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) )
  394. {
  395. if ( pClass /*&& !pClass->IsDormant()*/ )
  396. {
  397. touchlink_t *root = ( touchlink_t * )pClass->GetDataObject( TOUCHLINK );
  398. if ( root )
  399. {
  400. // check if the edict is already in the list
  401. for ( touchlink_t *link = root->nextLink; link != root; link = link->nextLink )
  402. {
  403. ++nCount;
  404. Msg("[%d] (%d) Link %d (%s) -> %d (%s)\n", nCount, pClass->IsDormant(),
  405. pClass->entindex(), pClass->GetClassname(),
  406. link->entityTouched->entindex(), link->entityTouched->GetClassname() );
  407. }
  408. }
  409. }
  410. }
  411. }
  412. #endif
  413. //-----------------------------------------------------------------------------
  414. // Returns the actual gravity
  415. //-----------------------------------------------------------------------------
  416. static inline float GetActualGravity( CBaseEntity *pEnt )
  417. {
  418. float ent_gravity = pEnt->GetGravity();
  419. if ( ent_gravity == 0.0f )
  420. {
  421. ent_gravity = 1.0f;
  422. }
  423. return ent_gravity * sv_gravity.GetFloat();
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose:
  427. // Output : inline touchlink_t
  428. //-----------------------------------------------------------------------------
  429. inline touchlink_t *AllocTouchLink( void )
  430. {
  431. touchlink_t *link = (touchlink_t*)g_EdictTouchLinks.Alloc( sizeof(touchlink_t) );
  432. if ( link )
  433. {
  434. ++linksallocated;
  435. }
  436. else
  437. {
  438. DevWarning( "AllocTouchLink: failed to allocate touchlink_t.\n" );
  439. }
  440. return link;
  441. }
  442. static touchlink_t *g_pNextLink = NULL;
  443. //-----------------------------------------------------------------------------
  444. // Purpose:
  445. // Input : *link -
  446. // Output : inline void
  447. //-----------------------------------------------------------------------------
  448. inline void FreeTouchLink( touchlink_t *link )
  449. {
  450. if ( link )
  451. {
  452. if ( link == g_pNextLink )
  453. {
  454. g_pNextLink = link->nextLink;
  455. }
  456. --linksallocated;
  457. link->prevLink = link->nextLink = NULL;
  458. }
  459. // Necessary to catch crashes
  460. g_EdictTouchLinks.Free( link );
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose:
  464. // Output : inline groundlink_t
  465. //-----------------------------------------------------------------------------
  466. inline groundlink_t *AllocGroundLink( void )
  467. {
  468. groundlink_t *link = (groundlink_t*)g_EntityGroundLinks.Alloc( sizeof(groundlink_t) );
  469. if ( link )
  470. {
  471. ++groundlinksallocated;
  472. }
  473. else
  474. {
  475. DevMsg( "AllocGroundLink: failed to allocate groundlink_t.!!!\n" );
  476. }
  477. return link;
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose:
  481. // Input : *link -
  482. // Output : inline void
  483. //-----------------------------------------------------------------------------
  484. inline void FreeGroundLink( groundlink_t *link )
  485. {
  486. if ( link )
  487. {
  488. --groundlinksallocated;
  489. }
  490. g_EntityGroundLinks.Free( link );
  491. }
  492. //-----------------------------------------------------------------------------
  493. // Purpose:
  494. // Output : Returns true on success, false on failure.
  495. //-----------------------------------------------------------------------------
  496. bool CBaseEntity::IsCurrentlyTouching( void ) const
  497. {
  498. if ( HasDataObjectType( TOUCHLINK ) )
  499. {
  500. return true;
  501. }
  502. return false;
  503. }
  504. static bool g_bCleanupDatObject = true;
  505. //-----------------------------------------------------------------------------
  506. // Purpose: Checks to see if any entities that have been touching this one
  507. // have stopped touching it, and notify the entity if so.
  508. // Called at the end of a frame, after all the entities have run
  509. //-----------------------------------------------------------------------------
  510. void CBaseEntity::PhysicsCheckForEntityUntouch( void )
  511. {
  512. Assert( g_pNextLink == NULL );
  513. touchlink_t *link;
  514. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  515. if ( root )
  516. {
  517. #ifdef PORTAL
  518. CPortalTouchScope scope;
  519. #endif
  520. bool saveCleanup = g_bCleanupDatObject;
  521. g_bCleanupDatObject = false;
  522. link = root->nextLink;
  523. while ( link != root )
  524. {
  525. g_pNextLink = link->nextLink;
  526. // these touchlinks are not polled. The ents are touching due to an outside
  527. // system that will add/delete them as necessary (vphysics in this case)
  528. if ( link->touchStamp == TOUCHSTAMP_EVENT_DRIVEN )
  529. {
  530. // refresh the touch call
  531. PhysicsTouch( link->entityTouched );
  532. }
  533. else
  534. {
  535. // check to see if the touch stamp is up to date
  536. if ( link->touchStamp != touchStamp )
  537. {
  538. // stamp is out of data, so entities are no longer touching
  539. // remove self from other entities touch list
  540. PhysicsNotifyOtherOfUntouch( this, link->entityTouched );
  541. // remove other entity from this list
  542. PhysicsRemoveToucher( this, link );
  543. }
  544. }
  545. link = g_pNextLink;
  546. }
  547. g_bCleanupDatObject = saveCleanup;
  548. // Nothing left in list, destroy root
  549. if ( root->nextLink == root &&
  550. root->prevLink == root )
  551. {
  552. DestroyDataObject( TOUCHLINK );
  553. }
  554. }
  555. g_pNextLink = NULL;
  556. SetCheckUntouch( false );
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Purpose: notifies an entity than another touching entity has moved out of contact.
  560. // Input : *other - the entity to be acted upon
  561. //-----------------------------------------------------------------------------
  562. void CBaseEntity::PhysicsNotifyOtherOfUntouch( CBaseEntity *ent, CBaseEntity *other )
  563. {
  564. if ( !other )
  565. return;
  566. // loop through ed's touch list, looking for the notifier
  567. // remove and call untouch if found
  568. touchlink_t *root = ( touchlink_t * )other->GetDataObject( TOUCHLINK );
  569. if ( root )
  570. {
  571. touchlink_t *link = root->nextLink;
  572. while ( link != root )
  573. {
  574. if ( link->entityTouched == ent )
  575. {
  576. PhysicsRemoveToucher( other, link );
  577. // Check for complete removal
  578. if ( g_bCleanupDatObject &&
  579. root->nextLink == root &&
  580. root->prevLink == root )
  581. {
  582. other->DestroyDataObject( TOUCHLINK );
  583. }
  584. return;
  585. }
  586. link = link->nextLink;
  587. }
  588. }
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose: removes a toucher from the list
  592. // Input : *link - the link to remove
  593. //-----------------------------------------------------------------------------
  594. void CBaseEntity::PhysicsRemoveToucher( CBaseEntity *otherEntity, touchlink_t *link )
  595. {
  596. // Every start Touch gets a corresponding end touch
  597. if ( (link->flags & FTOUCHLINK_START_TOUCH) &&
  598. link->entityTouched != NULL &&
  599. otherEntity != NULL )
  600. {
  601. otherEntity->EndTouch( link->entityTouched );
  602. }
  603. link->nextLink->prevLink = link->prevLink;
  604. link->prevLink->nextLink = link->nextLink;
  605. if ( DebugTouchlinks() )
  606. Msg( "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, link->entityTouched->GetDebugName(), otherEntity->GetDebugName(), link->entityTouched->entindex(), otherEntity->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
  607. FreeTouchLink( link );
  608. }
  609. //-----------------------------------------------------------------------------
  610. // Purpose: Clears all touches from the list
  611. //-----------------------------------------------------------------------------
  612. void CBaseEntity::PhysicsRemoveTouchedList( CBaseEntity *ent )
  613. {
  614. #ifdef PORTAL
  615. CPortalTouchScope scope;
  616. #endif
  617. touchlink_t *link, *nextLink;
  618. touchlink_t *root = ( touchlink_t * )ent->GetDataObject( TOUCHLINK );
  619. if ( root )
  620. {
  621. link = root->nextLink;
  622. bool saveCleanup = g_bCleanupDatObject;
  623. g_bCleanupDatObject = false;
  624. while ( link && link != root )
  625. {
  626. nextLink = link->nextLink;
  627. // notify the other entity that this ent has gone away
  628. PhysicsNotifyOtherOfUntouch( ent, link->entityTouched );
  629. // kill it
  630. if ( DebugTouchlinks() )
  631. Msg( "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, ent->GetDebugName(), link->entityTouched->GetDebugName(), ent->entindex(), link->entityTouched->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
  632. FreeTouchLink( link );
  633. link = nextLink;
  634. }
  635. g_bCleanupDatObject = saveCleanup;
  636. ent->DestroyDataObject( TOUCHLINK );
  637. }
  638. ent->touchStamp = 0;
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose:
  642. // Input : *other -
  643. // Output : groundlink_t
  644. //-----------------------------------------------------------------------------
  645. groundlink_t *CBaseEntity::AddEntityToGroundList( CBaseEntity *other )
  646. {
  647. groundlink_t *link;
  648. if ( this == other )
  649. return NULL;
  650. if ( other->IsMarkedForDeletion() )
  651. {
  652. return NULL;
  653. }
  654. // check if the edict is already in the list
  655. groundlink_t *root = ( groundlink_t * )GetDataObject( GROUNDLINK );
  656. if ( root )
  657. {
  658. for ( link = root->nextLink; link != root; link = link->nextLink )
  659. {
  660. if ( link->entity == other )
  661. {
  662. // no more to do
  663. return link;
  664. }
  665. }
  666. }
  667. else
  668. {
  669. root = ( groundlink_t * )CreateDataObject( GROUNDLINK );
  670. root->prevLink = root->nextLink = root;
  671. }
  672. // entity is not in list, so it's a new touch
  673. // add it to the touched list and then call the touch function
  674. // build new link
  675. link = AllocGroundLink();
  676. if ( !link )
  677. return NULL;
  678. link->entity = other;
  679. // add it to the list
  680. link->nextLink = root->nextLink;
  681. link->prevLink = root;
  682. link->prevLink->nextLink = link;
  683. link->nextLink->prevLink = link;
  684. PhysicsStartGroundContact( other );
  685. return link;
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Purpose: Called whenever two entities come in contact
  689. // Input : *pentOther - the entity who it has touched
  690. //-----------------------------------------------------------------------------
  691. void CBaseEntity::PhysicsStartGroundContact( CBaseEntity *pentOther )
  692. {
  693. if ( !pentOther )
  694. return;
  695. if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
  696. {
  697. pentOther->StartGroundContact( this );
  698. }
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose: notifies an entity than another touching entity has moved out of contact.
  702. // Input : *other - the entity to be acted upon
  703. //-----------------------------------------------------------------------------
  704. void CBaseEntity::PhysicsNotifyOtherOfGroundRemoval( CBaseEntity *ent, CBaseEntity *other )
  705. {
  706. if ( !other )
  707. return;
  708. // loop through ed's touch list, looking for the notifier
  709. // remove and call untouch if found
  710. groundlink_t *root = ( groundlink_t * )other->GetDataObject( GROUNDLINK );
  711. if ( root )
  712. {
  713. groundlink_t *link = root->nextLink;
  714. while ( link != root )
  715. {
  716. if ( link->entity == ent )
  717. {
  718. PhysicsRemoveGround( other, link );
  719. if ( root->nextLink == root &&
  720. root->prevLink == root )
  721. {
  722. other->DestroyDataObject( GROUNDLINK );
  723. }
  724. return;
  725. }
  726. link = link->nextLink;
  727. }
  728. }
  729. }
  730. //-----------------------------------------------------------------------------
  731. // Purpose: removes a toucher from the list
  732. // Input : *link - the link to remove
  733. //-----------------------------------------------------------------------------
  734. void CBaseEntity::PhysicsRemoveGround( CBaseEntity *other, groundlink_t *link )
  735. {
  736. // Every start Touch gets a corresponding end touch
  737. if ( link->entity != NULL )
  738. {
  739. CBaseEntity *linkEntity = link->entity;
  740. CBaseEntity *otherEntity = other;
  741. if ( linkEntity && otherEntity )
  742. {
  743. linkEntity->EndGroundContact( otherEntity );
  744. }
  745. }
  746. link->nextLink->prevLink = link->prevLink;
  747. link->prevLink->nextLink = link->nextLink;
  748. FreeGroundLink( link );
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Purpose: static method to remove ground list for an entity
  752. // Input : *ent -
  753. //-----------------------------------------------------------------------------
  754. void CBaseEntity::PhysicsRemoveGroundList( CBaseEntity *ent )
  755. {
  756. groundlink_t *link, *nextLink;
  757. groundlink_t *root = ( groundlink_t * )ent->GetDataObject( GROUNDLINK );
  758. if ( root )
  759. {
  760. link = root->nextLink;
  761. while ( link && link != root )
  762. {
  763. nextLink = link->nextLink;
  764. // notify the other entity that this ent has gone away
  765. PhysicsNotifyOtherOfGroundRemoval( ent, link->entity );
  766. // kill it
  767. FreeGroundLink( link );
  768. link = nextLink;
  769. }
  770. ent->DestroyDataObject( GROUNDLINK );
  771. }
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose: Called every frame that two entities are touching
  775. // Input : *pentOther - the entity who it has touched
  776. //-----------------------------------------------------------------------------
  777. void CBaseEntity::PhysicsTouch( CBaseEntity *pentOther )
  778. {
  779. if ( pentOther )
  780. {
  781. if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
  782. {
  783. Touch( pentOther );
  784. }
  785. }
  786. }
  787. //-----------------------------------------------------------------------------
  788. // Purpose: Called whenever two entities come in contact
  789. // Input : *pentOther - the entity who it has touched
  790. //-----------------------------------------------------------------------------
  791. void CBaseEntity::PhysicsStartTouch( CBaseEntity *pentOther )
  792. {
  793. if ( pentOther )
  794. {
  795. if ( !(IsMarkedForDeletion() || pentOther->IsMarkedForDeletion()) )
  796. {
  797. StartTouch( pentOther );
  798. Touch( pentOther );
  799. }
  800. }
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Purpose: Marks in an entity that it is touching another entity, and calls
  804. // it's Touch() function if it is a new touch.
  805. // Stamps the touch link with the new time so that when we check for
  806. // untouch we know things haven't changed.
  807. // Input : *other - entity that it is in contact with
  808. //-----------------------------------------------------------------------------
  809. touchlink_t *CBaseEntity::PhysicsMarkEntityAsTouched( CBaseEntity *other )
  810. {
  811. touchlink_t *link;
  812. if ( this == other )
  813. return NULL;
  814. // Entities in hierarchy should not interact
  815. if ( (this->GetMoveParent() == other) || (this == other->GetMoveParent()) )
  816. return NULL;
  817. // check if either entity doesn't generate touch functions
  818. if ( (GetFlags() | other->GetFlags()) & FL_DONTTOUCH )
  819. return NULL;
  820. // Pure triggers should not touch each other
  821. if ( IsSolidFlagSet( FSOLID_TRIGGER ) && other->IsSolidFlagSet( FSOLID_TRIGGER ) )
  822. {
  823. if (!IsSolid() && !other->IsSolid())
  824. return NULL;
  825. }
  826. // Don't do touching if marked for deletion
  827. if ( other->IsMarkedForDeletion() )
  828. {
  829. return NULL;
  830. }
  831. if ( IsMarkedForDeletion() )
  832. {
  833. return NULL;
  834. }
  835. #ifdef PORTAL
  836. CPortalTouchScope scope;
  837. #endif
  838. // check if the edict is already in the list
  839. touchlink_t *root = ( touchlink_t * )GetDataObject( TOUCHLINK );
  840. if ( root )
  841. {
  842. for ( link = root->nextLink; link != root; link = link->nextLink )
  843. {
  844. if ( link->entityTouched == other )
  845. {
  846. // update stamp
  847. link->touchStamp = touchStamp;
  848. if ( !CBaseEntity::sm_bDisableTouchFuncs )
  849. {
  850. PhysicsTouch( other );
  851. }
  852. // no more to do
  853. return link;
  854. }
  855. }
  856. }
  857. else
  858. {
  859. // Allocate the root object
  860. root = ( touchlink_t * )CreateDataObject( TOUCHLINK );
  861. root->nextLink = root->prevLink = root;
  862. }
  863. // entity is not in list, so it's a new touch
  864. // add it to the touched list and then call the touch function
  865. // build new link
  866. link = AllocTouchLink();
  867. if ( DebugTouchlinks() )
  868. Msg( "add 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n", link, GetDebugName(), other->GetDebugName(), entindex(), other->entindex(), linksallocated, g_EdictTouchLinks.PeakCount() );
  869. if ( !link )
  870. return NULL;
  871. link->touchStamp = touchStamp;
  872. link->entityTouched = other;
  873. link->flags = 0;
  874. // add it to the list
  875. link->nextLink = root->nextLink;
  876. link->prevLink = root;
  877. link->prevLink->nextLink = link;
  878. link->nextLink->prevLink = link;
  879. // non-solid entities don't get touched
  880. bool bShouldTouch = (IsSolid() && !IsSolidFlagSet(FSOLID_VOLUME_CONTENTS)) || IsSolidFlagSet(FSOLID_TRIGGER);
  881. if ( bShouldTouch && !other->IsSolidFlagSet(FSOLID_TRIGGER) )
  882. {
  883. link->flags |= FTOUCHLINK_START_TOUCH;
  884. if ( !CBaseEntity::sm_bDisableTouchFuncs )
  885. {
  886. PhysicsStartTouch( other );
  887. }
  888. }
  889. return link;
  890. }
  891. static trace_t g_TouchTrace;
  892. const trace_t &CBaseEntity::GetTouchTrace( void )
  893. {
  894. return g_TouchTrace;
  895. }
  896. //-----------------------------------------------------------------------------
  897. // Purpose: Marks the fact that two edicts are in contact
  898. // Input : *other - other entity
  899. //-----------------------------------------------------------------------------
  900. void CBaseEntity::PhysicsMarkEntitiesAsTouching( CBaseEntity *other, trace_t &trace )
  901. {
  902. g_TouchTrace = trace;
  903. touchlink_t *pLink0 = PhysicsMarkEntityAsTouched( other );
  904. touchlink_t *pLInk1 = other->PhysicsMarkEntityAsTouched( this );
  905. if ( pLink0 && !pLInk1 )
  906. {
  907. PhysicsNotifyOtherOfUntouch( other, this );
  908. }
  909. if ( pLInk1 && !pLink0 )
  910. {
  911. PhysicsNotifyOtherOfUntouch( this, other );
  912. }
  913. UTIL_ClearTrace( g_TouchTrace );
  914. }
  915. void CBaseEntity::PhysicsMarkEntitiesAsTouchingEventDriven( CBaseEntity *other, trace_t &trace )
  916. {
  917. g_TouchTrace = trace;
  918. g_TouchTrace.m_pEnt = other;
  919. touchlink_t *link;
  920. link = this->PhysicsMarkEntityAsTouched( other );
  921. if ( link )
  922. {
  923. // mark these links as event driven so they aren't untouched the next frame
  924. // when the physics doesn't refresh them
  925. link->touchStamp = TOUCHSTAMP_EVENT_DRIVEN;
  926. }
  927. g_TouchTrace.m_pEnt = this;
  928. link = other->PhysicsMarkEntityAsTouched( this );
  929. if ( link )
  930. {
  931. link->touchStamp = TOUCHSTAMP_EVENT_DRIVEN;
  932. }
  933. UTIL_ClearTrace( g_TouchTrace );
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Purpose: Two entities have touched, so run their touch functions
  937. // Input : *other -
  938. // *ptrace -
  939. //-----------------------------------------------------------------------------
  940. void CBaseEntity::PhysicsImpact( CBaseEntity *other, trace_t &trace )
  941. {
  942. if ( !other )
  943. {
  944. return;
  945. }
  946. // If either of the entities is flagged to be deleted,
  947. // don't call the touch functions
  948. if ( ( GetFlags() | other->GetFlags() ) & FL_KILLME )
  949. {
  950. return;
  951. }
  952. PhysicsMarkEntitiesAsTouching( other, trace );
  953. }
  954. //-----------------------------------------------------------------------------
  955. // Purpose: Returns the mask of what is solid for the given entity
  956. // Output : unsigned int
  957. //-----------------------------------------------------------------------------
  958. unsigned int CBaseEntity::PhysicsSolidMaskForEntity( void ) const
  959. {
  960. return MASK_SOLID;
  961. }
  962. static inline int GetWaterContents( const Vector &point )
  963. {
  964. #ifdef HL2_DLL
  965. return UTIL_PointContents(point, MASK_WATER);
  966. #else
  967. // left 4 dead doesn't support moveable water brushes, only world water
  968. return enginetrace->GetPointContents_WorldOnly(point, MASK_WATER);
  969. #endif
  970. }
  971. //-----------------------------------------------------------------------------
  972. // Computes the water level + type
  973. //-----------------------------------------------------------------------------
  974. void CBaseEntity::UpdateWaterState()
  975. {
  976. // FIXME: This computation is nonsensical for rigid child attachments
  977. // Should we just grab the type + level of the parent?
  978. // Probably for rigid children anyways...
  979. // Compute the point to check for water state
  980. Vector point;
  981. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &point );
  982. SetWaterLevel( 0 );
  983. SetWaterType( CONTENTS_EMPTY );
  984. int cont = GetWaterContents(point);
  985. if (( cont & MASK_WATER ) == 0)
  986. return;
  987. SetWaterType( cont );
  988. SetWaterLevel( 1 );
  989. // point sized entities are always fully submerged
  990. if ( IsPointSized() )
  991. {
  992. SetWaterLevel( 3 );
  993. }
  994. else
  995. {
  996. // Check the exact center of the box
  997. point[2] = WorldSpaceCenter().z;
  998. int midcont = GetWaterContents(point);
  999. if ( midcont & MASK_WATER )
  1000. {
  1001. // Now check where the eyes are...
  1002. SetWaterLevel( 2 );
  1003. point[2] = EyePosition().z;
  1004. int eyecont = GetWaterContents(point);
  1005. if ( eyecont & MASK_WATER )
  1006. {
  1007. SetWaterLevel( 3 );
  1008. }
  1009. }
  1010. }
  1011. }
  1012. //-----------------------------------------------------------------------------
  1013. // Purpose: Check if entity is in the water and applies any current to velocity
  1014. // and sets appropriate water flags
  1015. // Output : Returns true on success, false on failure.
  1016. //-----------------------------------------------------------------------------
  1017. bool CBaseEntity::PhysicsCheckWater( void )
  1018. {
  1019. return GetWaterLevel() > WL_Feet;
  1020. }
  1021. //-----------------------------------------------------------------------------
  1022. // Purpose: Bounds velocity
  1023. //-----------------------------------------------------------------------------
  1024. void CBaseEntity::PhysicsCheckVelocity( void )
  1025. {
  1026. Vector origin = GetAbsOrigin();
  1027. Vector vecAbsVelocity = GetAbsVelocity();
  1028. bool bReset = false;
  1029. for ( int i=0 ; i<3 ; i++ )
  1030. {
  1031. if ( IS_NAN(vecAbsVelocity[i]) )
  1032. {
  1033. Msg( "Got a NaN velocity on %s\n", GetClassname() );
  1034. vecAbsVelocity[i] = 0;
  1035. bReset = true;
  1036. }
  1037. if ( IS_NAN(origin[i]) )
  1038. {
  1039. Msg( "Got a NaN origin on %s\n", GetClassname() );
  1040. origin[i] = 0;
  1041. bReset = true;
  1042. }
  1043. if ( vecAbsVelocity[i] > sv_maxvelocity.GetFloat() )
  1044. {
  1045. #ifdef _DEBUG
  1046. DevWarning( 2, "Got a velocity too high on %s\n", GetClassname() );
  1047. #endif
  1048. vecAbsVelocity[i] = sv_maxvelocity.GetFloat();
  1049. bReset = true;
  1050. }
  1051. else if ( vecAbsVelocity[i] < -sv_maxvelocity.GetFloat() )
  1052. {
  1053. #ifdef _DEBUG
  1054. DevWarning( 2, "Got a velocity too low on %s\n", GetClassname() );
  1055. #endif
  1056. vecAbsVelocity[i] = -sv_maxvelocity.GetFloat();
  1057. bReset = true;
  1058. }
  1059. }
  1060. if (bReset)
  1061. {
  1062. SetAbsOrigin( origin );
  1063. SetAbsVelocity( vecAbsVelocity );
  1064. }
  1065. }
  1066. //-----------------------------------------------------------------------------
  1067. // Purpose: Applies gravity to falling objects
  1068. //-----------------------------------------------------------------------------
  1069. void CBaseEntity::PhysicsAddGravityMove( Vector &move )
  1070. {
  1071. Vector vecAbsVelocity = GetAbsVelocity();
  1072. move.x = (vecAbsVelocity.x + GetBaseVelocity().x ) * gpGlobals->frametime;
  1073. move.y = (vecAbsVelocity.y + GetBaseVelocity().y ) * gpGlobals->frametime;
  1074. if ( GetFlags() & FL_ONGROUND )
  1075. {
  1076. move.z = GetBaseVelocity().z * gpGlobals->frametime;
  1077. return;
  1078. }
  1079. // linear acceleration due to gravity
  1080. float newZVelocity = vecAbsVelocity.z - GetActualGravity( this ) * gpGlobals->frametime;
  1081. move.z = ((vecAbsVelocity.z + newZVelocity) / 2.0 + GetBaseVelocity().z ) * gpGlobals->frametime;
  1082. Vector vecBaseVelocity = GetBaseVelocity();
  1083. vecBaseVelocity.z = 0.0f;
  1084. SetBaseVelocity( vecBaseVelocity );
  1085. vecAbsVelocity.z = newZVelocity;
  1086. SetAbsVelocity( vecAbsVelocity );
  1087. // Bound velocity
  1088. PhysicsCheckVelocity();
  1089. }
  1090. #define STOP_EPSILON 0.1
  1091. //-----------------------------------------------------------------------------
  1092. // Purpose: Slide off of the impacting object. Returns the blocked flags (1 = floor, 2 = step / wall)
  1093. // Input : in -
  1094. // normal -
  1095. // out -
  1096. // overbounce -
  1097. // Output : int
  1098. //-----------------------------------------------------------------------------
  1099. int CBaseEntity::PhysicsClipVelocity( const Vector& in, const Vector& normal, Vector& out, float overbounce )
  1100. {
  1101. float backoff;
  1102. float change;
  1103. float angle;
  1104. int i, blocked;
  1105. blocked = 0;
  1106. angle = normal[ 2 ];
  1107. if ( angle > 0 )
  1108. {
  1109. blocked |= 1; // floor
  1110. }
  1111. if ( !angle )
  1112. {
  1113. blocked |= 2; // step
  1114. }
  1115. backoff = DotProduct (in, normal) * overbounce;
  1116. for ( i=0 ; i<3 ; i++ )
  1117. {
  1118. change = normal[i]*backoff;
  1119. out[i] = in[i] - change;
  1120. if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
  1121. {
  1122. out[i] = 0;
  1123. }
  1124. }
  1125. return blocked;
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Purpose:
  1129. //-----------------------------------------------------------------------------
  1130. void CBaseEntity::ResolveFlyCollisionBounce( trace_t &trace, Vector &vecVelocity, float flMinTotalElasticity )
  1131. {
  1132. // Get the impact surface's elasticity.
  1133. float flSurfaceElasticity;
  1134. physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, NULL, &flSurfaceElasticity );
  1135. float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
  1136. if ( flMinTotalElasticity > 0.9f )
  1137. {
  1138. flMinTotalElasticity = 0.9f;
  1139. }
  1140. flTotalElasticity = clamp( flTotalElasticity, flMinTotalElasticity, 0.9f );
  1141. // NOTE: A backoff of 2.0f is a reflection
  1142. Vector vecAbsVelocity;
  1143. PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
  1144. vecAbsVelocity *= flTotalElasticity;
  1145. // Get the total velocity (player + conveyors, etc.)
  1146. VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
  1147. float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
  1148. // Stop if on ground.
  1149. if ( trace.plane.normal.z > 0.7f ) // Floor
  1150. {
  1151. // Verify that we have an entity.
  1152. CBaseEntity *pEntity = trace.m_pEnt;
  1153. Assert( pEntity );
  1154. // Are we on the ground?
  1155. if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
  1156. {
  1157. vecAbsVelocity.z = 0.0f;
  1158. // Recompute speedsqr based on the new absvel
  1159. VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
  1160. flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
  1161. }
  1162. SetAbsVelocity( vecAbsVelocity );
  1163. if ( flSpeedSqr < ( 30 * 30 ) )
  1164. {
  1165. if ( pEntity->IsStandable() )
  1166. {
  1167. SetGroundEntity( pEntity );
  1168. }
  1169. // Reset velocities.
  1170. SetAbsVelocity( vec3_origin );
  1171. SetLocalAngularVelocity( vec3_angle );
  1172. }
  1173. else
  1174. {
  1175. Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
  1176. Vector vecBaseDir = GetBaseVelocity();
  1177. VectorNormalize( vecBaseDir );
  1178. float flScale = vecDelta.Dot( vecBaseDir );
  1179. VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity );
  1180. VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
  1181. PhysicsPushEntity( vecVelocity, &trace );
  1182. }
  1183. }
  1184. else
  1185. {
  1186. // If we get *too* slow, we'll stick without ever coming to rest because
  1187. // we'll get pushed down by gravity faster than we can escape from the wall.
  1188. if ( flSpeedSqr < ( 30 * 30 ) )
  1189. {
  1190. // Reset velocities.
  1191. SetAbsVelocity( vec3_origin );
  1192. SetLocalAngularVelocity( vec3_angle );
  1193. }
  1194. else
  1195. {
  1196. SetAbsVelocity( vecAbsVelocity );
  1197. }
  1198. }
  1199. }
  1200. //-----------------------------------------------------------------------------
  1201. // Purpose:
  1202. //-----------------------------------------------------------------------------
  1203. void CBaseEntity::ResolveFlyCollisionSlide( trace_t &trace, Vector &vecVelocity )
  1204. {
  1205. // Get the impact surface's friction.
  1206. float flSurfaceFriction;
  1207. physprops->GetPhysicsProperties( trace.surface.surfaceProps, NULL, NULL, &flSurfaceFriction, NULL );
  1208. // A backoff of 1.0 is a slide.
  1209. float flBackOff = 1.0f;
  1210. Vector vecAbsVelocity;
  1211. PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, flBackOff );
  1212. if ( trace.plane.normal.z <= 0.7 ) // Floor
  1213. {
  1214. SetAbsVelocity( vecAbsVelocity );
  1215. return;
  1216. }
  1217. // Stop if on ground.
  1218. // Get the total velocity (player + conveyors, etc.)
  1219. VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
  1220. float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
  1221. // Verify that we have an entity.
  1222. CBaseEntity *pEntity = trace.m_pEnt;
  1223. Assert( pEntity );
  1224. // Are we on the ground?
  1225. if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
  1226. {
  1227. vecAbsVelocity.z = 0.0f;
  1228. // Recompute speedsqr based on the new absvel
  1229. VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
  1230. flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
  1231. }
  1232. SetAbsVelocity( vecAbsVelocity );
  1233. if ( flSpeedSqr < ( 30 * 30 ) )
  1234. {
  1235. if ( pEntity->IsStandable() )
  1236. {
  1237. SetGroundEntity( pEntity );
  1238. }
  1239. // Reset velocities.
  1240. SetAbsVelocity( vec3_origin );
  1241. SetLocalAngularVelocity( vec3_angle );
  1242. }
  1243. else
  1244. {
  1245. vecAbsVelocity += GetBaseVelocity();
  1246. vecAbsVelocity *= ( 1.0f - trace.fraction ) * gpGlobals->frametime * flSurfaceFriction;
  1247. PhysicsPushEntity( vecAbsVelocity, &trace );
  1248. }
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Purpose:
  1252. //-----------------------------------------------------------------------------
  1253. void CBaseEntity::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
  1254. {
  1255. // Stop if on ground.
  1256. if ( trace.plane.normal.z > 0.7 ) // Floor
  1257. {
  1258. // Get the total velocity (player + conveyors, etc.)
  1259. VectorAdd( GetAbsVelocity(), GetBaseVelocity(), vecVelocity );
  1260. // Verify that we have an entity.
  1261. CBaseEntity *pEntity = trace.m_pEnt;
  1262. Assert( pEntity );
  1263. // Are we on the ground?
  1264. if ( vecVelocity.z < ( GetActualGravity( this ) * gpGlobals->frametime ) )
  1265. {
  1266. Vector vecAbsVelocity = GetAbsVelocity();
  1267. vecAbsVelocity.z = 0.0f;
  1268. SetAbsVelocity( vecAbsVelocity );
  1269. }
  1270. if ( pEntity->IsStandable() )
  1271. {
  1272. SetGroundEntity( pEntity );
  1273. }
  1274. }
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. // Performs the collision resolution for fliers.
  1278. //-----------------------------------------------------------------------------
  1279. void CBaseEntity::PerformFlyCollisionResolution( trace_t &trace, Vector &move )
  1280. {
  1281. switch( GetMoveCollide() )
  1282. {
  1283. case MOVECOLLIDE_FLY_CUSTOM:
  1284. {
  1285. ResolveFlyCollisionCustom( trace, move );
  1286. break;
  1287. }
  1288. case MOVECOLLIDE_FLY_BOUNCE:
  1289. {
  1290. ResolveFlyCollisionBounce( trace, move );
  1291. break;
  1292. }
  1293. case MOVECOLLIDE_FLY_SLIDE:
  1294. case MOVECOLLIDE_DEFAULT:
  1295. // NOTE: The default fly collision state is the same as a slide (for backward capatability).
  1296. {
  1297. ResolveFlyCollisionSlide( trace, move );
  1298. break;
  1299. }
  1300. default:
  1301. {
  1302. // Invalid MOVECOLLIDE_<type>
  1303. Assert( 0 );
  1304. break;
  1305. }
  1306. }
  1307. }
  1308. //-----------------------------------------------------------------------------
  1309. // Purpose: Checks if an object has passed into or out of water and sets water info, alters velocity, plays splash sounds, etc.
  1310. //-----------------------------------------------------------------------------
  1311. void CBaseEntity::PhysicsCheckWaterTransition( void )
  1312. {
  1313. int oldcont = GetWaterType();
  1314. UpdateWaterState();
  1315. int cont = GetWaterType();
  1316. // We can exit right out if we're a child... don't bother with this...
  1317. if (GetMoveParent())
  1318. return;
  1319. if ( cont & MASK_WATER )
  1320. {
  1321. if (oldcont == CONTENTS_EMPTY)
  1322. {
  1323. #ifndef CLIENT_DLL
  1324. Splash();
  1325. #endif // !CLIENT_DLL
  1326. // just crossed into water
  1327. EmitSound( "BaseEntity.EnterWater" );
  1328. if ( !IsEFlagSet( EFL_NO_WATER_VELOCITY_CHANGE ) )
  1329. {
  1330. Vector vecAbsVelocity = GetAbsVelocity();
  1331. vecAbsVelocity[2] *= 0.5;
  1332. SetAbsVelocity( vecAbsVelocity );
  1333. }
  1334. }
  1335. }
  1336. else
  1337. {
  1338. if ( oldcont != CONTENTS_EMPTY )
  1339. {
  1340. // just crossed out of water
  1341. EmitSound( "BaseEntity.ExitWater" );
  1342. }
  1343. }
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // Computes new angles based on the angular velocity
  1347. //-----------------------------------------------------------------------------
  1348. void CBaseEntity::SimulateAngles( float flFrameTime )
  1349. {
  1350. // move angles
  1351. QAngle angles;
  1352. VectorMA ( GetLocalAngles(), flFrameTime, GetLocalAngularVelocity(), angles );
  1353. SetLocalAngles( angles );
  1354. }
  1355. //-----------------------------------------------------------------------------
  1356. // Purpose: Toss, bounce, and fly movement. When onground, do nothing.
  1357. //-----------------------------------------------------------------------------
  1358. void CBaseEntity::PhysicsToss( void )
  1359. {
  1360. trace_t trace;
  1361. Vector move;
  1362. PhysicsCheckWater();
  1363. // regular thinking
  1364. if ( !PhysicsRunThink() )
  1365. return;
  1366. // Moving upward, off the ground, or resting on a client/monster, remove FL_ONGROUND
  1367. if ( GetAbsVelocity()[2] > 0 || !GetGroundEntity() || !GetGroundEntity()->IsStandable() )
  1368. {
  1369. SetGroundEntity( NULL );
  1370. }
  1371. // Check to see if entity is on the ground at rest
  1372. if ( GetFlags() & FL_ONGROUND )
  1373. {
  1374. if ( VectorCompare( GetAbsVelocity(), vec3_origin ) )
  1375. {
  1376. // Clear rotation if not moving (even if on a conveyor)
  1377. SetLocalAngularVelocity( vec3_angle );
  1378. if ( VectorCompare( GetBaseVelocity(), vec3_origin ) )
  1379. return;
  1380. }
  1381. }
  1382. PhysicsCheckVelocity();
  1383. // add gravity
  1384. if ( GetMoveType() == MOVETYPE_FLYGRAVITY && !(GetFlags() & FL_FLY) )
  1385. {
  1386. PhysicsAddGravityMove( move );
  1387. }
  1388. else
  1389. {
  1390. // Base velocity is not properly accounted for since this entity will move again after the bounce without
  1391. // taking it into account
  1392. Vector vecAbsVelocity = GetAbsVelocity();
  1393. vecAbsVelocity += GetBaseVelocity();
  1394. VectorScale( vecAbsVelocity, gpGlobals->frametime, move );
  1395. PhysicsCheckVelocity();
  1396. }
  1397. // move angles
  1398. SimulateAngles( gpGlobals->frametime );
  1399. // move origin
  1400. PhysicsPushEntity( move, &trace );
  1401. if ( VPhysicsGetObject() )
  1402. {
  1403. VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), vec3_angle, true, gpGlobals->frametime );
  1404. }
  1405. PhysicsCheckVelocity();
  1406. /*
  1407. NOTE[pmf]: removed this because grenades can start out inside a player volume (particularly in casual mode).
  1408. Behavior for this is handled properly in the grenade's custom FlyCollisionResolution function
  1409. if (trace.allsolid )
  1410. {
  1411. // entity is trapped in another solid
  1412. SetAbsVelocity(vec3_origin);
  1413. SetLocalAngularVelocity(vec3_angle);
  1414. return;
  1415. }
  1416. */
  1417. #if !defined( CLIENT_DLL )
  1418. if (IsEdictFree())
  1419. return;
  1420. #endif
  1421. if ( debugoverlay && sv_grenade_trajectory.GetInt() && (GetFlags() & FL_GRENADE) )
  1422. {
  1423. QAngle angGrTrajAngles;
  1424. Vector vec3tempOrientation = (trace.endpos - trace.startpos);
  1425. VectorAngles( vec3tempOrientation, angGrTrajAngles );
  1426. float flGrTraThickness = sv_grenade_trajectory_thickness.GetFloat();
  1427. Vector vec3_GrTrajMin = Vector( 0, -flGrTraThickness, -flGrTraThickness );
  1428. Vector vec3_GrTrajMax = Vector( vec3tempOrientation.Length(), flGrTraThickness, flGrTraThickness );
  1429. bool bDotted = ( sv_grenade_trajectory_dash.GetInt() && (fmod( gpGlobals->curtime, 0.1f ) < 0.05f) );
  1430. //extruded "line" is really a box for more visible thickness
  1431. debugoverlay->AddBoxOverlay( trace.startpos, vec3_GrTrajMin, vec3_GrTrajMax, angGrTrajAngles, 0, (bDotted ? 20 : 200), 0, 255, sv_grenade_trajectory_time.GetFloat() );
  1432. //per-bounce box
  1433. if (trace.fraction != 1.0f)
  1434. debugoverlay->AddBoxOverlay( trace.endpos, Vector( -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE, -GRENADE_DEFAULT_SIZE ), Vector( GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE, GRENADE_DEFAULT_SIZE ), QAngle( 0, 0, 0 ), 220, 0, 0, 190, sv_grenade_trajectory_time.GetFloat( ) );
  1435. }
  1436. if (trace.fraction != 1.0f)
  1437. {
  1438. PerformFlyCollisionResolution( trace, move );
  1439. }
  1440. // check for in water
  1441. PhysicsCheckWaterTransition();
  1442. }
  1443. //-----------------------------------------------------------------------------
  1444. // Simulation in local space of rigid children
  1445. //-----------------------------------------------------------------------------
  1446. void CBaseEntity::PhysicsRigidChild( void )
  1447. {
  1448. VPROF("CBaseEntity::PhysicsRigidChild");
  1449. // NOTE: rigidly attached children do simulation in local space
  1450. // Collision impulses will be handled either not at all, or by
  1451. // forwarding the information to the highest move parent
  1452. Vector vecPrevOrigin = GetAbsOrigin();
  1453. // regular thinking
  1454. if ( !PhysicsRunThink() )
  1455. return;
  1456. VPROF_SCOPE_BEGIN("CBaseEntity::PhysicsRigidChild-2");
  1457. #if !defined( CLIENT_DLL )
  1458. // Cause touch functions to be called
  1459. PhysicsTouchTriggers( &vecPrevOrigin );
  1460. // We have to do this regardless owing to hierarchy
  1461. if ( VPhysicsGetObject() )
  1462. {
  1463. int solidType = GetSolid();
  1464. bool bAxisAligned = ( solidType == SOLID_BBOX || solidType == SOLID_NONE ) ? true : false;
  1465. VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), bAxisAligned ? vec3_angle : GetAbsAngles(), true, gpGlobals->frametime );
  1466. }
  1467. #endif
  1468. VPROF_SCOPE_END();
  1469. }
  1470. //-----------------------------------------------------------------------------
  1471. // Computes the base velocity
  1472. //-----------------------------------------------------------------------------
  1473. void CBaseEntity::UpdateBaseVelocity( void )
  1474. {
  1475. #if !defined( CLIENT_DLL )
  1476. if ( GetFlags() & FL_ONGROUND )
  1477. {
  1478. CBaseEntity *groundentity = GetGroundEntity();
  1479. if ( groundentity )
  1480. {
  1481. // On conveyor belt that's moving?
  1482. if ( groundentity->GetFlags() & FL_CONVEYOR )
  1483. {
  1484. Vector vecNewBaseVelocity;
  1485. groundentity->GetGroundVelocityToApply( vecNewBaseVelocity );
  1486. if ( GetFlags() & FL_BASEVELOCITY )
  1487. {
  1488. vecNewBaseVelocity += GetBaseVelocity();
  1489. }
  1490. AddFlag( FL_BASEVELOCITY );
  1491. SetBaseVelocity( vecNewBaseVelocity );
  1492. }
  1493. }
  1494. }
  1495. #endif
  1496. }
  1497. //-----------------------------------------------------------------------------
  1498. // Purpose: Runs a frame of physics for a specific edict (and all it's children)
  1499. // Input : *ent - the thinking edict
  1500. //-----------------------------------------------------------------------------
  1501. void CBaseEntity::PhysicsSimulate( void )
  1502. {
  1503. VPROF( "CBaseEntity::PhysicsSimulate" );
  1504. // NOTE: Players override PhysicsSimulate and drive through their CUserCmds at that point instead of
  1505. // processng through this function call!!! They shouldn't chain to here ever.
  1506. // Make sure not to simulate this guy twice per frame
  1507. if ( !IsPlayerSimulated() && m_nSimulationTick == gpGlobals->tickcount )
  1508. {
  1509. return;
  1510. }
  1511. m_nSimulationTick = gpGlobals->tickcount;
  1512. Assert( !IsPlayer() );
  1513. // If we've got a moveparent, we must simulate that first.
  1514. CBaseEntity *pMoveParent = GetMoveParent();
  1515. if ( (GetMoveType() == MOVETYPE_NONE && !pMoveParent) || (GetMoveType() == MOVETYPE_VPHYSICS ) )
  1516. {
  1517. PhysicsNone();
  1518. return;
  1519. }
  1520. // If ground entity goes away, make sure FL_ONGROUND is valid
  1521. if ( !GetGroundEntity() )
  1522. {
  1523. RemoveFlag( FL_ONGROUND );
  1524. }
  1525. if (pMoveParent)
  1526. {
  1527. VPROF( "CBaseEntity::PhysicsSimulate-MoveParent" );
  1528. pMoveParent->PhysicsSimulate();
  1529. }
  1530. else
  1531. {
  1532. VPROF( "CBaseEntity::PhysicsSimulate-BaseVelocity" );
  1533. UpdateBaseVelocity();
  1534. if ( ((GetFlags() & FL_BASEVELOCITY) == 0) && (GetBaseVelocity() != vec3_origin) )
  1535. {
  1536. // Apply momentum (add in half of the previous frame of velocity first)
  1537. // BUGBUG: This will break with PhysicsStep() because of the timestep difference
  1538. Vector vecAbsVelocity;
  1539. VectorMA( GetAbsVelocity(), 1.0 + (gpGlobals->frametime*0.5), GetBaseVelocity(), vecAbsVelocity );
  1540. SetAbsVelocity( vecAbsVelocity );
  1541. SetBaseVelocity( vec3_origin );
  1542. }
  1543. RemoveFlag( FL_BASEVELOCITY );
  1544. }
  1545. switch( GetMoveType() )
  1546. {
  1547. case MOVETYPE_PUSH:
  1548. {
  1549. VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_PUSH" );
  1550. PhysicsPusher();
  1551. }
  1552. break;
  1553. case MOVETYPE_VPHYSICS:
  1554. {
  1555. }
  1556. break;
  1557. case MOVETYPE_NONE:
  1558. {
  1559. VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_NONE" );
  1560. Assert(pMoveParent);
  1561. PhysicsRigidChild();
  1562. }
  1563. break;
  1564. case MOVETYPE_NOCLIP:
  1565. {
  1566. VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_NOCLIP" );
  1567. PhysicsNoclip();
  1568. }
  1569. break;
  1570. case MOVETYPE_STEP:
  1571. {
  1572. VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_STEP" );
  1573. PhysicsStep();
  1574. }
  1575. break;
  1576. case MOVETYPE_FLY:
  1577. case MOVETYPE_FLYGRAVITY:
  1578. {
  1579. VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_FLY" );
  1580. PhysicsToss();
  1581. }
  1582. break;
  1583. case MOVETYPE_CUSTOM:
  1584. {
  1585. VPROF( "CBaseEntity::PhysicsSimulate-MOVETYPE_CUSTOM" );
  1586. PhysicsCustom();
  1587. }
  1588. break;
  1589. default:
  1590. Warning( "PhysicsSimulate: %s bad movetype %d", GetClassname(), GetMoveType() );
  1591. Assert(0);
  1592. break;
  1593. }
  1594. }
  1595. //-----------------------------------------------------------------------------
  1596. // Purpose: Runs thinking code if time. There is some play in the exact time the think
  1597. // function will be called, because it is called before any movement is done
  1598. // in a frame. Not used for pushmove objects, because they must be exact.
  1599. // Returns false if the entity removed itself.
  1600. // Output : Returns true on success, false on failure.
  1601. //-----------------------------------------------------------------------------
  1602. bool CBaseEntity::PhysicsRunThink( thinkmethods_t thinkMethod )
  1603. {
  1604. if ( IsEFlagSet( EFL_NO_THINK_FUNCTION ) )
  1605. return true;
  1606. bool bAlive = true;
  1607. // Don't fire the base if we're avoiding it
  1608. if ( thinkMethod != THINK_FIRE_ALL_BUT_BASE )
  1609. {
  1610. bAlive = PhysicsRunSpecificThink( -1, &CBaseEntity::Think );
  1611. if ( !bAlive )
  1612. return false;
  1613. }
  1614. // Are we just firing the base think?
  1615. if ( thinkMethod == THINK_FIRE_BASE_ONLY )
  1616. return bAlive;
  1617. // Fire the rest of 'em
  1618. for ( int i = 0; i < m_aThinkFunctions.Count(); i++ )
  1619. {
  1620. #ifdef _DEBUG
  1621. // Set the context
  1622. m_iCurrentThinkContext = i;
  1623. #endif
  1624. bAlive = PhysicsRunSpecificThink( i, m_aThinkFunctions[i].m_pfnThink );
  1625. #ifdef _DEBUG
  1626. // Clear our context
  1627. m_iCurrentThinkContext = NO_THINK_CONTEXT;
  1628. #endif
  1629. if ( !bAlive )
  1630. return false;
  1631. }
  1632. return bAlive;
  1633. }
  1634. //-----------------------------------------------------------------------------
  1635. // Purpose: For testing if all thinks are occuring at the same time
  1636. //-----------------------------------------------------------------------------
  1637. struct ThinkSync
  1638. {
  1639. float thinktime;
  1640. int thinktick;
  1641. CUtlVector< EHANDLE > entities;
  1642. ThinkSync()
  1643. {
  1644. thinktime = 0;
  1645. }
  1646. ThinkSync( const ThinkSync& src )
  1647. {
  1648. thinktime = src.thinktime;
  1649. thinktick = src.thinktick;
  1650. int c = src.entities.Count();
  1651. for ( int i = 0; i < c; i++ )
  1652. {
  1653. entities.AddToTail( src.entities[ i ] );
  1654. }
  1655. }
  1656. };
  1657. #if !defined( CLIENT_DLL )
  1658. static ConVar sv_thinktimecheck( "sv_thinktimecheck", "0", 0, "Check for thinktimes all on same timestamp." );
  1659. #endif
  1660. //-----------------------------------------------------------------------------
  1661. // Purpose: For testing if all thinks are occuring at the same time
  1662. //-----------------------------------------------------------------------------
  1663. class CThinkSyncTester
  1664. {
  1665. public:
  1666. CThinkSyncTester() :
  1667. m_Thinkers( 0, 0, ThinkLessFunc )
  1668. {
  1669. m_nLastFrameCount = -1;
  1670. m_bShouldCheck = false;
  1671. }
  1672. void EntityThinking( int framecount, CBaseEntity *ent, float thinktime, int thinktick )
  1673. {
  1674. #if !defined( CLIENT_DLL )
  1675. if ( m_nLastFrameCount != framecount )
  1676. {
  1677. if ( m_bShouldCheck )
  1678. {
  1679. // Report
  1680. Report();
  1681. m_Thinkers.RemoveAll();
  1682. m_nLastFrameCount = framecount;
  1683. }
  1684. m_bShouldCheck = sv_thinktimecheck.GetBool();
  1685. }
  1686. if ( !m_bShouldCheck )
  1687. return;
  1688. ThinkSync *p = FindOrAddItem( ent, thinktime );
  1689. if ( !p )
  1690. {
  1691. Assert( 0 );
  1692. }
  1693. p->thinktime = thinktime;
  1694. p->thinktick = thinktick;
  1695. EHANDLE h;
  1696. h = ent;
  1697. p->entities.AddToTail( h );
  1698. #endif
  1699. }
  1700. private:
  1701. static bool ThinkLessFunc( const ThinkSync& item1, const ThinkSync& item2 )
  1702. {
  1703. return item1.thinktime < item2.thinktime;
  1704. }
  1705. ThinkSync *FindOrAddItem( CBaseEntity *ent, float thinktime )
  1706. {
  1707. ThinkSync item;
  1708. item.thinktime = thinktime;
  1709. int idx = m_Thinkers.Find( item );
  1710. if ( idx == m_Thinkers.InvalidIndex() )
  1711. {
  1712. idx = m_Thinkers.Insert( item );
  1713. }
  1714. return &m_Thinkers[ idx ];
  1715. }
  1716. void Report()
  1717. {
  1718. if ( m_Thinkers.Count() == 0 )
  1719. return;
  1720. Msg( "-----------------\nThink report frame %i\n", gpGlobals->tickcount );
  1721. for ( int i = m_Thinkers.FirstInorder();
  1722. i != m_Thinkers.InvalidIndex();
  1723. i = m_Thinkers.NextInorder( i ) )
  1724. {
  1725. ThinkSync *p = &m_Thinkers[ i ];
  1726. Assert( p );
  1727. if ( !p )
  1728. continue;
  1729. int ecount = p->entities.Count();
  1730. if ( !ecount )
  1731. {
  1732. continue;
  1733. }
  1734. Msg( "thinktime %f, %i entities\n", p->thinktime, ecount );
  1735. for ( int j =0; j < ecount; j++ )
  1736. {
  1737. EHANDLE h = p->entities[ j ];
  1738. int lastthinktick = 0;
  1739. int nextthinktick = 0;
  1740. CBaseEntity *e = h.Get();
  1741. if ( e )
  1742. {
  1743. lastthinktick = e->m_nLastThinkTick;
  1744. nextthinktick = e->m_nNextThinkTick;
  1745. }
  1746. Msg( " %p : %30s (last %5i/next %5i)\n", h.Get(), h.Get() ? h->GetClassname() : "NULL",
  1747. lastthinktick, nextthinktick );
  1748. }
  1749. }
  1750. }
  1751. CUtlRBTree< ThinkSync > m_Thinkers;
  1752. int m_nLastFrameCount;
  1753. bool m_bShouldCheck;
  1754. };
  1755. static CThinkSyncTester g_ThinkChecker;
  1756. //-----------------------------------------------------------------------------
  1757. // Purpose:
  1758. //-----------------------------------------------------------------------------
  1759. bool CBaseEntity::PhysicsRunSpecificThink( int nContextIndex, BASEPTR thinkFunc )
  1760. {
  1761. int thinktick = GetNextThinkTick( nContextIndex );
  1762. if ( thinktick <= 0 || thinktick > gpGlobals->tickcount )
  1763. return true;
  1764. float thinktime = thinktick * TICK_INTERVAL;
  1765. // Don't let things stay in the past.
  1766. // it is possible to start that way
  1767. // by a trigger with a local time.
  1768. if ( thinktime < gpGlobals->curtime )
  1769. {
  1770. thinktime = gpGlobals->curtime;
  1771. }
  1772. // Only do this on the game server
  1773. #if !defined( CLIENT_DLL )
  1774. g_ThinkChecker.EntityThinking( gpGlobals->tickcount, this, thinktime, m_nNextThinkTick );
  1775. #endif
  1776. SetNextThink( nContextIndex, TICK_NEVER_THINK );
  1777. PhysicsDispatchThink( thinkFunc );
  1778. SetLastThink( nContextIndex, gpGlobals->curtime );
  1779. // Return whether entity is still valid
  1780. return ( !IsMarkedForDeletion() );
  1781. }
  1782. void CBaseEntity::SetGroundEntity( CBaseEntity *ground )
  1783. {
  1784. if ( m_hGroundEntity.Get() == ground )
  1785. return;
  1786. #ifdef GAME_DLL
  1787. // this can happen in-between updates to the held object controller (physcannon, +USE)
  1788. // so trap it here and release held objects when they become player ground
  1789. if ( ground && IsPlayer() && ground->GetMoveType()== MOVETYPE_VPHYSICS )
  1790. {
  1791. CBasePlayer *pPlayer = ToBasePlayer(this);
  1792. IPhysicsObject *pPhysGround = ground->VPhysicsGetObject();
  1793. if ( pPhysGround && pPlayer )
  1794. {
  1795. if ( pPhysGround->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  1796. {
  1797. pPlayer->ForceDropOfCarriedPhysObjects( ground );
  1798. }
  1799. }
  1800. }
  1801. #endif
  1802. CBaseEntity *oldGround = m_hGroundEntity;
  1803. m_hGroundEntity = ground;
  1804. // Just starting to touch
  1805. if ( !oldGround && ground )
  1806. {
  1807. ground->AddEntityToGroundList( this );
  1808. }
  1809. // Just stopping touching
  1810. else if ( oldGround && !ground )
  1811. {
  1812. PhysicsNotifyOtherOfGroundRemoval( this, oldGround );
  1813. }
  1814. // Changing out to new ground entity
  1815. else
  1816. {
  1817. PhysicsNotifyOtherOfGroundRemoval( this, oldGround );
  1818. ground->AddEntityToGroundList( this );
  1819. }
  1820. // HACK/PARANOID: This is redundant with the code above, but in case we get out of sync groundlist entries ever,
  1821. // this will force the appropriate flags
  1822. if ( ground )
  1823. {
  1824. AddFlag( FL_ONGROUND );
  1825. }
  1826. else
  1827. {
  1828. RemoveFlag( FL_ONGROUND );
  1829. }
  1830. }
  1831. CBaseEntity *CBaseEntity::GetGroundEntity( void )
  1832. {
  1833. return m_hGroundEntity;
  1834. }
  1835. void CBaseEntity::StartGroundContact( CBaseEntity *ground )
  1836. {
  1837. AddFlag( FL_ONGROUND );
  1838. // Msg( "+++ %s starting contact with ground %s\n", GetClassname(), ground->GetClassname() );
  1839. }
  1840. void CBaseEntity::EndGroundContact( CBaseEntity *ground )
  1841. {
  1842. RemoveFlag( FL_ONGROUND );
  1843. // Msg( "--- %s ending contact with ground %s\n", GetClassname(), ground->GetClassname() );
  1844. }
  1845. void CBaseEntity::SetGroundChangeTime( float flTime )
  1846. {
  1847. m_flGroundChangeTime = flTime;
  1848. }
  1849. float CBaseEntity::GetGroundChangeTime( void )
  1850. {
  1851. return m_flGroundChangeTime;
  1852. }
  1853. // Remove this as ground entity for all object resting on this object
  1854. //-----------------------------------------------------------------------------
  1855. // Purpose:
  1856. //-----------------------------------------------------------------------------
  1857. void CBaseEntity::WakeRestingObjects()
  1858. {
  1859. // Unset this as ground entity for everything resting on this object
  1860. // This calls endgroundcontact for everything on the list
  1861. PhysicsRemoveGroundList( this );
  1862. }
  1863. //-----------------------------------------------------------------------------
  1864. // Purpose:
  1865. // Input : *ent -
  1866. //-----------------------------------------------------------------------------
  1867. bool CBaseEntity::HasNPCsOnIt( void )
  1868. {
  1869. groundlink_t *link;
  1870. groundlink_t *root = ( groundlink_t * )GetDataObject( GROUNDLINK );
  1871. if ( root )
  1872. {
  1873. for ( link = root->nextLink; link != root; link = link->nextLink )
  1874. {
  1875. if ( link->entity && link->entity->MyNPCPointer() )
  1876. return true;
  1877. }
  1878. }
  1879. return false;
  1880. }