Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2340 lines
72 KiB

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