diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/shared/obstacle_pushaway.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/shared/obstacle_pushaway.cpp')
| -rw-r--r-- | mp/src/game/shared/obstacle_pushaway.cpp | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/mp/src/game/shared/obstacle_pushaway.cpp b/mp/src/game/shared/obstacle_pushaway.cpp new file mode 100644 index 00000000..043c1773 --- /dev/null +++ b/mp/src/game/shared/obstacle_pushaway.cpp @@ -0,0 +1,335 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "obstacle_pushaway.h"
+#include "props_shared.h"
+
+#if defined( CSTRIKE_DLL )
+#define SV_PUSH_CONVAR_FLAGS (FCVAR_REPLICATED)
+#else
+#define SV_PUSH_CONVAR_FLAGS (FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY)
+#endif // CSTRIKE_DLL
+
+//-----------------------------------------------------------------------------------------------------
+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." );
+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)." );
+ConVar sv_pushaway_max_force( "sv_pushaway_max_force", "1000", SV_PUSH_CONVAR_FLAGS, "Maximum amount of force applied to physics objects by players." );
+ConVar sv_pushaway_clientside( "sv_pushaway_clientside", "0", SV_PUSH_CONVAR_FLAGS, "Clientside physics push away (0=off, 1=only localplayer, 1=all players)" );
+
+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)." );
+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." );
+
+#ifdef CLIENT_DLL
+ConVar sv_turbophysics( "sv_turbophysics", "0", FCVAR_REPLICATED, "Turns on turbo physics" );
+#else
+extern ConVar sv_turbophysics;
+#endif
+
+//-----------------------------------------------------------------------------------------------------
+bool IsPushAwayEntity( CBaseEntity *pEnt )
+{
+ if ( pEnt == NULL )
+ return false;
+
+ if ( pEnt->GetCollisionGroup() != COLLISION_GROUP_PUSHAWAY )
+ {
+ // Try backing away from doors that are currently rotating, to prevent blocking them
+#ifndef CLIENT_DLL
+ if ( FClassnameIs( pEnt, "func_door_rotating" ) )
+ {
+ CBaseDoor *door = dynamic_cast<CBaseDoor *>(pEnt);
+ if ( !door )
+ {
+ return false;
+ }
+
+ if ( door->m_toggle_state != TS_GOING_UP && door->m_toggle_state != TS_GOING_DOWN )
+ {
+ return false;
+ }
+ }
+ else if ( FClassnameIs( pEnt, "prop_door_rotating" ) )
+ {
+ CBasePropDoor *door = dynamic_cast<CBasePropDoor *>(pEnt);
+ if ( !door )
+ {
+ return false;
+ }
+
+ if ( !door->IsDoorOpening() && !door->IsDoorClosing() )
+ {
+ return false;
+ }
+ }
+ else
+#endif // !CLIENT_DLL
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------------------------------
+bool IsPushableEntity( CBaseEntity *pEnt )
+{
+ if ( pEnt == NULL )
+ return false;
+
+ if ( sv_turbophysics.GetBool() )
+ {
+ if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_NONE )
+ {
+#ifdef CLIENT_DLL
+ if ( FClassnameIs( pEnt, "class CPhysicsPropMultiplayer" ) )
+#else
+ if ( FClassnameIs( pEnt, "prop_physics_multiplayer" ) )
+#endif // CLIENT_DLL
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------------------------------
+#ifndef CLIENT_DLL
+bool IsBreakableEntity( CBaseEntity *pEnt )
+{
+ if ( pEnt == NULL )
+ return false;
+
+ // If we won't be able to break it, don't try
+ if ( pEnt->m_takedamage != DAMAGE_YES )
+ return false;
+
+ if ( pEnt->GetCollisionGroup() != COLLISION_GROUP_PUSHAWAY && pEnt->GetCollisionGroup() != COLLISION_GROUP_BREAKABLE_GLASS && pEnt->GetCollisionGroup() != COLLISION_GROUP_NONE )
+ return false;
+
+ if ( pEnt->m_iHealth > 200 )
+ return false;
+
+ IMultiplayerPhysics *pPhysicsInterface = dynamic_cast< IMultiplayerPhysics * >( pEnt );
+ if ( pPhysicsInterface )
+ {
+ if ( pPhysicsInterface->GetMultiplayerPhysicsMode() != PHYSICS_MULTIPLAYER_SOLID )
+ return false;
+ }
+ else
+ {
+ if ((FClassnameIs( pEnt, "func_breakable" ) || FClassnameIs( pEnt, "func_breakable_surf" )))
+ {
+ if (FClassnameIs( pEnt, "func_breakable_surf" ))
+ {
+ // don't try to break it if it has already been broken
+ CBreakableSurface *surf = static_cast< CBreakableSurface * >( pEnt );
+
+ if ( surf->m_bIsBroken )
+ return false;
+ }
+ }
+ else if ( pEnt->PhysicsSolidMaskForEntity() & CONTENTS_PLAYERCLIP )
+ {
+ // hostages and players use CONTENTS_PLAYERCLIP, so we can use it to ignore them
+ return false;
+ }
+ }
+
+ IBreakableWithPropData *pBreakableInterface = dynamic_cast< IBreakableWithPropData * >( pEnt );
+ if ( pBreakableInterface )
+ {
+ // Bullets don't damage it - ignore
+ if ( pBreakableInterface->GetDmgModBullet() <= 0.0f )
+ {
+ return false;
+ }
+ }
+
+ CBreakableProp *pProp = dynamic_cast< CBreakableProp * >( pEnt );
+ if ( pProp )
+ {
+ // It takes a large amount of damage to even scratch it - ignore
+ if ( pProp->m_iMinHealthDmg >= 50 )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+#endif // !CLIENT_DLL
+
+//-----------------------------------------------------------------------------------------------------
+int GetPushawayEnts( CBaseCombatCharacter *pPushingEntity, CBaseEntity **ents, int nMaxEnts, float flPlayerExpand, int PartitionMask, CPushAwayEnumerator *enumerator )
+{
+
+ Vector vExpand( flPlayerExpand, flPlayerExpand, flPlayerExpand );
+
+ Ray_t ray;
+ ray.Init( pPushingEntity->GetAbsOrigin(), pPushingEntity->GetAbsOrigin(), pPushingEntity->GetCollideable()->OBBMins() - vExpand, pPushingEntity->GetCollideable()->OBBMaxs() + vExpand );
+
+ CPushAwayEnumerator *physPropEnum = NULL;
+ if ( !enumerator )
+ {
+ physPropEnum = new CPushAwayEnumerator( ents, nMaxEnts );
+ enumerator = physPropEnum;
+ }
+
+ partition->EnumerateElementsAlongRay( PartitionMask, ray, false, enumerator );
+
+ int numHit = enumerator->m_nAlreadyHit;
+
+ if ( physPropEnum )
+ delete physPropEnum;
+
+ return numHit;
+}
+
+void AvoidPushawayProps( CBaseCombatCharacter *pPlayer, CUserCmd *pCmd )
+{
+ // Figure out what direction we're moving and the extents of the box we're going to sweep
+ // against physics objects.
+ Vector currentdir;
+ Vector rightdir;
+ AngleVectors( pCmd->viewangles, ¤tdir, &rightdir, NULL );
+
+ CBaseEntity *props[512];
+#ifdef CLIENT_DLL
+ int nEnts = GetPushawayEnts( pPlayer, props, ARRAYSIZE( props ), 0.0f, PARTITION_CLIENT_SOLID_EDICTS, NULL );
+#else
+ int nEnts = GetPushawayEnts( pPlayer, props, ARRAYSIZE( props ), 0.0f, PARTITION_ENGINE_SOLID_EDICTS, NULL );
+#endif
+
+ const Vector & ourCenter = pPlayer->WorldSpaceCenter();
+ Vector nearestPropPoint;
+ Vector nearestPlayerPoint;
+
+ for ( int i=0; i < nEnts; i++ )
+ {
+ // Don't respond to this entity on the client unless it has PHYSICS_MULTIPLAYER_FULL set.
+ IMultiplayerPhysics *pInterface = dynamic_cast<IMultiplayerPhysics*>( props[i] );
+ if ( pInterface && pInterface->GetMultiplayerPhysicsMode() != PHYSICS_MULTIPLAYER_SOLID )
+ continue;
+
+ const float minMass = 10.0f; // minimum mass that can push a player back
+ const float maxMass = 30.0f; // cap at a decently large value
+ float mass = maxMass;
+ if ( pInterface )
+ {
+ mass = pInterface->GetMass();
+ }
+ mass = clamp( mass, minMass, maxMass );
+
+ mass = MAX( mass, 0 );
+ mass /= maxMass; // bring into a 0..1 range
+
+ // Push away from the collision point. The closer our center is to the collision point,
+ // the harder we push away.
+ props[i]->CollisionProp()->CalcNearestPoint( ourCenter, &nearestPropPoint );
+ pPlayer->CollisionProp()->CalcNearestPoint( nearestPropPoint, &nearestPlayerPoint );
+ Vector vPushAway = (nearestPlayerPoint - nearestPropPoint);
+ float flDist = VectorNormalize( vPushAway );
+
+ const float MaxPushawayDistance = 5.0f;
+ if ( flDist > MaxPushawayDistance && !pPlayer->CollisionProp()->IsPointInBounds( nearestPropPoint ) )
+ {
+ continue;
+ }
+
+ // If we're not pushing, try from our center to the nearest edge of the prop
+ if ( vPushAway.IsZero() )
+ {
+ vPushAway = (ourCenter - nearestPropPoint);
+ flDist = VectorNormalize( vPushAway );
+ }
+
+ // If we're still not pushing, try from our center to the center of the prop
+ if ( vPushAway.IsZero() )
+ {
+ vPushAway = (ourCenter - props[i]->WorldSpaceCenter());
+ flDist = VectorNormalize( vPushAway );
+ }
+
+ flDist = MAX( flDist, 1 );
+
+ float flForce = sv_pushaway_player_force.GetFloat() / flDist * mass;
+ flForce = MIN( flForce, sv_pushaway_max_player_force.GetFloat() );
+
+#ifndef CLIENT_DLL
+ pPlayer->PushawayTouch( props[i] );
+
+ // 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
+ if ( FClassnameIs( props[i], "func_door_rotating" ) || FClassnameIs( props[i], "prop_door_rotating" ) )
+#endif
+ {
+ flForce *= 0.25f;
+ }
+
+ vPushAway *= flForce;
+
+ pCmd->forwardmove += vPushAway.Dot( currentdir );
+ pCmd->sidemove += vPushAway.Dot( rightdir );
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------
+void PerformObstaclePushaway( CBaseCombatCharacter *pPushingEntity )
+{
+ if ( pPushingEntity->m_lifeState != LIFE_ALIVE )
+ return;
+
+ // Give a push to any barrels that we're touching.
+ // The client handles adjusting our usercmd to push us away.
+ CBaseEntity *props[256];
+
+#ifdef CLIENT_DLL
+ // if sv_pushaway_clientside is disabled, clientside phys objects don't bounce away
+ if ( sv_pushaway_clientside.GetInt() == 0 )
+ return;
+
+ // if sv_pushaway_clientside is 1, only local player can push them
+ CBasePlayer *pPlayer = pPushingEntity->IsPlayer() ? (dynamic_cast< CBasePlayer * >(pPushingEntity)) : NULL;
+ if ( (sv_pushaway_clientside.GetInt() == 1) && (!pPlayer || !pPlayer->IsLocalPlayer()) )
+ return;
+
+ int nEnts = GetPushawayEnts( pPushingEntity, props, ARRAYSIZE( props ), 3.0f, PARTITION_CLIENT_RESPONSIVE_EDICTS, NULL );
+#else
+ int nEnts = GetPushawayEnts( pPushingEntity, props, ARRAYSIZE( props ), 3.0f, PARTITION_ENGINE_SOLID_EDICTS, NULL );
+#endif
+
+ for ( int i=0; i < nEnts; i++ )
+ {
+ // 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.
+ // Instead, let the client bounce off it. This allows players to get close to and duck behind things without knocking them over.
+ IMultiplayerPhysics *pInterface = dynamic_cast<IMultiplayerPhysics*>( props[i] );
+
+ if ( pInterface && pInterface->GetMultiplayerPhysicsMode() == PHYSICS_MULTIPLAYER_SOLID )
+ {
+ if ( pPushingEntity->GetAbsVelocity().Length2D() < sv_pushaway_min_player_speed.GetFloat() )
+ continue;
+ }
+
+ IPhysicsObject *pObj = props[i]->VPhysicsGetObject();
+
+ if ( pObj )
+ {
+ Vector vPushAway = (props[i]->WorldSpaceCenter() - pPushingEntity->WorldSpaceCenter());
+ vPushAway.z = 0;
+
+ float flDist = VectorNormalize( vPushAway );
+ flDist = MAX( flDist, 1 );
+
+ float flForce = sv_pushaway_force.GetFloat() / flDist;
+ flForce = MIN( flForce, sv_pushaway_max_force.GetFloat() );
+
+ pObj->ApplyForceOffset( vPushAway * flForce, pPushingEntity->WorldSpaceCenter() );
+ }
+ }
+}
|