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

468 lines
12 KiB

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