summaryrefslogtreecommitdiff
path: root/game/shared/portal/portal_gamemovement.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/portal/portal_gamemovement.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/portal/portal_gamemovement.cpp')
-rw-r--r--game/shared/portal/portal_gamemovement.cpp752
1 files changed, 752 insertions, 0 deletions
diff --git a/game/shared/portal/portal_gamemovement.cpp b/game/shared/portal/portal_gamemovement.cpp
new file mode 100644
index 0000000..a966290
--- /dev/null
+++ b/game/shared/portal/portal_gamemovement.cpp
@@ -0,0 +1,752 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Special handling for Portal usable ladders
+//
+//=============================================================================//
+#include "cbase.h"
+#include "hl_gamemovement.h"
+#include "in_buttons.h"
+#include "utlrbtree.h"
+#include "movevars_shared.h"
+#include "portal_shareddefs.h"
+#include "portal_collideable_enumerator.h"
+#include "prop_portal_shared.h"
+#include "rumble_shared.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_portal_player.h"
+ #include "c_rumble.h"
+#else
+ #include "portal_player.h"
+ #include "env_player_surface_trigger.h"
+ #include "portal_gamestats.h"
+ #include "physicsshadowclone.h"
+ #include "recipientfilter.h"
+ #include "SoundEmitterSystem/isoundemittersystembase.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar sv_player_trace_through_portals("sv_player_trace_through_portals", "1", FCVAR_REPLICATED | FCVAR_CHEAT, "Causes player movement traces to trace through portals." );
+ConVar sv_player_funnel_into_portals("sv_player_funnel_into_portals", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Causes the player to auto correct toward the center of floor portals." );
+
+class CReservePlayerSpot;
+
+#define PORTAL_FUNNEL_AMOUNT 6.0f
+
+extern bool g_bAllowForcePortalTrace;
+extern bool g_bForcePortalTrace;
+
+static inline CBaseEntity *TranslateGroundEntity( CBaseEntity *pGroundEntity )
+{
+#ifndef CLIENT_DLL
+ CPhysicsShadowClone *pClone = dynamic_cast<CPhysicsShadowClone *>(pGroundEntity);
+
+ if( pClone && pClone->IsUntransformedClone() )
+ {
+ CBaseEntity *pSource = pClone->GetClonedEntity();
+
+ if( pSource )
+ return pSource;
+ }
+#endif //#ifndef CLIENT_DLL
+
+ return pGroundEntity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Portal specific movement code
+//-----------------------------------------------------------------------------
+class CPortalGameMovement : public CHL2GameMovement
+{
+ typedef CGameMovement BaseClass;
+public:
+
+ CPortalGameMovement();
+
+ bool m_bInPortalEnv;
+// Overrides
+ virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove );
+ virtual bool CheckJumpButton( void );
+
+ void FunnelIntoPortal( CProp_Portal *pPortal, Vector &wishdir );
+
+ virtual void AirAccelerate( Vector& wishdir, float wishspeed, float accel );
+ virtual void AirMove( void );
+
+ virtual void PlayerRoughLandingEffects( float fvol );
+
+ virtual void CategorizePosition( void );
+
+ // Traces the player bbox as it is swept from start to end
+ virtual void TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm );
+
+ // Tests the player position
+ virtual CBaseHandle TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm );
+
+ virtual void Duck( void ); // Check for a forced duck
+
+ virtual int CheckStuck( void );
+
+ virtual void SetGroundEntity( trace_t *pm );
+
+private:
+
+
+ CPortal_Player *GetPortalPlayer();
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CPortalGameMovement::CPortalGameMovement()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline CPortal_Player *CPortalGameMovement::GetPortalPlayer()
+{
+ return static_cast< CPortal_Player * >( player );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pMove -
+//-----------------------------------------------------------------------------
+void CPortalGameMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove )
+{
+ Assert( pMove && pPlayer );
+
+ float flStoreFrametime = gpGlobals->frametime;
+
+ //!!HACK HACK: Adrian - slow down all player movement by this factor.
+ //!!Blame Yahn for this one.
+ gpGlobals->frametime *= pPlayer->GetLaggedMovementValue();
+
+ ResetGetPointContentsCache();
+
+ // Cropping movement speed scales mv->m_fForwardSpeed etc. globally
+ // Once we crop, we don't want to recursively crop again, so we set the crop
+ // flag globally here once per usercmd cycle.
+ m_iSpeedCropped = SPEED_CROPPED_RESET;
+
+ player = pPlayer;
+ mv = pMove;
+ mv->m_flMaxSpeed = sv_maxspeed.GetFloat();
+
+ m_bInPortalEnv = (((CPortal_Player *)pPlayer)->m_hPortalEnvironment != NULL);
+
+ g_bAllowForcePortalTrace = m_bInPortalEnv;
+ g_bForcePortalTrace = m_bInPortalEnv;
+
+ // Run the command.
+ PlayerMove();
+
+ FinishMove();
+
+ g_bAllowForcePortalTrace = false;
+ g_bForcePortalTrace = false;
+
+#ifndef CLIENT_DLL
+ pPlayer->UnforceButtons( IN_DUCK );
+ pPlayer->UnforceButtons( IN_JUMP );
+#endif
+
+ //This is probably not needed, but just in case.
+ gpGlobals->frametime = flStoreFrametime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Base jump behavior, plus an anim event
+// Input : -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPortalGameMovement::CheckJumpButton()
+{
+ if ( BaseClass::CheckJumpButton() && GetPortalPlayer() )
+ {
+ GetPortalPlayer()->DoAnimationEvent( PLAYERANIMEVENT_JUMP, 0 );
+ return true;
+ }
+
+ return false;
+}
+
+void CPortalGameMovement::FunnelIntoPortal( CProp_Portal *pPortal, Vector &wishdir )
+{
+ // Make sure there's a portal
+ if ( !pPortal )
+ return;
+
+ // Get portal vectors
+ Vector vPortalForward, vPortalRight, vPortalUp;
+ pPortal->GetVectors( &vPortalForward, &vPortalRight, &vPortalUp );
+
+ // Make sure it's a floor portal
+ if ( vPortalForward.z < 0.8f )
+ return;
+
+ vPortalRight.z = 0.0f;
+ vPortalUp.z = 0.0f;
+ VectorNormalize( vPortalRight );
+ VectorNormalize( vPortalUp );
+
+ // Make sure the player is looking downward
+ CPortal_Player *pPlayer = GetPortalPlayer();
+
+ Vector vPlayerForward;
+ pPlayer->EyeVectors( &vPlayerForward );
+
+ if ( vPlayerForward.z > -0.1f )
+ return;
+
+ Vector vPlayerOrigin = pPlayer->GetAbsOrigin();
+ Vector vPlayerToPortal = pPortal->GetAbsOrigin() - vPlayerOrigin;
+
+ // Make sure the player is trying to air control, they're falling downward and they are vertically close to the portal
+ if ( fabsf( wishdir[ 0 ] ) > 64.0f || fabsf( wishdir[ 1 ] ) > 64.0f || mv->m_vecVelocity[ 2 ] > -165.0f || vPlayerToPortal.z < -512.0f )
+ return;
+
+ // Make sure we're in the 2D portal rectangle
+ if ( ( vPlayerToPortal.Dot( vPortalRight ) * vPortalRight ).Length() > PORTAL_HALF_WIDTH * 1.5f )
+ return;
+ if ( ( vPlayerToPortal.Dot( vPortalUp ) * vPortalUp ).Length() > PORTAL_HALF_HEIGHT * 1.5f )
+ return;
+
+ if ( vPlayerToPortal.z > -8.0f )
+ {
+ // We're too close the the portal to continue correcting, but zero the velocity so our fling velocity is nice
+ mv->m_vecVelocity[ 0 ] = 0.0f;
+ mv->m_vecVelocity[ 1 ] = 0.0f;
+ }
+ else
+ {
+ // Funnel toward the portal
+ float fFunnelX = vPlayerToPortal.x * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[ 0 ];
+ float fFunnelY = vPlayerToPortal.y * PORTAL_FUNNEL_AMOUNT - mv->m_vecVelocity[ 1 ];
+
+ wishdir[ 0 ] += fFunnelX;
+ wishdir[ 1 ] += fFunnelY;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : wishdir -
+// accel -
+//-----------------------------------------------------------------------------
+void CPortalGameMovement::AirAccelerate( Vector& wishdir, float wishspeed, float accel )
+{
+ int i;
+ float addspeed, accelspeed, currentspeed;
+ float wishspd;
+
+ wishspd = wishspeed;
+
+ if (player->pl.deadflag)
+ return;
+
+ if (player->m_flWaterJumpTime)
+ return;
+
+ // Cap speed
+ if (wishspd > 60.0f)
+ wishspd = 60.0f;
+
+ // Determine veer amount
+ currentspeed = mv->m_vecVelocity.Dot(wishdir);
+
+ // See how much to add
+ addspeed = wishspd - currentspeed;
+
+ // If not adding any, done.
+ if (addspeed <= 0)
+ return;
+
+ // Determine acceleration speed after acceleration
+ accelspeed = accel * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
+
+ // Cap it
+ if (accelspeed > addspeed)
+ accelspeed = addspeed;
+
+ // Adjust pmove vel.
+ for (i=0 ; i<3 ; i++)
+ {
+ mv->m_vecVelocity[i] += accelspeed * wishdir[i];
+ mv->m_outWishVel[i] += accelspeed * wishdir[i];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPortalGameMovement::AirMove( void )
+{
+ int i;
+ Vector wishvel;
+ float fmove, smove;
+ Vector wishdir;
+ float wishspeed;
+ Vector forward, right, up;
+
+ AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
+
+ // Copy movement amounts
+ fmove = mv->m_flForwardMove;
+ smove = mv->m_flSideMove;
+
+ // Zero out z components of movement vectors
+ forward[2] = 0;
+ right[2] = 0;
+ VectorNormalize(forward); // Normalize remainder of vectors
+ VectorNormalize(right); //
+
+ for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
+ wishvel[i] = forward[i]*fmove + right[i]*smove;
+ wishvel[2] = 0; // Zero out z part of velocity
+
+ VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
+
+ //
+ // Don't let the player screw their fling because of adjusting into a floor portal
+ //
+ if ( mv->m_vecVelocity[ 0 ] * mv->m_vecVelocity[ 0 ] + mv->m_vecVelocity[ 1 ] * mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * MIN_FLING_SPEED )
+ {
+ if ( mv->m_vecVelocity[ 0 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] < 0.0f )
+ wishdir[ 0 ] = 0.0f;
+ else if ( mv->m_vecVelocity[ 0 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 0 ] > 0.0f )
+ wishdir[ 0 ] = 0.0f;
+
+ if ( mv->m_vecVelocity[ 1 ] > MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] < 0.0f )
+ wishdir[ 1 ] = 0.0f;
+ else if ( mv->m_vecVelocity[ 1 ] < -MIN_FLING_SPEED * 0.5f && wishdir[ 1 ] > 0.0f )
+ wishdir[ 1 ] = 0.0f;
+ }
+
+ //
+ // Try to autocorrect the player to fall into the middle of the portal
+ //
+ else if ( sv_player_funnel_into_portals.GetBool() )
+ {
+ int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
+ if( iPortalCount != 0 )
+ {
+ CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
+ for( int i = 0; i != iPortalCount; ++i )
+ {
+ CProp_Portal *pTempPortal = pPortals[i];
+ if( pTempPortal->IsActivedAndLinked() )
+ {
+ FunnelIntoPortal( pTempPortal, wishdir );
+ }
+ }
+ }
+ }
+
+ wishspeed = VectorNormalize(wishdir);
+
+ //
+ // clamp to server defined max speed
+ //
+ if ( wishspeed != 0 && (wishspeed > mv->m_flMaxSpeed))
+ {
+ VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
+ wishspeed = mv->m_flMaxSpeed;
+ }
+
+ AirAccelerate( wishdir, wishspeed, 15.0f );
+
+ // Add in any base velocity to the current velocity.
+ VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ TryPlayerMove();
+
+ // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
+ VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+}
+
+void CPortalGameMovement::PlayerRoughLandingEffects( float fvol )
+{
+ BaseClass::PlayerRoughLandingEffects( fvol );
+
+#ifndef CLIENT_DLL
+ if ( fvol >= 1.0 )
+ {
+ // Play the future shoes sound
+ CRecipientFilter filter;
+ filter.AddRecipientsByPAS( player->GetAbsOrigin() );
+
+ CSoundParameters params;
+ if ( CBaseEntity::GetParametersForSound( "PortalPlayer.FallRecover", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nPitch = 125.0f - player->m_Local.m_flFallVelocity * 0.03f; // lower pitch the harder they land
+ ep.m_flVolume = MIN( player->m_Local.m_flFallVelocity * 0.00075f - 0.38, 1.0f ); // louder the harder they land
+
+ CBaseEntity::EmitSound( filter, player->entindex(), ep );
+ }
+ }
+#endif
+}
+
+void TracePlayerBBoxForGround2( const Vector& start, const Vector& end, const Vector& minsSrc,
+ const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask,
+ int collisionGroup, trace_t& pm )
+{
+
+ VPROF( "TracePlayerBBoxForGround" );
+
+ CPortal_Player *pPortalPlayer = dynamic_cast<CPortal_Player *>(player->GetRefEHandle().Get());
+ CProp_Portal *pPlayerPortal = pPortalPlayer->m_hPortalEnvironment;
+
+#ifndef CLIENT_DLL
+ if( pPlayerPortal && pPlayerPortal->m_PortalSimulator.IsReadyToSimulate() == false )
+ pPlayerPortal = NULL;
+#endif
+
+ Ray_t ray;
+ Vector mins, maxs;
+
+ float fraction = pm.fraction;
+ Vector endpos = pm.endpos;
+
+ // Check the -x, -y quadrant
+ mins = minsSrc;
+ maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z );
+ ray.Init( start, end, mins, maxs );
+
+ if( pPlayerPortal )
+ UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
+ else
+ UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
+
+ if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
+ {
+ pm.fraction = fraction;
+ pm.endpos = endpos;
+ return;
+ }
+
+ // Check the +x, +y quadrant
+ mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z );
+ maxs = maxsSrc;
+ ray.Init( start, end, mins, maxs );
+
+ if( pPlayerPortal )
+ UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
+ else
+ UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
+
+ if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
+ {
+ pm.fraction = fraction;
+ pm.endpos = endpos;
+ return;
+ }
+
+ // Check the -x, +y quadrant
+ mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z );
+ maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z );
+ ray.Init( start, end, mins, maxs );
+
+ if( pPlayerPortal )
+ UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
+ else
+ UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
+
+ if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
+ {
+ pm.fraction = fraction;
+ pm.endpos = endpos;
+ return;
+ }
+
+ // Check the +x, -y quadrant
+ mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z );
+ maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z );
+ ray.Init( start, end, mins, maxs );
+
+ if( pPlayerPortal )
+ UTIL_Portal_TraceRay( pPlayerPortal, ray, fMask, player, collisionGroup, &pm );
+ else
+ UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm );
+
+ if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
+ {
+ pm.fraction = fraction;
+ pm.endpos = endpos;
+ return;
+ }
+
+ pm.fraction = fraction;
+ pm.endpos = endpos;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &input -
+//-----------------------------------------------------------------------------
+void CPortalGameMovement::CategorizePosition( void )
+{
+ Vector point;
+ trace_t pm;
+
+ // if the player hull point one unit down is solid, the player
+ // is on ground
+
+ // see if standing on something solid
+
+ // Doing this before we move may introduce a potential latency in water detection, but
+ // doing it after can get us stuck on the bottom in water if the amount we move up
+ // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
+ // this several times per frame, so we really need to avoid sticking to the bottom of
+ // water on each call, and the converse case will correct itself if called twice.
+ CheckWater();
+
+ // observers don't have a ground entity
+ if ( player->IsObserver() )
+ return;
+
+ point[0] = mv->GetAbsOrigin()[0];
+ point[1] = mv->GetAbsOrigin()[1];
+ point[2] = mv->GetAbsOrigin()[2] - 2;
+
+ Vector bumpOrigin;
+ bumpOrigin = mv->GetAbsOrigin();
+
+ // Shooting up really fast. Definitely not on ground.
+ // On ladder moving up, so not on ground either
+ // NOTE: 145 is a jump.
+ if ( mv->m_vecVelocity[2] > 140 ||
+ ( mv->m_vecVelocity[2] > 0.0f && player->GetMoveType() == MOVETYPE_LADDER ) )
+ {
+ SetGroundEntity( NULL );
+ }
+ else
+ {
+ // Try and move down.
+ TracePlayerBBox( bumpOrigin, point, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+
+ // If we hit a steep plane, we are not on ground
+ if ( pm.plane.normal[2] < 0.7)
+ {
+ // Test four sub-boxes, to see if any of them would have found shallower slope we could
+ // actually stand on
+
+ TracePlayerBBoxForGround2( bumpOrigin, point, GetPlayerMins(), GetPlayerMaxs(), mv->m_nPlayerHandle.Get(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+ if ( pm.plane.normal[2] < 0.7)
+ {
+
+ SetGroundEntity( NULL ); // too steep
+ // probably want to add a check for a +z velocity too!
+ if ( ( mv->m_vecVelocity.z > 0.0f ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
+ {
+ player->m_surfaceFriction = 0.25f;
+ }
+ }
+ else
+ {
+ SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
+ }
+ }
+ else
+ {
+ SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
+ }
+
+ // If we are on something...
+ if (player->GetGroundEntity() != NULL)
+ {
+ // Then we are not in water jump sequence
+ player->m_flWaterJumpTime = 0;
+
+ // If we could make the move, drop us down that 1 pixel
+ if ( player->GetWaterLevel() < WL_Waist && !pm.startsolid && !pm.allsolid )
+ {
+ // check distance we would like to move -- this is supposed to just keep up
+ // "on the ground" surface not stap us back to earth (i.e. on move origin to
+ // end position when the ground is within .5 units away) (2 units)
+ if( pm.fraction )
+ // if( pm.fraction < 0.5)
+ {
+ mv->SetAbsOrigin( pm.endpos );
+ }
+ }
+ }
+
+#ifndef CLIENT_DLL
+
+ //Adrian: vehicle code handles for us.
+ if ( player->IsInAVehicle() == false )
+ {
+ // If our gamematerial has changed, tell any player surface triggers that are watching
+ IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
+ surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( pm.surface.surfaceProps );
+ char cCurrGameMaterial = pSurfaceProp->game.material;
+ if ( !player->GetGroundEntity() )
+ {
+ cCurrGameMaterial = 0;
+ }
+
+ // Changed?
+ if ( player->m_chPreviousTextureType != cCurrGameMaterial )
+ {
+ CEnvPlayerSurfaceTrigger::SetPlayerSurface( player, cCurrGameMaterial );
+ }
+
+ player->m_chPreviousTextureType = cCurrGameMaterial;
+ }
+#endif
+ }
+}
+
+void CPortalGameMovement::Duck( void )
+{
+ return BaseClass::Duck();
+}
+
+int CPortalGameMovement::CheckStuck( void )
+{
+ if( BaseClass::CheckStuck() )
+ {
+ CPortal_Player *pPortalPlayer = GetPortalPlayer();
+
+#ifndef CLIENT_DLL
+ if( pPortalPlayer->IsAlive() )
+ g_PortalGameStats.Event_PlayerStuck( pPortalPlayer );
+#endif
+
+ //try to fix it, then recheck
+ Vector vIndecisive;
+ if( pPortalPlayer->m_hPortalEnvironment )
+ {
+ pPortalPlayer->m_hPortalEnvironment->GetVectors( &vIndecisive, NULL, NULL );
+ }
+ else
+ {
+ vIndecisive.Init( 0.0f, 0.0f, 1.0f );
+ }
+ Vector ptOldOrigin = pPortalPlayer->GetAbsOrigin();
+
+ if( pPortalPlayer->m_hPortalEnvironment )
+ {
+ if( !FindClosestPassableSpace( pPortalPlayer, vIndecisive ) )
+ {
+#ifndef CLIENT_DLL
+ DevMsg( "Hurting the player for FindClosestPassableSpaceFailure!" );
+
+ CTakeDamageInfo info( pPortalPlayer, pPortalPlayer, vec3_origin, vec3_origin, 1e10, DMG_CRUSH );
+ pPortalPlayer->OnTakeDamage( info );
+#endif
+ }
+
+ //make sure we didn't get put behind the portal >_<
+ Vector ptCurrentOrigin = pPortalPlayer->GetAbsOrigin();
+ if( vIndecisive.Dot( ptCurrentOrigin - ptOldOrigin ) < 0.0f )
+ {
+ pPortalPlayer->SetAbsOrigin( ptOldOrigin + (vIndecisive * 5.0f) ); //this is an anti-bug hack, since this would have probably popped them out of the world, we're just going to move them forward a few units
+ }
+ }
+
+ mv->SetAbsOrigin( pPortalPlayer->GetAbsOrigin() );
+ return BaseClass::CheckStuck();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+void CPortalGameMovement::SetGroundEntity( trace_t *pm )
+{
+#ifndef CLIENT_DLL
+ if ( !player->GetGroundEntity() && pm && pm->m_pEnt )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "portal_player_touchedground" );
+ if ( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+#endif
+
+ BaseClass::SetGroundEntity( pm );
+}
+
+void CPortalGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm )
+{
+ VPROF( "CGameMovement::TracePlayerBBox" );
+
+ CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get());
+
+ Ray_t ray;
+ ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() );
+
+#ifdef CLIENT_DLL
+ CTraceFilterSimple traceFilter( mv->m_nPlayerHandle.Get(), collisionGroup );
+#else
+ CTraceFilterSimple baseFilter( mv->m_nPlayerHandle.Get(), collisionGroup );
+ CTraceFilterTranslateClones traceFilter( &baseFilter );
+#endif
+
+ UTIL_Portal_TraceRay_With( pPortalPlayer->m_hPortalEnvironment, ray, fMask, &traceFilter, &pm );
+
+ // If we're moving through a portal and failed to hit anything with the above ray trace
+ // Use UTIL_Portal_TraceEntity to test this movement through a portal and override the trace with the result
+ if ( pm.fraction == 1.0f && UTIL_DidTraceTouchPortals( ray, pm ) && sv_player_trace_through_portals.GetBool() )
+ {
+ trace_t tempTrace;
+ UTIL_Portal_TraceEntity( pPortalPlayer, start, end, fMask, &traceFilter, &tempTrace );
+
+ if ( tempTrace.DidHit() && tempTrace.fraction < pm.fraction && !tempTrace.startsolid && !tempTrace.allsolid )
+ {
+ pm = tempTrace;
+ }
+ }
+}
+
+CBaseHandle CPortalGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm )
+{
+ TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm ); //hook into the existing portal special trace functionality
+
+ //Ray_t ray;
+ //ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() );
+ //UTIL_TraceRay( ray, MASK_PLAYERSOLID, mv->m_nPlayerHandle.Get(), collisionGroup, &pm );
+ if( pm.startsolid && pm.m_pEnt && (pm.contents & MASK_PLAYERSOLID) )
+ {
+#ifdef _DEBUG
+ AssertMsgOnce( false, "The player got stuck on something. Break to investigate." ); //happens enough to just leave in a perma-debugger
+ //this next trace is PURELY for tracking down how the player got stuck. Nothing new is discovered over the same trace about 10 lines up
+ TracePlayerBBox( pos, pos, MASK_PLAYERSOLID, collisionGroup, pm );
+#endif
+ return pm.m_pEnt->GetRefEHandle();
+ }
+#ifndef CLIENT_DLL
+ else if ( pm.startsolid && pm.m_pEnt && CPSCollisionEntity::IsPortalSimulatorCollisionEntity( pm.m_pEnt ) )
+ {
+ // Stuck in a portal environment object, so unstick them!
+ CPortal_Player *pPortalPlayer = (CPortal_Player *)((CBaseEntity *)mv->m_nPlayerHandle.Get());
+ pPortalPlayer->SetStuckOnPortalCollisionObject();
+
+ return INVALID_EHANDLE_INDEX;
+ }
+#endif
+ else
+ {
+ return INVALID_EHANDLE_INDEX;
+ }
+}
+
+
+// Expose our interface.
+static CPortalGameMovement g_GameMovement;
+IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );
+