From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/game/shared/obstacle_pushaway.cpp | 335 +++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 mp/src/game/shared/obstacle_pushaway.cpp (limited to 'mp/src/game/shared/obstacle_pushaway.cpp') 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(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(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( 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( 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() ); + } + } +} -- cgit v1.2.3