Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.

753 lines
22 KiB

//========= 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 );