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.

353 lines
11 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "obstacle_pushaway.h"
  8. #include "props_shared.h"
  9. #ifdef CLIENT_DLL
  10. #include "c_breakableprop.h"
  11. #include "c_func_breakablesurf.h"
  12. #else
  13. #include "vehicle_base.h"
  14. #endif
  15. // NOTE: This has to be the last file included!
  16. #include "tier0/memdbgon.h"
  17. //-----------------------------------------------------------------------------------------------------
  18. ConVar sv_pushaway_force( "sv_pushaway_force", "30000", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "How hard physics objects are pushed away from the players on the server." );
  19. ConVar sv_pushaway_min_player_speed( "sv_pushaway_min_player_speed", "75", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "If a player is moving slower than this, don't push away physics objects (enables ducking behind things)." );
  20. ConVar sv_pushaway_max_force( "sv_pushaway_max_force", "1000", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Maximum amount of force applied to physics objects by players." );
  21. ConVar sv_pushaway_clientside( "sv_pushaway_clientside", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Clientside physics push away (0=off, 1=only localplayer, 1=all players)" );
  22. ConVar sv_pushaway_player_force( "sv_pushaway_player_force", "200000", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "How hard the player is pushed away from physics objects (falls off with inverse square of distance)." );
  23. ConVar sv_pushaway_max_player_force( "sv_pushaway_max_player_force", "10000", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Maximum of how hard the player is pushed away from physics objects." );
  24. #ifdef CLIENT_DLL
  25. #define CBreakableProp C_BreakableProp
  26. #define CBreakableSurface C_BreakableSurface
  27. ConVar sv_turbophysics( "sv_turbophysics", "0", FCVAR_REPLICATED, "Turns on turbo physics" );
  28. #else
  29. extern ConVar sv_turbophysics;
  30. #endif
  31. //-----------------------------------------------------------------------------------------------------
  32. bool IsPushAwayEntity( CBaseEntity *pEnt )
  33. {
  34. if ( pEnt == NULL )
  35. return false;
  36. if ( pEnt->GetCollisionGroup() != COLLISION_GROUP_PUSHAWAY )
  37. {
  38. // Try backing away from doors that are currently rotating, to prevent blocking them
  39. #ifndef CLIENT_DLL
  40. if ( FClassnameIs( pEnt, "func_door_rotating" ) )
  41. {
  42. CBaseDoor *door = dynamic_cast<CBaseDoor *>(pEnt);
  43. if ( !door )
  44. {
  45. return false;
  46. }
  47. if ( door->m_toggle_state != TS_GOING_UP && door->m_toggle_state != TS_GOING_DOWN )
  48. {
  49. return false;
  50. }
  51. }
  52. else if ( FClassnameIs( pEnt, "prop_door_rotating" ) )
  53. {
  54. CBasePropDoor *door = dynamic_cast<CBasePropDoor *>(pEnt);
  55. if ( !door )
  56. {
  57. return false;
  58. }
  59. if ( !door->IsDoorOpening() && !door->IsDoorClosing() )
  60. {
  61. return false;
  62. }
  63. }
  64. else
  65. #endif // !CLIENT_DLL
  66. {
  67. return false;
  68. }
  69. }
  70. return true;
  71. }
  72. //-----------------------------------------------------------------------------------------------------
  73. bool IsPushableEntity( CBaseEntity *pEnt )
  74. {
  75. if ( pEnt == NULL )
  76. return false;
  77. if ( sv_turbophysics.GetBool() )
  78. {
  79. if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_NONE )
  80. {
  81. #ifdef CLIENT_DLL
  82. if ( FClassnameIs( pEnt, "class CPhysicsPropMultiplayer" ) )
  83. #else
  84. if ( FClassnameIs( pEnt, "prop_physics_multiplayer" ) )
  85. #endif // CLIENT_DLL
  86. {
  87. return true;
  88. }
  89. }
  90. }
  91. return false;
  92. }
  93. //-----------------------------------------------------------------------------------------------------
  94. bool IsBreakableEntity( CBaseEntity *pEnt )
  95. {
  96. if ( pEnt == NULL )
  97. return false;
  98. // first check to see if it's already broken
  99. if ( pEnt->m_iHealth < 0 && pEnt->GetMaxHealth() > 0 )
  100. return true;
  101. // If we won't be able to break it, don't try
  102. if ( pEnt->m_takedamage != DAMAGE_YES )
  103. return false;
  104. if ( pEnt->GetCollisionGroup() != COLLISION_GROUP_PUSHAWAY && pEnt->GetCollisionGroup() != COLLISION_GROUP_BREAKABLE_GLASS && pEnt->GetCollisionGroup() != COLLISION_GROUP_NONE )
  105. return false;
  106. if ( pEnt->m_iHealth > 200 )
  107. return false;
  108. IMultiplayerPhysics *pPhysicsInterface = dynamic_cast< IMultiplayerPhysics * >( pEnt );
  109. if ( pPhysicsInterface )
  110. {
  111. if ( pPhysicsInterface->GetMultiplayerPhysicsMode() != PHYSICS_MULTIPLAYER_SOLID )
  112. return false;
  113. }
  114. else
  115. {
  116. if ((FClassnameIs( pEnt, "func_breakable" ) || FClassnameIs( pEnt, "func_breakable_surf" )))
  117. {
  118. if (FClassnameIs( pEnt, "func_breakable_surf" ))
  119. {
  120. // don't try to break it if it has already been broken
  121. CBreakableSurface *surf = static_cast< CBreakableSurface * >( pEnt );
  122. if ( surf->m_bIsBroken )
  123. return false;
  124. }
  125. }
  126. else if ( pEnt->PhysicsSolidMaskForEntity() & CONTENTS_PLAYERCLIP )
  127. {
  128. // hostages and players use CONTENTS_PLAYERCLIP, so we can use it to ignore them
  129. return false;
  130. }
  131. }
  132. IBreakableWithPropData *pBreakableInterface = dynamic_cast< IBreakableWithPropData * >( pEnt );
  133. if ( pBreakableInterface )
  134. {
  135. // Bullets don't damage it - ignore
  136. if ( pBreakableInterface->GetDmgModBullet() <= 0.0f )
  137. {
  138. return false;
  139. }
  140. }
  141. // what the shit is this
  142. /*
  143. CBreakableProp *pProp = dynamic_cast< CBreakableProp * >( pEnt );
  144. if ( pProp )
  145. {
  146. // It takes a large amount of damage to even scratch it - ignore
  147. if ( pProp->m_iMinHealthDmg >= 50 )
  148. {
  149. return false;
  150. }
  151. }
  152. */
  153. return true;
  154. }
  155. //-----------------------------------------------------------------------------------------------------
  156. int GetPushawayEnts( CBaseCombatCharacter *pPushingEntity, CBaseEntity **ents, int nMaxEnts, float flPlayerExpand, int PartitionMask, CPushAwayEnumerator *enumerator )
  157. {
  158. Vector vExpand( flPlayerExpand, flPlayerExpand, flPlayerExpand );
  159. Ray_t ray;
  160. ray.Init( pPushingEntity->GetAbsOrigin(), pPushingEntity->GetAbsOrigin(), pPushingEntity->GetCollideable()->OBBMins() - vExpand, pPushingEntity->GetCollideable()->OBBMaxs() + vExpand );
  161. CPushAwayEnumerator *physPropEnum = NULL;
  162. if ( !enumerator )
  163. {
  164. physPropEnum = new CPushAwayEnumerator( ents, nMaxEnts );
  165. enumerator = physPropEnum;
  166. }
  167. ::partition->EnumerateElementsAlongRay( PartitionMask, ray, false, enumerator );
  168. int numHit = enumerator->m_nAlreadyHit;
  169. if ( physPropEnum )
  170. delete physPropEnum;
  171. return numHit;
  172. }
  173. void AvoidPushawayProps( CBaseCombatCharacter *pPlayer, CUserCmd *pCmd )
  174. {
  175. // Figure out what direction we're moving and the extents of the box we're going to sweep
  176. // against physics objects.
  177. Vector currentdir;
  178. Vector rightdir;
  179. if (g_pGameRules->IsTopDown())
  180. {
  181. AngleVectors( g_pGameRules->GetTopDownMovementAxis(), &currentdir, &rightdir, NULL );
  182. }
  183. else
  184. {
  185. AngleVectors( pCmd->viewangles, &currentdir, &rightdir, NULL );
  186. }
  187. CBaseEntity *props[512];
  188. #ifdef CLIENT_DLL
  189. int nEnts = GetPushawayEnts( pPlayer, props, ARRAYSIZE( props ), 0.0f, PARTITION_CLIENT_SOLID_EDICTS, NULL );
  190. #else
  191. int nEnts = GetPushawayEnts( pPlayer, props, ARRAYSIZE( props ), 0.0f, PARTITION_ENGINE_SOLID_EDICTS, NULL );
  192. #endif
  193. const Vector & ourCenter = pPlayer->WorldSpaceCenter();
  194. Vector nearestPropPoint;
  195. Vector nearestPlayerPoint;
  196. for ( int i=0; i < nEnts; i++ )
  197. {
  198. // Don't respond to this entity on the client unless it has PHYSICS_MULTIPLAYER_FULL set.
  199. IMultiplayerPhysics *pInterface = dynamic_cast<IMultiplayerPhysics*>( props[i] );
  200. if ( pInterface && pInterface->GetMultiplayerPhysicsMode() != PHYSICS_MULTIPLAYER_SOLID )
  201. continue;
  202. const float minMass = 10.0f; // minimum mass that can push a player back
  203. const float maxMass = 30.0f; // cap at a decently large value
  204. float mass = maxMass;
  205. if ( pInterface )
  206. {
  207. mass = pInterface->GetMass();
  208. }
  209. mass = clamp( mass, minMass, maxMass );
  210. mass = MAX( mass, 0 );
  211. mass /= maxMass; // bring into a 0..1 range
  212. // Push away from the collision point. The closer our center is to the collision point,
  213. // the harder we push away.
  214. props[i]->CollisionProp()->CalcNearestPoint( ourCenter, &nearestPropPoint );
  215. pPlayer->CollisionProp()->CalcNearestPoint( nearestPropPoint, &nearestPlayerPoint );
  216. Vector vPushAway = (nearestPlayerPoint - nearestPropPoint);
  217. float flDist = VectorNormalize( vPushAway );
  218. const float MaxPushawayDistance = 5.0f;
  219. if ( flDist > MaxPushawayDistance && !pPlayer->CollisionProp()->IsPointInBounds( nearestPropPoint ) )
  220. {
  221. continue;
  222. }
  223. // If we're not pushing, try from our center to the nearest edge of the prop
  224. if ( vPushAway.IsZero() )
  225. {
  226. vPushAway = (ourCenter - nearestPropPoint);
  227. flDist = VectorNormalize( vPushAway );
  228. }
  229. // If we're still not pushing, try from our center to the center of the prop
  230. if ( vPushAway.IsZero() )
  231. {
  232. vPushAway = (ourCenter - props[i]->WorldSpaceCenter());
  233. flDist = VectorNormalize( vPushAway );
  234. }
  235. flDist = MAX( flDist, 1 );
  236. float flForce = sv_pushaway_player_force.GetFloat() / flDist * mass;
  237. flForce = MIN( flForce, sv_pushaway_max_player_force.GetFloat() );
  238. #ifndef CLIENT_DLL
  239. pPlayer->PushawayTouch( props[i] );
  240. // We can get right up next to rotating doors before they start to move, so scale back our force so we don't go flying
  241. if ( FClassnameIs( props[i], "func_door_rotating" ) || FClassnameIs( props[i], "prop_door_rotating" ) )
  242. #endif
  243. {
  244. flForce *= 0.25f;
  245. }
  246. vPushAway *= flForce;
  247. pCmd->forwardmove += vPushAway.Dot( currentdir );
  248. pCmd->sidemove += vPushAway.Dot( rightdir );
  249. }
  250. }
  251. //-----------------------------------------------------------------------------------------------------
  252. void PerformObstaclePushaway( CBaseCombatCharacter *pPushingEntity )
  253. {
  254. if ( pPushingEntity->m_lifeState != LIFE_ALIVE )
  255. return;
  256. // Give a push to any barrels that we're touching.
  257. // The client handles adjusting our usercmd to push us away.
  258. CBaseEntity *props[256];
  259. #ifdef CLIENT_DLL
  260. // if sv_pushaway_clientside is disabled, clientside phys objects don't bounce away
  261. if ( sv_pushaway_clientside.GetInt() == 0 )
  262. return;
  263. // if sv_pushaway_clientside is 1, only local player can push them
  264. CBasePlayer *pPlayer = pPushingEntity->IsPlayer() ? (dynamic_cast< CBasePlayer * >(pPushingEntity)) : NULL;
  265. if ( (sv_pushaway_clientside.GetInt() == 1) && (!pPlayer || !C_BasePlayer::IsLocalPlayer( pPlayer )) )
  266. return;
  267. int nEnts = GetPushawayEnts( pPushingEntity, props, ARRAYSIZE( props ), 3.0f, PARTITION_CLIENT_RESPONSIVE_EDICTS, NULL );
  268. #else
  269. int nEnts = GetPushawayEnts( pPushingEntity, props, ARRAYSIZE( props ), 3.0f, PARTITION_ENGINE_SOLID_EDICTS, NULL );
  270. #endif
  271. for ( int i=0; i < nEnts; i++ )
  272. {
  273. // If this entity uas PHYSICS_MULTIPLAYER_FULL set (ie: it's not just debris), and we're moving too slow, don't push it away.
  274. // Instead, let the client bounce off it. This allows players to get close to and duck behind things without knocking them over.
  275. IMultiplayerPhysics *pInterface = dynamic_cast<IMultiplayerPhysics*>( props[i] );
  276. if ( pInterface && pInterface->GetMultiplayerPhysicsMode() == PHYSICS_MULTIPLAYER_SOLID )
  277. {
  278. if ( pPushingEntity->GetAbsVelocity().Length2D() < sv_pushaway_min_player_speed.GetFloat() )
  279. continue;
  280. }
  281. IPhysicsObject *pObj = props[i]->VPhysicsGetObject();
  282. if ( pObj )
  283. {
  284. Vector vPushAway = (props[i]->WorldSpaceCenter() - pPushingEntity->WorldSpaceCenter());
  285. vPushAway.z = 0;
  286. float flDist = VectorNormalize( vPushAway );
  287. flDist = MAX( flDist, 1 );
  288. float flForce = sv_pushaway_force.GetFloat() / flDist;
  289. flForce = MIN( flForce, sv_pushaway_max_force.GetFloat() );
  290. pObj->ApplyForceOffset( vPushAway * flForce, pPushingEntity->WorldSpaceCenter() );
  291. }
  292. }
  293. }