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.

335 lines
11 KiB

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