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.

2079 lines
62 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Physics simulation for non-havok/ipion objects
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #ifdef _WIN32
  9. #include "typeinfo.h"
  10. // BUGBUG: typeinfo stomps some of the warning settings (in yvals.h)
  11. #pragma warning(disable:4244)
  12. #elif POSIX
  13. #include <typeinfo>
  14. #else
  15. #error "need typeinfo defined"
  16. #endif
  17. #include "player.h"
  18. #include "ai_basenpc.h"
  19. #include "gamerules.h"
  20. #include "vphysics_interface.h"
  21. #include "mempool.h"
  22. #include "entitylist.h"
  23. #include "engine/IEngineSound.h"
  24. #include "datacache/imdlcache.h"
  25. #include "ispatialpartition.h"
  26. #include "tier0/vprof.h"
  27. #include "movevars_shared.h"
  28. #include "hierarchy.h"
  29. #include "trains.h"
  30. #include "vphysicsupdateai.h"
  31. #include "tier0/vcrmode.h"
  32. #include "pushentity.h"
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. extern ConVar think_limit;
  36. #ifdef _XBOX
  37. ConVar vprof_think_limit( "vprof_think_limit", "0" );
  38. #endif
  39. ConVar vprof_scope_entity_thinks( "vprof_scope_entity_thinks", "0" );
  40. ConVar vprof_scope_entity_gamephys( "vprof_scope_entity_gamephys", "0" );
  41. ConVar npc_vphysics ( "npc_vphysics","0");
  42. //-----------------------------------------------------------------------------
  43. // helper method for trace hull as used by physics...
  44. //-----------------------------------------------------------------------------
  45. static void Physics_TraceEntity( CBaseEntity* pBaseEntity, const Vector &vecAbsStart,
  46. const Vector &vecAbsEnd, unsigned int mask, trace_t *ptr )
  47. {
  48. // FIXME: I really am not sure the best way of doing this
  49. // The TraceHull code below for shots will make sure the object passes
  50. // through shields which do not block that damage type. It will also
  51. // send messages to the shields that they've been hit.
  52. if (pBaseEntity->GetDamageType() != DMG_GENERIC)
  53. {
  54. GameRules()->WeaponTraceEntity( pBaseEntity, vecAbsStart, vecAbsEnd, mask, ptr );
  55. }
  56. else
  57. {
  58. UTIL_TraceEntity( pBaseEntity, vecAbsStart, vecAbsEnd, mask, ptr );
  59. }
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Does not change the entities velocity at all
  63. // Input : push -
  64. // Output : trace_t
  65. //-----------------------------------------------------------------------------
  66. static void PhysicsCheckSweep( CBaseEntity *pEntity, const Vector& vecAbsStart, const Vector &vecAbsDelta, trace_t *pTrace )
  67. {
  68. unsigned int mask = pEntity->PhysicsSolidMaskForEntity();
  69. Vector vecAbsEnd;
  70. VectorAdd( vecAbsStart, vecAbsDelta, vecAbsEnd );
  71. // Set collision type
  72. if ( !pEntity->IsSolid() || pEntity->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS) )
  73. {
  74. if ( pEntity->GetMoveParent() )
  75. {
  76. UTIL_ClearTrace( *pTrace );
  77. return;
  78. }
  79. // don't collide with monsters
  80. mask &= ~CONTENTS_MONSTER;
  81. }
  82. Physics_TraceEntity( pEntity, vecAbsStart, vecAbsEnd, mask, pTrace );
  83. }
  84. CPhysicsPushedEntities s_PushedEntities;
  85. #ifndef TF_DLL
  86. CPhysicsPushedEntities *g_pPushedEntities = &s_PushedEntities;
  87. #endif
  88. //-----------------------------------------------------------------------------
  89. // Purpose:
  90. //-----------------------------------------------------------------------------
  91. CPhysicsPushedEntities::CPhysicsPushedEntities( void ) : m_rgPusher(8, 8), m_rgMoved(32, 32)
  92. {
  93. m_flMoveTime = -1.0f;
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose: Store off entity and copy original origin to temporary array
  97. //-----------------------------------------------------------------------------
  98. void CPhysicsPushedEntities::AddEntity( CBaseEntity *ent )
  99. {
  100. int i = m_rgMoved.AddToTail();
  101. m_rgMoved[i].m_pEntity = ent;
  102. m_rgMoved[i].m_vecStartAbsOrigin = ent->GetAbsOrigin();
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Unlink + relink the pusher list so we can actually do the push
  106. //-----------------------------------------------------------------------------
  107. void CPhysicsPushedEntities::UnlinkPusherList( int *pPusherHandles )
  108. {
  109. for ( int i = m_rgPusher.Count(); --i >= 0; )
  110. {
  111. pPusherHandles[i] = ::partition->HideElement( m_rgPusher[i].m_pEntity->CollisionProp()->GetPartitionHandle() );
  112. }
  113. }
  114. void CPhysicsPushedEntities::RelinkPusherList( int *pPusherHandles )
  115. {
  116. for ( int i = m_rgPusher.Count(); --i >= 0; )
  117. {
  118. ::partition->UnhideElement( m_rgPusher[i].m_pEntity->CollisionProp()->GetPartitionHandle(), pPusherHandles[i] );
  119. }
  120. }
  121. //-----------------------------------------------------------------------------
  122. // Compute the direction to move the rotation blocker
  123. //-----------------------------------------------------------------------------
  124. void CPhysicsPushedEntities::ComputeRotationalPushDirection( CBaseEntity *pBlocker, const RotatingPushMove_t &rotPushMove, Vector *pMove, CBaseEntity *pRoot )
  125. {
  126. // calculate destination position
  127. // "start" is relative to the *root* pusher, world orientation
  128. Vector start = pBlocker->CollisionProp()->GetCollisionOrigin();
  129. if ( pRoot->GetSolid() == SOLID_VPHYSICS )
  130. {
  131. // HACKHACK: Use move dir to guess which corner of the box determines contact and rotate the box so
  132. // that corner remains in the same local position.
  133. // BUGBUG: This will break, but not as badly as the previous solution!!!
  134. Vector vecAbsMins, vecAbsMaxs;
  135. pBlocker->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  136. start.x = (pMove->x < 0) ? vecAbsMaxs.x : vecAbsMins.x;
  137. start.y = (pMove->y < 0) ? vecAbsMaxs.y : vecAbsMins.y;
  138. start.z = (pMove->z < 0) ? vecAbsMaxs.z : vecAbsMins.z;
  139. CBasePlayer *pPlayer = ToBasePlayer(pBlocker);
  140. if ( pPlayer )
  141. {
  142. // notify the player physics code so it can use vphysics to keep players from getting stuck
  143. pPlayer->SetPhysicsFlag( PFLAG_GAMEPHYSICS_ROTPUSH, true );
  144. }
  145. }
  146. // org is pusher local coordinate of start
  147. Vector local;
  148. // transform starting point into local space
  149. VectorITransform( start, rotPushMove.startLocalToWorld, local );
  150. // rotate local org into world space at end of rotation
  151. Vector end;
  152. VectorTransform( local, rotPushMove.endLocalToWorld, end );
  153. // move is the difference (in world space) that the move will push this object
  154. VectorSubtract( end, start, *pMove );
  155. }
  156. class CTraceFilterPushFinal : public CTraceFilterSimple
  157. {
  158. DECLARE_CLASS( CTraceFilterPushFinal, CTraceFilterSimple );
  159. public:
  160. CTraceFilterPushFinal( CBaseEntity *pEntity, int nCollisionGroup )
  161. : CTraceFilterSimple( pEntity, nCollisionGroup )
  162. {
  163. }
  164. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  165. {
  166. Assert( dynamic_cast<CBaseEntity*>(pHandleEntity) );
  167. CBaseEntity *pTestEntity = static_cast<CBaseEntity*>(pHandleEntity);
  168. // UNDONE: This should really filter to just the pushing entities
  169. if ( pTestEntity->GetMoveType() == MOVETYPE_VPHYSICS &&
  170. pTestEntity->VPhysicsGetObject() && pTestEntity->VPhysicsGetObject()->IsMoveable() )
  171. return false;
  172. return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
  173. }
  174. };
  175. bool CPhysicsPushedEntities::IsPushedPositionValid( CBaseEntity *pBlocker )
  176. {
  177. CTraceFilterPushFinal pushFilter(pBlocker, pBlocker->GetCollisionGroup() );
  178. trace_t trace;
  179. UTIL_TraceEntity( pBlocker, pBlocker->GetAbsOrigin(), pBlocker->GetAbsOrigin(), pBlocker->PhysicsSolidMaskForEntity(), &pushFilter, &trace );
  180. return !trace.startsolid;
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Speculatively checks to see if all entities in this list can be pushed
  184. //-----------------------------------------------------------------------------
  185. bool CPhysicsPushedEntities::SpeculativelyCheckPush( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, bool bRotationalPush )
  186. {
  187. CBaseEntity *pBlocker = info.m_pEntity;
  188. // See if it's possible to move the entity, but disable all pushers in the hierarchy first
  189. int *pPusherHandles = (int*)stackalloc( m_rgPusher.Count() * sizeof(int) );
  190. UnlinkPusherList( pPusherHandles );
  191. CTraceFilterPushMove pushFilter(pBlocker, pBlocker->GetCollisionGroup() );
  192. Vector pushDestPosition = pBlocker->GetAbsOrigin() + vecAbsPush;
  193. UTIL_TraceEntity( pBlocker, pBlocker->GetAbsOrigin(), pushDestPosition,
  194. pBlocker->PhysicsSolidMaskForEntity(), &pushFilter, &info.m_Trace );
  195. RelinkPusherList(pPusherHandles);
  196. info.m_bPusherIsGround = false;
  197. if ( pBlocker->GetGroundEntity() && pBlocker->GetGroundEntity()->GetRootMoveParent() == m_rgPusher[0].m_pEntity )
  198. {
  199. info.m_bPusherIsGround = true;
  200. }
  201. bool bIsUnblockable = (m_bIsUnblockableByPlayer && (pBlocker->IsPlayer() || pBlocker->MyNPCPointer())) ? true : false;
  202. if ( bIsUnblockable )
  203. {
  204. pBlocker->SetAbsOrigin( pushDestPosition );
  205. }
  206. else
  207. {
  208. // Move the blocker into its new position
  209. if ( info.m_Trace.fraction )
  210. {
  211. pBlocker->SetAbsOrigin( info.m_Trace.endpos );
  212. }
  213. // We're not blocked if the blocker is point-sized or non-solid
  214. if ( pBlocker->IsPointSized() || !pBlocker->IsSolid() ||
  215. pBlocker->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  216. {
  217. return true;
  218. }
  219. if ( (!bRotationalPush) && (info.m_Trace.fraction == 1.0) )
  220. {
  221. //Assert( pBlocker->PhysicsTestEntityPosition() == false );
  222. if ( !IsPushedPositionValid(pBlocker) )
  223. {
  224. Warning("Interpenetrating entities! (%s and %s)\n",
  225. pBlocker->GetClassname(), m_rgPusher[0].m_pEntity->GetClassname() );
  226. }
  227. return true;
  228. }
  229. }
  230. // Check to see if we're still blocked by the pushers
  231. // FIXME: If the trace fraction == 0 can we early out also?
  232. info.m_bBlocked = !IsPushedPositionValid(pBlocker);
  233. if ( !info.m_bBlocked )
  234. return true;
  235. // if the player is blocking the train try nudging him around to fix accumulated error
  236. if ( bIsUnblockable )
  237. {
  238. Vector org = pBlocker->GetAbsOrigin();
  239. for ( int checkCount = 0; checkCount < 4; checkCount++ )
  240. {
  241. Vector move;
  242. MatrixGetColumn( m_rgPusher[0].m_pEntity->EntityToWorldTransform(), checkCount>>1, move );
  243. // alternate movements 1/2" in each direction
  244. float factor = ( checkCount & 1 ) ? -0.5f : 0.5f;
  245. pBlocker->SetAbsOrigin( org + move * factor );
  246. info.m_bBlocked = !IsPushedPositionValid(pBlocker);
  247. if ( !info.m_bBlocked )
  248. return true;
  249. }
  250. pBlocker->SetAbsOrigin( pushDestPosition );
  251. #ifndef TF_DLL
  252. DevMsg(1, "Ignoring player blocking train!\n");
  253. #endif
  254. return true;
  255. }
  256. return false;
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Speculatively checks to see if all entities in this list can be pushed
  260. //-----------------------------------------------------------------------------
  261. bool CPhysicsPushedEntities::SpeculativelyCheckRotPush( const RotatingPushMove_t &rotPushMove, CBaseEntity *pRoot )
  262. {
  263. Vector vecAbsPush;
  264. m_nBlocker = -1;
  265. for (int i = m_rgMoved.Count(); --i >= 0; )
  266. {
  267. ComputeRotationalPushDirection( m_rgMoved[i].m_pEntity, rotPushMove, &vecAbsPush, pRoot );
  268. if (!SpeculativelyCheckPush( m_rgMoved[i], vecAbsPush, true ))
  269. {
  270. m_nBlocker = i;
  271. return false;
  272. }
  273. }
  274. return true;
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Speculatively checks to see if all entities in this list can be pushed
  278. //-----------------------------------------------------------------------------
  279. bool CPhysicsPushedEntities::SpeculativelyCheckLinearPush( const Vector &vecAbsPush )
  280. {
  281. m_nBlocker = -1;
  282. for (int i = m_rgMoved.Count(); --i >= 0; )
  283. {
  284. if (!SpeculativelyCheckPush( m_rgMoved[i], vecAbsPush, false ))
  285. {
  286. m_nBlocker = i;
  287. return false;
  288. }
  289. }
  290. return true;
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Causes all entities in the list to touch triggers from their prev position
  294. //-----------------------------------------------------------------------------
  295. void CPhysicsPushedEntities::FinishPushers()
  296. {
  297. // We succeeded! Now that we know the final location of all entities,
  298. // touch triggers + update physics objects + do other fixup
  299. for ( int i = m_rgPusher.Count(); --i >= 0; )
  300. {
  301. PhysicsPusherInfo_t &info = m_rgPusher[i];
  302. // Cause touch functions to be called
  303. // FIXME: Need to make moved entities not touch triggers until we know we're ok
  304. // FIXME: it'd be better for the engine to just have a touch method
  305. info.m_pEntity->PhysicsTouchTriggers( &info.m_vecStartAbsOrigin );
  306. info.m_pEntity->UpdatePhysicsShadowToCurrentPosition( gpGlobals->frametime );
  307. }
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Causes all entities in the list to touch triggers from their prev position
  311. //-----------------------------------------------------------------------------
  312. void CPhysicsPushedEntities::FinishRotPushedEntity( CBaseEntity *pPushedEntity, const RotatingPushMove_t &rotPushMove )
  313. {
  314. // Impart angular velocity of push onto pushed objects
  315. if ( pPushedEntity->IsPlayer() )
  316. {
  317. QAngle angVel = pPushedEntity->GetLocalAngularVelocity();
  318. angVel[1] = rotPushMove.amove[1];
  319. pPushedEntity->SetLocalAngularVelocity(angVel);
  320. // Look up associated client
  321. CBasePlayer *player = ( CBasePlayer * )pPushedEntity;
  322. player->pl.fixangle = FIXANGLE_RELATIVE;
  323. // Because we can run multiple ticks per server frame, accumulate a total offset here instead of straight
  324. // setting it. The engine will reset anglechange to 0 when the message is actually sent to the client
  325. player->pl.anglechange += rotPushMove.amove;
  326. }
  327. else
  328. {
  329. QAngle angles = pPushedEntity->GetAbsAngles();
  330. // only rotate YAW with pushing. Freely rotateable entities should either use VPHYSICS
  331. // or be set up as children
  332. angles.y += rotPushMove.amove.y;
  333. pPushedEntity->SetAbsAngles( angles );
  334. }
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Causes all entities in the list to touch triggers from their prev position
  338. //-----------------------------------------------------------------------------
  339. void CPhysicsPushedEntities::FinishPush( bool bIsRotPush, const RotatingPushMove_t *pRotPushMove )
  340. {
  341. FinishPushers();
  342. for ( int i = m_rgMoved.Count(); --i >= 0; )
  343. {
  344. PhysicsPushedInfo_t &info = m_rgMoved[i];
  345. CBaseEntity *pPushedEntity = info.m_pEntity;
  346. // Cause touch functions to be called
  347. // FIXME: it'd be better for the engine to just have a touch method
  348. info.m_pEntity->PhysicsTouchTriggers( &info.m_vecStartAbsOrigin );
  349. info.m_pEntity->UpdatePhysicsShadowToCurrentPosition( gpGlobals->frametime );
  350. CAI_BaseNPC *pNPC = info.m_pEntity->MyNPCPointer();
  351. if ( info.m_bPusherIsGround && pNPC )
  352. {
  353. pNPC->NotifyPushMove();
  354. }
  355. // Register physics impacts...
  356. if (info.m_Trace.m_pEnt)
  357. {
  358. pPushedEntity->PhysicsImpact( info.m_Trace.m_pEnt, info.m_Trace );
  359. }
  360. if (bIsRotPush)
  361. {
  362. FinishRotPushedEntity( pPushedEntity, *pRotPushMove );
  363. }
  364. }
  365. }
  366. // save initial state when beginning a push sequence
  367. void CPhysicsPushedEntities::BeginPush( CBaseEntity *pRoot )
  368. {
  369. m_rgMoved.RemoveAll();
  370. m_rgPusher.RemoveAll();
  371. m_rootPusherStartLocalOrigin = pRoot->GetLocalOrigin();
  372. m_rootPusherStartLocalAngles = pRoot->GetLocalAngles();
  373. m_rootPusherStartLocaltime = pRoot->GetLocalTime();
  374. }
  375. // store off a list of what has changed - so vphysicsUpdate can undo this if the object gets blocked
  376. void CPhysicsPushedEntities::StoreMovedEntities( physicspushlist_t &list )
  377. {
  378. list.localMoveTime = m_rootPusherStartLocaltime;
  379. list.localOrigin = m_rootPusherStartLocalOrigin;
  380. list.localAngles = m_rootPusherStartLocalAngles;
  381. list.pushedCount = CountMovedEntities();
  382. Assert(list.pushedCount < ARRAYSIZE(list.pushedEnts));
  383. if ( list.pushedCount > ARRAYSIZE(list.pushedEnts) )
  384. {
  385. list.pushedCount = ARRAYSIZE(list.pushedEnts);
  386. }
  387. for ( int i = 0; i < list.pushedCount; i++ )
  388. {
  389. list.pushedEnts[i] = m_rgMoved[i].m_pEntity;
  390. list.pushVec[i] = m_rgMoved[i].m_pEntity->GetAbsOrigin() - m_rgMoved[i].m_vecStartAbsOrigin;
  391. }
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Registers a blockage
  395. //-----------------------------------------------------------------------------
  396. CBaseEntity *CPhysicsPushedEntities::RegisterBlockage()
  397. {
  398. Assert( m_nBlocker >= 0 );
  399. // Generate a PhysicsImpact against the blocker...
  400. PhysicsPushedInfo_t &info = m_rgMoved[m_nBlocker];
  401. if ( info.m_Trace.m_pEnt )
  402. {
  403. info.m_pEntity->PhysicsImpact( info.m_Trace.m_pEnt, info.m_Trace );
  404. }
  405. // This is the dude
  406. return info.m_pEntity;
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Purpose: Restore entities that might have been moved
  410. // Input : fromrotation - if the move is from a rotation, then angular move must also be reverted
  411. // *amove -
  412. //-----------------------------------------------------------------------------
  413. void CPhysicsPushedEntities::RestoreEntities( )
  414. {
  415. // Reset all of the pushed entities to get them back into place also
  416. for ( int i = m_rgMoved.Count(); --i >= 0; )
  417. {
  418. m_rgMoved[ i ].m_pEntity->SetAbsOrigin( m_rgMoved[ i ].m_vecStartAbsOrigin );
  419. }
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: This is a trace filter that only hits an exclusive list of entities
  423. //-----------------------------------------------------------------------------
  424. class CTraceFilterAgainstEntityList : public ITraceFilter
  425. {
  426. public:
  427. virtual bool ShouldHitEntity( IHandleEntity *pEntity, int contentsMask )
  428. {
  429. for ( int i = m_entityList.Count()-1; i >= 0; --i )
  430. {
  431. if ( m_entityList[i] == pEntity )
  432. return true;
  433. }
  434. return false;
  435. }
  436. virtual TraceType_t GetTraceType() const
  437. {
  438. return TRACE_ENTITIES_ONLY;
  439. }
  440. void AddEntityToHit( IHandleEntity *pEntity )
  441. {
  442. m_entityList.AddToTail(pEntity);
  443. }
  444. CUtlVector<IHandleEntity *> m_entityList;
  445. };
  446. //-----------------------------------------------------------------------------
  447. // Generates a list of potential blocking entities
  448. //-----------------------------------------------------------------------------
  449. class CPushBlockerEnum : public IPartitionEnumerator
  450. {
  451. public:
  452. CPushBlockerEnum( CPhysicsPushedEntities *pPushedEntities ) : m_pPushedEntities(pPushedEntities)
  453. {
  454. // All elements are part of the same hierarchy, so they all have
  455. // the same root, so it doesn't matter which one we grab
  456. m_pRootHighestParent = m_pPushedEntities->m_rgPusher[0].m_pEntity->GetRootMoveParent();
  457. ++s_nEnumCount;
  458. m_collisionGroupCount = 0;
  459. for ( int i = m_pPushedEntities->m_rgPusher.Count(); --i >= 0; )
  460. {
  461. if ( !m_pPushedEntities->m_rgPusher[i].m_pEntity->IsSolid() )
  462. continue;
  463. m_pushersOnly.AddEntityToHit( m_pPushedEntities->m_rgPusher[i].m_pEntity );
  464. int collisionGroup = m_pPushedEntities->m_rgPusher[i].m_pEntity->GetCollisionGroup();
  465. AddCollisionGroup(collisionGroup);
  466. }
  467. }
  468. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  469. {
  470. CBaseEntity *pCheck = GetPushableEntity( pHandleEntity );
  471. if ( !pCheck )
  472. return ITERATION_CONTINUE;
  473. // Mark it as seen
  474. pCheck->m_nPushEnumCount = s_nEnumCount;
  475. m_pPushedEntities->AddEntity( pCheck );
  476. return ITERATION_CONTINUE;
  477. }
  478. private:
  479. inline void AddCollisionGroup(int collisionGroup)
  480. {
  481. for ( int i = 0; i < m_collisionGroupCount; i++ )
  482. {
  483. if ( m_collisionGroups[i] == collisionGroup )
  484. return;
  485. }
  486. if ( m_collisionGroupCount < ARRAYSIZE(m_collisionGroups) )
  487. {
  488. m_collisionGroups[m_collisionGroupCount] = collisionGroup;
  489. m_collisionGroupCount++;
  490. }
  491. }
  492. bool IsStandingOnPusher( CBaseEntity *pCheck )
  493. {
  494. CBaseEntity *pGroundEnt = pCheck->GetGroundEntity();
  495. if ( pCheck->GetFlags() & FL_ONGROUND || pGroundEnt )
  496. {
  497. for ( int i = m_pPushedEntities->m_rgPusher.Count(); --i >= 0; )
  498. {
  499. if (m_pPushedEntities->m_rgPusher[i].m_pEntity == pGroundEnt)
  500. {
  501. return true;
  502. }
  503. }
  504. }
  505. return false;
  506. }
  507. bool IntersectsPushers( CBaseEntity *pTest )
  508. {
  509. trace_t tr;
  510. ICollideable *pCollision = pTest->GetCollideable();
  511. enginetrace->SweepCollideable( pCollision, pTest->GetAbsOrigin(), pTest->GetAbsOrigin(), pCollision->GetCollisionAngles(),
  512. pTest->PhysicsSolidMaskForEntity(), &m_pushersOnly, &tr );
  513. return tr.startsolid;
  514. }
  515. CBaseEntity *GetPushableEntity( IHandleEntity *pHandleEntity )
  516. {
  517. CBaseEntity *pCheck = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  518. if ( !pCheck )
  519. return NULL;
  520. // Don't bother if we've already seen this one...
  521. if (pCheck->m_nPushEnumCount == s_nEnumCount)
  522. return NULL;
  523. if ( !pCheck->IsSolid() )
  524. return NULL;
  525. if ( pCheck->GetMoveType() == MOVETYPE_PUSH ||
  526. pCheck->GetMoveType() == MOVETYPE_NONE ||
  527. pCheck->GetMoveType() == MOVETYPE_VPHYSICS ||
  528. pCheck->GetMoveType() == MOVETYPE_NOCLIP )
  529. {
  530. return NULL;
  531. }
  532. bool bCollide = false;
  533. for ( int i = 0; i < m_collisionGroupCount; i++ )
  534. {
  535. if ( g_pGameRules->ShouldCollide( pCheck->GetCollisionGroup(), m_collisionGroups[i] ) )
  536. {
  537. bCollide = true;
  538. break;
  539. }
  540. }
  541. if ( !bCollide )
  542. return NULL;
  543. // We're not pushing stuff we're hierarchically attached to
  544. CBaseEntity *pCheckHighestParent = pCheck->GetRootMoveParent();
  545. if (pCheckHighestParent == m_pRootHighestParent)
  546. return NULL;
  547. // If we're standing on the pusher or any rigidly attached child
  548. // of the pusher, we don't need to bother checking for interpenetration
  549. if ( !IsStandingOnPusher(pCheck) )
  550. {
  551. // Our surrounding boxes are touching. But we may well not be colliding....
  552. // see if the ent's bbox is inside the pusher's final position
  553. if ( !IntersectsPushers( pCheck ) )
  554. return NULL;
  555. }
  556. // NOTE: This is pretty tricky here. If a rigidly attached child comes into
  557. // contact with a pusher, we *cannot* push the child. Instead, we must push
  558. // the highest parent of that child.
  559. return pCheckHighestParent;
  560. }
  561. private:
  562. static int s_nEnumCount;
  563. CPhysicsPushedEntities *m_pPushedEntities;
  564. CBaseEntity *m_pRootHighestParent;
  565. CTraceFilterAgainstEntityList m_pushersOnly;
  566. int m_collisionGroups[8];
  567. int m_collisionGroupCount;
  568. };
  569. int CPushBlockerEnum::s_nEnumCount = 0;
  570. //-----------------------------------------------------------------------------
  571. // Generates a list of potential blocking entities
  572. //-----------------------------------------------------------------------------
  573. void CPhysicsPushedEntities::GenerateBlockingEntityList()
  574. {
  575. VPROF("CPhysicsPushedEntities::GenerateBlockingEntityList");
  576. m_rgMoved.RemoveAll();
  577. CPushBlockerEnum blockerEnum( this );
  578. for ( int i = m_rgPusher.Count(); --i >= 0; )
  579. {
  580. CBaseEntity *pPusher = m_rgPusher[i].m_pEntity;
  581. // Don't bother if the pusher isn't solid
  582. if ( !pPusher->IsSolid() || pPusher->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  583. {
  584. continue;
  585. }
  586. Vector vecAbsMins, vecAbsMaxs;
  587. pPusher->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  588. ::partition->EnumerateElementsInBox( PARTITION_ENGINE_NON_STATIC_EDICTS, vecAbsMins, vecAbsMaxs, false, &blockerEnum );
  589. //Go back throught the generated list.
  590. }
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Generates a list of potential blocking entities
  594. //-----------------------------------------------------------------------------
  595. void CPhysicsPushedEntities::GenerateBlockingEntityListAddBox( const Vector &vecMoved )
  596. {
  597. VPROF("CPhysicsPushedEntities::GenerateBlockingEntityListAddBox");
  598. m_rgMoved.RemoveAll();
  599. CPushBlockerEnum blockerEnum( this );
  600. for ( int i = m_rgPusher.Count(); --i >= 0; )
  601. {
  602. CBaseEntity *pPusher = m_rgPusher[i].m_pEntity;
  603. // Don't bother if the pusher isn't solid
  604. if ( !pPusher->IsSolid() || pPusher->IsSolidFlagSet( FSOLID_VOLUME_CONTENTS ) )
  605. {
  606. continue;
  607. }
  608. Vector vecAbsMins, vecAbsMaxs;
  609. pPusher->CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  610. for ( int iAxis = 0; iAxis < 3; ++iAxis )
  611. {
  612. if ( vecMoved[iAxis] >= 0.0f )
  613. {
  614. vecAbsMins[iAxis] -= vecMoved[iAxis];
  615. }
  616. else
  617. {
  618. vecAbsMaxs[iAxis] -= vecMoved[iAxis];
  619. }
  620. }
  621. ::partition->EnumerateElementsInBox( PARTITION_ENGINE_NON_STATIC_EDICTS, vecAbsMins, vecAbsMaxs, false, &blockerEnum );
  622. //Go back throught the generated list.
  623. }
  624. }
  625. #ifdef TF_DLL
  626. #include "tf_logic_robot_destruction.h"
  627. #endif
  628. //-----------------------------------------------------------------------------
  629. // Purpose: Gets a list of all entities hierarchically attached to the root
  630. //-----------------------------------------------------------------------------
  631. void CPhysicsPushedEntities::SetupAllInHierarchy( CBaseEntity *pParent )
  632. {
  633. if (!pParent)
  634. return;
  635. VPROF("CPhysicsPushedEntities::SetupAllInHierarchy");
  636. // Make sure to snack the position +before+ relink because applying the
  637. // rotation (which occurs in relink) will put it at the final location
  638. // NOTE: The root object at this point is actually at its final position.
  639. // We'll fix that up later
  640. int i = m_rgPusher.AddToTail();
  641. m_rgPusher[i].m_pEntity = pParent;
  642. m_rgPusher[i].m_vecStartAbsOrigin = pParent->GetAbsOrigin();
  643. CBaseEntity *pChild;
  644. for ( pChild = pParent->FirstMoveChild(); pChild != NULL; pChild = pChild->NextMovePeer() )
  645. {
  646. SetupAllInHierarchy( pChild );
  647. }
  648. }
  649. //-----------------------------------------------------------------------------
  650. // Purpose: Rotates the root entity, fills in the pushmove structure
  651. //-----------------------------------------------------------------------------
  652. void CPhysicsPushedEntities::RotateRootEntity( CBaseEntity *pRoot, float movetime, RotatingPushMove_t &rotation )
  653. {
  654. VPROF("CPhysicsPushedEntities::RotateRootEntity");
  655. rotation.amove = pRoot->GetLocalAngularVelocity() * movetime;
  656. rotation.origin = pRoot->GetAbsOrigin();
  657. // Knowing the initial + ending basis is needed for determining
  658. // which corner we're pushing
  659. MatrixCopy( pRoot->EntityToWorldTransform(), rotation.startLocalToWorld );
  660. // rotate the pusher to it's final position
  661. QAngle angles = pRoot->GetLocalAngles();
  662. angles += pRoot->GetLocalAngularVelocity() * movetime;
  663. pRoot->SetLocalAngles( angles );
  664. // Compute the change in absangles
  665. MatrixCopy( pRoot->EntityToWorldTransform(), rotation.endLocalToWorld );
  666. }
  667. //-----------------------------------------------------------------------------
  668. // Purpose: Tries to rotate an entity hierarchy, returns the blocker if any
  669. //-----------------------------------------------------------------------------
  670. CBaseEntity *CPhysicsPushedEntities::PerformRotatePush( CBaseEntity *pRoot, float movetime )
  671. {
  672. VPROF("CPhysicsPushedEntities::PerformRotatePush");
  673. m_bIsUnblockableByPlayer = (pRoot->GetFlags() & FL_UNBLOCKABLE_BY_PLAYER) ? true : false;
  674. // Build a list of this entity + all its children because we're going to try to move them all
  675. // This will also make sure each entity is linked in the appropriate place
  676. // with correct absboxes
  677. m_rgPusher.RemoveAll();
  678. SetupAllInHierarchy( pRoot );
  679. // save where we rotated from, in case we're blocked
  680. QAngle angPrevAngles = pRoot->GetLocalAngles();
  681. // Apply the rotation
  682. RotatingPushMove_t rotPushMove;
  683. RotateRootEntity( pRoot, movetime, rotPushMove );
  684. // Next generate a list of all entities that could potentially be intersecting with
  685. // any of the children in their new locations...
  686. GenerateBlockingEntityList( );
  687. // Now we have a unique list of things that could potentially block our push
  688. // and need to be pushed out of the way. Lets try to push them all out of the way.
  689. // If we fail, undo it all
  690. if (!SpeculativelyCheckRotPush( rotPushMove, pRoot ))
  691. {
  692. CBaseEntity *pBlocker = RegisterBlockage();
  693. pRoot->SetLocalAngles( angPrevAngles );
  694. RestoreEntities( );
  695. return pBlocker;
  696. }
  697. FinishPush( true, &rotPushMove );
  698. return NULL;
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose: Linearly moves the root entity
  702. //-----------------------------------------------------------------------------
  703. void CPhysicsPushedEntities::LinearlyMoveRootEntity( CBaseEntity *pRoot, float movetime, Vector *pAbsPushVector )
  704. {
  705. VPROF("CPhysicsPushedEntities::LinearlyMoveRootEntity");
  706. // move the pusher to it's final position
  707. Vector move = pRoot->GetLocalVelocity() * movetime;
  708. Vector origin = pRoot->GetLocalOrigin();
  709. origin += move;
  710. pRoot->SetLocalOrigin( origin );
  711. // Store off the abs push vector
  712. *pAbsPushVector = pRoot->GetAbsVelocity() * movetime;
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Purpose: Tries to linearly push an entity hierarchy, returns the blocker if any
  716. //-----------------------------------------------------------------------------
  717. CBaseEntity *CPhysicsPushedEntities::PerformLinearPush( CBaseEntity *pRoot, float movetime )
  718. {
  719. VPROF("CPhysicsPushedEntities::PerformLinearPush");
  720. m_flMoveTime = movetime;
  721. m_bIsUnblockableByPlayer = (pRoot->GetFlags() & FL_UNBLOCKABLE_BY_PLAYER) ? true : false;
  722. // Build a list of this entity + all its children because we're going to try to move them all
  723. // This will also make sure each entity is linked in the appropriate place
  724. // with correct absboxes
  725. m_rgPusher.RemoveAll();
  726. SetupAllInHierarchy( pRoot );
  727. // save where we started from, in case we're blocked
  728. Vector vecPrevOrigin = pRoot->GetLocalOrigin();
  729. // Move the root (and all children) into its new position
  730. Vector vecAbsPush;
  731. LinearlyMoveRootEntity( pRoot, movetime, &vecAbsPush );
  732. // Next generate a list of all entities that could potentially be intersecting with
  733. // any of the children in their new locations...
  734. GenerateBlockingEntityListAddBox( vecAbsPush );
  735. // Now we have a unique list of things that could potentially block our push
  736. // and need to be pushed out of the way. Lets try to push them all out of the way.
  737. // If we fail, undo it all
  738. if (!SpeculativelyCheckLinearPush( vecAbsPush ))
  739. {
  740. CBaseEntity *pBlocker = RegisterBlockage();
  741. pRoot->SetLocalOrigin( vecPrevOrigin );
  742. RestoreEntities();
  743. return pBlocker;
  744. }
  745. FinishPush( );
  746. return NULL;
  747. }
  748. //-----------------------------------------------------------------------------
  749. //
  750. // CBaseEntity methods
  751. //
  752. //-----------------------------------------------------------------------------
  753. //-----------------------------------------------------------------------------
  754. // Purpose: Called when it's time for a physically moved objects (plats, doors, etc)
  755. // to run it's game code.
  756. // All other entity thinking is done during worldspawn's think
  757. //-----------------------------------------------------------------------------
  758. void CBaseEntity::PhysicsDispatchThink( BASEPTR thinkFunc )
  759. {
  760. VPROF_ENTER_SCOPE( ( !vprof_scope_entity_thinks.GetBool() ) ?
  761. "CBaseEntity::PhysicsDispatchThink" :
  762. EntityFactoryDictionary()->GetCannonicalName( GetClassname() ) );
  763. float thinkLimit = think_limit.GetFloat();
  764. // The thinkLimit stuff makes a LOT of calls to Sys_FloatTime, which winds up calling into
  765. // VCR mode so much that the framerate becomes unusable.
  766. if ( VCRGetMode() != VCR_Disabled )
  767. thinkLimit = 0;
  768. float startTime = 0.0;
  769. if ( IsDormant() )
  770. {
  771. Warning( "Dormant entity %s (%s) is thinking!!\n", GetClassname(), GetDebugName() );
  772. Assert(0);
  773. }
  774. if ( thinkLimit )
  775. {
  776. startTime = engine->Time();
  777. }
  778. if ( thinkFunc )
  779. {
  780. MDLCACHE_CRITICAL_SECTION();
  781. (this->*thinkFunc)();
  782. }
  783. if ( thinkLimit )
  784. {
  785. // calculate running time of the AI in milliseconds
  786. float time = ( engine->Time() - startTime ) * 1000.0f;
  787. if ( time > thinkLimit )
  788. {
  789. #if defined( _XBOX ) && !defined( _RETAIL )
  790. if ( vprof_think_limit.GetBool() )
  791. {
  792. extern bool g_VProfSignalSpike;
  793. g_VProfSignalSpike = true;
  794. }
  795. #endif
  796. // If its an NPC print out the shedule/task that took so long
  797. CAI_BaseNPC *pNPC = MyNPCPointer();
  798. if (pNPC && pNPC->GetCurSchedule())
  799. {
  800. pNPC->ReportOverThinkLimit( time );
  801. }
  802. else
  803. {
  804. #ifdef _WIN32
  805. Msg( "%s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).raw_name(), time );
  806. #elif POSIX
  807. Msg( "%s(%s) thinking for %.02f ms!!!\n", GetClassname(), typeid(this).name(), time );
  808. #else
  809. #error "typeinfo"
  810. #endif
  811. }
  812. }
  813. }
  814. VPROF_EXIT_SCOPE();
  815. }
  816. //-----------------------------------------------------------------------------
  817. // Purpose: Does not change the entities velocity at all
  818. // Input : push -
  819. // Output : trace_t
  820. //-----------------------------------------------------------------------------
  821. void CBaseEntity::PhysicsCheckSweep( const Vector& vecAbsStart, const Vector &vecAbsDelta, trace_t *pTrace )
  822. {
  823. ::PhysicsCheckSweep( this, vecAbsStart, vecAbsDelta, pTrace );
  824. }
  825. #define MAX_CLIP_PLANES 5
  826. //-----------------------------------------------------------------------------
  827. // Purpose: The basic solid body movement attempt/clip that slides along multiple planes
  828. // Input : time - Amount of time to try moving for
  829. // *steptrace - if not NULL, the trace results of any vertical wall hit will be stored
  830. // Output : int - the clipflags if the velocity was modified (hit something solid)
  831. // 1 = floor
  832. // 2 = wall / step
  833. // 4 = dead stop
  834. //-----------------------------------------------------------------------------
  835. int CBaseEntity::PhysicsTryMove( float flTime, trace_t *steptrace )
  836. {
  837. VPROF("CBaseEntity::PhysicsTryMove");
  838. int bumpcount, numbumps;
  839. Vector dir;
  840. float d;
  841. int numplanes;
  842. Vector planes[MAX_CLIP_PLANES];
  843. Vector primal_velocity, original_velocity, new_velocity;
  844. int i, j;
  845. trace_t trace;
  846. Vector end;
  847. float time_left;
  848. int blocked;
  849. unsigned int mask = PhysicsSolidMaskForEntity();
  850. new_velocity.Init();
  851. numbumps = 4;
  852. Vector vecAbsVelocity = GetAbsVelocity();
  853. blocked = 0;
  854. VectorCopy (vecAbsVelocity, original_velocity);
  855. VectorCopy (vecAbsVelocity, primal_velocity);
  856. numplanes = 0;
  857. time_left = flTime;
  858. for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
  859. {
  860. if (vecAbsVelocity == vec3_origin)
  861. break;
  862. VectorMA( GetAbsOrigin(), time_left, vecAbsVelocity, end );
  863. Physics_TraceEntity( this, GetAbsOrigin(), end, mask, &trace );
  864. if (trace.startsolid)
  865. { // entity is trapped in another solid
  866. SetAbsVelocity(vec3_origin);
  867. return 4;
  868. }
  869. if (trace.fraction > 0)
  870. { // actually covered some distance
  871. SetAbsOrigin( trace.endpos );
  872. VectorCopy (vecAbsVelocity, original_velocity);
  873. numplanes = 0;
  874. }
  875. if (trace.fraction == 1)
  876. break; // moved the entire distance
  877. if (!trace.m_pEnt)
  878. {
  879. SetAbsVelocity( vecAbsVelocity );
  880. Warning( "PhysicsTryMove: !trace.u.ent" );
  881. Assert(0);
  882. return 4;
  883. }
  884. if (trace.plane.normal[2] > 0.7)
  885. {
  886. blocked |= 1; // floor
  887. if (CanStandOn( trace.m_pEnt ))
  888. {
  889. // keep track of time when changing ground entity
  890. if (GetGroundEntity() != trace.m_pEnt)
  891. {
  892. SetGroundChangeTime( gpGlobals->curtime + (flTime - (1 - trace.fraction) * time_left) );
  893. }
  894. SetGroundEntity( trace.m_pEnt );
  895. }
  896. }
  897. if (!trace.plane.normal[2])
  898. {
  899. blocked |= 2; // step
  900. if (steptrace)
  901. *steptrace = trace; // save for player extrafriction
  902. }
  903. // run the impact function
  904. PhysicsImpact( trace.m_pEnt, trace );
  905. // Removed by the impact function
  906. if ( IsMarkedForDeletion() || IsEdictFree() )
  907. break;
  908. time_left -= time_left * trace.fraction;
  909. // clipped to another plane
  910. if (numplanes >= MAX_CLIP_PLANES)
  911. { // this shouldn't really happen
  912. SetAbsVelocity(vec3_origin);
  913. return blocked;
  914. }
  915. VectorCopy (trace.plane.normal, planes[numplanes]);
  916. numplanes++;
  917. // modify original_velocity so it parallels all of the clip planes
  918. if ( GetMoveType() == MOVETYPE_WALK && (!(GetFlags() & FL_ONGROUND) || GetFriction()!=1) ) // relfect player velocity
  919. {
  920. for ( i = 0; i < numplanes; i++ )
  921. {
  922. if ( planes[i][2] > 0.7 )
  923. {// floor or slope
  924. PhysicsClipVelocity( original_velocity, planes[i], new_velocity, 1 );
  925. VectorCopy( new_velocity, original_velocity );
  926. }
  927. else
  928. {
  929. PhysicsClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1-GetFriction()) );
  930. }
  931. }
  932. VectorCopy( new_velocity, vecAbsVelocity );
  933. VectorCopy( new_velocity, original_velocity );
  934. }
  935. else
  936. {
  937. for (i=0 ; i<numplanes ; i++)
  938. {
  939. PhysicsClipVelocity (original_velocity, planes[i], new_velocity, 1);
  940. for (j=0 ; j<numplanes ; j++)
  941. if (j != i)
  942. {
  943. if (DotProduct (new_velocity, planes[j]) < 0)
  944. break; // not ok
  945. }
  946. if (j == numplanes)
  947. break;
  948. }
  949. if (i != numplanes)
  950. {
  951. // go along this plane
  952. VectorCopy (new_velocity, vecAbsVelocity);
  953. }
  954. else
  955. {
  956. // go along the crease
  957. if (numplanes != 2)
  958. {
  959. // Msg( "clip velocity, numplanes == %i\n",numplanes);
  960. SetAbsVelocity( vecAbsVelocity );
  961. return blocked;
  962. }
  963. CrossProduct (planes[0], planes[1], dir);
  964. d = DotProduct (dir, vecAbsVelocity);
  965. VectorScale (dir, d, vecAbsVelocity);
  966. }
  967. //
  968. // if original velocity is against the original velocity, stop dead
  969. // to avoid tiny oscillations in sloping corners
  970. //
  971. if (DotProduct (vecAbsVelocity, primal_velocity) <= 0)
  972. {
  973. SetAbsVelocity(vec3_origin);
  974. return blocked;
  975. }
  976. }
  977. }
  978. SetAbsVelocity( vecAbsVelocity );
  979. return blocked;
  980. }
  981. //-----------------------------------------------------------------------------
  982. // Purpose: Applies 1/2 gravity to falling movetype step objects
  983. // Simulation should be done assuming average velocity over the time
  984. // interval. Since that would effect a lot of code, and since most of
  985. // that code is going away, it's easier to just add in the average effect
  986. // of gravity on the velocity over the interval at the beginning of similation,
  987. // then add it in again at the end of simulation so that the final velocity is
  988. // correct for the entire interval.
  989. //-----------------------------------------------------------------------------
  990. void CBaseEntity::PhysicsAddHalfGravity( float timestep )
  991. {
  992. VPROF("CBaseEntity::PhysicsAddHalfGravity");
  993. float ent_gravity;
  994. if ( GetGravity() )
  995. {
  996. ent_gravity = GetGravity();
  997. }
  998. else
  999. {
  1000. ent_gravity = 1.0;
  1001. }
  1002. // Add 1/2 of the total gravitational effects over this timestep
  1003. Vector vecAbsVelocity = GetAbsVelocity();
  1004. vecAbsVelocity[2] -= ( 0.5 * ent_gravity * GetCurrentGravity() * timestep );
  1005. vecAbsVelocity[2] += GetBaseVelocity()[2] * gpGlobals->frametime;
  1006. SetAbsVelocity( vecAbsVelocity );
  1007. Vector vecNewBaseVelocity = GetBaseVelocity();
  1008. vecNewBaseVelocity[2] = 0;
  1009. SetBaseVelocity( vecNewBaseVelocity );
  1010. // Bound velocity
  1011. PhysicsCheckVelocity();
  1012. }
  1013. //-----------------------------------------------------------------------------
  1014. // Purpose: Does not change the entities velocity at all
  1015. // Input : push -
  1016. // Output : trace_t
  1017. //-----------------------------------------------------------------------------
  1018. void CBaseEntity::PhysicsPushEntity( const Vector& push, trace_t *pTrace )
  1019. {
  1020. VPROF("CBaseEntity::PhysicsPushEntity");
  1021. if ( GetMoveParent() )
  1022. {
  1023. Warning( "pushing entity (%s) that has parent (%s)!\n", GetDebugName(), GetMoveParent()->GetDebugName() );
  1024. Assert(0);
  1025. }
  1026. // NOTE: absorigin and origin must be equal because there is no moveparent
  1027. Vector prevOrigin;
  1028. VectorCopy( GetAbsOrigin(), prevOrigin );
  1029. ::PhysicsCheckSweep( this, prevOrigin, push, pTrace );
  1030. if ( pTrace->fraction )
  1031. {
  1032. SetAbsOrigin( pTrace->endpos );
  1033. // FIXME(ywb): Should we try to enable this here
  1034. // WakeRestingObjects();
  1035. }
  1036. // Passing in the previous abs origin here will cause the relinker
  1037. // to test the swept ray from previous to current location for trigger intersections
  1038. PhysicsTouchTriggers( &prevOrigin );
  1039. if ( pTrace->m_pEnt )
  1040. {
  1041. PhysicsImpact( pTrace->m_pEnt, *pTrace );
  1042. }
  1043. }
  1044. //-----------------------------------------------------------------------------
  1045. // Purpose: See if entity is inside another entity, if so, returns true if so, fills in *ppEntity if ppEntity is not NULL
  1046. // Input : **ppEntity - optional return pointer to entity we are inside of
  1047. // Output : Returns true on success, false on failure.
  1048. //-----------------------------------------------------------------------------
  1049. bool CBaseEntity::PhysicsTestEntityPosition( CBaseEntity **ppEntity /*=NULL*/ )
  1050. {
  1051. VPROF("CBaseEntity::PhysicsTestEntityPosition");
  1052. trace_t trace;
  1053. unsigned int mask = PhysicsSolidMaskForEntity();
  1054. Physics_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), mask, &trace );
  1055. if ( trace.startsolid )
  1056. {
  1057. if ( ppEntity )
  1058. {
  1059. *ppEntity = trace.m_pEnt;
  1060. }
  1061. return true;
  1062. }
  1063. return false;
  1064. }
  1065. //-----------------------------------------------------------------------------
  1066. // Purpose:
  1067. //-----------------------------------------------------------------------------
  1068. CBaseEntity *CBaseEntity::PhysicsPushMove( float movetime )
  1069. {
  1070. VPROF("CBaseEntity::PhysicsPushMove");
  1071. // If this entity isn't moving, just update the time.
  1072. IncrementLocalTime( movetime );
  1073. if ( GetLocalVelocity() == vec3_origin )
  1074. {
  1075. return NULL;
  1076. }
  1077. // Now check that the entire hierarchy can rotate into the new location
  1078. CBaseEntity *pBlocker = g_pPushedEntities->PerformLinearPush( this, movetime );
  1079. if ( pBlocker )
  1080. {
  1081. IncrementLocalTime( -movetime );
  1082. }
  1083. return pBlocker;
  1084. }
  1085. //-----------------------------------------------------------------------------
  1086. // Purpose: Tries to rotate, returns success or failure
  1087. // Input : movetime -
  1088. // Output : bool
  1089. //-----------------------------------------------------------------------------
  1090. CBaseEntity *CBaseEntity::PhysicsPushRotate( float movetime )
  1091. {
  1092. VPROF("CBaseEntity::PhysicsPushRotate");
  1093. IncrementLocalTime( movetime );
  1094. // Not rotating
  1095. if ( GetLocalAngularVelocity() == vec3_angle )
  1096. {
  1097. return NULL;
  1098. }
  1099. // Now check that the entire hierarchy can rotate into the new location
  1100. CBaseEntity *pBlocker = g_pPushedEntities->PerformRotatePush( this, movetime );
  1101. if ( pBlocker )
  1102. {
  1103. IncrementLocalTime( -movetime );
  1104. }
  1105. return pBlocker;
  1106. }
  1107. //-----------------------------------------------------------------------------
  1108. // Block of icky shared code from PhysicsParent + PhysicsPusher
  1109. //-----------------------------------------------------------------------------
  1110. void CBaseEntity::PerformPush( float movetime )
  1111. {
  1112. VPROF("CBaseEntity::PerformPush");
  1113. // NOTE: Use handle index because the previous blocker could have been deleted
  1114. int hPrevBlocker = m_pBlocker.ToInt();
  1115. CBaseEntity *pBlocker;
  1116. g_pPushedEntities->BeginPush( this );
  1117. if (movetime > 0)
  1118. {
  1119. if ( GetLocalAngularVelocity() != vec3_angle )
  1120. {
  1121. if ( GetLocalVelocity() != vec3_origin )
  1122. {
  1123. // NOTE: Both PhysicsPushRotate + PhysicsPushMove
  1124. // will attempt to advance local time. Choose the one that's
  1125. // the greater of the two from push + move
  1126. // FIXME: Should we really be doing them both simultaneously??
  1127. // FIXME: Choose the *greater* of the two?!? That's strange...
  1128. float flInitialLocalTime = m_flLocalTime;
  1129. // moving and rotating, so rotate first, then move
  1130. pBlocker = PhysicsPushRotate( movetime );
  1131. if ( !pBlocker )
  1132. {
  1133. float flRotateLocalTime = m_flLocalTime;
  1134. // Reset the local time to what it was before we rotated
  1135. m_flLocalTime = flInitialLocalTime;
  1136. pBlocker = PhysicsPushMove( movetime );
  1137. if ( m_flLocalTime < flRotateLocalTime )
  1138. {
  1139. m_flLocalTime = flRotateLocalTime;
  1140. }
  1141. }
  1142. }
  1143. else
  1144. {
  1145. // only rotating
  1146. pBlocker = PhysicsPushRotate( movetime );
  1147. }
  1148. }
  1149. else
  1150. {
  1151. // only moving
  1152. pBlocker = PhysicsPushMove( movetime );
  1153. }
  1154. m_pBlocker = pBlocker;
  1155. if (m_pBlocker.ToInt() != hPrevBlocker)
  1156. {
  1157. if (hPrevBlocker != INVALID_EHANDLE_INDEX)
  1158. {
  1159. EndBlocked();
  1160. }
  1161. if (m_pBlocker)
  1162. {
  1163. StartBlocked( pBlocker );
  1164. }
  1165. }
  1166. if (m_pBlocker)
  1167. {
  1168. Blocked( m_pBlocker );
  1169. }
  1170. // NOTE NOTE: This is here for brutal reasons.
  1171. // For MOVETYPE_PUSH objects with VPhysics shadow objects, the move done time
  1172. // is handled by CBaseEntity::VPhyicsUpdatePusher, which only gets called if
  1173. // the physics system thinks the entity is awake. That will happen if the
  1174. // shadow gets updated, but the push code above doesn't update unless the
  1175. // move is successful or non-zero. So we must make sure it's awake
  1176. if ( VPhysicsGetObject() )
  1177. {
  1178. VPhysicsGetObject()->Wake();
  1179. }
  1180. }
  1181. // move done is handled by physics if it has any
  1182. if ( VPhysicsGetObject() )
  1183. {
  1184. // store the list of moved entities for later
  1185. // if you actually did an unblocked push that moved entities, and you're using physics (which may block later)
  1186. if ( movetime > 0 && !m_pBlocker && GetSolid() == SOLID_VPHYSICS && g_pPushedEntities->CountMovedEntities() > 0 )
  1187. {
  1188. // UNDONE: Any reason to want to call this twice before physics runs?
  1189. // If so, maybe just append to the list?
  1190. Assert( !GetDataObject( PHYSICSPUSHLIST ) );
  1191. physicspushlist_t *pList = (physicspushlist_t *)CreateDataObject( PHYSICSPUSHLIST );
  1192. if ( pList )
  1193. {
  1194. g_pPushedEntities->StoreMovedEntities( *pList );
  1195. }
  1196. }
  1197. }
  1198. else
  1199. {
  1200. if ( m_flMoveDoneTime <= m_flLocalTime && m_flMoveDoneTime > 0 )
  1201. {
  1202. SetMoveDoneTime( -1 );
  1203. MoveDone();
  1204. }
  1205. }
  1206. }
  1207. //-----------------------------------------------------------------------------
  1208. // Purpose: UNDONE: This is only different from PhysicsParent because of the callback to PhysicsVelocity()
  1209. // Can we support that callback in push objects as well?
  1210. //-----------------------------------------------------------------------------
  1211. void CBaseEntity::PhysicsPusher( void )
  1212. {
  1213. VPROF("CBaseEntity::PhysicsPusher");
  1214. // regular thinking
  1215. if ( !PhysicsRunThink() )
  1216. return;
  1217. m_flVPhysicsUpdateLocalTime = m_flLocalTime;
  1218. float movetime = GetMoveDoneTime();
  1219. if (movetime > gpGlobals->frametime)
  1220. {
  1221. movetime = gpGlobals->frametime;
  1222. }
  1223. PerformPush( movetime );
  1224. }
  1225. //-----------------------------------------------------------------------------
  1226. // Purpose: Non moving objects can only think
  1227. //-----------------------------------------------------------------------------
  1228. void CBaseEntity::PhysicsNone( void )
  1229. {
  1230. VPROF("CBaseEntity::PhysicsNone");
  1231. // regular thinking
  1232. PhysicsRunThink();
  1233. }
  1234. //-----------------------------------------------------------------------------
  1235. // Purpose: A moving object that doesn't obey physics
  1236. //-----------------------------------------------------------------------------
  1237. void CBaseEntity::PhysicsNoclip( void )
  1238. {
  1239. VPROF("CBaseEntity::PhysicsNoclip");
  1240. // regular thinking
  1241. if ( !PhysicsRunThink() )
  1242. {
  1243. return;
  1244. }
  1245. // Apply angular velocity
  1246. SimulateAngles( gpGlobals->frametime );
  1247. Vector origin;
  1248. VectorMA( GetLocalOrigin(), gpGlobals->frametime, GetLocalVelocity(), origin );
  1249. SetLocalOrigin( origin );
  1250. }
  1251. void CBaseEntity::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
  1252. {
  1253. // If you're going to use custom physics, you need to implement this!
  1254. Assert(0);
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. // Allows entities to describe their own physics
  1258. //-----------------------------------------------------------------------------
  1259. void CBaseEntity::PhysicsCustom()
  1260. {
  1261. VPROF("CBaseEntity::PhysicsCustom");
  1262. PhysicsCheckWater();
  1263. // regular thinking
  1264. if ( !PhysicsRunThink() )
  1265. return;
  1266. // Moving upward, off the ground, or resting on a client/monster, remove FL_ONGROUND
  1267. if ( m_vecVelocity[2] > 0 || !GetGroundEntity() || !GetGroundEntity()->IsStandable() )
  1268. {
  1269. SetGroundEntity( NULL );
  1270. }
  1271. // NOTE: The entity must set the position, angles, velocity in its custom movement
  1272. Vector vecNewPosition = GetAbsOrigin();
  1273. Vector vecNewVelocity = GetAbsVelocity();
  1274. QAngle angNewAngles = GetAbsAngles();
  1275. QAngle angNewAngVelocity = GetLocalAngularVelocity();
  1276. PerformCustomPhysics( &vecNewPosition, &vecNewVelocity, &angNewAngles, &angNewAngVelocity );
  1277. // Store off all of the new state information...
  1278. SetAbsVelocity( vecNewVelocity );
  1279. SetAbsAngles( angNewAngles );
  1280. SetLocalAngularVelocity( angNewAngVelocity );
  1281. Vector move;
  1282. VectorSubtract( vecNewPosition, GetAbsOrigin(), move );
  1283. // move origin
  1284. trace_t trace;
  1285. PhysicsPushEntity( move, &trace );
  1286. PhysicsCheckVelocity();
  1287. if (trace.allsolid)
  1288. {
  1289. // entity is trapped in another solid
  1290. // UNDONE: does this entity needs to be removed?
  1291. SetAbsVelocity(vec3_origin);
  1292. SetLocalAngularVelocity(vec3_angle);
  1293. return;
  1294. }
  1295. if (IsEdictFree())
  1296. return;
  1297. // check for in water
  1298. PhysicsCheckWaterTransition();
  1299. }
  1300. bool g_bTestMoveTypeStepSimulation = true;
  1301. ConVar sv_teststepsimulation( "sv_teststepsimulation", "1", 0 );
  1302. //-----------------------------------------------------------------------------
  1303. // Purpose: Until we remove the above cvar, we need to have the entities able
  1304. // to dynamically deal with changing their simulation stuff here.
  1305. //-----------------------------------------------------------------------------
  1306. void CBaseEntity::CheckStepSimulationChanged()
  1307. {
  1308. if ( g_bTestMoveTypeStepSimulation != IsSimulatedEveryTick() )
  1309. {
  1310. SetSimulatedEveryTick( g_bTestMoveTypeStepSimulation );
  1311. }
  1312. bool hadobject = HasDataObjectType( STEPSIMULATION );
  1313. if ( g_bTestMoveTypeStepSimulation )
  1314. {
  1315. if ( !hadobject )
  1316. {
  1317. CreateDataObject( STEPSIMULATION );
  1318. }
  1319. }
  1320. else
  1321. {
  1322. if ( hadobject )
  1323. {
  1324. DestroyDataObject( STEPSIMULATION );
  1325. }
  1326. }
  1327. }
  1328. #define STEP_TELPORTATION_VEL_SQ ( 4096.0f * 4096.0f )
  1329. //-----------------------------------------------------------------------------
  1330. // Purpose: Run regular think and latch off angle/origin changes so we can interpolate them on the server to fake simulation
  1331. // Input : *step -
  1332. //-----------------------------------------------------------------------------
  1333. void CBaseEntity::StepSimulationThink( float dt )
  1334. {
  1335. // See if we need to allocate, deallocate step simulation object
  1336. CheckStepSimulationChanged();
  1337. StepSimulationData *step = ( StepSimulationData * )GetDataObject( STEPSIMULATION );
  1338. if ( !step )
  1339. {
  1340. PhysicsStepRunTimestep( dt );
  1341. // Just call the think function directly
  1342. PhysicsRunThink( THINK_FIRE_BASE_ONLY );
  1343. }
  1344. else
  1345. {
  1346. // Assume that it's in use
  1347. step->m_bOriginActive = true;
  1348. step->m_bAnglesActive = true;
  1349. // Reset networked versions of origin and angles
  1350. step->m_nLastProcessTickCount = -1;
  1351. step->m_vecNetworkOrigin.Init();
  1352. step->m_angNetworkAngles.Init();
  1353. // Remember old old values
  1354. step->m_Previous2 = step->m_Previous;
  1355. // Remember old values
  1356. step->m_Previous.nTickCount = gpGlobals->tickcount;
  1357. step->m_Previous.vecOrigin = GetStepOrigin();
  1358. QAngle stepAngles = GetStepAngles();
  1359. AngleQuaternion( stepAngles, step->m_Previous.qRotation );
  1360. // Run simulation
  1361. PhysicsStepRunTimestep( dt );
  1362. // Call the actual think function...
  1363. PhysicsRunThink( THINK_FIRE_BASE_ONLY );
  1364. // do any local processing that's needed
  1365. if (GetBaseAnimating() != NULL)
  1366. {
  1367. GetBaseAnimating()->UpdateStepOrigin();
  1368. }
  1369. // Latch new values to see if external code modifies our position/orientation
  1370. step->m_Next.vecOrigin = GetStepOrigin();
  1371. stepAngles = GetStepAngles();
  1372. AngleQuaternion( stepAngles, step->m_Next.qRotation );
  1373. // Also store of non-Quaternion version for simple comparisons
  1374. step->m_angNextRotation = GetStepAngles();
  1375. step->m_Next.nTickCount = GetNextThinkTick();
  1376. // Hack: Add a tick if we are simulating every other tick
  1377. if ( CBaseEntity::IsSimulatingOnAlternateTicks() )
  1378. {
  1379. ++step->m_Next.nTickCount;
  1380. }
  1381. // Check for teleportation/snapping of the origin
  1382. if ( dt > 0.0f )
  1383. {
  1384. Vector deltaorigin = step->m_Next.vecOrigin - step->m_Previous.vecOrigin;
  1385. float velSq = deltaorigin.LengthSqr() / ( dt * dt );
  1386. if ( velSq >= STEP_TELPORTATION_VEL_SQ )
  1387. {
  1388. // Deactivate it due to large origin change
  1389. step->m_bOriginActive = false;
  1390. step->m_bAnglesActive = false;
  1391. }
  1392. }
  1393. }
  1394. }
  1395. //-----------------------------------------------------------------------------
  1396. // Purpose: Monsters freefall when they don't have a ground entity, otherwise
  1397. // all movement is done with discrete steps.
  1398. // This is also used for objects that have become still on the ground, but
  1399. // will fall if the floor is pulled out from under them.
  1400. // JAY: Extended this to synchronize movement and thinking wherever possible.
  1401. // This allows the client-side interpolation to interpolate animation and simulation
  1402. // data at the same time.
  1403. // UNDONE: Remove all other cases from this loop - only use MOVETYPE_STEP to simulate
  1404. // entities that are currently animating/thinking.
  1405. //-----------------------------------------------------------------------------
  1406. void CBaseEntity::PhysicsStep()
  1407. {
  1408. // EVIL HACK: Force these to appear as if they've changed!!!
  1409. // The underlying values don't actually change, but we need the network sendproxy on origin/angles
  1410. // to get triggered, and that only happens if NetworkStateChanged() appears to have occured.
  1411. // Getting them for modify marks them as changed automagically.
  1412. m_vecOrigin.GetForModify();
  1413. m_angRotation.GetForModify();
  1414. // HACK: Make sure that the client latches the networked origin/orientation changes with the current server tick count
  1415. // so that we don't get jittery interpolation. All of this is necessary to mimic actual continuous simulation of the underlying
  1416. // variables.
  1417. SetSimulationTime( gpGlobals->curtime );
  1418. // Run all but the base think function
  1419. PhysicsRunThink( THINK_FIRE_ALL_BUT_BASE );
  1420. int thinktick = GetNextThinkTick();
  1421. float thinktime = thinktick * TICK_INTERVAL;
  1422. // Is the next think too far out, or non-existent?
  1423. // BUGBUG: Interpolation is going to look bad in here. But it should only
  1424. // be for dead things - and those should be ragdolls (client-side sim) anyway.
  1425. // UNDONE: Remove this and assert? Force MOVETYPE_STEP objs to become MOVETYPE_TOSS when
  1426. // they aren't thinking?
  1427. // UNDONE: this happens as the first frame for a bunch of things like dynamically created ents.
  1428. // can't remove until initial conditions are resolved
  1429. float deltaThink = thinktime - gpGlobals->curtime;
  1430. if ( thinktime <= 0 || deltaThink > 0.5 )
  1431. {
  1432. PhysicsStepRunTimestep( gpGlobals->frametime );
  1433. PhysicsCheckWaterTransition();
  1434. SetLastThink( -1, gpGlobals->curtime );
  1435. UpdatePhysicsShadowToCurrentPosition(gpGlobals->frametime);
  1436. PhysicsRelinkChildren(gpGlobals->frametime);
  1437. return;
  1438. }
  1439. Vector oldOrigin = GetAbsOrigin();
  1440. // Feed the position delta back from vphysics if enabled
  1441. bool updateFromVPhysics = npc_vphysics.GetBool();
  1442. if ( HasDataObjectType(VPHYSICSUPDATEAI) )
  1443. {
  1444. vphysicsupdateai_t *pUpdate = static_cast<vphysicsupdateai_t *>(GetDataObject( VPHYSICSUPDATEAI ));
  1445. if ( pUpdate->stopUpdateTime > gpGlobals->curtime )
  1446. {
  1447. updateFromVPhysics = true;
  1448. }
  1449. else
  1450. {
  1451. float maxAngular;
  1452. VPhysicsGetObject()->GetShadowController()->GetMaxSpeed( NULL, &maxAngular );
  1453. VPhysicsGetObject()->GetShadowController()->MaxSpeed( pUpdate->savedShadowControllerMaxSpeed, maxAngular );
  1454. DestroyDataObject(VPHYSICSUPDATEAI);
  1455. }
  1456. }
  1457. if ( updateFromVPhysics && VPhysicsGetObject() && !GetParent() )
  1458. {
  1459. Vector position;
  1460. VPhysicsGetObject()->GetShadowPosition( &position, NULL );
  1461. float delta = (GetAbsOrigin() - position).LengthSqr();
  1462. // for now, use a tolerance of 1 inch for these tests
  1463. if ( delta < 1 )
  1464. {
  1465. // physics is really close, check to see if my current position is valid.
  1466. // If so, ignore the physics result.
  1467. trace_t tr;
  1468. Physics_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin(), PhysicsSolidMaskForEntity(), &tr );
  1469. updateFromVPhysics = tr.startsolid;
  1470. }
  1471. if ( updateFromVPhysics )
  1472. {
  1473. SetAbsOrigin( position );
  1474. PhysicsTouchTriggers();
  1475. }
  1476. //NDebugOverlay::Box( position, WorldAlignMins(), WorldAlignMaxs(), 255, 255, 0, 0, 0.0 );
  1477. }
  1478. // not going to think, don't run game physics either
  1479. if ( thinktick > gpGlobals->tickcount )
  1480. return;
  1481. // Don't let things stay in the past.
  1482. // it is possible to start that way
  1483. // by a trigger with a local time.
  1484. if ( thinktime < gpGlobals->curtime )
  1485. {
  1486. thinktime = gpGlobals->curtime;
  1487. }
  1488. // simulate over the timestep
  1489. float dt = thinktime - GetLastThink();
  1490. // Now run step simulator
  1491. StepSimulationThink( dt );
  1492. PhysicsCheckWaterTransition();
  1493. if ( VPhysicsGetObject() )
  1494. {
  1495. if ( !VectorCompare( oldOrigin, GetAbsOrigin() ) )
  1496. {
  1497. VPhysicsGetObject()->UpdateShadow( GetAbsOrigin(), vec3_angle, (GetFlags() & FL_FLY) ? true : false, dt );
  1498. }
  1499. }
  1500. PhysicsRelinkChildren(dt);
  1501. }
  1502. void UTIL_TraceLineFilterEntity( CBaseEntity *pEntity, const Vector &vecAbsStart, const Vector &vecAbsEnd,
  1503. unsigned int mask, const int nCollisionGroup, trace_t *ptr );
  1504. // Check to see what (if anything) this MOVETYPE_STEP entity is standing on
  1505. void CBaseEntity::PhysicsStepRecheckGround()
  1506. {
  1507. unsigned int mask = PhysicsSolidMaskForEntity();
  1508. // determine if it's on solid ground at all
  1509. Vector mins, maxs, point;
  1510. int x, y;
  1511. trace_t trace;
  1512. VectorAdd (GetAbsOrigin(), WorldAlignMins(), mins);
  1513. VectorAdd (GetAbsOrigin(), WorldAlignMaxs(), maxs);
  1514. point[2] = mins[2] - 1;
  1515. for (x=0 ; x<=1 ; x++)
  1516. {
  1517. for (y=0 ; y<=1 ; y++)
  1518. {
  1519. point[0] = x ? maxs[0] : mins[0];
  1520. point[1] = y ? maxs[1] : mins[1];
  1521. ICollideable *pCollision = GetCollideable();
  1522. if ( pCollision && IsNPC() )
  1523. {
  1524. UTIL_TraceLineFilterEntity( this, point, point, mask, COLLISION_GROUP_NONE, &trace );
  1525. }
  1526. else
  1527. {
  1528. UTIL_TraceLine( point, point, mask, this, COLLISION_GROUP_NONE, &trace );
  1529. }
  1530. if ( trace.startsolid )
  1531. {
  1532. SetGroundEntity( trace.m_pEnt );
  1533. return;
  1534. }
  1535. }
  1536. }
  1537. }
  1538. //-----------------------------------------------------------------------------
  1539. // Purpose:
  1540. // Input : timestep -
  1541. //-----------------------------------------------------------------------------
  1542. void CBaseEntity::PhysicsStepRunTimestep( float timestep )
  1543. {
  1544. bool wasonground;
  1545. bool inwater;
  1546. #if 0
  1547. bool hitsound = false;
  1548. #endif
  1549. float speed, newspeed, control;
  1550. float friction;
  1551. PhysicsCheckVelocity();
  1552. wasonground = ( GetFlags() & FL_ONGROUND ) ? true : false;
  1553. // add gravity except:
  1554. // flying monsters
  1555. // swimming monsters who are in the water
  1556. inwater = PhysicsCheckWater();
  1557. bool isfalling = false;
  1558. if ( !wasonground )
  1559. {
  1560. if ( !( GetFlags() & FL_FLY ) )
  1561. {
  1562. if ( !( ( GetFlags() & FL_SWIM ) && ( GetWaterLevel() > 0 ) ) )
  1563. {
  1564. #if 0
  1565. if ( GetAbsVelocity()[2] < ( GetCurrentGravity() * -0.1 ) )
  1566. {
  1567. hitsound = true;
  1568. }
  1569. #endif
  1570. if ( !inwater )
  1571. {
  1572. PhysicsAddHalfGravity( timestep );
  1573. isfalling = true;
  1574. }
  1575. }
  1576. }
  1577. }
  1578. if ( !(GetFlags() & FL_STEPMOVEMENT) &&
  1579. (!VectorCompare(GetAbsVelocity(), vec3_origin) ||
  1580. !VectorCompare(GetBaseVelocity(), vec3_origin)))
  1581. {
  1582. Vector vecAbsVelocity = GetAbsVelocity();
  1583. SetGroundEntity( NULL );
  1584. // apply friction
  1585. // let dead monsters who aren't completely onground slide
  1586. if ( wasonground )
  1587. {
  1588. speed = VectorLength( vecAbsVelocity );
  1589. if (speed)
  1590. {
  1591. friction = sv_friction.GetFloat() * GetFriction();
  1592. control = speed < sv_stopspeed.GetFloat() ? sv_stopspeed.GetFloat() : speed;
  1593. newspeed = speed - timestep*control*friction;
  1594. if (newspeed < 0)
  1595. newspeed = 0;
  1596. newspeed /= speed;
  1597. vecAbsVelocity[0] *= newspeed;
  1598. vecAbsVelocity[1] *= newspeed;
  1599. }
  1600. }
  1601. vecAbsVelocity += GetBaseVelocity();
  1602. SetAbsVelocity( vecAbsVelocity );
  1603. // Apply angular velocity
  1604. SimulateAngles( timestep );
  1605. PhysicsCheckVelocity();
  1606. PhysicsTryMove( timestep, NULL );
  1607. PhysicsCheckVelocity();
  1608. vecAbsVelocity = GetAbsVelocity();
  1609. vecAbsVelocity -= GetBaseVelocity();
  1610. SetAbsVelocity( vecAbsVelocity );
  1611. PhysicsCheckVelocity();
  1612. if ( !(GetFlags() & FL_ONGROUND) )
  1613. {
  1614. PhysicsStepRecheckGround();
  1615. }
  1616. PhysicsTouchTriggers();
  1617. }
  1618. if (!( GetFlags() & FL_ONGROUND ) && isfalling)
  1619. {
  1620. PhysicsAddHalfGravity( timestep );
  1621. }
  1622. }
  1623. // After this long, if a player isn't updating, then return it's projectiles to server control
  1624. #define PLAYER_PACKETS_STOPPED_SO_RETURN_TO_PHYSICS_TIME 1.0f
  1625. void Physics_SimulateEntity( CBaseEntity *pEntity )
  1626. {
  1627. VPROF( ( !vprof_scope_entity_gamephys.GetBool() ) ?
  1628. "Physics_SimulateEntity" :
  1629. EntityFactoryDictionary()->GetCannonicalName( pEntity->GetClassname() ) );
  1630. if ( pEntity->edict() )
  1631. {
  1632. #if !defined( NO_ENTITY_PREDICTION )
  1633. // Player drives simulation of this entity
  1634. if ( pEntity->IsPlayerSimulated() )
  1635. {
  1636. // If the player is gone, dropped, crashed, then return
  1637. // control to the game code.
  1638. CBasePlayer *simulatingPlayer = pEntity->GetSimulatingPlayer();
  1639. if ( simulatingPlayer &&
  1640. ( simulatingPlayer->GetTimeBase() > gpGlobals->curtime - PLAYER_PACKETS_STOPPED_SO_RETURN_TO_PHYSICS_TIME ) )
  1641. {
  1642. // Okay, the guy is still around
  1643. return;
  1644. }
  1645. pEntity->UnsetPlayerSimulated();
  1646. }
  1647. #endif
  1648. MDLCACHE_CRITICAL_SECTION();
  1649. #if !defined( NO_ENTITY_PREDICTION )
  1650. // If an object was at one point player simulated, but had that status revoked (as just
  1651. // above when no packets have arrived in a while ), then we still will assume that the
  1652. // owner/player will be predicting the entity locally (even if the game is playing like butt)
  1653. // and so we won't spam that player with additional network data such as effects/sounds
  1654. // that are theoretically being predicted by the player anyway.
  1655. if ( pEntity->m_PredictableID->IsActive() )
  1656. {
  1657. CBasePlayer *playerowner = ToBasePlayer( pEntity->GetOwnerEntity() );
  1658. if ( playerowner )
  1659. {
  1660. CBasePlayer *pl = ToBasePlayer( UTIL_PlayerByIndex( pEntity->m_PredictableID->GetPlayer() + 1 ) );
  1661. // Is the player who created it still the owner?
  1662. if ( pl == playerowner )
  1663. {
  1664. // Set up to suppress sending events to owner player
  1665. if ( pl->IsPredictingWeapons() )
  1666. {
  1667. IPredictionSystem::SuppressHostEvents( playerowner );
  1668. }
  1669. }
  1670. }
  1671. {
  1672. VPROF( ( !vprof_scope_entity_gamephys.GetBool() ) ?
  1673. "pEntity->PhysicsSimulate" :
  1674. EntityFactoryDictionary()->GetCannonicalName( pEntity->GetClassname() ) );
  1675. // Run entity physics
  1676. pEntity->PhysicsSimulate();
  1677. }
  1678. // Restore suppression filter
  1679. IPredictionSystem::SuppressHostEvents( NULL );
  1680. }
  1681. else
  1682. #endif
  1683. {
  1684. // Run entity physics
  1685. pEntity->PhysicsSimulate();
  1686. }
  1687. }
  1688. else
  1689. {
  1690. pEntity->PhysicsRunThink();
  1691. }
  1692. }
  1693. //-----------------------------------------------------------------------------
  1694. // Purpose: Runs the main physics simulation loop against all entities ( except players )
  1695. //-----------------------------------------------------------------------------
  1696. void Physics_RunThinkFunctions( bool simulating )
  1697. {
  1698. VPROF( "Physics_RunThinkFunctions");
  1699. g_bTestMoveTypeStepSimulation = sv_teststepsimulation.GetBool();
  1700. float starttime = gpGlobals->curtime;
  1701. // clear all entites freed outside of this loop
  1702. gEntList.CleanupDeleteList();
  1703. if ( !simulating )
  1704. {
  1705. // only simulate players
  1706. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1707. {
  1708. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1709. if ( pPlayer )
  1710. {
  1711. // Always reset clock to real sv.time
  1712. gpGlobals->curtime = starttime;
  1713. // Force usercmd processing even though gpGlobals->tickcount isn't incrementing
  1714. pPlayer->ForceSimulation();
  1715. Physics_SimulateEntity( pPlayer );
  1716. }
  1717. }
  1718. }
  1719. else
  1720. {
  1721. UTIL_DisableRemoveImmediate();
  1722. int listMax = SimThink_ListCount();
  1723. listMax = MAX(listMax,1);
  1724. CBaseEntity **list = (CBaseEntity **)stackalloc( sizeof(CBaseEntity *) * listMax );
  1725. // iterate through all entities and have them think or simulate
  1726. // UNDONE: This has problems with UTIL_RemoveImmediate() (now disabled during this loop).
  1727. // Do we really need UTIL_RemoveImmediate()?
  1728. int count = SimThink_ListCopy( list, listMax );
  1729. //DevMsg(1, "Count: %d\n", count );
  1730. for ( int i = 0; i < count; i++ )
  1731. {
  1732. if ( !list[i] )
  1733. continue;
  1734. // Always reset clock to real sv.time
  1735. gpGlobals->curtime = starttime;
  1736. Physics_SimulateEntity( list[i] );
  1737. }
  1738. stackfree( list );
  1739. UTIL_EnableRemoveImmediate();
  1740. }
  1741. gpGlobals->curtime = starttime;
  1742. }