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.

495 lines
18 KiB

  1. //========= Copyright Valve Corporation,k All rights reserved. ============//
  2. //
  3. //
  4. // Note: This code integrated then adapted from TF:
  5. // //ValveGames/staging/src/game/server/tf/tf_pushentity.cpp
  6. //
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "pushentity.h"
  11. #include "cs_player.h"
  12. #include "collisionutils.h"
  13. #include "cs_gamerules.h"
  14. //#include "mathlib/mathlib.h"
  15. class CCSPhysicsPushEntities : public CPhysicsPushedEntities
  16. {
  17. public:
  18. DECLARE_CLASS( CCSPhysicsPushEntities, CPhysicsPushedEntities );
  19. // Constructor/Destructor.
  20. CCSPhysicsPushEntities();
  21. ~CCSPhysicsPushEntities();
  22. protected:
  23. // Speculatively checks to see if all entities in this list can be pushed
  24. virtual bool SpeculativelyCheckRotPush( const RotatingPushMove_t &rotPushMove, CBaseEntity *pRoot ) OVERRIDE;
  25. virtual bool SpeculativelyCheckLinearPush( const Vector &vecAbsPush ) OVERRIDE;
  26. virtual void FinishRotPushedEntity( CBaseEntity *pPushedEntity, const RotatingPushMove_t &rotPushMove ) OVERRIDE;
  27. private:
  28. bool RotationPushCSPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, const RotatingPushMove_t &rotPushMove, bool bRotationalPush, CBaseEntity *pRoot );
  29. bool RotationCheckPush( PhysicsPushedInfo_t &info, bool bIgnoreTeammates );
  30. bool LinearPushCSPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, bool bRotationalPush );
  31. bool LinearCheckPush( PhysicsPushedInfo_t &info, bool bIgnoreTeammates );
  32. void EnsureValidPushWhileRiding( CBaseEntity *pBlocker, CBaseEntity *pPusher );
  33. bool IsPlayerAABBIntersetingPusherOBB( CBaseEntity *pEntity, CBaseEntity *pRootEntity );
  34. void MovePlayer( CBaseEntity *pBlocker, PhysicsPushedInfo_t &info, float flMoveScale, bool bPusherIsTrain, bool bIgnoreTeammates );
  35. void FindNewPushDirection( Vector &vecCurrent, Vector &vecNormal, Vector &vecOutput );
  36. float m_flPushDist;
  37. Vector m_vecPushVector;
  38. };
  39. CCSPhysicsPushEntities s_CSPushedEntities;
  40. CPhysicsPushedEntities *g_pPushedEntities = &s_CSPushedEntities;
  41. //-----------------------------------------------------------------------------
  42. // Purpose: Constructor.
  43. //-----------------------------------------------------------------------------
  44. CCSPhysicsPushEntities::CCSPhysicsPushEntities()
  45. {
  46. }
  47. //-----------------------------------------------------------------------------
  48. // Purpose: Destructor.
  49. //-----------------------------------------------------------------------------
  50. CCSPhysicsPushEntities::~CCSPhysicsPushEntities()
  51. {
  52. }
  53. //-----------------------------------------------------------------------------
  54. // Purpose:
  55. //-----------------------------------------------------------------------------
  56. bool CCSPhysicsPushEntities::SpeculativelyCheckRotPush( const RotatingPushMove_t &rotPushMove, CBaseEntity *pRoot )
  57. {
  58. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  59. Vector vecAbsPush( 0.0f, 0.0f, 0.0f );
  60. m_nBlocker = -1;
  61. int nMovedCount = m_rgMoved.Count();
  62. for ( int i = ( nMovedCount - 1 ); i >= 0; --i )
  63. {
  64. // Is the entity and CS Player?
  65. CCSPlayer *pCSPlayer = NULL;
  66. bool bPusherIsTrain = false;
  67. if ( m_rgMoved[i].m_pEntity && m_rgMoved[i].m_pEntity->IsPlayer() )
  68. {
  69. pCSPlayer = ToCSPlayer( m_rgMoved[i].m_pEntity );
  70. CBaseEntity* pPusher = m_rgPusher[ 0 ].m_pEntity->GetRootMoveParent();
  71. bPusherIsTrain = pPusher && pPusher->IsBaseTrain();
  72. }
  73. // Special code to move the player away from the func_train.
  74. // Only do this if it's a train pushing a player--otherwise use base class.
  75. if ( pCSPlayer && bPusherIsTrain )
  76. {
  77. // Rotationally push the player!
  78. ComputeRotationalPushDirection( m_rgMoved[ i ].m_pEntity, rotPushMove, &vecAbsPush, pRoot );
  79. RotationPushCSPlayer( m_rgMoved[i], vecAbsPush, rotPushMove, true, pRoot );
  80. }
  81. else
  82. {
  83. // Keep this in sync with BaseClass::SpeculativelyCheckRotPush
  84. ComputeRotationalPushDirection( m_rgMoved[i].m_pEntity, rotPushMove, &vecAbsPush, pRoot );
  85. if ( !SpeculativelyCheckPush( m_rgMoved[i], vecAbsPush, true, pRoot ) )
  86. {
  87. m_nBlocker = i;
  88. return false;
  89. }
  90. }
  91. }
  92. return true;
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Speculatively checks to see if all entities in this list can be pushed
  96. //-----------------------------------------------------------------------------
  97. bool CCSPhysicsPushEntities::SpeculativelyCheckLinearPush( const Vector &vecAbsPush )
  98. {
  99. TM_ZONE_DEFAULT( TELEMETRY_LEVEL0 );
  100. m_nBlocker = -1;
  101. int nMovedCount = m_rgMoved.Count();
  102. for ( int i = ( nMovedCount - 1 ); i >= 0; --i )
  103. {
  104. // Is the entity and CS Player?
  105. CCSPlayer *pCSPlayer = NULL;
  106. bool bPusherIsTrain = false;
  107. if ( m_rgMoved[i].m_pEntity && m_rgMoved[i].m_pEntity->IsPlayer() )
  108. {
  109. pCSPlayer = ToCSPlayer( m_rgMoved[i].m_pEntity );
  110. CBaseEntity* pPusher = m_rgPusher[0].m_pEntity->GetRootMoveParent();
  111. bPusherIsTrain = pPusher && pPusher->IsBaseTrain();
  112. }
  113. // Special code to move the player away from the func_train.
  114. // Only do this if it's a train pushing a player--otherwise use base class.
  115. if ( pCSPlayer && bPusherIsTrain )
  116. {
  117. // Linearly push the player!
  118. LinearPushCSPlayer( m_rgMoved[i], vecAbsPush, false );
  119. }
  120. else
  121. {
  122. // Keep this in sync with BaseClass::SpeculativelyCheckLinearPush
  123. if ( !SpeculativelyCheckPush( m_rgMoved[i], vecAbsPush, false, NULL ) )
  124. {
  125. m_nBlocker = i;
  126. return false;
  127. }
  128. }
  129. }
  130. return true;
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Purpose:
  134. //-----------------------------------------------------------------------------
  135. bool CCSPhysicsPushEntities::RotationPushCSPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, const RotatingPushMove_t &rotPushMove, bool bRotationalPush, CBaseEntity* pRoot )
  136. {
  137. const bool cbIgnoreTeammates = !CSGameRules() || !CSGameRules()->IsTeammateSolid();
  138. Assert( cbIgnoreTeammates ); // This code doesn't behave if teammates are solid.
  139. // Clear out the collision entity so that if we early out we don't send bogus collision data to the physics system.
  140. info.m_Trace.m_pEnt = NULL;
  141. // Look into doing a full engine->CM_Clear( trace)
  142. // Get the player.
  143. CCSPlayer *pPlayer = ToCSPlayer( info.m_pEntity );
  144. if ( !pPlayer )
  145. return false;
  146. info.m_vecStartAbsOrigin = pPlayer->GetAbsOrigin();
  147. // Get the player collision data.
  148. CCollisionProperty *pCollisionPlayer = info.m_pEntity->CollisionProp();
  149. if ( !pCollisionPlayer )
  150. return false;
  151. // Find the root object if in hierarchy.
  152. CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent();
  153. if ( !pRootEntity )
  154. return false;
  155. // This code doesn't at all match the code in AvoidPushawayProps, which is a bummer. It'd be
  156. // great if this just got rolled into that. Unfortunately, doing so would also require
  157. // making trains predictive--they are not currently.
  158. if ( !pPlayer->GetGroundEntity() || pPlayer->GetGroundEntity()->GetRootMoveParent() != pRootEntity )
  159. {
  160. Vector vMinPushAway = vecAbsPush;
  161. m_flPushDist = VectorNormalize( vMinPushAway );
  162. m_vecPushVector = vMinPushAway;
  163. Assert( !m_vecPushVector.IsZero() ); // Is our push vector legit?
  164. }
  165. else
  166. {
  167. SpeculativelyCheckPush( info, vecAbsPush, true, pRoot, cbIgnoreTeammates );
  168. EnsureValidPushWhileRiding( pPlayer, pRootEntity );
  169. }
  170. return RotationCheckPush( info, cbIgnoreTeammates );
  171. }
  172. //-----------------------------------------------------------------------------
  173. // Purpose:
  174. //-----------------------------------------------------------------------------
  175. bool CCSPhysicsPushEntities::RotationCheckPush( PhysicsPushedInfo_t &info, bool bIgnoreTeammates )
  176. {
  177. // Get the blocking and pushing entities.
  178. CBaseEntity *pBlocker = info.m_pEntity;
  179. CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent();
  180. if ( !pBlocker || !pRootEntity )
  181. return true;
  182. int *pPusherHandles = ( int* )stackalloc( m_rgPusher.Count() * sizeof( int ) );
  183. UnlinkPusherList( pPusherHandles );
  184. for ( int iPushTry = 0; iPushTry < 3; ++iPushTry )
  185. {
  186. MovePlayer( pBlocker, info, 0.35f, pRootEntity->IsBaseTrain(), bIgnoreTeammates );
  187. if ( IsPushedPositionValid( pBlocker, bIgnoreTeammates ) )
  188. break;
  189. }
  190. RelinkPusherList( pPusherHandles );
  191. // Is the blocked ground the push entity?
  192. info.m_bPusherIsGround = false;
  193. if ( pBlocker->GetGroundEntity() && pBlocker->GetGroundEntity()->GetRootMoveParent() == m_rgPusher[0].m_pEntity )
  194. {
  195. info.m_bPusherIsGround = true;
  196. }
  197. // Check to see if the player is in a good spot and attempt a move again if not - but only if it isn't being ridden on.
  198. if ( !IsPushedPositionValid( pBlocker, bIgnoreTeammates ) )
  199. {
  200. // Try again is the player is still blocked.
  201. DevMsg( 2, "Pushing rotation hard!\n" );
  202. UnlinkPusherList( pPusherHandles );
  203. MovePlayer( pBlocker, info, 1.0f, pRootEntity->IsBaseTrain(), bIgnoreTeammates );
  204. RelinkPusherList( pPusherHandles );
  205. }
  206. // The player will never stop a train from moving in CS.
  207. info.m_bBlocked = false;
  208. return true;
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose:
  212. //-----------------------------------------------------------------------------
  213. bool CCSPhysicsPushEntities::LinearPushCSPlayer( PhysicsPushedInfo_t &info, const Vector &vecAbsPush, bool bRotationalPush )
  214. {
  215. const bool cbIgnoreTeammates = !CSGameRules() || !CSGameRules()->IsTeammateSolid();
  216. Assert( cbIgnoreTeammates ); // This code doesn't behave if teammates are solid.
  217. // Clear out the collision entity so that if we early out we don't send bogus collision data to the physics system.
  218. info.m_Trace.m_pEnt = NULL;
  219. // Get the player.
  220. CCSPlayer *pPlayer = ToCSPlayer( info.m_pEntity );
  221. if ( !pPlayer )
  222. return false;
  223. info.m_vecStartAbsOrigin = pPlayer->GetAbsOrigin();
  224. // Get the player collision data.
  225. CCollisionProperty *pCollisionPlayer = info.m_pEntity->CollisionProp();
  226. if ( !pCollisionPlayer )
  227. return false;
  228. // Find the root object if in hierarchy.
  229. CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent();
  230. if ( !pRootEntity )
  231. return false;
  232. // Get the pusher collision data.
  233. CCollisionProperty *pCollisionPusher = pRootEntity->CollisionProp();
  234. if ( !pCollisionPusher )
  235. return false;
  236. if ( !pPlayer->GetGroundEntity() || pPlayer->GetGroundEntity()->GetRootMoveParent() != pRootEntity )
  237. {
  238. m_vecPushVector = vecAbsPush;
  239. m_flPushDist = VectorNormalize( m_vecPushVector );
  240. }
  241. else
  242. {
  243. // Try to get the base class first.
  244. SpeculativelyCheckPush( info, vecAbsPush, false, NULL, cbIgnoreTeammates );
  245. EnsureValidPushWhileRiding( pPlayer, pRootEntity );
  246. }
  247. return LinearCheckPush( info, cbIgnoreTeammates );
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose:
  251. //-----------------------------------------------------------------------------
  252. bool CCSPhysicsPushEntities::LinearCheckPush( PhysicsPushedInfo_t &info, bool bIgnoreTeammates )
  253. {
  254. // Get the blocking and pushing entities.
  255. CBaseEntity *pBlocker = info.m_pEntity;
  256. CBaseEntity *pRootEntity = m_rgPusher[0].m_pEntity->GetRootMoveParent();
  257. if ( !pBlocker || !pRootEntity )
  258. return true;
  259. // Unlink the pusher from the spatial partition and attempt a player move.
  260. int *pPusherHandles = ( int* )stackalloc( m_rgPusher.Count() * sizeof( int ) );
  261. UnlinkPusherList( pPusherHandles );
  262. MovePlayer( pBlocker, info, 1.0f, pRootEntity->IsBaseTrain(), bIgnoreTeammates );
  263. RelinkPusherList( pPusherHandles );
  264. // Is the pusher the ground entity the blocker is standing on?
  265. info.m_bPusherIsGround = false;
  266. if ( pBlocker->GetGroundEntity() && pBlocker->GetGroundEntity()->GetRootMoveParent() == m_rgPusher[0].m_pEntity )
  267. {
  268. info.m_bPusherIsGround = true;
  269. }
  270. // Check to see if the player is in a good spot and attempt a move again if not - but only if it isn't being ridden on.
  271. if ( !info.m_bPusherIsGround && !IsPushedPositionValid( pBlocker, bIgnoreTeammates ) )
  272. {
  273. // Try again is the player is still blocked.
  274. DevMsg( 2, "Pushing linear hard!\n" );
  275. UnlinkPusherList( pPusherHandles );
  276. MovePlayer( pBlocker, info, 1.0f, pRootEntity->IsBaseTrain(), bIgnoreTeammates );
  277. RelinkPusherList( pPusherHandles );
  278. }
  279. // The player will never stop a train from moving in CS.
  280. info.m_bBlocked = false;
  281. return true;
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: When riding atop a vehicle, try to ensure that the final push vector and
  285. // push distance will land us in a valid location.
  286. //-----------------------------------------------------------------------------
  287. void CCSPhysicsPushEntities::EnsureValidPushWhileRiding( CBaseEntity *pBlocker, CBaseEntity *pPusher )
  288. {
  289. const bool cbIgnoreTeammates = !CSGameRules() || !CSGameRules()->IsTeammateSolid();
  290. Assert( cbIgnoreTeammates ); // This code doesn't behave if teammates are solid.
  291. TM_ZONE_DEFAULT( TELEMETRY_LEVEL3 );
  292. m_vecPushVector.Zero();
  293. m_flPushDist = 0.0f;
  294. // Do we still have a collision?
  295. if ( IsPushedPositionValid( pBlocker, cbIgnoreTeammates ) )
  296. return;
  297. const float cMaxDistToLookForPlacement = 72;
  298. // Try nudging them upwards a bit.
  299. if ( FindValidLocationUpwards( &m_flPushDist, pBlocker, cMaxDistToLookForPlacement, 1.1 ) )
  300. {
  301. m_vecPushVector.z = 1.0f;
  302. }
  303. else
  304. {
  305. // Try to push the player backwards along the direction of travel of the vehicle (and also up a bit)?
  306. Vector vBackwardsAndUp( 0, 0, 1 );
  307. Vector vTraceEndpoint = pPusher->GetAbsVelocity();
  308. vTraceEndpoint.x = -vTraceEndpoint.x;
  309. vTraceEndpoint.y = -vTraceEndpoint.y;
  310. vTraceEndpoint.z = sqrt( vTraceEndpoint.x * vTraceEndpoint.x + vTraceEndpoint.y * vTraceEndpoint.y );
  311. vTraceEndpoint = vTraceEndpoint.Normalized() * cMaxDistToLookForPlacement;
  312. vTraceEndpoint += pBlocker->GetAbsOrigin();
  313. Vector vDelta;
  314. if ( FindValidLocationAlongVector( &vDelta, pBlocker, vTraceEndpoint, 1.1 ) )
  315. {
  316. m_vecPushVector = vDelta;
  317. m_flPushDist = VectorNormalize( m_vecPushVector );
  318. }
  319. else
  320. {
  321. Assert( !"Failed to find a location upwards or backwards for a blocker, sadness!" );
  322. }
  323. }
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. //-----------------------------------------------------------------------------
  328. bool CCSPhysicsPushEntities::IsPlayerAABBIntersetingPusherOBB( CBaseEntity *pEntity, CBaseEntity *pRootEntity )
  329. {
  330. // Get the player.
  331. CCSPlayer *pPlayer = ToCSPlayer( pEntity );
  332. if ( !pPlayer )
  333. return false;
  334. // Get the player collision data.
  335. CCollisionProperty *pCollisionPlayer = pEntity->CollisionProp();
  336. if ( !pCollisionPlayer )
  337. return false;
  338. // Get the pusher collision data.
  339. CCollisionProperty *pCollisionPusher = pRootEntity->CollisionProp();
  340. if ( !pCollisionPusher )
  341. return false;
  342. // Do we have a collision.
  343. return IsOBBIntersectingOBB( pCollisionPlayer->GetCollisionOrigin(), pCollisionPlayer->GetCollisionAngles(), pCollisionPlayer->OBBMins(), pCollisionPlayer->OBBMaxs(),
  344. pCollisionPusher->GetCollisionOrigin(), pCollisionPusher->GetCollisionAngles(), pCollisionPusher->OBBMins(), pCollisionPusher->OBBMaxs(),
  345. 0.0f );
  346. }
  347. //-----------------------------------------------------------------------------
  348. // Purpose:
  349. //-----------------------------------------------------------------------------
  350. void CCSPhysicsPushEntities::FindNewPushDirection( Vector &vecCurrent, Vector &vecNormal, Vector &vecOutput )
  351. {
  352. // Determine how far along plane to slide based on incoming direction.
  353. float flBackOff = DotProduct( vecCurrent, vecNormal );
  354. for ( int iAxis = 0; iAxis < 3; ++iAxis )
  355. {
  356. float flDelta = vecNormal[iAxis] * flBackOff;
  357. vecOutput[iAxis] = vecCurrent[iAxis] - flDelta;
  358. }
  359. // iterate once to make sure we aren't still moving through the plane
  360. float flAdjust = DotProduct( vecOutput, vecNormal );
  361. if( flAdjust < 0.0f )
  362. {
  363. vecOutput -= ( vecNormal * flAdjust );
  364. }
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose:
  368. //-----------------------------------------------------------------------------
  369. void CCSPhysicsPushEntities::MovePlayer( CBaseEntity *pBlocker, PhysicsPushedInfo_t &info, float flMoveScale, bool bPusherIsTrain, bool bIgnoreTeammates )
  370. {
  371. // Find out how far we still need to move.
  372. float flFractionLeft = 1.0f;
  373. float flNewDist = m_flPushDist *flMoveScale;
  374. Vector vecPush = m_vecPushVector;
  375. // Find a new push vector.
  376. Vector vecStart = pBlocker->GetAbsOrigin();
  377. Vector logVecStart = vecStart;
  378. Vector logVecEnd = vecStart;
  379. int iSteps = 0;
  380. vecStart.z += 4.0f;
  381. for ( int iTest = 0; iTest < 4; ++iTest )
  382. {
  383. // Clear the trace entity.
  384. Vector vecEnd = pBlocker->GetAbsOrigin() + ( flNewDist * vecPush );
  385. TraceBlockerEntity( pBlocker, vecStart, vecEnd, bIgnoreTeammates, &info.m_Trace );
  386. if ( info.m_Trace.fraction > 0.0f )
  387. {
  388. pBlocker->SetAbsOrigin( info.m_Trace.endpos );
  389. logVecEnd = info.m_Trace.endpos;
  390. iSteps = iTest + 1;
  391. }
  392. if ( info.m_Trace.fraction == 1.0f || !info.m_Trace.m_pEnt )
  393. break;
  394. // New test distance and position.
  395. flFractionLeft = 1.0f - info.m_Trace.fraction;
  396. flNewDist = flFractionLeft * flNewDist;
  397. flNewDist = flNewDist * ( 1.0f + ( 1.0f - fabs( info.m_Trace.plane.normal.Dot( vecPush ) ) ) );
  398. // Find the new push direction.
  399. Vector vecTmp;
  400. FindNewPushDirection( vecPush, info.m_Trace.plane.normal, vecTmp );
  401. VectorCopy( vecTmp, vecPush );
  402. }
  403. Vector finalPushVec = logVecEnd - logVecStart;
  404. DevMsg( 2, "Pushed player by %.2f over %d steps (push vector: %.2f, %.2f, %.2f)\n", finalPushVec.Length(), iSteps, finalPushVec.x, finalPushVec.y, finalPushVec.z );
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Causes all entities in the list to touch triggers from their prev position
  408. //-----------------------------------------------------------------------------
  409. void CCSPhysicsPushEntities::FinishRotPushedEntity( CBaseEntity *pPushedEntity, const RotatingPushMove_t &rotPushMove )
  410. {
  411. if ( !pPushedEntity->IsPlayer() )
  412. {
  413. QAngle angles = pPushedEntity->GetAbsAngles();
  414. // only rotate YAW with pushing. Freely rotateable entities should either use VPHYSICS
  415. // or be set up as children
  416. angles.y += rotPushMove.amove.y;
  417. pPushedEntity->SetAbsAngles( angles );
  418. }
  419. }