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.
2149 lines
61 KiB
2149 lines
61 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "cbase.h" |
|
#include "tf_gamemovement.h" |
|
#include "in_buttons.h" |
|
#include "tier0/vprof.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
|
|
#define SPEED_STOP_THRESHOLD 1.0f |
|
#define BUMP_MAX_COUNT 8 |
|
|
|
//#define IMPACT_NORMAL_FLOOR 0.35f |
|
#define IMPACT_NORMAL_FLOOR 0.7f |
|
#define IMPACT_NORMAL_WALL 0.0f |
|
|
|
enum |
|
{ |
|
MOVEMENT_BLOCKED_NONE = 0x0, |
|
MOVEMENT_BLOCKED_WALL = 0x1, |
|
MOVEMENT_BLOCKED_FLOOR = 0x2, |
|
MOVEMENT_BLOCKED_ALL = 0x4 |
|
}; |
|
|
|
#define SPEED_CROP_FRACTION_WALKING 0.4f |
|
#define SPEED_CROP_FRACTION_USING 0.3f |
|
#define SPEED_CROP_FRACTION_DUCKING 0.3f |
|
|
|
char *va(char *format, ...); |
|
|
|
extern bool g_bMovementOptimizations; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::CategorizePosition( void ) |
|
{ |
|
VPROF( "CTFGameMovement::CategorizePosition" ); |
|
|
|
// 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(); |
|
|
|
trace_t trace; |
|
Vector vStart( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z ); |
|
Vector vEnd( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - 66 ); |
|
|
|
// Assume we are not on the ground |
|
SetGroundEntity( NULL ); |
|
m_pSurfaceData = NULL; |
|
m_surfaceFriction = 1.0f; |
|
m_chTextureType = 'C'; |
|
|
|
// Check our velocity in z (are we shooting up really fast - then we are not on ground). |
|
if ( mv->m_vecVelocity.z <= 180.0f ) |
|
{ |
|
// Move downward. |
|
TracePlayerBBox( vStart, vEnd, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
|
|
|
|
// Check to see if we are on the ground |
|
if ( ( ( trace.plane.normal.z >= IMPACT_NORMAL_FLOOR ) || trace.IsDispSurfaceWalkable() ) && ( trace.fraction < 0.06f ) ) |
|
{ |
|
SetGroundEntity( &trace ); |
|
VectorCopy( trace.plane.normal, m_vecGroundNormal ); |
|
|
|
// Add standing on object to touch list |
|
if ( trace.DidHitNonWorldEntity() ) |
|
{ |
|
MoveHelper()->AddToTouched( trace, mv->m_vecVelocity ); |
|
} |
|
|
|
// Reset water jumping. |
|
player->m_flWaterJumpTime = 0.0f; |
|
|
|
// If we could make the move, drop us down that 1 pixel |
|
if ( ( int )player->GetWaterLevel() < WL_Waist && !trace.startsolid && !trace.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 |
|
mv->m_vecAbsOrigin = vStart + trace.fraction * ( vEnd - vStart ); |
|
// VectorCopy( trace.endpos, mv->m_vecAbsOrigin ); |
|
} |
|
} |
|
|
|
// NOTE: should this be surrounded by trace.fraction <= 1.0f ????? |
|
|
|
// Setup surface properties. |
|
{ |
|
VPROF( "CTFGameMovement::CategorizePosition / Surface Props" ); |
|
IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps(); |
|
m_surfaceProps = trace.surface.surfaceProps; |
|
m_pSurfaceData = physprops->GetSurfaceData( m_surfaceProps ); |
|
physprops->GetPhysicsProperties( m_surfaceProps, NULL, NULL, &m_surfaceFriction, NULL ); |
|
} |
|
|
|
// HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values. |
|
// A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players. |
|
// This scaling trivially makes them equivalent. REVISIT if this affects low friction surfaces too much. |
|
m_surfaceFriction *= 1.25f; |
|
if ( m_surfaceFriction > 1.0f ) |
|
m_surfaceFriction = 1.0f; |
|
m_chTextureType = m_pSurfaceData->game.material; |
|
} |
|
|
|
// If we are not on the ground... |
|
if ( player->GetGroundEntity() == NULL ) |
|
{ |
|
player->m_Local.m_flFallVelocity = -mv->m_vecVelocity.z; |
|
} |
|
|
|
// Store off the starting water level |
|
m_nOldWaterLevel = player->GetWaterLevel(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::HandleLadder( void ) |
|
{ |
|
// No ladder movement if the player is dead or on a train. |
|
if ( player->pl.deadflag || ( player->GetFlags() & FL_ONTRAIN ) ) |
|
return; |
|
|
|
// Ladder initialization. |
|
m_nOnLadder = 0; |
|
|
|
if ( !BaseClass::LadderMove() && ( player->GetMoveType() == MOVETYPE_LADDER ) ) |
|
{ |
|
// clear ladder stuff unless player is noclipping or being lifted by barnacle. |
|
// it will be set immediately again next frame if necessary |
|
player->SetMoveType( MOVETYPE_WALK ); |
|
player->SetMoveCollide( MOVECOLLIDE_DEFAULT ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::SpeedCrop( void ) |
|
{ |
|
// Verify speed hasn't been cropped already (shouldn't have!!!). |
|
if ( m_iSpeedCropped & SPEED_CROPPED_DUCK ) |
|
return; |
|
|
|
// Set the speed cropped flag. |
|
m_iSpeedCropped |= SPEED_CROPPED_DUCK; |
|
|
|
// If the "walking" key is pressed -- crop speed. |
|
if ( mv->m_nButtons & IN_SPEED ) |
|
{ |
|
mv->m_flForwardMove *= SPEED_CROP_FRACTION_WALKING; |
|
mv->m_flSideMove *= SPEED_CROP_FRACTION_WALKING; |
|
mv->m_flUpMove *= SPEED_CROP_FRACTION_WALKING; |
|
} |
|
// If the "use" key is pressed and the player is on the ground -- crop speed. |
|
else if ( ( mv->m_nButtons & IN_USE ) && ( player->GetGroundEntity() != NULL ) ) |
|
{ |
|
mv->m_flForwardMove *= SPEED_CROP_FRACTION_USING; |
|
mv->m_flSideMove *= SPEED_CROP_FRACTION_USING; |
|
mv->m_flUpMove *= SPEED_CROP_FRACTION_USING; |
|
} |
|
// If the player is "ducking" -- crop speed |
|
else if ( player->GetFlags() & FL_DUCKING ) |
|
{ |
|
mv->m_flForwardMove *= SPEED_CROP_FRACTION_DUCKING; |
|
mv->m_flSideMove *= SPEED_CROP_FRACTION_DUCKING; |
|
mv->m_flUpMove *= SPEED_CROP_FRACTION_DUCKING; |
|
} |
|
|
|
// Moving backwards happens more slowly |
|
float flAngle = atan2( mv->m_flSideMove, mv->m_flForwardMove ) / M_PI; |
|
flAngle = 2.0f * (fabs(flAngle) - 0.5f); |
|
flAngle = clamp( flAngle, 0.0f, 1.0f ); |
|
float flFactor = 1.0f - fabs(flAngle) * (1.0f - sv_backspeed.GetFloat()); |
|
|
|
mv->m_flForwardMove *= flFactor; |
|
mv->m_flSideMove *= flFactor; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Figures out how the constraint should slow us down |
|
//----------------------------------------------------------------------------- |
|
float CTFGameMovement::ComputeConstraintSpeedFactor( void ) |
|
{ |
|
// If we have a constraint, slow down because of that too... |
|
// Get the TF movement data. |
|
CTFMoveData *pTFMove = TFMove(); |
|
if ( !pTFMove || pTFMove->m_flConstraintRadius == 0.0f ) |
|
return 1.0f; |
|
|
|
float flDistSq = mv->m_vecAbsOrigin.DistToSqr( pTFMove->m_vecConstraintCenter ); |
|
|
|
float flOuterRadiusSq = pTFMove->m_flConstraintRadius * pTFMove->m_flConstraintRadius; |
|
float flInnerRadiusSq = pTFMove->m_flConstraintRadius - pTFMove->m_flConstraintWidth; |
|
flInnerRadiusSq *= flInnerRadiusSq; |
|
|
|
// Only slow us down if we're inside the constraint ring |
|
if ((flDistSq <= flInnerRadiusSq) || (flDistSq >= flOuterRadiusSq)) |
|
return 1.0f; |
|
|
|
// Only slow us down if we're running away from the center |
|
Vector vecDesired; |
|
VectorMultiply( m_vecForward, mv->m_flForwardMove, vecDesired ); |
|
VectorMA( vecDesired, mv->m_flSideMove, m_vecRight, vecDesired ); |
|
VectorMA( vecDesired, mv->m_flUpMove, m_vecUp, vecDesired ); |
|
|
|
Vector vecDelta; |
|
VectorSubtract( mv->m_vecAbsOrigin, pTFMove->m_vecConstraintCenter, vecDelta ); |
|
VectorNormalize( vecDelta ); |
|
VectorNormalize( vecDesired ); |
|
if (DotProduct( vecDelta, vecDesired ) < 0.0f) |
|
return 1.0f; |
|
|
|
float flFrac = (sqrt(flDistSq) - (pTFMove->m_flConstraintRadius - pTFMove->m_flConstraintWidth)) / pTFMove->m_flConstraintWidth; |
|
|
|
float flSpeedFactor = Lerp( flFrac, 1.0f, pTFMove->m_flConstraintSpeedFactor ); |
|
return flSpeedFactor; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called in PrePlayerMove(), this function clamps the player's overall |
|
// speed. It is clamped to either the maximum client's or server's |
|
// speed (whichever is lower). |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::SetupSpeed( void ) |
|
{ |
|
// Reset the outgoing applied velocity. |
|
mv->m_outWishVel.Init(); |
|
|
|
// Don't clamp speed if we are sin an "ISOMETRIC" or "NOCLIP" movetype. |
|
if ( ( player->GetMoveType() == MOVETYPE_ISOMETRIC ) || |
|
( player->GetMoveType() == MOVETYPE_NOCLIP ) ) |
|
return; |
|
|
|
// Some events negate speed, check for these first. |
|
if ( ( player->GetFlags() & FL_FROZEN ) || ( player->GetFlags() & FL_ONTRAIN ) || IsDead() ) |
|
{ |
|
mv->m_flForwardMove = 0; |
|
mv->m_flSideMove = 0; |
|
mv->m_flUpMove = 0; |
|
return; |
|
} |
|
|
|
// Calculate the players max speed given movements. |
|
float flSpeed = ( mv->m_flForwardMove * mv->m_flForwardMove ) + |
|
( mv->m_flSideMove * mv->m_flSideMove ) + |
|
( mv->m_flUpMove * mv->m_flUpMove ); |
|
if ( flSpeed == 0.0f ) |
|
return; |
|
|
|
flSpeed = sqrt( flSpeed ); |
|
|
|
// NOTE: The client max speed was set to the movement max speed (mv->m_flMaxSpeed) |
|
// in the SetupMove, however the _ProcessMovement code post sets |
|
// mv->m_flMaxSpeed to the maximum server speed (sv_maxspeed), |
|
// thus, the need to the check. If we forego the maximum server speed, |
|
// this code can be removed. |
|
if ( mv->m_flClientMaxSpeed ) |
|
{ |
|
mv->m_flMaxSpeed = MIN( mv->m_flClientMaxSpeed, mv->m_flMaxSpeed ); |
|
} |
|
|
|
// Slow down by the speed factor |
|
float flSpeedFactor = 1.0f; |
|
if (m_pSurfaceData) |
|
{ |
|
flSpeedFactor = m_pSurfaceData->game.maxSpeedFactor; |
|
} |
|
|
|
// If we have a constraint, slow down because of that too... |
|
// Get the TF movement data |
|
float flConstraintSpeedFactor = ComputeConstraintSpeedFactor(); |
|
if (flConstraintSpeedFactor < flSpeedFactor) |
|
flSpeedFactor = flConstraintSpeedFactor; |
|
|
|
mv->m_flMaxSpeed *= flSpeedFactor; |
|
|
|
if ( flSpeed > mv->m_flMaxSpeed ) |
|
{ |
|
float flRatio = mv->m_flMaxSpeed / flSpeed; |
|
mv->m_flForwardMove *= flRatio; |
|
mv->m_flSideMove *= flRatio; |
|
mv->m_flUpMove *= flRatio; |
|
} |
|
|
|
// Crop speed if necessary. |
|
SpeedCrop(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::FinishUnDuck( void ) |
|
{ |
|
int i; |
|
trace_t trace; |
|
Vector newOrigin; |
|
|
|
Vector vDuckHullMin = GetPlayerMins( true ); |
|
Vector vStandHullMin = GetPlayerMins( false ); |
|
|
|
VectorCopy( mv->m_vecAbsOrigin, newOrigin ); |
|
|
|
if ( player->GetGroundEntity() != NULL ) |
|
{ |
|
for ( i = 0; i < 3; i++ ) |
|
{ |
|
newOrigin[i] += ( vDuckHullMin[i] - vStandHullMin[i] ); |
|
} |
|
} |
|
else |
|
{ |
|
Vector viewDelta = player->GetViewOffset() - GetPlayerViewOffset( false ); |
|
VectorAdd( newOrigin, viewDelta, newOrigin ); |
|
} |
|
|
|
TracePlayerBBox( newOrigin, newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
|
|
if ( !trace.startsolid ) |
|
{ |
|
player->m_Local.m_bDucked = false; |
|
|
|
// Oh, no, changing hulls stuck us into something, try unsticking downward first. |
|
TracePlayerBBox( newOrigin, newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
if ( trace.startsolid ) |
|
{ |
|
// See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot |
|
player->m_Local.m_bDucked = true; |
|
return; |
|
} |
|
|
|
player->RemoveFlag( FL_DUCKING ); |
|
player->m_Local.m_bDucking = false; |
|
player->SetViewOffset( GetPlayerViewOffset( false ) ); |
|
player->m_Local.m_flDucktime = 0; |
|
|
|
VectorCopy( newOrigin, mv->m_vecAbsOrigin ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::HandleDuck( void ) |
|
{ |
|
// Store button presses and changes. |
|
int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame |
|
int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed" |
|
int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released" |
|
|
|
if ( mv->m_nButtons & IN_DUCK ) |
|
{ |
|
mv->m_nOldButtons |= IN_DUCK; |
|
} |
|
else |
|
{ |
|
mv->m_nOldButtons &= ~IN_DUCK; |
|
} |
|
|
|
// Handle the player dead case. |
|
if ( IsDead() && ( player->GetFlags() & FL_DUCKING ) ) |
|
{ |
|
FinishUnDuck(); |
|
return; |
|
} |
|
|
|
if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) |
|
{ |
|
// Remove all movement when ducking! |
|
mv->m_flForwardMove = 0.0f; |
|
mv->m_flSideMove = 0.0f; |
|
mv->m_flUpMove = 0.0f; |
|
|
|
if ( mv->m_nButtons & IN_DUCK ) |
|
{ |
|
if ( ( buttonsPressed & IN_DUCK ) && !( player->GetFlags() & FL_DUCKING ) ) |
|
{ |
|
// Use 1 second so super long jump will work |
|
player->m_Local.m_flDucktime = 1000; |
|
player->m_Local.m_bDucking = true; |
|
} |
|
|
|
float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime ); |
|
float duckseconds = duckmilliseconds / 1000.0f; |
|
|
|
if ( player->m_Local.m_bDucking ) |
|
{ |
|
// Finish ducking immediately if duck time is over or not on ground |
|
if ( ( duckseconds > TIME_TO_DUCK ) || |
|
( player->GetGroundEntity() == NULL ) ) |
|
{ |
|
FinishDuck(); |
|
} |
|
else |
|
{ |
|
// Calc parametric time |
|
float duckFraction = SimpleSpline( duckseconds / TIME_TO_DUCK ); |
|
SetDuckedEyeOffset( duckFraction ); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if ( (buttonsReleased & IN_DUCK ) && ( player->GetFlags() & FL_DUCKING ) ) |
|
{ |
|
// Use 1 second so super long jump will work |
|
player->m_Local.m_flDucktime = 1000; |
|
player->m_Local.m_bDucking = true; // or unducking |
|
} |
|
|
|
float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime ); |
|
float duckseconds = duckmilliseconds / 1000.0f; |
|
|
|
if ( player->m_Local.m_bDucking ) // or unducking |
|
{ |
|
// Finish ducking immediately if duck time is over or not on ground |
|
if ( ( duckseconds > TIME_TO_UNDUCK ) || |
|
( player->GetGroundEntity() == NULL ) ) |
|
{ |
|
FinishUnDuck(); |
|
} |
|
else |
|
{ |
|
// Calc parametric time |
|
float duckFraction = SimpleSpline( 1.0f - ( duckseconds / TIME_TO_UNDUCK ) ); |
|
SetDuckedEyeOffset( duckFraction ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::SetupViewAngles( void ) |
|
{ |
|
// Cache the view angles. |
|
AngleVectors( mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp ); |
|
|
|
// Add a view punch if necessary. |
|
QAngle v_angle = ( mv->m_vecViewAngles + player->m_Local.m_vecPunchAngle ); |
|
|
|
// Adjust the view roll angle. |
|
if ( ( player->GetMoveType() != MOVETYPE_ISOMETRIC ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) ) |
|
{ |
|
mv->m_vecViewAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ) * 4.0f; |
|
} |
|
else |
|
{ |
|
mv->m_vecViewAngles[ROLL] = 0.0f; |
|
} |
|
|
|
// Copy the yaw and pitch. |
|
// NOTE: Adjust the client view angles to match the values on the server. |
|
mv->m_vecViewAngles[PITCH] = v_angle[PITCH]; |
|
mv->m_vecViewAngles[YAW] = v_angle[YAW]; |
|
if ( mv->m_vecViewAngles[YAW] > 180.0f ) |
|
{ |
|
mv->m_vecViewAngles[YAW] -= 360.0f; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::CheckDeath( void ) |
|
{ |
|
// If we are dead, setup the appropriate data |
|
if ( IsDead() ) |
|
{ |
|
mv->m_flForwardMove = 0.0f; |
|
mv->m_flSideMove = 0.0f; |
|
mv->m_flUpMove = 0.0f; |
|
|
|
VectorCopy( mv->m_vecOldAngles, mv->m_vecViewAngles ); |
|
|
|
player->SetViewOffset( VEC_DEAD_VIEWHEIGHT_SCALED( this ) ); |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::UpdateTimers( void ) |
|
{ |
|
BaseClass::ReduceTimers(); |
|
BaseClass::DecayPunchAngle(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Should the step sound play? |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::ShouldPlayStepSound( surfacedata_t *psurface, float fvol ) |
|
{ |
|
if ( !m_nOnLadder && Vector2DLength( mv->m_vecVelocity.AsVector2D() ) <= 100 ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : step - |
|
// fvol - |
|
// force - force sound to play |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::PlayStepSound( surfacedata_t *psurface, float fvol, bool force ) |
|
{ |
|
if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) |
|
return; |
|
|
|
if ( !psurface ) |
|
return; |
|
|
|
if (!force && !ShouldPlayStepSound( psurface, fvol )) |
|
return; |
|
|
|
// TODO: See note above, should this be hooked up? |
|
// PlantFootprint( psurface ); |
|
|
|
unsigned short stepSoundName = player->m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright; |
|
player->m_Local.m_nStepside = !player->m_Local.m_nStepside; |
|
|
|
if ( !stepSoundName ) |
|
return; |
|
|
|
if ( !mv->m_bFirstRunOfFunctions ) |
|
return; |
|
|
|
IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps(); |
|
const char *pSoundName = physprops->GetString( stepSoundName ); |
|
char szSound[256]; |
|
|
|
// Prepend our team's footsteps |
|
if ( player->GetTeamNumber() == TEAM_HUMANS ) |
|
{ |
|
Q_snprintf( szSound, sizeof(szSound), "Human.%s", pSoundName ); |
|
} |
|
else if ( player->GetTeamNumber() == TEAM_ALIENS ) |
|
{ |
|
Q_snprintf( szSound, sizeof(szSound), "Alien.%s", pSoundName ); |
|
} |
|
else |
|
{ |
|
return; |
|
} |
|
|
|
CSoundParameters params; |
|
if ( !CBaseEntity::GetParametersForSound( szSound, params, NULL ) ) |
|
return; |
|
|
|
MoveHelper( )->StartSound( mv->m_vecAbsOrigin, CHAN_BODY, params.soundname, fvol, params.soundlevel, 0, params.pitch ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFGameMovement::CheckStuck( void ) |
|
{ |
|
VPROF( "CTFGameMovement::CheckStuck" ); |
|
|
|
// NOTE: I am not bothering much with this as I am going to |
|
// implement an new UnSticking process. |
|
if ( ( player->GetMoveType() == MOVETYPE_NOCLIP ) || |
|
( player->GetMoveType() == MOVETYPE_NONE ) || |
|
( player->GetMoveType() == MOVETYPE_ISOMETRIC ) ) |
|
return 0; |
|
|
|
return BaseClass::CheckStuck(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::PrePlayerMove( void ) |
|
{ |
|
VPROF( "CTFGameMovement::PrePlayerMove" ); |
|
|
|
// Assume we don't touch anything (Reset the touch list). |
|
MoveHelper()->ResetTouchList(); |
|
|
|
// Check to see if we are stuck. |
|
if ( CheckStuck() ) |
|
return false; |
|
|
|
// Update (reduce) movement timers. |
|
UpdateTimers(); |
|
|
|
// Check to see if the player is dead and setup death data, otherwise setup |
|
// the players view angles. |
|
if ( !CheckDeath() ) |
|
{ |
|
SetupViewAngles(); |
|
} |
|
|
|
// Handle ducking. |
|
HandleDuck(); |
|
|
|
// Handle ladder. |
|
HandleLadder(); |
|
|
|
// Categorize the player's position. |
|
CategorizePosition(); |
|
|
|
// Calculate the player's movement speed (has to happen after categorize position) |
|
SetupSpeed(); |
|
|
|
// Update our stepping sound (based on the player's location). |
|
player->UpdateStepSound( m_pSurfaceData, mv->m_vecAbsOrigin, mv->m_vecVelocity ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::PostPlayerMove( void ) |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::HandlePlayerMove( void ) |
|
{ |
|
// Handle movement. |
|
switch ( player->GetMoveType() ) |
|
{ |
|
case MOVETYPE_NONE: |
|
{ |
|
break; |
|
} |
|
case MOVETYPE_NOCLIP: |
|
{ |
|
FullNoClipMove( sv_noclipspeed.GetFloat(), sv_noclipaccelerate.GetFloat() ); |
|
break; |
|
} |
|
case MOVETYPE_FLY: |
|
case MOVETYPE_FLYGRAVITY: |
|
{ |
|
FullTossMove(); |
|
break; |
|
} |
|
|
|
case MOVETYPE_LADDER: |
|
{ |
|
FullLadderMove(); |
|
break; |
|
} |
|
|
|
case MOVETYPE_WALK: |
|
{ |
|
// This should be moved elsewhere!!! Just get it going for now. |
|
CTFMoveData *pTFMove = TFMove(); |
|
Vector vecPlayerOrigin( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z ); |
|
FullWalkMove(); |
|
Vector vecPlayerDelta; |
|
VectorSubtract( mv->m_vecAbsOrigin, vecPlayerOrigin, vecPlayerDelta ); |
|
|
|
if ( ( fabs( vecPlayerDelta.x ) > 0.0001f ) || |
|
( fabs( vecPlayerDelta.y ) > 0.0001f ) || |
|
( fabs( vecPlayerDelta.z ) > 0.0001f ) ) |
|
{ |
|
VectorCopy( vecPlayerDelta, pTFMove->m_vecPosDelta ); |
|
} |
|
break; |
|
} |
|
|
|
case MOVETYPE_ISOMETRIC: |
|
{ |
|
//IsometricMove(); |
|
// Could also try: FullTossMove(); |
|
FullWalkMove(); |
|
break; |
|
} |
|
|
|
default: |
|
{ |
|
DevMsg( 1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer()); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::PlayerMove( void ) |
|
{ |
|
VPROF( "CTFGameMovement::PlayerMove" ); |
|
|
|
// Setup and initialization pre-player movement. |
|
if (!PrePlayerMove()) |
|
return; |
|
|
|
// Handle Movement |
|
HandlePlayerMove(); |
|
|
|
// Clean-up and updates post-player movement. |
|
PostPlayerMove(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::FullWalkMove() |
|
{ |
|
VPROF( "CTFGameMovement::FullWalkMove" ); |
|
|
|
if ( !CheckWater() ) |
|
{ |
|
StartGravity(); |
|
} |
|
|
|
// If we are leaping out of the water, just update the counters. |
|
if (player->m_flWaterJumpTime) |
|
{ |
|
WaterJump(); |
|
TryPlayerMove(); |
|
// See if we are still in water? |
|
CheckWater(); |
|
return; |
|
} |
|
|
|
// If we are swimming in the water, see if we are nudging against a place we can jump up out |
|
// of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0 |
|
if ( player->GetWaterLevel() >= WL_Waist ) |
|
{ |
|
if ( player->GetWaterLevel() == WL_Waist ) |
|
{ |
|
CheckWaterJump(); |
|
} |
|
|
|
// If we are falling again, then we must not trying to jump out of water any more. |
|
if ( mv->m_vecVelocity[2] < 0 && |
|
player->m_flWaterJumpTime ) |
|
{ |
|
player->m_flWaterJumpTime = 0; |
|
} |
|
|
|
// Was jump button pressed? |
|
if (mv->m_nButtons & IN_JUMP) |
|
{ |
|
CheckJumpButton(); |
|
} |
|
else |
|
{ |
|
mv->m_nOldButtons &= ~IN_JUMP; |
|
} |
|
|
|
// Perform regular water movement |
|
WaterMove(); |
|
VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity); |
|
|
|
// Redetermine position vars |
|
CategorizePosition(); |
|
} |
|
else |
|
// Not fully underwater |
|
{ |
|
// Was jump button pressed? |
|
if (mv->m_nButtons & IN_JUMP) |
|
{ |
|
CheckJumpButton(); |
|
} |
|
else |
|
{ |
|
mv->m_nOldButtons &= ~IN_JUMP; |
|
} |
|
|
|
// Fricion is handled before we add in any base velocity. That way, if we are on a conveyor, |
|
// we don't slow when standing still, relative to the conveyor. |
|
if (player->GetGroundEntity() != NULL) |
|
{ |
|
mv->m_vecVelocity[2] = 0.0; |
|
Friction(); |
|
} |
|
|
|
// Make sure velocity is valid. |
|
CheckVelocity(); |
|
|
|
if (player->GetGroundEntity() != NULL) |
|
{ |
|
// WalkMove(); |
|
WalkMove2(); |
|
} |
|
else |
|
{ |
|
AirMove(); // Take into account movement when in air. |
|
} |
|
|
|
// Set final flags. |
|
CategorizePosition(); |
|
|
|
// 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 ); |
|
|
|
// Make sure velocity is valid. |
|
CheckVelocity(); |
|
|
|
// Add any remaining gravitational component. |
|
if ( !CheckWater() ) |
|
{ |
|
FinishGravity(); |
|
} |
|
|
|
// If we are on ground, no downward velocity. |
|
if ( player->GetGroundEntity() != NULL ) |
|
{ |
|
mv->m_vecVelocity[2] = 0; |
|
} |
|
CheckFalling(); |
|
} |
|
|
|
if ( ( m_nOldWaterLevel == WL_NotInWater && player->GetWaterLevel() != WL_NotInWater ) || |
|
( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) ) |
|
{ |
|
PlaySwimSound(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::AirMove( void ) |
|
{ |
|
VPROF( "CTFGameMovement::AirMove" ); |
|
|
|
// NOTE: This was causing some additional problems with walking on physics |
|
// objects. The movement system needs to be cleaned up! Keep the old |
|
// code until the system has been fixed.s |
|
BaseClass::AirMove(); |
|
return; |
|
|
|
|
|
int i; |
|
Vector wishvel; |
|
float fmove, smove; |
|
Vector wishdir; |
|
float wishspeed; |
|
Vector forward, right, up; |
|
|
|
// Initialize the movement stack. |
|
VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[0].m_vecPosition ); |
|
VectorCopy( m_vecGroundNormal, m_aMovementStack[0].m_vecImpactNormal ); |
|
m_nMovementStackSize = 1; |
|
|
|
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 |
|
wishspeed = VectorNormalize(wishdir); |
|
|
|
// |
|
// clamp to server defined max speed |
|
// |
|
if (wishspeed > mv->m_flMaxSpeed) |
|
{ |
|
VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); |
|
wishspeed = mv->m_flMaxSpeed; |
|
} |
|
|
|
AirAccelerate( wishdir, wishspeed, sv_airaccelerate.GetFloat() ); |
|
|
|
// Add in any base velocity to the current velocity. |
|
VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); |
|
|
|
TryPlayerMove2(); |
|
|
|
// Try to stand up. |
|
TryStanding(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::WalkMove( void ) |
|
{ |
|
_WalkMove(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: This is here until the new movement code is complete. I needed |
|
// to override hl2's walk move so that "floors" are identified |
|
// differently. |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::_WalkMove( void ) |
|
{ |
|
VPROF( "CTFGameMovement::_WalkMove" ); |
|
|
|
int clip; |
|
int i; |
|
|
|
Vector wishvel; |
|
float spd; |
|
float fmove, smove; |
|
Vector wishdir; |
|
float wishspeed; |
|
|
|
Vector dest, start; |
|
Vector original, originalvel; |
|
Vector down, downvel; |
|
float downdist, updist; |
|
trace_t pm; |
|
Vector forward, right, up; |
|
|
|
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles |
|
|
|
CHandle< CBaseEntity > oldground; |
|
oldground = player->GetGroundEntity(); |
|
|
|
// 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 |
|
wishspeed = VectorNormalize(wishdir); |
|
|
|
// |
|
// Clamp to server defined max speed |
|
// |
|
if (wishspeed > mv->m_flMaxSpeed) |
|
{ |
|
VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); |
|
wishspeed = mv->m_flMaxSpeed; |
|
} |
|
|
|
// Set pmove velocity |
|
mv->m_vecVelocity[2] = 0; |
|
Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() ); |
|
mv->m_vecVelocity[2] = 0; |
|
|
|
// Add in any base velocity to the current velocity. |
|
VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); |
|
|
|
spd = VectorLength( mv->m_vecVelocity ); |
|
|
|
if (spd < 1.0f) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
return; |
|
} |
|
|
|
// first try just moving to the destination |
|
dest[0] = mv->m_vecAbsOrigin[0] + mv->m_vecVelocity[0]*gpGlobals->frametime; |
|
dest[1] = mv->m_vecAbsOrigin[1] + mv->m_vecVelocity[1]*gpGlobals->frametime; |
|
dest[2] = mv->m_vecAbsOrigin[2]; |
|
|
|
// first try moving directly to the next spot |
|
VectorCopy (dest, start); |
|
|
|
TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm ); |
|
|
|
// If we made it all the way, then copy trace end |
|
// as new player position. |
|
|
|
mv->m_outWishVel += wishdir * wishspeed; |
|
|
|
if (pm.fraction == 1) |
|
{ |
|
VectorCopy( pm.endpos, mv->m_vecAbsOrigin ); |
|
return; |
|
} |
|
|
|
if (oldground == NULL && // Don't walk up stairs if not on ground. |
|
player->GetWaterLevel() == 0) |
|
return; |
|
|
|
if (player->m_flWaterJumpTime) // If we are jumping out of water, don't do anything more. |
|
return; |
|
|
|
// Try sliding forward both on ground and up 16 pixels |
|
// take the move that goes farthest |
|
VectorCopy (mv->m_vecAbsOrigin, original); // Save out original pos & |
|
VectorCopy (mv->m_vecVelocity, originalvel); // velocity. |
|
|
|
// Slide move |
|
clip = TryPlayerMove(); |
|
|
|
// Copy the results out |
|
VectorCopy (mv->m_vecAbsOrigin , down); |
|
VectorCopy (mv->m_vecVelocity, downvel); |
|
|
|
// Reset original values. |
|
VectorCopy (original, mv->m_vecAbsOrigin); |
|
VectorCopy (originalvel, mv->m_vecVelocity); |
|
|
|
// Start out up one stair height |
|
VectorCopy (mv->m_vecAbsOrigin, dest); |
|
dest[2] += player->m_Local.m_flStepSize; |
|
|
|
TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm ); |
|
|
|
// If we started okay and made it part of the way at least, |
|
// copy the results to the movement start position and then |
|
// run another move try. |
|
if (!pm.startsolid && !pm.allsolid) |
|
{ |
|
VectorCopy (pm.endpos, mv->m_vecAbsOrigin); |
|
} |
|
|
|
// slide move the rest of the way. |
|
clip = TryPlayerMove(); |
|
|
|
// Now try going back down from the end point |
|
// press down the stepheight |
|
VectorCopy (mv->m_vecAbsOrigin, dest); |
|
dest[2] -= player->m_Local.m_flStepSize; |
|
|
|
TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm ); |
|
|
|
// If we are not on the ground any more then |
|
// use the original movement attempt |
|
if ( pm.plane.normal[2] < IMPACT_NORMAL_FLOOR ) |
|
goto usedown; |
|
// If the trace ended up in empty space, copy the end |
|
// over to the origin. |
|
if (!pm.startsolid && !pm.allsolid) |
|
{ |
|
VectorCopy (pm.endpos, mv->m_vecAbsOrigin); |
|
} |
|
// Copy this origion to up. |
|
VectorCopy (mv->m_vecAbsOrigin, up); |
|
|
|
// decide which one went farther |
|
downdist = (down[0] - original[0])*(down[0] - original[0]) |
|
+ (down[1] - original[1])*(down[1] - original[1]); |
|
updist = (up[0] - original[0])*(up[0] - original[0]) |
|
+ (up[1] - original[1])*(up[1] - original[1]); |
|
|
|
if (downdist > updist) |
|
{ |
|
usedown: |
|
; |
|
VectorCopy (down , mv->m_vecAbsOrigin); |
|
VectorCopy (downvel, mv->m_vecVelocity); |
|
} |
|
else // copy z value from slide move |
|
mv->m_vecVelocity[2] = downvel[2]; |
|
|
|
float stepDist = mv->m_vecAbsOrigin.z - original.z; |
|
if ( stepDist > 0 ) |
|
{ |
|
mv->m_outStepHeight += stepDist; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::AccelerateWithoutMomentum( Vector &wishdir, float wishspeed, float accel ) |
|
{ |
|
// No acceleration if the player is water-jumping or dead. |
|
if ( player->pl.deadflag || player->m_flWaterJumpTime ) |
|
return; |
|
|
|
// See if we are changing direction a bit |
|
// float flCurrentSpeed = mv->m_vecVelocity.Dot( wishdir ); |
|
float flCurrentSpeed = mv->m_vecVelocity.Length(); |
|
|
|
// Reduce wishspeed by the amount of veer. |
|
float flAddSpeed = wishspeed - flCurrentSpeed; |
|
|
|
// If not going to add any speed, done. |
|
if ( flAddSpeed <= 0.0f ) |
|
return; |
|
|
|
// Determine amount of accleration. |
|
float flAccelSpeed = accel * gpGlobals->frametime * wishspeed * m_surfaceFriction; |
|
|
|
// Cap at addspeed |
|
if ( flAccelSpeed > flAddSpeed ) |
|
{ |
|
flAccelSpeed = flAddSpeed; |
|
} |
|
|
|
// Adjust velocity. |
|
for ( int iAxis = 0; iAxis < 3; iAxis++ ) |
|
{ |
|
mv->m_vecVelocity[iAxis] += flAccelSpeed * wishdir[iAxis]; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::Accelerate( Vector &wishdir, float wishspeed, float accel ) |
|
{ |
|
// No acceleration if the player is water-jumping or dead. |
|
if ( player->pl.deadflag || player->m_flWaterJumpTime ) |
|
return; |
|
|
|
// See if we are changing direction a bit |
|
// float flCurrentSpeed = mv->m_vecVelocity.Dot( wishdir ); |
|
float flCurrentSpeed = mv->m_vecVelocity.Length(); |
|
|
|
// Reduce wishspeed by the amount of veer. |
|
float flAddSpeed = wishspeed - flCurrentSpeed; |
|
|
|
// If not going to add any speed, done. |
|
if ( flAddSpeed <= 0.0f ) |
|
return; |
|
|
|
// Determine amount of accleration. |
|
float flAccelSpeed = accel * gpGlobals->frametime * wishspeed * m_surfaceFriction; |
|
|
|
// Cap at addspeed |
|
if ( flAccelSpeed > flAddSpeed ) |
|
{ |
|
flAccelSpeed = flAddSpeed; |
|
} |
|
|
|
// Gravity. |
|
float flGravityAdj = CalcGravityAdjustment( wishdir ); |
|
|
|
// Adjust velocity. |
|
for ( int iAxis = 0; iAxis < 3; iAxis++ ) |
|
{ |
|
mv->m_vecVelocity[iAxis] += ( flAccelSpeed * wishdir[iAxis] * flGravityAdj ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFGameMovement::CalcGravityAdjustment( const Vector &wishdir ) |
|
{ |
|
// Get the TF movement data. |
|
CTFMoveData *pTFMove = TFMove(); |
|
if ( !pTFMove ) |
|
return 1.0f; |
|
|
|
if ( player->GetMoveType() == MOVETYPE_NOCLIP ) |
|
return 1.0f; |
|
|
|
Vector vecPositionDelta = pTFMove->m_vecPosDelta; |
|
VectorNormalize( vecPositionDelta ); |
|
|
|
// Hard-code my table |
|
float flModifier = 0.0f; |
|
if ( vecPositionDelta.z < -0.342f ) |
|
{ |
|
flModifier = 1.25f; |
|
} |
|
else if ( vecPositionDelta.z < 0.0f ) |
|
{ |
|
flModifier = 1.15f; |
|
} |
|
else if ( vecPositionDelta.z < 0.342f ) /* >20 */ |
|
{ |
|
flModifier = 1.0f; |
|
} |
|
else if ( vecPositionDelta.z < 0.5f ) /* 20-30 */ |
|
{ |
|
float flDelta = 0.5f - vecPositionDelta.z; |
|
flDelta /= 0.158f; |
|
flDelta = 1.0f - flDelta; |
|
flModifier = 1.0f - ( 0.25 * flDelta ); |
|
} |
|
else if ( vecPositionDelta.z < 0.707f ) /* 30-45 */ |
|
{ |
|
float flDelta = 0.707f - vecPositionDelta.z; |
|
flDelta /= 0.207f; |
|
flDelta = 1.0f - flDelta; |
|
flModifier = 0.75 - ( 0.25f * flDelta ); |
|
} |
|
else if ( vecPositionDelta.z < 0.766f ) /* 45-50 */ |
|
{ |
|
float flDelta = 0.766f - vecPositionDelta.z; |
|
flDelta /= 0.059f; |
|
flDelta = 1.0f - flDelta; |
|
flModifier = 0.5f - ( 0.40f * flDelta ); |
|
} |
|
else |
|
{ |
|
flModifier = 0.35f; |
|
} |
|
|
|
AddToMomentumList( flModifier ); |
|
flModifier = GetMomentum(); |
|
|
|
#if 0 |
|
// Debug! |
|
if ( player->IsServer() ) |
|
{ |
|
Msg( "Server:Gravity Adj = %lf\n", flModifier ); |
|
} |
|
else |
|
{ |
|
Msg( "Client:Gravity Adj = %lf\n", flModifier ); |
|
} |
|
#endif |
|
|
|
return flModifier; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::CalcWishVelocityAndPosition( Vector &vWishPos, Vector &vWishDir, |
|
float &flWishSpeed ) |
|
{ |
|
// |
|
// Determine the movement angles. |
|
// |
|
Vector vForward, vRight, vUp; |
|
AngleVectors( mv->m_vecViewAngles, &vForward, &vRight, &vUp ); |
|
|
|
// |
|
// Zero out the z component of the movement vectors and renormalize. |
|
// |
|
vForward.z = 0.0f; |
|
VectorNormalize( vForward ); |
|
vRight.z = 0.0f; |
|
VectorNormalize( vRight ); |
|
|
|
// |
|
// Determine the xy parts of the velocity. |
|
// |
|
Vector vWishVel( 0.0f, 0.0f, 0.0f ); |
|
for ( int axis = 0; axis < 2; axis++ ) |
|
{ |
|
vWishVel[axis] = ( vForward[axis] * mv->m_flForwardMove ) + |
|
( vRight[axis] * mv->m_flSideMove ); |
|
} |
|
vWishVel.z = 0.0f; |
|
|
|
// |
|
// Componentize the velocity into direction and speed. |
|
// |
|
VectorCopy( vWishVel, vWishDir ); |
|
flWishSpeed = VectorNormalize( vWishDir ); |
|
if ( flWishSpeed > mv->m_flMaxSpeed ) |
|
{ |
|
VectorScale( vWishVel, ( mv->m_flMaxSpeed / flWishSpeed ), vWishVel ); |
|
flWishSpeed = mv->m_flMaxSpeed; |
|
} |
|
|
|
// |
|
// Accelerate (in the plane). |
|
// |
|
mv->m_vecVelocity.z = 0.0f; |
|
Accelerate( vWishDir, flWishSpeed, sv_accelerate.GetFloat() ); |
|
mv->m_vecVelocity.z = 0.0f; |
|
|
|
// Add in any base velocity (from conveyers, etc.) to the current velocity. |
|
VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); |
|
|
|
// |
|
// Stop the player (zero out velocity) if the player's speed is below a |
|
// given threshold. |
|
// |
|
float flSpeed = VectorLength( mv->m_vecVelocity ); |
|
if ( flSpeed < SPEED_STOP_THRESHOLD ) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
return false; |
|
} |
|
|
|
// |
|
// Calculate the wish position. |
|
// |
|
vWishPos.x = mv->m_vecAbsOrigin.x + ( mv->m_vecVelocity.x * gpGlobals->frametime ); |
|
vWishPos.y = mv->m_vecAbsOrigin.y + ( mv->m_vecVelocity.y * gpGlobals->frametime ); |
|
vWishPos.z = mv->m_vecAbsOrigin.z; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline void CTFGameMovement::TracePlayerBBoxWithStep( const Vector &vStart, const Vector &vEnd, |
|
unsigned int fMask, int collisionGroup, trace_t &trace ) |
|
{ |
|
VPROF( "CTFGameMovement::TracePlayerBBoxWithStep" ); |
|
|
|
Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked ); |
|
vHullMin.z += player->m_Local.m_flStepSize; |
|
Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked ); |
|
|
|
Ray_t ray; |
|
ray.Init( vStart, vEnd, vHullMin, vHullMax ); |
|
UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &trace ); |
|
} |
|
|
|
#if 0 |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline void CGameMovement::TracePlayerBBox( const Vector &start, const Vector &end, |
|
unsigned int fMask, int collisionGroup, trace_t& pm ) |
|
{ |
|
VPROF( "CTFGameMovement::TracePlayerBBox" ); |
|
|
|
Ray_t ray; |
|
ray.Init( start, end, GetPlayerMins( player->m_Local.m_bDucked ), GetPlayerMaxs( player->m_Local.m_bDucked ) ); |
|
UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm ); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
inline int CTFGameMovement::BlockerType( const Vector &vImpactNormal ) |
|
{ |
|
// If the impact plane has a high z component in the normal, then |
|
// it is probably a floor. |
|
if ( vImpactNormal.z > IMPACT_NORMAL_FLOOR ) |
|
return 1; |
|
|
|
// If the impact plane has a zero z component in the normal, then it is |
|
// probably a step or wall. |
|
if ( vImpactNormal.z == IMPACT_NORMAL_WALL ) |
|
return 2; |
|
|
|
// None |
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::RedirectGroundVelocity( const trace_t &trace ) |
|
{ |
|
// Check for max planes. |
|
if ( m_nImpactPlaneCount >= MAX_IMPACT_PLANES ) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
return false; |
|
} |
|
|
|
// Add the impact plane normal to the list. |
|
VectorCopy( trace.plane.normal, m_aImpactPlaneNormals[m_nImpactPlaneCount] ); |
|
m_nImpactPlaneCount++; |
|
|
|
int iPlane, iPlane2; |
|
for ( iPlane = 0; iPlane < m_nImpactPlaneCount; iPlane++ ) |
|
{ |
|
// Reduce and redirect the player's velocity. |
|
Vector vecVelocity( mv->m_vecVelocity.x, mv->m_vecVelocity.y, mv->m_vecVelocity.z ); |
|
|
|
// Don't let negatively sloped surfaces drive you into the ground. |
|
if ( m_aImpactPlaneNormals[iPlane].z < 0.0f ) |
|
{ |
|
m_aImpactPlaneNormals[iPlane].z = 0.0f; |
|
VectorNormalize( m_aImpactPlaneNormals[iPlane] ); |
|
} |
|
|
|
ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f ); |
|
|
|
// Check to see if we need to continue clipping? |
|
for( iPlane2 = 0; iPlane2 < m_nImpactPlaneCount; iPlane2++ ) |
|
{ |
|
if ( iPlane != iPlane2 ) |
|
{ |
|
if ( mv->m_vecVelocity.Dot( m_aImpactPlaneNormals[iPlane2] ) < 0.0f ) |
|
break; |
|
} |
|
} |
|
if ( iPlane2 == m_nImpactPlaneCount ) |
|
break; |
|
} |
|
|
|
if ( iPlane == m_nImpactPlaneCount ) |
|
{ |
|
// Go along the crease here! |
|
if ( m_nImpactPlaneCount != 2 ) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
return false; |
|
} |
|
else |
|
{ |
|
Vector vecCross; |
|
CrossProduct( m_aImpactPlaneNormals[0], m_aImpactPlaneNormals[1], vecCross ); |
|
float flScalar = vecCross.Dot( mv->m_vecVelocity ); |
|
VectorScale( vecCross, flScalar, mv->m_vecVelocity ); |
|
} |
|
} |
|
|
|
// If the original velocity is against the current velocity, stop dead to |
|
// avoid tiny occilations in sloping corners |
|
if ( mv->m_vecVelocity.Dot( m_vecOriginalVelocity ) <= 0.0f ) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::RedirectAirVelocity( const trace_t &trace ) |
|
{ |
|
// Check for max planes. |
|
if ( m_nImpactPlaneCount >= MAX_IMPACT_PLANES ) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
return false; |
|
} |
|
|
|
// Add the impact plane normal to the list. |
|
VectorCopy( trace.plane.normal, m_aImpactPlaneNormals[m_nImpactPlaneCount] ); |
|
m_nImpactPlaneCount++; |
|
|
|
for ( int iPlane = 0; iPlane < m_nImpactPlaneCount; iPlane++ ) |
|
{ |
|
// Reduce and redirect the player's velocity. |
|
Vector vecVelocity( mv->m_vecVelocity.x, mv->m_vecVelocity.y, mv->m_vecVelocity.z ); |
|
|
|
// Don't let negatively sloped surfaces drive you into the ground. |
|
if ( m_aImpactPlaneNormals[iPlane].z < 0.0f ) |
|
{ |
|
m_aImpactPlaneNormals[iPlane].z = 0.0f; |
|
VectorNormalize( m_aImpactPlaneNormals[iPlane] ); |
|
} |
|
|
|
if ( m_aImpactPlaneNormals[iPlane].z > IMPACT_NORMAL_FLOOR ) |
|
{ |
|
ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f ); |
|
} |
|
else |
|
{ |
|
ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f + sv_bounce.GetFloat() * ( 1.0f - m_surfaceFriction ) ); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::CollisionResponseNone( const trace_t &trace ) |
|
{ |
|
// Move the player to the trace's ending position. |
|
VectorCopy( trace.endpos, mv->m_vecAbsOrigin ); |
|
|
|
// Add the position to the stack. |
|
if ( m_nMovementStackSize < MOVEMENTSTACK_MAXSIZE ) |
|
{ |
|
VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[m_nMovementStackSize].m_vecPosition ); |
|
VectorCopy( mv->m_vecVelocity, m_aMovementStack[m_nMovementStackSize].m_vecVelocity ); |
|
VectorCopy( m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal, |
|
m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal ); |
|
m_nMovementStackSize++; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::CollisionResponseStuck( void ) |
|
{ |
|
mv->m_vecVelocity.Init(); |
|
//DevMsg( 1, "CollisionResponseStuck: %s is stuck (%s).\n", player->GetClassname(), player->IsServer() ? "sv" : "cl" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::CollisionResponseGeneric( const trace_t &trace, int &nBlocked ) |
|
{ |
|
// Check for any movement. |
|
if ( trace.fraction > 0.0f ) |
|
{ |
|
// Move the partial move and reset the impact plane count. |
|
VectorCopy( trace.endpos, mv->m_vecAbsOrigin ); |
|
m_nImpactPlaneCount = 0; |
|
|
|
// Add the data to the movement stack. -- the velocity is wrong here!!! |
|
if ( m_nMovementStackSize < MOVEMENTSTACK_MAXSIZE ) |
|
{ |
|
VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[m_nMovementStackSize].m_vecPosition ); |
|
VectorCopy( mv->m_vecVelocity, m_aMovementStack[m_nMovementStackSize].m_vecVelocity ); |
|
VectorCopy( trace.plane.normal, m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal ); |
|
m_nMovementStackSize++; |
|
} |
|
} |
|
|
|
// Sanity check - Impact plane count. |
|
Assert( m_nImpactPlaneCount < MAX_IMPACT_PLANES ); |
|
|
|
// Add the entity to the touched list (list itself maintains uniqueness). |
|
MoveHelper()->AddToTouched( trace, mv->m_vecVelocity ); |
|
|
|
// Check for special "blockers" (walls, steps, floor). |
|
nBlocked |= BlockerType( trace.plane.normal ); |
|
|
|
// Re-direct or bounce the player's velocity vector. |
|
if ( player->GetGroundEntity() != NULL ) |
|
{ |
|
return RedirectGroundVelocity( trace ); |
|
} |
|
else |
|
{ |
|
return RedirectAirVelocity( trace ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: See comment _WalkMove |
|
//----------------------------------------------------------------------------- |
|
int CTFGameMovement::TryPlayerMove( Vector *pFirstDest=NULL, trace_t *pFirstTrace=NULL ) |
|
{ |
|
VPROF( "CTFGameMovement::TryPlayerMove" ); |
|
|
|
int bumpcount, numbumps; |
|
Vector dir; |
|
float d; |
|
int numplanes; |
|
Vector planes[5/*MAX_CLIP_PLANES*/]; |
|
Vector primal_velocity, original_velocity; |
|
Vector new_velocity; |
|
int i, j; |
|
trace_t pm; |
|
Vector end; |
|
float time_left, allFraction; |
|
int blocked; |
|
|
|
numbumps = 4; // Bump up to four times |
|
|
|
blocked = 0; // Assume not blocked |
|
numplanes = 0; // and not sliding along any planes |
|
VectorCopy (mv->m_vecVelocity, original_velocity); // Store original velocity |
|
VectorCopy (mv->m_vecVelocity, primal_velocity); |
|
|
|
allFraction = 0; |
|
time_left = gpGlobals->frametime; // Total time for this movement operation. |
|
|
|
new_velocity.Init(); |
|
|
|
for (bumpcount=0 ; bumpcount < numbumps; bumpcount++) |
|
{ |
|
if ( mv->m_vecVelocity.Length() == 0.0 ) |
|
break; |
|
|
|
// Assume we can move all the way from the current origin to the |
|
// end point. |
|
VectorMA( mv->m_vecAbsOrigin, time_left, mv->m_vecVelocity, end ); |
|
|
|
// See if we can make it from origin to end point. |
|
if ( g_bMovementOptimizations ) |
|
{ |
|
// If their velocity Z is 0, then we can avoid an extra trace here during WalkMove. |
|
if ( pFirstDest && end == *pFirstDest ) |
|
pm = *pFirstTrace; |
|
else |
|
TracePlayerBBox( mv->m_vecAbsOrigin, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); |
|
} |
|
else |
|
{ |
|
TracePlayerBBox( mv->m_vecAbsOrigin, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); |
|
} |
|
|
|
allFraction += pm.fraction; |
|
|
|
// If we started in a solid object, or we were in solid space |
|
// the whole way, zero out our velocity and return that we |
|
// are blocked by floor and wall. |
|
if (pm.allsolid) |
|
{ |
|
// entity is trapped in another solid |
|
VectorCopy (vec3_origin, mv->m_vecVelocity); |
|
return 4; |
|
} |
|
|
|
// If we moved some portion of the total distance, then |
|
// copy the end position into the pmove.origin and |
|
// zero the plane counter. |
|
if( pm.fraction > 0 ) |
|
{ |
|
// actually covered some distance |
|
VectorCopy (pm.endpos, mv->m_vecAbsOrigin); |
|
VectorCopy (mv->m_vecVelocity, original_velocity); |
|
numplanes = 0; |
|
} |
|
|
|
// If we covered the entire distance, we are done |
|
// and can return. |
|
if (pm.fraction == 1) |
|
{ |
|
break; // moved the entire distance |
|
} |
|
|
|
// Indicate a collision occurred... |
|
OnTryPlayerMoveCollision( pm ); |
|
|
|
// Save entity that blocked us (since fraction was < 1.0) |
|
// for contact |
|
// Add it if it's not already in the list!!! |
|
MoveHelper( )->AddToTouched( pm, mv->m_vecVelocity ); |
|
|
|
// If the plane we hit has a high z component in the normal, then |
|
// it's probably a floor |
|
if (pm.plane.normal[2] > IMPACT_NORMAL_FLOOR) |
|
{ |
|
blocked |= 1; // floor |
|
} |
|
// If the plane has a zero z component in the normal, then it's a |
|
// step or wall |
|
if (!pm.plane.normal[2]) |
|
{ |
|
blocked |= 2; // step / wall |
|
} |
|
|
|
// Reduce amount of m_flFrameTime left by total time left * fraction |
|
// that we covered. |
|
time_left -= time_left * pm.fraction; |
|
|
|
// Did we run out of planes to clip against? |
|
if (numplanes >= 5/*MAX_CLIP_PLANES*/) |
|
{ |
|
// this shouldn't really happen |
|
// Stop our movement if so. |
|
VectorCopy (vec3_origin, mv->m_vecVelocity); |
|
//Con_DPrintf("Too many planes 4\n"); |
|
|
|
break; |
|
} |
|
|
|
// Set up next clipping plane |
|
VectorCopy (pm.plane.normal, planes[numplanes]); |
|
numplanes++; |
|
|
|
// modify original_velocity so it parallels all of the clip planes |
|
// |
|
if ( player->GetMoveType() == MOVETYPE_WALK && |
|
((player->GetGroundEntity() == NULL) /*|| (mv->m_fFriction != 1)*/ ) ) // relfect player velocity |
|
{ |
|
for ( i = 0; i < numplanes; i++ ) |
|
{ |
|
if ( planes[i][2] > IMPACT_NORMAL_FLOOR ) |
|
{ |
|
// floor or slope |
|
ClipVelocity( original_velocity, planes[i], new_velocity, 1 ); |
|
VectorCopy( new_velocity, original_velocity ); |
|
} |
|
else |
|
{ |
|
ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1 - m_surfaceFriction) ); |
|
} |
|
} |
|
|
|
VectorCopy( new_velocity, mv->m_vecVelocity ); |
|
VectorCopy( new_velocity, original_velocity ); |
|
} |
|
else |
|
{ |
|
for (i=0 ; i < numplanes ; i++) |
|
{ |
|
ClipVelocity ( |
|
original_velocity, |
|
planes[i], |
|
mv->m_vecVelocity, |
|
1); |
|
|
|
for (j=0 ; j<numplanes ; j++) |
|
if (j != i) |
|
{ |
|
// Are we now moving against this plane? |
|
if (mv->m_vecVelocity.Dot(planes[j]) < 0) |
|
break; // not ok |
|
} |
|
if (j == numplanes) // Didn't have to clip, so we're ok |
|
break; |
|
} |
|
|
|
// Did we go all the way through plane set |
|
if (i != numplanes) |
|
{ // go along this plane |
|
// pmove.velocity is set in clipping call, no need to set again. |
|
; |
|
} |
|
else |
|
{ // go along the crease |
|
if (numplanes != 2) |
|
{ |
|
VectorCopy (vec3_origin, mv->m_vecVelocity); |
|
break; |
|
} |
|
CrossProduct (planes[0], planes[1], dir); |
|
d = dir.Dot(mv->m_vecVelocity); |
|
VectorScale (dir, d, mv->m_vecVelocity ); |
|
} |
|
|
|
// |
|
// if original velocity is against the original velocity, stop dead |
|
// to avoid tiny occilations in sloping corners |
|
// |
|
d = mv->m_vecVelocity.Dot(primal_velocity); |
|
if (d <= 0) |
|
{ |
|
//Con_DPrintf("Back\n"); |
|
VectorCopy (vec3_origin, mv->m_vecVelocity); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( allFraction == 0 ) |
|
{ |
|
VectorCopy (vec3_origin, mv->m_vecVelocity); |
|
} |
|
|
|
return blocked; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
int CTFGameMovement::TryPlayerMove2( void ) |
|
{ |
|
VPROF( "CTFGameMovement::TryPlayerMove2" ); |
|
|
|
int nBlocked = MOVEMENT_BLOCKED_NONE; |
|
trace_t trace; |
|
float flTimeLeft = gpGlobals->frametime; |
|
m_nImpactPlaneCount = 0; |
|
|
|
// Save off the original velocity -- coming in. |
|
VectorCopy( mv->m_vecVelocity, m_vecOriginalVelocity ); |
|
|
|
float flTotalFraction = 0.0f; |
|
for ( int iBump = 0; iBump < BUMP_MAX_COUNT; iBump++ ) |
|
{ |
|
// Check to see if we have any velocity left. EXPENSIVE!!!! |
|
if ( mv->m_vecVelocity.Length() == 0.0f ) |
|
break; |
|
|
|
// |
|
// Calculate the wish position. |
|
// |
|
Vector vecWishPos; |
|
VectorMA( mv->m_vecAbsOrigin, flTimeLeft, mv->m_vecVelocity, vecWishPos ); |
|
|
|
// |
|
// Attempt the move. |
|
// |
|
// NOTE: This check can hit players and objects |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER, trace ); |
|
|
|
// but if it didn't no need to do another trace for movement only |
|
if ( trace.fraction == 1.0f ) |
|
{ |
|
flTotalFraction += trace.fraction; |
|
CollisionResponseNone( trace ); |
|
break; |
|
} |
|
else |
|
{ |
|
// Add the entity to the touched list (list itself maintains uniqueness). |
|
MoveHelper()->AddToTouched( trace, mv->m_vecVelocity ); |
|
// Do a non-solid to player trace now, instead, and override what's in trace |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
} |
|
|
|
flTotalFraction += trace.fraction; |
|
|
|
// Handle stuck in solid. |
|
if ( trace.allsolid ) |
|
{ |
|
CollisionResponseStuck(); |
|
return MOVEMENT_BLOCKED_ALL; |
|
} |
|
|
|
// Handle full movement. |
|
if ( trace.fraction == 1.0f ) |
|
{ |
|
CollisionResponseNone( trace ); |
|
break; |
|
} |
|
|
|
// Handle partial movement. |
|
if ( !CollisionResponseGeneric( trace, nBlocked ) ) |
|
break; |
|
|
|
// Reduce the time left. |
|
flTimeLeft -= ( flTimeLeft * trace.fraction ); |
|
|
|
/* |
|
// |
|
// Attempt the move. |
|
// |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
|
|
flTotalFraction += trace.fraction; |
|
|
|
// Handle stuck in solid. |
|
if ( trace.allsolid ) |
|
{ |
|
CollisionResponseStuck(); |
|
return MOVEMENT_BLOCKED_ALL; |
|
} |
|
|
|
// Handle full movement. |
|
if ( trace.fraction == 1.0f ) |
|
{ |
|
CollisionResponseNone( trace ); |
|
break; |
|
} |
|
|
|
// Handle partial movement. |
|
if ( !CollisionResponseGeneric( trace, nBlocked ) ) |
|
break; |
|
|
|
// Reduce the time left. |
|
flTimeLeft -= ( flTimeLeft * trace.fraction ); |
|
*/ |
|
} |
|
|
|
// Finally, check for any movement. |
|
if ( flTotalFraction == 0.0f ) |
|
{ |
|
CollisionResponseStuck(); |
|
} |
|
|
|
return nBlocked; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::TryStanding( void ) |
|
{ |
|
VPROF( "CTFGameMovement::TryStanding" ); |
|
|
|
// Step down. |
|
Vector vecStandPos( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - player->m_Local.m_flStepSize ); |
|
|
|
trace_t trace; |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
if ( trace.fraction == 1.0f ) |
|
return; |
|
|
|
// Attempt to stand up. |
|
vecStandPos.z = mv->m_vecAbsOrigin.z + ( ( 1.0f - trace.fraction ) * player->m_Local.m_flStepSize ); |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
|
|
// A fraction of 1.0 means we stood up fine - done. |
|
if ( trace.fraction == 1.0f ) |
|
{ |
|
VectorCopy( trace.endpos, mv->m_vecAbsOrigin ); |
|
return; |
|
} |
|
|
|
// Use the movement stack to pop back and resolve the stand. |
|
if ( m_nMovementStackSize > 0 ) |
|
{ |
|
VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecPosition, mv->m_vecAbsOrigin ); |
|
VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecVelocity, mv->m_vecVelocity ); |
|
m_nMovementStackSize--; |
|
TryStanding(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::ResolveStanding( void ) |
|
{ |
|
VPROF( "CTFGameMovement::ResolveStanding" ); |
|
|
|
// |
|
// Attempt to move down twice your step height. Anything between 0.5 and 1.0 |
|
// is a valid "stand" value. |
|
// |
|
Vector vecStandPos( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - ( player->m_Local.m_flStepSize * 2.0f ) ); |
|
|
|
trace_t trace; |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
|
|
// Anything between 0.5 and 1.0 is a valid stand value |
|
if ( fabs( trace.fraction - 0.5 ) < 0.0004f ) |
|
{ |
|
return; |
|
} |
|
|
|
// if ( trace.fraction == 0.5 ) |
|
// { |
|
// VectorCopy( trace.endpos, mv->m_vecAbsOrigin ); |
|
// return; |
|
// } |
|
|
|
if ( trace.fraction > 0.5f ) |
|
{ |
|
trace.fraction -= 0.5f; |
|
Vector vecNewOrigin; |
|
vecNewOrigin = mv->m_vecAbsOrigin + trace.fraction * ( vecStandPos - mv->m_vecAbsOrigin ); |
|
mv->m_vecAbsOrigin = vecNewOrigin; |
|
return; |
|
} |
|
|
|
// Less than 0.5 mean we need to attempt to push up the difference. |
|
vecStandPos.z = ( mv->m_vecAbsOrigin.z + ( ( 0.5f - trace.fraction ) * ( player->m_Local.m_flStepSize * 2.0f ) ) ); |
|
TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace ); |
|
|
|
// A fraction of 1.0 means we stood up fine - done. |
|
if ( trace.fraction == 1.0f ) |
|
{ |
|
VectorCopy( trace.endpos, mv->m_vecAbsOrigin ); |
|
return; |
|
} |
|
|
|
// Use the movement stack to pop back and resolve the stand. |
|
if ( m_nMovementStackSize > 0 ) |
|
{ |
|
VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecPosition, mv->m_vecAbsOrigin ); |
|
VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecVelocity, mv->m_vecVelocity ); |
|
m_nMovementStackSize--; |
|
ResolveStanding(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::WalkMove2( void ) |
|
{ |
|
VPROF( "CTFGameMovement::WalkMove2" ); |
|
|
|
// Initialize the movement stack. |
|
VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[0].m_vecPosition ); |
|
VectorCopy( m_vecGroundNormal, m_aMovementStack[0].m_vecImpactNormal ); |
|
m_nMovementStackSize = 1; |
|
|
|
// Calculate the wish velocity and position. The function returns false if |
|
// the velocity is zero, it returns true otherwise. |
|
Vector vWishPos, vWishDir; |
|
float flWishSpeed; |
|
if ( !CalcWishVelocityAndPosition( vWishPos, vWishDir, flWishSpeed ) ) |
|
return; |
|
|
|
// For physics player shadow. |
|
mv->m_outWishVel += vWishDir * flWishSpeed; |
|
|
|
// Lift up the players feet (bring the minimum z componenet up by the step |
|
// size) and sweep. |
|
TryPlayerMove2(); |
|
|
|
// Try to stand up at movement's end. |
|
ResolveStanding(); |
|
|
|
// For physics player shadow. |
|
float flStepHeight = mv->m_vecAbsOrigin.z - m_aMovementStack[0].m_vecPosition.z; |
|
if ( flStepHeight > 0.0f ) |
|
{ |
|
mv->m_outStepHeight += flStepHeight; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::SetMomentumList( float flValue ) |
|
{ |
|
// Get the TF movement data. |
|
CTFMoveData *pTFMove = TFMove(); |
|
if ( !pTFMove ) |
|
return; |
|
|
|
// Fill in the momentum list. |
|
pTFMove->m_iMomentumHead = 0; |
|
for ( int iMomentum = 0; iMomentum < CTFMoveData::MOMENTUM_MAXSIZE; iMomentum++ ) |
|
{ |
|
pTFMove->m_aMomentum[iMomentum] = flValue; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFGameMovement::AddToMomentumList( float flValue ) |
|
{ |
|
// Get the TF movement data. |
|
CTFMoveData *pTFMove = TFMove(); |
|
if ( !pTFMove ) |
|
return; |
|
|
|
// Insert the new gravity value into the list. |
|
pTFMove->m_aMomentum[pTFMove->m_iMomentumHead] = flValue; |
|
pTFMove->m_iMomentumHead = ( pTFMove->m_iMomentumHead + 1 ) % CTFMoveData::MOMENTUM_MAXSIZE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
float CTFGameMovement::GetMomentum( void ) |
|
{ |
|
// Get the TF movement data. |
|
CTFMoveData *pTFMove = TFMove(); |
|
if ( !pTFMove ) |
|
return 1.0f; |
|
|
|
float flTotal = 0.0f; |
|
for ( int iMomentum = 0; iMomentum < CTFMoveData::MOMENTUM_MAXSIZE; iMomentum++ ) |
|
{ |
|
flTotal += pTFMove->m_aMomentum[iMomentum]; |
|
} |
|
|
|
flTotal /= CTFMoveData::MOMENTUM_MAXSIZE; |
|
|
|
return flTotal; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFGameMovement::CheckJumpButton( void ) |
|
{ |
|
if (player->pl.deadflag) |
|
{ |
|
mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released |
|
return false; |
|
} |
|
|
|
// See if we are waterjumping. If so, decrement count and return. |
|
if (player->m_flWaterJumpTime) |
|
{ |
|
player->m_flWaterJumpTime -= gpGlobals->frametime; |
|
if (player->m_flWaterJumpTime < 0) |
|
player->m_flWaterJumpTime = 0; |
|
|
|
return false; |
|
} |
|
|
|
// If we are in the water most of the way... |
|
if ( player->GetWaterLevel() >= 2 ) |
|
{ |
|
// swimming, not jumping |
|
SetGroundEntity( NULL ); |
|
|
|
if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount |
|
mv->m_vecVelocity[2] = 100; |
|
else if (player->GetWaterType() == CONTENTS_SLIME) |
|
mv->m_vecVelocity[2] = 80; |
|
|
|
// play swiming sound |
|
if ( player->m_flSwimSoundTime <= 0 ) |
|
{ |
|
// Don't play sound again for 1 second |
|
player->m_flSwimSoundTime = 1000; |
|
PlaySwimSound(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
// No more effect |
|
if (player->GetGroundEntity() == NULL) |
|
{ |
|
mv->m_nOldButtons |= IN_JUMP; |
|
return false; // in air, so no effect |
|
} |
|
|
|
if ( mv->m_nOldButtons & IN_JUMP ) |
|
return false; // don't pogo stick |
|
|
|
// In the air now. |
|
SetGroundEntity( NULL ); |
|
|
|
PlayStepSound( m_pSurfaceData, 1.0, true ); |
|
|
|
MoveHelper()->PlayerSetAnimation( PLAYER_JUMP ); |
|
|
|
float flGroundFactor = 1.0f; |
|
if (m_pSurfaceData) |
|
{ |
|
flGroundFactor = m_pSurfaceData->game.jumpFactor; |
|
} |
|
|
|
float flMul; |
|
flMul = sqrt(2 * GetCurrentGravity() * 45.0); |
|
|
|
// Acclerate upward |
|
// If we are ducking... |
|
float startz = mv->m_vecVelocity[2]; |
|
if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) |
|
{ |
|
// d = 0.5 * g * t^2 - distance traveled with linear accel |
|
// t = sqrt(2.0 * 45 / g) - how long to fall 45 units |
|
// v = g * t - velocity at the end (just invert it to jump up that high) |
|
// v = g * sqrt(2.0 * 45 / g ) |
|
// v^2 = g * g * 2.0 * 45 / g |
|
// v = sqrt( g * 2.0 * 45 ) |
|
mv->m_vecVelocity[2] = flGroundFactor * flMul; // 2 * gravity * height |
|
} |
|
else |
|
{ |
|
mv->m_vecVelocity[2] += flGroundFactor * flMul; // 2 * gravity * height |
|
} |
|
|
|
FinishGravity(); |
|
|
|
mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz; |
|
mv->m_outStepHeight += 0.1f; |
|
|
|
// Flag that we jumped. |
|
mv->m_nOldButtons |= IN_JUMP; // don't jump again until released |
|
return true; |
|
}
|
|
|