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.

479 lines
13 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "physics_saverestore.h"
  8. #include "vphysics/friction.h"
  9. #include "ai_basenpc.h"
  10. #include "movevars_shared.h"
  11. #ifdef PORTAL2
  12. #include "portal_player.h"
  13. #include "portal_grabcontroller_shared.h"
  14. #endif
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. class CPhysicsNPCSolver : public CLogicalEntity, public IMotionEvent
  18. {
  19. DECLARE_CLASS( CPhysicsNPCSolver, CLogicalEntity );
  20. public:
  21. CPhysicsNPCSolver();
  22. ~CPhysicsNPCSolver();
  23. DECLARE_DATADESC();
  24. void Init( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject, bool disableCollisions, float separationTime );
  25. static CPhysicsNPCSolver *Create( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject, bool disableCollisions, float separationTime );
  26. // CBaseEntity
  27. virtual void Spawn();
  28. virtual void UpdateOnRemove();
  29. virtual void Think();
  30. virtual void OnRestore()
  31. {
  32. BaseClass::OnRestore();
  33. if ( m_allowIntersection )
  34. {
  35. PhysDisableEntityCollisions( m_hNPC, m_hEntity );
  36. }
  37. }
  38. // IMotionEvent
  39. virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  40. public:
  41. CPhysicsNPCSolver *m_pNext;
  42. private:
  43. // locals
  44. void ResetCancelTime();
  45. void BecomePenetrationSolver();
  46. bool IsIntersecting();
  47. bool IsContactOnNPCHead( IPhysicsFrictionSnapshot *pSnapshot, IPhysicsObject *pPhysics, CAI_BaseNPC *pNPC );
  48. bool CheckTouching();
  49. friend bool NPCPhysics_SolverExists( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject );
  50. CHandle<CAI_BaseNPC> m_hNPC;
  51. EHANDLE m_hEntity;
  52. IPhysicsMotionController *m_pController;
  53. float m_separationDuration;
  54. float m_cancelTime;
  55. bool m_allowIntersection;
  56. };
  57. LINK_ENTITY_TO_CLASS( physics_npc_solver, CPhysicsNPCSolver );
  58. BEGIN_DATADESC( CPhysicsNPCSolver )
  59. DEFINE_FIELD( m_hNPC, FIELD_EHANDLE ),
  60. DEFINE_FIELD( m_hEntity, FIELD_EHANDLE ),
  61. DEFINE_FIELD( m_separationDuration, FIELD_FLOAT ),
  62. DEFINE_FIELD( m_cancelTime, FIELD_TIME ),
  63. DEFINE_FIELD( m_allowIntersection, FIELD_BOOLEAN ),
  64. DEFINE_PHYSPTR( m_pController ),
  65. //DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
  66. END_DATADESC()
  67. CEntityClassList<CPhysicsNPCSolver> g_SolverList;
  68. template <> CPhysicsNPCSolver *CEntityClassList<CPhysicsNPCSolver>::m_pClassList = NULL;
  69. bool NPCPhysics_SolverExists( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject )
  70. {
  71. CPhysicsNPCSolver *pSolver = g_SolverList.m_pClassList;
  72. while ( pSolver )
  73. {
  74. if ( pSolver->m_hEntity == pPhysicsObject && pSolver->m_hNPC == pNPC )
  75. return true;
  76. pSolver = pSolver->m_pNext;
  77. }
  78. return false;
  79. }
  80. CPhysicsNPCSolver *CPhysicsNPCSolver::Create( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject, bool disableCollisions, float separationTime )
  81. {
  82. CPhysicsNPCSolver *pSolver = (CPhysicsNPCSolver *)CBaseEntity::CreateNoSpawn( "physics_npc_solver", vec3_origin, vec3_angle, NULL );
  83. pSolver->Init( pNPC, pPhysicsObject, disableCollisions, separationTime );
  84. pSolver->Spawn();
  85. //NDebugOverlay::EntityBounds(pNPC, 255, 255, 0, 64, 0.5f );
  86. return pSolver;
  87. }
  88. CPhysicsNPCSolver::CPhysicsNPCSolver()
  89. {
  90. g_SolverList.Insert( this );
  91. }
  92. CPhysicsNPCSolver::~CPhysicsNPCSolver()
  93. {
  94. g_SolverList.Remove( this );
  95. }
  96. void CPhysicsNPCSolver::Init( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject, bool disableCollisions, float separationTime )
  97. {
  98. m_hNPC = pNPC;
  99. m_hEntity = pPhysicsObject;
  100. m_pController = NULL;
  101. m_separationDuration = separationTime;
  102. m_allowIntersection = disableCollisions;
  103. }
  104. void CPhysicsNPCSolver::ResetCancelTime()
  105. {
  106. m_cancelTime = gpGlobals->curtime + m_separationDuration;
  107. SetNextThink( m_cancelTime );
  108. }
  109. void CPhysicsNPCSolver::BecomePenetrationSolver()
  110. {
  111. CBaseEntity *pEntity = m_hEntity.Get();
  112. if ( pEntity )
  113. {
  114. m_allowIntersection = true;
  115. IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
  116. int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
  117. PhysDisableEntityCollisions( m_hNPC, pEntity );
  118. m_pController = physenv->CreateMotionController( this );
  119. for ( int i = 0; i < listCount; i++ )
  120. {
  121. m_pController->AttachObject( pList[i], false );
  122. pList[i]->Wake();
  123. }
  124. m_pController->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
  125. }
  126. }
  127. void CPhysicsNPCSolver::Spawn()
  128. {
  129. if ( m_allowIntersection )
  130. {
  131. BecomePenetrationSolver();
  132. }
  133. else
  134. {
  135. m_hEntity->SetNavIgnore();
  136. }
  137. ResetCancelTime();
  138. }
  139. void CPhysicsNPCSolver::UpdateOnRemove()
  140. {
  141. if ( m_allowIntersection )
  142. {
  143. physenv->DestroyMotionController( m_pController );
  144. m_pController = NULL;
  145. PhysEnableEntityCollisions( m_hNPC, m_hEntity );
  146. }
  147. else
  148. {
  149. if ( m_hEntity.Get() )
  150. {
  151. m_hEntity->ClearNavIgnore();
  152. }
  153. }
  154. //NDebugOverlay::EntityBounds(m_hNPC, 0, 255, 0, 64, 0.5f );
  155. BaseClass::UpdateOnRemove();
  156. }
  157. bool CPhysicsNPCSolver::IsIntersecting()
  158. {
  159. CAI_BaseNPC *pNPC = m_hNPC.Get();
  160. CBaseEntity *pPhysics = m_hEntity.Get();
  161. if ( pNPC && pPhysics )
  162. {
  163. Ray_t ray;
  164. // bloated bounds to force slight separation
  165. Vector mins = pNPC->WorldAlignMins() - Vector(1,1,1);
  166. Vector maxs = pNPC->WorldAlignMaxs() + Vector(1,1,1);
  167. ray.Init( pNPC->GetAbsOrigin(), pNPC->GetAbsOrigin(), mins, maxs );
  168. trace_t tr;
  169. enginetrace->ClipRayToEntity( ray, pNPC->PhysicsSolidMaskForEntity(), pPhysics, &tr );
  170. if ( tr.startsolid )
  171. return true;
  172. }
  173. return false;
  174. }
  175. bool CPhysicsNPCSolver::IsContactOnNPCHead( IPhysicsFrictionSnapshot *pSnapshot, IPhysicsObject *pPhysics, CAI_BaseNPC *pNPC )
  176. {
  177. float heightCheck = pNPC->GetAbsOrigin().z + pNPC->GetHullMaxs().z;
  178. Vector vel, point;
  179. pPhysics->GetVelocity( &vel, NULL );
  180. pSnapshot->GetContactPoint( point );
  181. // don't care if the object is already moving away
  182. if ( vel.LengthSqr() < 10.0f*10.0f )
  183. {
  184. float topdist = fabs(point.z-heightCheck);
  185. if ( topdist < 2.0f )
  186. {
  187. return true;
  188. }
  189. }
  190. return false;
  191. }
  192. bool CPhysicsNPCSolver::CheckTouching()
  193. {
  194. CAI_BaseNPC *pNPC = m_hNPC.Get();
  195. if ( !pNPC )
  196. return false;
  197. CBaseEntity *pPhysicsEnt = m_hEntity.Get();
  198. if ( !pPhysicsEnt )
  199. return false;
  200. IPhysicsObject *pPhysics = pPhysicsEnt->VPhysicsGetObject();
  201. IPhysicsObject *pNPCPhysics = pNPC->VPhysicsGetObject();
  202. if ( !pNPCPhysics || !pPhysics )
  203. return false;
  204. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  205. bool found = false;
  206. bool penetrate = false;
  207. while ( pSnapshot->IsValid() )
  208. {
  209. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  210. if ( pOther == pNPCPhysics )
  211. {
  212. found = true;
  213. if ( IsContactOnNPCHead(pSnapshot, pPhysics, pNPC ) )
  214. {
  215. penetrate = true;
  216. pSnapshot->MarkContactForDelete();
  217. }
  218. break;
  219. }
  220. pSnapshot->NextFrictionData();
  221. }
  222. pSnapshot->DeleteAllMarkedContacts( true );
  223. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  224. // if the object is penetrating something, check to see if it's intersecting this NPC
  225. // if so, go ahead and switch over to penetration solver mode
  226. if ( !penetrate && (pPhysics->GetGameFlags() & FVPHYSICS_PENETRATING) )
  227. {
  228. penetrate = IsIntersecting();
  229. }
  230. if ( penetrate )
  231. {
  232. pPhysicsEnt->ClearNavIgnore();
  233. BecomePenetrationSolver();
  234. }
  235. return found;
  236. }
  237. void CPhysicsNPCSolver::Think()
  238. {
  239. bool finished = m_allowIntersection ? !IsIntersecting() : !CheckTouching();
  240. if ( finished )
  241. {
  242. UTIL_Remove(this);
  243. return;
  244. }
  245. if ( m_allowIntersection )
  246. {
  247. IPhysicsObject *pObject = m_hEntity->VPhysicsGetObject();
  248. if ( !pObject )
  249. {
  250. UTIL_Remove(this);
  251. return;
  252. }
  253. pObject->Wake();
  254. }
  255. ResetCancelTime();
  256. }
  257. IMotionEvent::simresult_e CPhysicsNPCSolver::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject,
  258. float deltaTime, Vector &linear, AngularImpulse &angular )
  259. {
  260. if ( IsIntersecting() )
  261. {
  262. const float PUSH_SPEED = 150.0f;
  263. if ( pObject->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  264. {
  265. CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
  266. if ( pPlayer )
  267. {
  268. #ifdef PORTAL2
  269. CPortal_Player *pPortalPlayer = (CPortal_Player*)pPlayer;
  270. if ( !pPortalPlayer->IsUsingVMGrab() )
  271. {
  272. pPlayer->ForceDropOfCarriedPhysObjects( m_hEntity );
  273. }
  274. #endif
  275. }
  276. }
  277. ResetCancelTime();
  278. angular.Init();
  279. linear.Init();
  280. // Don't push on vehicles because they won't move
  281. if ( pObject->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY )
  282. {
  283. if ( m_hEntity->GetServerVehicle() )
  284. return SIM_NOTHING;
  285. }
  286. Vector origin, vel;
  287. pObject->GetPosition( &origin, NULL );
  288. pObject->GetVelocity( &vel, NULL );
  289. Vector dir = origin - m_hNPC->GetAbsOrigin();
  290. dir.z = dir.z > 0 ? 0.1f : -0.1f;
  291. VectorNormalize(dir);
  292. AngularImpulse angVel;
  293. angVel.Init();
  294. // NOTE: Iterate this object's contact points
  295. // if it can't move in this direction, try sliding along the plane/crease
  296. Vector pushImpulse;
  297. PhysComputeSlideDirection( pObject, dir * PUSH_SPEED, angVel, &pushImpulse, NULL, 0 );
  298. dir = pushImpulse;
  299. VectorNormalize(dir);
  300. if ( DotProduct( vel, dir ) < PUSH_SPEED * 0.5f )
  301. {
  302. linear = pushImpulse;
  303. if ( pObject->GetContactPoint(NULL,NULL) )
  304. {
  305. linear.z += sv_gravity.GetFloat();
  306. }
  307. }
  308. return SIM_GLOBAL_ACCELERATION;
  309. }
  310. return SIM_NOTHING;
  311. }
  312. CBaseEntity *NPCPhysics_CreateSolver( CAI_BaseNPC *pNPC, CBaseEntity *pPhysicsObject, bool disableCollisions, float separationDuration )
  313. {
  314. if ( disableCollisions )
  315. {
  316. if ( PhysEntityCollisionsAreDisabled( pNPC, pPhysicsObject ) )
  317. return NULL;
  318. }
  319. else
  320. {
  321. if ( pPhysicsObject->IsNavIgnored() )
  322. return NULL;
  323. }
  324. return CPhysicsNPCSolver::Create( pNPC, pPhysicsObject, disableCollisions, separationDuration );
  325. }
  326. class CPhysicsEntitySolver : public CLogicalEntity//, public IMotionEvent
  327. {
  328. DECLARE_CLASS( CPhysicsEntitySolver, CLogicalEntity );
  329. public:
  330. DECLARE_DATADESC();
  331. void Init( CBaseEntity *pMovingEntity, CBaseEntity *pPhysicsBlocker, float separationTime );
  332. static CPhysicsEntitySolver *Create( CBaseEntity *pMovingEntity, CBaseEntity *pPhysicsBlocker, float separationTime );
  333. // CBaseEntity
  334. virtual void Spawn();
  335. virtual void UpdateOnRemove();
  336. virtual void Think();
  337. // IMotionEvent
  338. //virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
  339. private:
  340. // locals
  341. void ResetCancelTime();
  342. void BecomePenetrationSolver();
  343. //bool IsIntersecting();
  344. //bool IsTouching();
  345. EHANDLE m_hMovingEntity;
  346. EHANDLE m_hPhysicsBlocker;
  347. //IPhysicsMotionController *m_pController;
  348. float m_separationDuration;
  349. float m_cancelTime;
  350. int m_savedCollisionGroup;
  351. };
  352. LINK_ENTITY_TO_CLASS( physics_entity_solver, CPhysicsEntitySolver );
  353. BEGIN_DATADESC( CPhysicsEntitySolver )
  354. DEFINE_FIELD( m_hMovingEntity, FIELD_EHANDLE ),
  355. DEFINE_FIELD( m_hPhysicsBlocker, FIELD_EHANDLE ),
  356. DEFINE_FIELD( m_separationDuration, FIELD_FLOAT ),
  357. DEFINE_FIELD( m_cancelTime, FIELD_TIME ),
  358. DEFINE_FIELD( m_savedCollisionGroup, FIELD_INTEGER ),
  359. //DEFINE_PHYSPTR( m_pController ),
  360. END_DATADESC()
  361. CPhysicsEntitySolver *CPhysicsEntitySolver::Create( CBaseEntity *pMovingEntity, CBaseEntity *pPhysicsBlocker, float separationTime )
  362. {
  363. CPhysicsEntitySolver *pSolver = (CPhysicsEntitySolver *)CBaseEntity::CreateNoSpawn( "physics_entity_solver", vec3_origin, vec3_angle, NULL );
  364. pSolver->Init( pMovingEntity, pPhysicsBlocker, separationTime );
  365. pSolver->Spawn();
  366. //NDebugOverlay::EntityBounds(pNPC, 255, 255, 0, 64, 0.5f );
  367. return pSolver;
  368. }
  369. void CPhysicsEntitySolver::Init( CBaseEntity *pMovingEntity, CBaseEntity *pPhysicsBlocker, float separationTime )
  370. {
  371. m_hMovingEntity = pMovingEntity;
  372. m_hPhysicsBlocker = pPhysicsBlocker;
  373. //m_pController = NULL;
  374. m_separationDuration = separationTime;
  375. }
  376. void CPhysicsEntitySolver::Spawn()
  377. {
  378. SetNextThink( gpGlobals->curtime + m_separationDuration );
  379. PhysDisableEntityCollisions( m_hMovingEntity, m_hPhysicsBlocker );
  380. m_savedCollisionGroup = m_hPhysicsBlocker->GetCollisionGroup();
  381. m_hPhysicsBlocker->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  382. if ( m_hPhysicsBlocker->VPhysicsGetObject() )
  383. {
  384. m_hPhysicsBlocker->VPhysicsGetObject()->RecheckContactPoints();
  385. }
  386. }
  387. void CPhysicsEntitySolver::Think()
  388. {
  389. UTIL_Remove(this);
  390. }
  391. void CPhysicsEntitySolver::UpdateOnRemove()
  392. {
  393. //physenv->DestroyMotionController( m_pController );
  394. //m_pController = NULL;
  395. CBaseEntity *pEntity = m_hMovingEntity.Get();
  396. CBaseEntity *pPhysics = m_hPhysicsBlocker.Get();
  397. if ( pEntity && pPhysics )
  398. {
  399. PhysEnableEntityCollisions( pEntity, pPhysics );
  400. }
  401. if ( pPhysics )
  402. {
  403. pPhysics->SetCollisionGroup( m_savedCollisionGroup );
  404. }
  405. BaseClass::UpdateOnRemove();
  406. }
  407. CBaseEntity *EntityPhysics_CreateSolver( CBaseEntity *pMovingEntity, CBaseEntity *pPhysicsObject, bool disableCollisions, float separationDuration )
  408. {
  409. if ( PhysEntityCollisionsAreDisabled( pMovingEntity, pPhysicsObject ) )
  410. return NULL;
  411. return CPhysicsEntitySolver::Create( pMovingEntity, pPhysicsObject, separationDuration );
  412. }