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

2238 lines
57 KiB

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