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.

320 lines
9.2 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //===========================================================================//
  9. #include "quakedef.h"
  10. #include "world.h"
  11. #include "eiface.h"
  12. #include "server.h"
  13. #include "cmodel_engine.h"
  14. #include "gl_model_private.h"
  15. #include "sv_main.h"
  16. #include "vengineserver_impl.h"
  17. #include "collisionutils.h"
  18. #include "vphysics_interface.h"
  19. #include "ispatialpartitioninternal.h"
  20. #include "staticpropmgr.h"
  21. #include "shadowmgr.h"
  22. #include "string_t.h"
  23. #include "enginetrace.h"
  24. #include "sys_dll.h"
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. //============================================================================
  28. /*
  29. ===============
  30. SV_ClearWorld
  31. ===============
  32. */
  33. void SV_ClearWorld (void)
  34. {
  35. MDLCACHE_COARSE_LOCK_(g_pMDLCache);
  36. // Clean up static props from the previous level
  37. #if !defined( SWDS )
  38. g_pShadowMgr->LevelShutdown();
  39. #endif // SWDS
  40. StaticPropMgr()->LevelShutdown();
  41. for ( int i = 0; i < 3; i++ )
  42. {
  43. if ( host_state.worldmodel->mins[i] < MIN_COORD_INTEGER || host_state.worldmodel->maxs[i] > MAX_COORD_INTEGER )
  44. {
  45. Host_EndGame(true, "Map coordinate extents are too large!!\nCheck for errors!\n" );
  46. }
  47. }
  48. SpatialPartition()->Init( host_state.worldmodel->mins, host_state.worldmodel->maxs );
  49. // Load all static props into the spatial partition
  50. StaticPropMgr()->LevelInit();
  51. #if !defined( SWDS )
  52. g_pShadowMgr->LevelInit( host_state.worldbrush->numsurfaces );
  53. #endif
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Trigger world-space bounds
  57. //-----------------------------------------------------------------------------
  58. static void CM_TriggerWorldSpaceBounds( ICollideable *pCollideable, Vector *pMins, Vector *pMaxs )
  59. {
  60. if ( pCollideable->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  61. {
  62. pCollideable->WorldSpaceTriggerBounds( pMins, pMaxs );
  63. }
  64. else
  65. {
  66. CM_WorldSpaceBounds( pCollideable, pMins, pMaxs );
  67. }
  68. }
  69. static void CM_GetCollideableTriggerTestBox( ICollideable *pCollide, Vector *pMins, Vector *pMaxs, bool bUseAccurateBbox )
  70. {
  71. if ( bUseAccurateBbox && pCollide->GetSolid() == SOLID_BBOX )
  72. {
  73. *pMins = pCollide->OBBMins();
  74. *pMaxs = pCollide->OBBMaxs();
  75. }
  76. else
  77. {
  78. const Vector &vecStart = pCollide->GetCollisionOrigin();
  79. pCollide->WorldSpaceSurroundingBounds( pMins, pMaxs );
  80. *pMins -= vecStart;
  81. *pMaxs -= vecStart;
  82. }
  83. }
  84. //-----------------------------------------------------------------------------
  85. // Little enumeration class used to try touching all triggers
  86. //-----------------------------------------------------------------------------
  87. class CTouchLinks : public IPartitionEnumerator
  88. {
  89. public:
  90. CTouchLinks( edict_t* pEnt, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
  91. {
  92. m_pEnt = pEnt;
  93. m_pCollide = pEnt->GetCollideable();
  94. Assert( m_pCollide );
  95. Vector vecMins, vecMaxs;
  96. CM_GetCollideableTriggerTestBox( m_pCollide, &vecMins, &vecMaxs, accurateBboxTriggerChecks );
  97. const Vector &vecStart = m_pCollide->GetCollisionOrigin();
  98. if (pPrevAbsOrigin)
  99. {
  100. m_Ray.Init( *pPrevAbsOrigin, vecStart, vecMins, vecMaxs );
  101. }
  102. else
  103. {
  104. m_Ray.Init( vecStart, vecStart, vecMins, vecMaxs );
  105. }
  106. }
  107. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  108. {
  109. // Static props should never be in the trigger list
  110. Assert( !StaticPropMgr()->IsStaticProp( pHandleEntity ) );
  111. IServerUnknown *pUnk = static_cast<IServerUnknown*>( pHandleEntity );
  112. Assert( pUnk );
  113. // Convert the IHandleEntity to an edict_t*...
  114. // Context is the thing we're testing everything against
  115. edict_t* pTouch = pUnk->GetNetworkable()->GetEdict();
  116. // Can't bump against itself
  117. if ( pTouch == m_pEnt )
  118. return ITERATION_CONTINUE;
  119. IServerEntity *pTriggerEntity = pTouch->GetIServerEntity();
  120. if ( !pTriggerEntity )
  121. return ITERATION_CONTINUE;
  122. // Hmmm.. everything in this list should be a trigger....
  123. ICollideable *pTriggerCollideable = pTriggerEntity->GetCollideable();
  124. if ( !m_pCollide->ShouldTouchTrigger(pTriggerCollideable->GetSolidFlags()) )
  125. return ITERATION_CONTINUE;
  126. Assert(pTriggerCollideable->GetSolidFlags() & FSOLID_TRIGGER );
  127. if ( pTriggerCollideable->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  128. {
  129. Vector vecTriggerMins, vecTriggerMaxs;
  130. pTriggerCollideable->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  131. if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, m_Ray ) )
  132. {
  133. return ITERATION_CONTINUE;
  134. }
  135. }
  136. else
  137. {
  138. trace_t tr;
  139. g_pEngineTraceServer->ClipRayToCollideable( m_Ray, MASK_SOLID, pTriggerCollideable, &tr );
  140. if ( !(tr.contents & MASK_SOLID) )
  141. return ITERATION_CONTINUE;
  142. }
  143. m_TouchedEntities.AddToTail( pTouch );
  144. return ITERATION_CONTINUE;
  145. }
  146. void HandleTouchedEntities( )
  147. {
  148. for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
  149. {
  150. serverGameEnts->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pEnt );
  151. }
  152. }
  153. Ray_t m_Ray;
  154. private:
  155. edict_t *m_pEnt;
  156. ICollideable *m_pCollide;
  157. CUtlVector< edict_t* > m_TouchedEntities;
  158. };
  159. // enumerator class that's used to update touch links for a trigger when
  160. // it moves or changes solid type
  161. class CTriggerMoved : public IPartitionEnumerator
  162. {
  163. public:
  164. CTriggerMoved( bool accurateBboxTriggerChecks ) : m_TouchedEntities( 8, 8 )
  165. {
  166. m_bAccurateBBoxCheck = accurateBboxTriggerChecks;
  167. }
  168. void TriggerMoved( edict_t *pTriggerEntity )
  169. {
  170. m_pTriggerEntity = pTriggerEntity;
  171. m_pTrigger = pTriggerEntity->GetCollideable();
  172. m_triggerSolidFlags = m_pTrigger->GetSolidFlags();
  173. Vector vecAbsMins, vecAbsMaxs;
  174. CM_TriggerWorldSpaceBounds( m_pTrigger, &vecAbsMins, &vecAbsMaxs );
  175. SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS,
  176. vecAbsMins, vecAbsMaxs, false, this );
  177. }
  178. IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  179. {
  180. // skip static props, the game DLL doesn't care about them
  181. if ( StaticPropMgr()->IsStaticProp( pHandleEntity ) )
  182. return ITERATION_CONTINUE;
  183. IServerUnknown *pUnk = static_cast< IServerUnknown* >( pHandleEntity );
  184. Assert( pUnk );
  185. // Convert the user ID to and edict_t*...
  186. edict_t* pTouch = pUnk->GetNetworkable()->GetEdict();
  187. Assert( pTouch );
  188. ICollideable *pTouchCollide = pUnk->GetCollideable();
  189. // Can't ever touch itself because it's in the other list
  190. if ( pTouchCollide == m_pTrigger )
  191. return ITERATION_CONTINUE;
  192. if ( !pTouchCollide->ShouldTouchTrigger(m_triggerSolidFlags) )
  193. return ITERATION_CONTINUE;
  194. IServerEntity *serverEntity = pTouch->GetIServerEntity();
  195. if ( !serverEntity )
  196. return ITERATION_CONTINUE;
  197. // FIXME: Should we be using the surrounding bounds here?
  198. Vector vecMins, vecMaxs;
  199. CM_GetCollideableTriggerTestBox( pTouchCollide, &vecMins, &vecMaxs, m_bAccurateBBoxCheck );
  200. const Vector &vecStart = pTouchCollide->GetCollisionOrigin();
  201. Ray_t ray;
  202. ray.Init( vecStart, vecStart, vecMins, vecMaxs );
  203. if ( m_pTrigger->GetSolidFlags() & FSOLID_USE_TRIGGER_BOUNDS )
  204. {
  205. Vector vecTriggerMins, vecTriggerMaxs;
  206. m_pTrigger->WorldSpaceTriggerBounds( &vecTriggerMins, &vecTriggerMaxs );
  207. if ( !IsBoxIntersectingRay( vecTriggerMins, vecTriggerMaxs, ray ) )
  208. {
  209. return ITERATION_CONTINUE;
  210. }
  211. }
  212. else
  213. {
  214. trace_t tr;
  215. g_pEngineTraceServer->ClipRayToCollideable( ray, MASK_SOLID, m_pTrigger, &tr );
  216. if ( !(tr.contents & MASK_SOLID) )
  217. return ITERATION_CONTINUE;
  218. }
  219. m_TouchedEntities.AddToTail( pTouch );
  220. return ITERATION_CONTINUE;
  221. }
  222. void HandleTouchedEntities( )
  223. {
  224. for ( int i = 0; i < m_TouchedEntities.Count(); ++i )
  225. {
  226. serverGameEnts->MarkEntitiesAsTouching( m_TouchedEntities[i], m_pTriggerEntity );
  227. }
  228. }
  229. private:
  230. edict_t* m_pTriggerEntity;
  231. ICollideable* m_pTrigger;
  232. int m_triggerSolidFlags;
  233. Vector m_vecDelta;
  234. CUtlVector< edict_t* > m_TouchedEntities;
  235. bool m_bAccurateBBoxCheck;
  236. };
  237. //-----------------------------------------------------------------------------
  238. // Touches triggers. Or, if it is a trigger, causes other things to touch it
  239. // returns true if untouch needs to be checked
  240. //-----------------------------------------------------------------------------
  241. void SV_TriggerMoved( edict_t *pTriggerEnt, bool accurateBboxTriggerChecks )
  242. {
  243. CTriggerMoved triggerEnum( accurateBboxTriggerChecks );
  244. triggerEnum.TriggerMoved( pTriggerEnt );
  245. triggerEnum.HandleTouchedEntities( );
  246. }
  247. void SV_SolidMoved( edict_t *pSolidEnt, ICollideable *pSolidCollide, const Vector* pPrevAbsOrigin, bool accurateBboxTriggerChecks )
  248. {
  249. if (!pPrevAbsOrigin)
  250. {
  251. CTouchLinks touchEnumerator(pSolidEnt, NULL, accurateBboxTriggerChecks);
  252. Vector vecWorldMins, vecWorldMaxs;
  253. pSolidCollide->WorldSpaceSurroundingBounds( &vecWorldMins, &vecWorldMaxs );
  254. SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_TRIGGER_EDICTS,
  255. vecWorldMins, vecWorldMaxs, false, &touchEnumerator );
  256. touchEnumerator.HandleTouchedEntities( );
  257. }
  258. else
  259. {
  260. CTouchLinks touchEnumerator(pSolidEnt, pPrevAbsOrigin, accurateBboxTriggerChecks);
  261. // A version that checks against an extruded ray indicating the motion
  262. SpatialPartition()->EnumerateElementsAlongRay( PARTITION_ENGINE_TRIGGER_EDICTS,
  263. touchEnumerator.m_Ray, false, &touchEnumerator );
  264. touchEnumerator.HandleTouchedEntities( );
  265. }
  266. }