mirror of
https://github.com/Gigaslav/HL2Overcharged.git
synced 2026-01-02 17:48:11 +03:00
4931 lines
132 KiB
C++
4931 lines
132 KiB
C++
|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
#include "gamemovement.h"
|
|
#include "in_buttons.h"
|
|
#include <stdarg.h>
|
|
#include "movevars_shared.h"
|
|
#include "engine/IEngineTrace.h"
|
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
|
#include "decals.h"
|
|
#include "coordsize.h"
|
|
#include "rumble_shared.h"
|
|
|
|
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
|
|
#include "hl_movedata.h"
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#define STOP_EPSILON 0.1
|
|
#define MAX_CLIP_PLANES 5
|
|
|
|
#include "filesystem.h"
|
|
#include <stdarg.h>
|
|
|
|
extern IFileSystem *filesystem;
|
|
|
|
#ifndef CLIENT_DLL
|
|
#include "env_player_surface_trigger.h"
|
|
static ConVar dispcoll_drawplane("dispcoll_drawplane", "0");
|
|
#endif
|
|
|
|
|
|
// tickcount currently isn't set during prediction, although gpGlobals->curtime and
|
|
// gpGlobals->frametime are. We should probably set tickcount (to player->m_nTickBase),
|
|
// but we're REALLY close to shipping, so we can change that later and people can use
|
|
// player->CurrentCommandNumber() in the meantime.
|
|
#define tickcount USE_PLAYER_CURRENT_COMMAND_NUMBER__INSTEAD_OF_TICKCOUNT
|
|
|
|
#if defined( HL2_DLL )
|
|
ConVar xc_uncrouch_on_jump("xc_uncrouch_on_jump", "1", FCVAR_ARCHIVE, "Uncrouch when jump occurs");
|
|
#endif
|
|
|
|
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
|
|
ConVar player_limit_jump_speed("player_limit_jump_speed", "1", FCVAR_REPLICATED);
|
|
#endif
|
|
|
|
// option_duck_method is a carrier convar. Its sole purpose is to serve an easy-to-flip
|
|
// convar which is ONLY set by the X360 controller menu to tell us which way to bind the
|
|
// duck controls. Its value is meaningless anytime we don't have the options window open.
|
|
ConVar option_duck_method("option_duck_method", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE);// 0 = HOLD to duck, 1 = Duck is a toggle
|
|
|
|
#ifdef STAGING_ONLY
|
|
#ifdef CLIENT_DLL
|
|
ConVar debug_latch_reset_onduck("debug_latch_reset_onduck", "1", FCVAR_CHEAT);
|
|
#endif
|
|
#endif
|
|
|
|
// [MD] I'll remove this eventually. For now, I want the ability to A/B the optimizations.
|
|
bool g_bMovementOptimizations = true;
|
|
|
|
// Roughly how often we want to update the info about the ground surface we're on.
|
|
// We don't need to do this very often.
|
|
#define CATEGORIZE_GROUND_SURFACE_INTERVAL 0.3f
|
|
#define CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL ( (int)( CATEGORIZE_GROUND_SURFACE_INTERVAL / TICK_INTERVAL ) )
|
|
|
|
#define CHECK_STUCK_INTERVAL 1.0f
|
|
#define CHECK_STUCK_TICK_INTERVAL ( (int)( CHECK_STUCK_INTERVAL / TICK_INTERVAL ) )
|
|
|
|
#define CHECK_STUCK_INTERVAL_SP 0.2f
|
|
#define CHECK_STUCK_TICK_INTERVAL_SP ( (int)( CHECK_STUCK_INTERVAL_SP / TICK_INTERVAL ) )
|
|
|
|
#define CHECK_LADDER_INTERVAL 0.2f
|
|
#define CHECK_LADDER_TICK_INTERVAL ( (int)( CHECK_LADDER_INTERVAL / TICK_INTERVAL ) )
|
|
|
|
#define NUM_CROUCH_HINTS 3
|
|
|
|
extern IGameMovement *g_pGameMovement;
|
|
|
|
#if defined( PLAYER_GETTING_STUCK_TESTING )
|
|
|
|
// If you ever get stuck walking around, then you can run this code to find the code which would leave the player in a bad spot
|
|
void CMoveData::SetAbsOrigin(const Vector &vec)
|
|
{
|
|
CGameMovement *gm = dynamic_cast< CGameMovement * >(g_pGameMovement);
|
|
if (gm && gm->GetMoveData() &&
|
|
gm->player &&
|
|
gm->player->entindex() == 1 &&
|
|
gm->player->GetMoveType() == MOVETYPE_WALK)
|
|
{
|
|
trace_t pm;
|
|
gm->TracePlayerBBox(vec, vec, gm->PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
if (pm.startsolid || pm.allsolid || pm.fraction != 1.0f)
|
|
{
|
|
Msg("Player will become stuck at %f %f %f\n", VectorExpand(vec));
|
|
}
|
|
}
|
|
|
|
m_vecAbsOrigin = vec;
|
|
}
|
|
|
|
#endif
|
|
|
|
// See shareddefs.h
|
|
#if PREDICTION_ERROR_CHECK_LEVEL > 0
|
|
|
|
static ConVar diffcheck("diffcheck", "0", FCVAR_REPLICATED);
|
|
|
|
class IDiffMgr
|
|
{
|
|
public:
|
|
virtual void StartCommand(bool bServer, int nCommandNumber) = 0;
|
|
virtual void AddToDiff(bool bServer, int nCommandNumber, char const *string) = 0;
|
|
virtual void Validate(bool bServer, int nCommandNumber) = 0;
|
|
};
|
|
|
|
static IDiffMgr *g_pDiffMgr = NULL;
|
|
|
|
class CDiffStr
|
|
{
|
|
public:
|
|
CDiffStr()
|
|
{
|
|
m_str[0] = 0;
|
|
}
|
|
|
|
CDiffStr(char const *str)
|
|
{
|
|
Q_strncpy(m_str, str, sizeof(m_str));
|
|
}
|
|
|
|
CDiffStr(const CDiffStr &src)
|
|
{
|
|
Q_strncpy(m_str, src.m_str, sizeof(m_str));
|
|
}
|
|
|
|
char const *String()
|
|
{
|
|
return m_str;
|
|
}
|
|
private:
|
|
|
|
char m_str[128];
|
|
};
|
|
|
|
// Per tick data
|
|
class CDiffInfo
|
|
{
|
|
public:
|
|
CDiffInfo() : m_nCommandNumber(0) {}
|
|
CDiffInfo(const CDiffInfo& src)
|
|
{
|
|
m_nCommandNumber = src.m_nCommandNumber;
|
|
for (int i = 0; i < src.m_Lines.Count(); ++i)
|
|
{
|
|
m_Lines.AddToTail(src.m_Lines[i]);
|
|
}
|
|
}
|
|
|
|
static bool Less(const CDiffInfo& lhs, const CDiffInfo& rhs)
|
|
{
|
|
return lhs.m_nCommandNumber < rhs.m_nCommandNumber;
|
|
}
|
|
int m_nCommandNumber;
|
|
CUtlVector< CDiffStr > m_Lines;
|
|
bool m_bChecked;
|
|
};
|
|
|
|
class CDiffManager : public IDiffMgr
|
|
{
|
|
public:
|
|
CDiffManager() :
|
|
m_Client(0, 0, CDiffInfo::Less),
|
|
m_Server(0, 0, CDiffInfo::Less),
|
|
m_flLastSpew(-1.0f)
|
|
{
|
|
g_pDiffMgr = this;
|
|
}
|
|
|
|
virtual void StartCommand(bool bServer, int nCommandNumber)
|
|
{
|
|
#if defined( CLIENT_DLL )
|
|
|
|
if (!diffcheck.GetInt())
|
|
return;
|
|
|
|
g_pDiffMgr = reinterpret_cast< IDiffMgr * >(diffcheck.GetInt());
|
|
g_pDiffMgr->StartCommand(bServer, nCommandNumber);
|
|
return;
|
|
#endif
|
|
|
|
// Msg( "%s Startcommand %d\n", bServer ? "sv" : "cl", nCommandNumber );
|
|
|
|
diffcheck.SetValue(reinterpret_cast< int >(this));
|
|
|
|
Assert(CBaseEntity::IsServer());
|
|
|
|
CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;
|
|
|
|
CDiffInfo search;
|
|
search.m_nCommandNumber = nCommandNumber;
|
|
int idx = rb.Find(search);
|
|
if (idx == rb.InvalidIndex())
|
|
{
|
|
idx = rb.Insert(search);
|
|
}
|
|
|
|
CDiffInfo *slot = &rb[idx];
|
|
slot->m_Lines.RemoveAll();
|
|
}
|
|
|
|
virtual void AddToDiff(bool bServer, int nCommandNumber, char const *string)
|
|
{
|
|
#if defined( CLIENT_DLL )
|
|
|
|
if (!diffcheck.GetInt())
|
|
return;
|
|
|
|
g_pDiffMgr = reinterpret_cast< IDiffMgr * >(diffcheck.GetInt());
|
|
g_pDiffMgr->AddToDiff(bServer, nCommandNumber, string);
|
|
return;
|
|
#endif
|
|
Assert(CBaseEntity::IsServer());
|
|
|
|
// Msg( "%s Add %d %s\n", bServer ? "sv" : "cl", nCommandNumber, string );
|
|
|
|
CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;
|
|
|
|
CDiffInfo search;
|
|
search.m_nCommandNumber = nCommandNumber;
|
|
int idx = rb.Find(search);
|
|
if (idx == rb.InvalidIndex())
|
|
{
|
|
Assert(0);
|
|
idx = rb.Insert(search);
|
|
}
|
|
|
|
CDiffInfo *slot = &rb[idx];
|
|
CDiffStr line(string);
|
|
slot->m_Lines.AddToTail(line);
|
|
}
|
|
|
|
enum EMismatched
|
|
{
|
|
DIFFCHECK_NOTREADY = 0,
|
|
DIFFCHECK_MATCHED,
|
|
DIFFCHECK_DIFFERS
|
|
};
|
|
|
|
bool ClientRecordExists(int cmd)
|
|
{
|
|
CDiffInfo clsearch;
|
|
clsearch.m_nCommandNumber = cmd;
|
|
int clidx = m_Client.Find(clsearch);
|
|
return m_Client.IsValidIndex(clidx);
|
|
}
|
|
|
|
EMismatched IsMismatched(int svidx)
|
|
{
|
|
CDiffInfo *serverslot = &m_Server[svidx];
|
|
|
|
// Now find the client version of this one
|
|
CDiffInfo clsearch;
|
|
clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
|
|
int clidx = m_Client.Find(clsearch);
|
|
if (clidx == m_Client.InvalidIndex())
|
|
return DIFFCHECK_NOTREADY;
|
|
|
|
// Now compare them
|
|
CDiffInfo *clientslot = &m_Client[clidx];
|
|
|
|
bool bSpew = false;
|
|
if (serverslot->m_Lines.Count() !=
|
|
clientslot->m_Lines.Count())
|
|
{
|
|
return DIFFCHECK_DIFFERS;
|
|
}
|
|
|
|
int maxSlot = MAX(serverslot->m_Lines.Count(), clientslot->m_Lines.Count());
|
|
if (!bSpew)
|
|
{
|
|
for (int i = 0; i < maxSlot; ++i)
|
|
{
|
|
CDiffStr *sv = NULL;
|
|
CDiffStr *cl = NULL;
|
|
if (i < serverslot->m_Lines.Count())
|
|
{
|
|
sv = &serverslot->m_Lines[i];
|
|
}
|
|
if (i < clientslot->m_Lines.Count())
|
|
{
|
|
cl = &clientslot->m_Lines[i];
|
|
}
|
|
|
|
if (Q_stricmp(sv ? sv->String() : "(missing)", cl ? cl->String() : "(missing)"))
|
|
{
|
|
return DIFFCHECK_DIFFERS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return DIFFCHECK_MATCHED;
|
|
}
|
|
|
|
virtual void Validate(bool bServer, int nCommandNumber)
|
|
{
|
|
#if defined( CLIENT_DLL )
|
|
|
|
if (!diffcheck.GetInt())
|
|
return;
|
|
|
|
g_pDiffMgr = reinterpret_cast< IDiffMgr * >(diffcheck.GetInt());
|
|
g_pDiffMgr->Validate(bServer, nCommandNumber);
|
|
return;
|
|
#endif
|
|
Assert(CBaseEntity::IsServer());
|
|
|
|
// Only do this on the client
|
|
if (!bServer)
|
|
return;
|
|
|
|
// Find the last server command number
|
|
if (m_Server.Count() <= 0)
|
|
return;
|
|
|
|
int svidx = m_Server.LastInorder();
|
|
EMismatched eMisMatched = IsMismatched(svidx);
|
|
if (eMisMatched == DIFFCHECK_NOTREADY)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (eMisMatched == DIFFCHECK_DIFFERS)
|
|
{
|
|
CUtlVector< int > vecPrev;
|
|
|
|
int nCur = svidx;
|
|
do
|
|
{
|
|
int prev = m_Server.PrevInorder(nCur);
|
|
if (m_Server.IsValidIndex(prev) &&
|
|
ClientRecordExists(m_Server[prev].m_nCommandNumber))
|
|
{
|
|
//SpewRecords( "prev", prev );
|
|
vecPrev.AddToHead(prev);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
nCur = prev;
|
|
} while (vecPrev.Count() < 10);
|
|
|
|
Msg("-----\n");
|
|
|
|
for (int p = 0; p < vecPrev.Count(); ++p)
|
|
{
|
|
SpewRecords("prev", vecPrev[p]);
|
|
}
|
|
|
|
SpewRecords("bad ", svidx);
|
|
}
|
|
}
|
|
|
|
void SpewRecords(char const *prefix, int svidx)
|
|
{
|
|
CDiffInfo *serverslot = &m_Server[svidx];
|
|
|
|
// Now find the client version of this one
|
|
CDiffInfo clsearch;
|
|
clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
|
|
int clidx = m_Client.Find(clsearch);
|
|
if (clidx == m_Client.InvalidIndex())
|
|
return;
|
|
|
|
// Now compare them
|
|
CDiffInfo *clientslot = &m_Client[clidx];
|
|
|
|
int maxSlot = MAX(serverslot->m_Lines.Count(), clientslot->m_Lines.Count());
|
|
|
|
for (int i = 0; i < maxSlot; ++i)
|
|
{
|
|
char const *sv = "(missing)";
|
|
char const *cl = "(missing)";
|
|
|
|
if (i < serverslot->m_Lines.Count())
|
|
{
|
|
sv = serverslot->m_Lines[i].String();
|
|
}
|
|
if (i < clientslot->m_Lines.Count())
|
|
{
|
|
cl = clientslot->m_Lines[i].String();
|
|
}
|
|
|
|
bool bDiffers = Q_stricmp(sv, cl) ? true : false;
|
|
|
|
Msg("%s%s%d: sv[%50.50s] cl[%50.50s]\n",
|
|
prefix,
|
|
bDiffers ? "+++" : " ",
|
|
serverslot->m_nCommandNumber,
|
|
sv,
|
|
cl);
|
|
}
|
|
}
|
|
private:
|
|
|
|
CUtlRBTree< CDiffInfo, int > m_Server;
|
|
CUtlRBTree< CDiffInfo, int > m_Client;
|
|
float m_flLastSpew;
|
|
};
|
|
|
|
static CDiffManager g_DiffMgr;
|
|
|
|
void DiffPrint(bool bServer, int nCommandNumber, char const *fmt, ...)
|
|
{
|
|
// Only track stuff for local player
|
|
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
|
|
if (pPlayer && pPlayer->entindex() != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
va_list argptr;
|
|
char string[1024];
|
|
va_start(argptr, fmt);
|
|
int len = Q_vsnprintf(string, sizeof(string), fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
if (g_pDiffMgr)
|
|
{
|
|
// Strip any \n at the end that the user accidently put int
|
|
if (len > 0 && string[len - 1] == '\n')
|
|
{
|
|
string[len - 1] = 0;
|
|
}
|
|
|
|
g_pDiffMgr->AddToDiff(bServer, nCommandNumber, string);
|
|
}
|
|
}
|
|
|
|
void _CheckV(int tick, char const *ctx, const Vector &vel)
|
|
{
|
|
DiffPrint(CBaseEntity::IsServer(), tick, "%20.20s %f %f %f", ctx, vel.x, vel.y, vel.z);
|
|
}
|
|
|
|
#define CheckV( tick, ctx, vel ) _CheckV( tick, ctx, vel );
|
|
|
|
static void StartCommand(bool bServer, int nCommandNumber)
|
|
{
|
|
// Only track stuff for local player
|
|
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
|
|
if (pPlayer && pPlayer->entindex() != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (g_pDiffMgr)
|
|
{
|
|
g_pDiffMgr->StartCommand(bServer, nCommandNumber);
|
|
}
|
|
}
|
|
|
|
static void Validate(bool bServer, int nCommandNumber)
|
|
{
|
|
// Only track stuff for local player
|
|
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer();
|
|
if (pPlayer && pPlayer->entindex() != 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if (g_pDiffMgr)
|
|
{
|
|
g_pDiffMgr->Validate(bServer, nCommandNumber);
|
|
}
|
|
}
|
|
|
|
void CGameMovement::DiffPrint(char const *fmt, ...)
|
|
{
|
|
if (!player)
|
|
return;
|
|
|
|
va_list argptr;
|
|
char string[1024];
|
|
va_start(argptr, fmt);
|
|
Q_vsnprintf(string, sizeof(string), fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
::DiffPrint(CBaseEntity::IsServer(), player->CurrentCommandNumber(), "%s", string);
|
|
}
|
|
|
|
#else
|
|
static void DiffPrint(bool bServer, int nCommandNumber, char const *fmt, ...)
|
|
{
|
|
// Nothing
|
|
}
|
|
static void StartCommand(bool bServer, int nCommandNumber)
|
|
{
|
|
}
|
|
|
|
static void Validate(bool bServer, int nCommandNumber)
|
|
{
|
|
}
|
|
|
|
#define CheckV( tick, ctx, vel )
|
|
|
|
void CGameMovement::DiffPrint(char const *fmt, ...)
|
|
{
|
|
}
|
|
|
|
#endif // !PREDICTION_ERROR_CHECK_LEVEL
|
|
|
|
#ifndef _XBOX
|
|
void COM_Log(char *pszFile, const char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
FileHandle_t fp;
|
|
const char *pfilename;
|
|
|
|
if (!pszFile)
|
|
{
|
|
pfilename = "hllog.txt";
|
|
}
|
|
else
|
|
{
|
|
pfilename = pszFile;
|
|
}
|
|
va_start(argptr, fmt);
|
|
Q_vsnprintf(string, sizeof(string), fmt, argptr);
|
|
va_end(argptr);
|
|
|
|
fp = filesystem->Open(pfilename, "a+t");
|
|
if (fp)
|
|
{
|
|
filesystem->FPrintf(fp, "%s", string);
|
|
filesystem->Close(fp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef CLIENT_DLL
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Debug - draw the displacement collision plane.
|
|
//-----------------------------------------------------------------------------
|
|
void DrawDispCollPlane(CBaseTrace *pTrace)
|
|
{
|
|
float flLength = 30.0f;
|
|
|
|
// Create a basis, based on the impact normal.
|
|
int nMajorAxis = 0;
|
|
Vector vecBasisU, vecBasisV, vecNormal;
|
|
vecNormal = pTrace->plane.normal;
|
|
float flAxisValue = vecNormal[0];
|
|
if (fabs(vecNormal[1]) > fabs(flAxisValue)) { nMajorAxis = 1; flAxisValue = vecNormal[1]; }
|
|
if (fabs(vecNormal[2]) > fabs(flAxisValue)) { nMajorAxis = 2; }
|
|
if ((nMajorAxis == 1) || (nMajorAxis == 2))
|
|
{
|
|
vecBasisU.Init(1.0f, 0.0f, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
vecBasisU.Init(0.0f, 1.0f, 0.0f);
|
|
}
|
|
|
|
vecBasisV = vecNormal.Cross(vecBasisU);
|
|
VectorNormalize(vecBasisV);
|
|
|
|
vecBasisU = vecBasisV.Cross(vecNormal);
|
|
VectorNormalize(vecBasisU);
|
|
|
|
// Create the impact point. Push off the surface a bit.
|
|
Vector vecImpactPoint = pTrace->startpos + pTrace->fraction * (pTrace->endpos - pTrace->startpos);
|
|
vecImpactPoint += vecNormal;
|
|
|
|
// Generate a quad to represent the plane.
|
|
Vector vecPlanePoints[4];
|
|
vecPlanePoints[0] = vecImpactPoint + (vecBasisU * -flLength) + (vecBasisV * -flLength);
|
|
vecPlanePoints[1] = vecImpactPoint + (vecBasisU * -flLength) + (vecBasisV * flLength);
|
|
vecPlanePoints[2] = vecImpactPoint + (vecBasisU * flLength) + (vecBasisV * flLength);
|
|
vecPlanePoints[3] = vecImpactPoint + (vecBasisU * flLength) + (vecBasisV * -flLength);
|
|
|
|
#if 0
|
|
// Test facing.
|
|
Vector vecEdges[2];
|
|
vecEdges[0] = vecPlanePoints[1] - vecPlanePoints[0];
|
|
vecEdges[1] = vecPlanePoints[2] - vecPlanePoints[0];
|
|
Vector vecCross = vecEdges[0].Cross(vecEdges[1]);
|
|
if (vecCross.Dot(vecNormal) < 0.0f)
|
|
{
|
|
// Reverse winding.
|
|
}
|
|
#endif
|
|
|
|
// Draw the plane.
|
|
NDebugOverlay::Triangle(vecPlanePoints[0], vecPlanePoints[1], vecPlanePoints[2], 125, 125, 125, 125, false, 5.0f);
|
|
NDebugOverlay::Triangle(vecPlanePoints[0], vecPlanePoints[2], vecPlanePoints[3], 125, 125, 125, 125, false, 5.0f);
|
|
|
|
NDebugOverlay::Line(vecPlanePoints[0], vecPlanePoints[1], 255, 255, 255, false, 5.0f);
|
|
NDebugOverlay::Line(vecPlanePoints[1], vecPlanePoints[2], 255, 255, 255, false, 5.0f);
|
|
NDebugOverlay::Line(vecPlanePoints[2], vecPlanePoints[3], 255, 255, 255, false, 5.0f);
|
|
NDebugOverlay::Line(vecPlanePoints[3], vecPlanePoints[0], 255, 255, 255, false, 5.0f);
|
|
|
|
// Draw the normal.
|
|
NDebugOverlay::Line(vecImpactPoint, vecImpactPoint + (vecNormal * flLength), 255, 0, 0, false, 5.0f);
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructs GameMovement interface
|
|
//-----------------------------------------------------------------------------
|
|
CGameMovement::CGameMovement(void)
|
|
{
|
|
m_nOldWaterLevel = WL_NotInWater;
|
|
m_flWaterEntryTime = 0;
|
|
m_nOnLadder = 0;
|
|
|
|
mv = NULL;
|
|
|
|
memset(m_flStuckCheckTime, 0, sizeof(m_flStuckCheckTime));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CGameMovement::~CGameMovement(void)
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Allow bots etc to use slightly different solid masks
|
|
//-----------------------------------------------------------------------------
|
|
unsigned int CGameMovement::PlayerSolidMask(bool brushOnly)
|
|
{
|
|
return (brushOnly) ? MASK_PLAYERSOLID_BRUSHONLY : MASK_PLAYERSOLID;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : type -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CGameMovement::GetCheckInterval(IntervalType_t type)
|
|
{
|
|
int tickInterval = 1;
|
|
switch (type)
|
|
{
|
|
default:
|
|
tickInterval = 1;
|
|
break;
|
|
case GROUND:
|
|
tickInterval = CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL;
|
|
break;
|
|
case STUCK:
|
|
// If we are in the process of being "stuck", then try a new position every command tick until m_StuckLast gets reset back down to zero
|
|
if (player->m_StuckLast != 0)
|
|
{
|
|
tickInterval = 1;
|
|
}
|
|
else
|
|
{
|
|
if (gpGlobals->maxClients == 1)
|
|
{
|
|
tickInterval = CHECK_STUCK_TICK_INTERVAL_SP;
|
|
}
|
|
else
|
|
{
|
|
tickInterval = CHECK_STUCK_TICK_INTERVAL;
|
|
}
|
|
}
|
|
break;
|
|
case LADDER:
|
|
tickInterval = CHECK_LADDER_TICK_INTERVAL;
|
|
break;
|
|
}
|
|
return tickInterval;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : type -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool CGameMovement::CheckInterval(IntervalType_t type)
|
|
{
|
|
int tickInterval = GetCheckInterval(type);
|
|
|
|
if (g_bMovementOptimizations)
|
|
{
|
|
return (player->CurrentCommandNumber() + player->entindex()) % tickInterval == 0;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : ducked -
|
|
// Output : const Vector
|
|
//-----------------------------------------------------------------------------
|
|
Vector CGameMovement::GetPlayerMins(bool ducked) const
|
|
{
|
|
return ducked ? VEC_DUCK_HULL_MIN_SCALED(player) : VEC_HULL_MIN_SCALED(player);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : ducked -
|
|
// Output : const Vector
|
|
//-----------------------------------------------------------------------------
|
|
Vector CGameMovement::GetPlayerMaxs(bool ducked) const
|
|
{
|
|
return ducked ? VEC_DUCK_HULL_MAX_SCALED(player) : VEC_HULL_MAX_SCALED(player);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
// Output : const Vector
|
|
//-----------------------------------------------------------------------------
|
|
Vector CGameMovement::GetPlayerMins(void) const
|
|
{
|
|
if (player->IsObserver())
|
|
{
|
|
return VEC_OBS_HULL_MIN_SCALED(player);
|
|
}
|
|
else
|
|
{
|
|
return player->m_Local.m_bDucked ? VEC_DUCK_HULL_MIN_SCALED(player) : VEC_HULL_MIN_SCALED(player);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
// Output : const Vector
|
|
//-----------------------------------------------------------------------------
|
|
Vector CGameMovement::GetPlayerMaxs(void) const
|
|
{
|
|
if (player->IsObserver())
|
|
{
|
|
return VEC_OBS_HULL_MAX_SCALED(player);
|
|
}
|
|
else
|
|
{
|
|
return player->m_Local.m_bDucked ? VEC_DUCK_HULL_MAX_SCALED(player) : VEC_HULL_MAX_SCALED(player);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : ducked -
|
|
// Output : const Vector
|
|
//-----------------------------------------------------------------------------
|
|
Vector CGameMovement::GetPlayerViewOffset(bool ducked) const
|
|
{
|
|
return ducked ? VEC_DUCK_VIEW_SCALED(player) : VEC_VIEW_SCALED(player);
|
|
}
|
|
|
|
#if 0
|
|
//-----------------------------------------------------------------------------
|
|
// Traces player movement + position
|
|
//-----------------------------------------------------------------------------
|
|
inline void CGameMovement::TracePlayerBBox(const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm)
|
|
{
|
|
VPROF("CGameMovement::TracePlayerBBox");
|
|
|
|
Ray_t ray;
|
|
ray.Init(start, end, GetPlayerMins(), GetPlayerMaxs());
|
|
UTIL_TraceRay(ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm);
|
|
}
|
|
#endif
|
|
|
|
CBaseHandle CGameMovement::TestPlayerPosition(const Vector& pos, int collisionGroup, trace_t& pm)
|
|
{
|
|
Ray_t ray;
|
|
ray.Init(pos, pos, GetPlayerMins(), GetPlayerMaxs());
|
|
UTIL_TraceRay(ray, PlayerSolidMask(), mv->m_nPlayerHandle.Get(), collisionGroup, &pm);
|
|
if ((pm.contents & PlayerSolidMask()) && pm.m_pEnt)
|
|
{
|
|
return pm.m_pEnt->GetRefEHandle();
|
|
}
|
|
else
|
|
{
|
|
return INVALID_EHANDLE_INDEX;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
// FIXME FIXME: Does this need to be hooked up?
|
|
bool CGameMovement::IsWet() const
|
|
{
|
|
return ((pev->flags & FL_INRAIN) != 0) || (m_WetTime >= gpGlobals->time);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Plants player footprint decals
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#define PLAYER_HALFWIDTH 12
|
|
void CGameMovement::PlantFootprint( surfacedata_t *psurface )
|
|
{
|
|
// Can't plant footprints on fake materials (ladders, wading)
|
|
if ( psurface->gameMaterial != 'X' )
|
|
{
|
|
int footprintDecal = -1;
|
|
|
|
// Figure out which footprint type to plant...
|
|
// Use the wet footprint if we're wet...
|
|
if (IsWet())
|
|
{
|
|
footprintDecal = DECAL_FOOTPRINT_WET;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Activate this once we decide to pull the trigger on it.
|
|
// NOTE: We could add in snow, mud, others here
|
|
// switch(psurface->gameMaterial)
|
|
// {
|
|
// case 'D':
|
|
// footprintDecal = DECAL_FOOTPRINT_DIRT;
|
|
// break;
|
|
// }
|
|
}
|
|
|
|
if (footprintDecal != -1)
|
|
{
|
|
Vector right;
|
|
AngleVectors( pev->angles, 0, &right, 0 );
|
|
|
|
// Figure out where the top of the stepping leg is
|
|
trace_t tr;
|
|
Vector hipOrigin;
|
|
VectorMA( pev->origin,
|
|
m_IsFootprintOnLeft ? -PLAYER_HALFWIDTH : PLAYER_HALFWIDTH,
|
|
right, hipOrigin );
|
|
|
|
// Find where that leg hits the ground
|
|
UTIL_TraceLine( hipOrigin, hipOrigin + Vector(0, 0, -COORD_EXTENT * 1.74),
|
|
MASK_SOLID_BRUSHONLY, edict(), COLLISION_GROUP_NONE, &tr);
|
|
|
|
unsigned char mType = TEXTURETYPE_Find( &tr );
|
|
|
|
// Splat a decal
|
|
CPVSFilter filter( tr.endpos );
|
|
te->FootprintDecal( filter, 0.0f, &tr.endpos, &right, ENTINDEX(tr.u.ent),
|
|
gDecals[footprintDecal].index, mType );
|
|
|
|
}
|
|
}
|
|
|
|
// Switch feet for next time
|
|
m_IsFootprintOnLeft = !m_IsFootprintOnLeft;
|
|
}
|
|
|
|
#define WET_TIME 5.f // how many seconds till we're completely wet/dry
|
|
#define DRY_TIME 20.f // how many seconds till we're completely wet/dry
|
|
|
|
void CBasePlayer::UpdateWetness()
|
|
{
|
|
// BRJ 1/7/01
|
|
// Check for whether we're in a rainy area....
|
|
// Do this by tracing a line straight down with a size guaranteed to
|
|
// be larger than the map
|
|
// Update wetness based on whether we're in rain or not...
|
|
|
|
trace_t tr;
|
|
UTIL_TraceLine( pev->origin, pev->origin + Vector(0, 0, -COORD_EXTENT * 1.74),
|
|
MASK_SOLID_BRUSHONLY, edict(), COLLISION_GROUP_NONE, &tr);
|
|
if (tr.surface.flags & SURF_WET)
|
|
{
|
|
if (! (pev->flags & FL_INRAIN) )
|
|
{
|
|
// Transition...
|
|
// Figure out how wet we are now (we were drying off...)
|
|
float wetness = (m_WetTime - gpGlobals->time) / DRY_TIME;
|
|
if (wetness < 0.0f)
|
|
wetness = 0.0f;
|
|
|
|
// Here, wet time represents the time at which we get totally wet
|
|
m_WetTime = gpGlobals->time + (1.0 - wetness) * WET_TIME;
|
|
|
|
pev->flags |= FL_INRAIN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pev->flags & FL_INRAIN) != 0)
|
|
{
|
|
// Transition...
|
|
// Figure out how wet we are now (we were getting more wet...)
|
|
float wetness = 1.0f + (gpGlobals->time - m_WetTime) / WET_TIME;
|
|
if (wetness > 1.0f)
|
|
wetness = 1.0f;
|
|
|
|
// Here, wet time represents the time at which we get totally dry
|
|
m_WetTime = gpGlobals->time + wetness * DRY_TIME;
|
|
|
|
pev->flags &= ~FL_INRAIN;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::CategorizeGroundSurface(trace_t &pm)
|
|
{
|
|
IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
|
|
player->m_surfaceProps = pm.surface.surfaceProps;
|
|
player->m_pSurfaceData = physprops->GetSurfaceData(player->m_surfaceProps);
|
|
physprops->GetPhysicsProperties(player->m_surfaceProps, NULL, NULL, &player->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.
|
|
player->m_surfaceFriction *= 1.25f;
|
|
if (player->m_surfaceFriction > 1.0f)
|
|
player->m_surfaceFriction = 1.0f;
|
|
|
|
player->m_chTextureType = player->m_pSurfaceData->game.material;
|
|
}
|
|
|
|
bool CGameMovement::IsDead(void) const
|
|
{
|
|
return (player->m_iHealth <= 0 && !player->IsAlive());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Figures out how the constraint should slow us down
|
|
//-----------------------------------------------------------------------------
|
|
float CGameMovement::ComputeConstraintSpeedFactor(void)
|
|
{
|
|
// If we have a constraint, slow down because of that too.
|
|
if (!mv || mv->m_flConstraintRadius == 0.0f)
|
|
return 1.0f;
|
|
|
|
float flDistSq = mv->GetAbsOrigin().DistToSqr(mv->m_vecConstraintCenter);
|
|
|
|
float flOuterRadiusSq = mv->m_flConstraintRadius * mv->m_flConstraintRadius;
|
|
float flInnerRadiusSq = mv->m_flConstraintRadius - mv->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->GetAbsOrigin(), mv->m_vecConstraintCenter, vecDelta);
|
|
VectorNormalize(vecDelta);
|
|
VectorNormalize(vecDesired);
|
|
if (DotProduct(vecDelta, vecDesired) < 0.0f)
|
|
return 1.0f;
|
|
|
|
float flFrac = (sqrt(flDistSq) - (mv->m_flConstraintRadius - mv->m_flConstraintWidth)) / mv->m_flConstraintWidth;
|
|
|
|
float flSpeedFactor = Lerp(flFrac, 1.0f, mv->m_flConstraintSpeedFactor);
|
|
return flSpeedFactor;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::CheckParameters(void)
|
|
{
|
|
QAngle v_angle;
|
|
|
|
if (player->GetMoveType() != MOVETYPE_ISOMETRIC &&
|
|
player->GetMoveType() != MOVETYPE_NOCLIP &&
|
|
player->GetMoveType() != MOVETYPE_OBSERVER)
|
|
{
|
|
float spd;
|
|
float maxspeed;
|
|
|
|
spd = (mv->m_flForwardMove * mv->m_flForwardMove) +
|
|
(mv->m_flSideMove * mv->m_flSideMove) +
|
|
(mv->m_flUpMove * mv->m_flUpMove);
|
|
|
|
maxspeed = mv->m_flClientMaxSpeed;
|
|
if (maxspeed != 0.0)
|
|
{
|
|
mv->m_flMaxSpeed = MIN(maxspeed, mv->m_flMaxSpeed);
|
|
}
|
|
|
|
// Slow down by the speed factor
|
|
float flSpeedFactor = 1.0f;
|
|
if (player->m_pSurfaceData)
|
|
{
|
|
flSpeedFactor = player->m_pSurfaceData->game.maxSpeedFactor;
|
|
}
|
|
|
|
// If we have a constraint, slow down because of that too.
|
|
float flConstraintSpeedFactor = ComputeConstraintSpeedFactor();
|
|
if (flConstraintSpeedFactor < flSpeedFactor)
|
|
flSpeedFactor = flConstraintSpeedFactor;
|
|
|
|
mv->m_flMaxSpeed *= flSpeedFactor;
|
|
|
|
if (g_bMovementOptimizations)
|
|
{
|
|
// Same thing but only do the sqrt if we have to.
|
|
if ((spd != 0.0) && (spd > mv->m_flMaxSpeed*mv->m_flMaxSpeed))
|
|
{
|
|
float fRatio = mv->m_flMaxSpeed / sqrt(spd);
|
|
mv->m_flForwardMove *= fRatio;
|
|
mv->m_flSideMove *= fRatio;
|
|
mv->m_flUpMove *= fRatio;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
spd = sqrt(spd);
|
|
if ((spd != 0.0) && (spd > mv->m_flMaxSpeed))
|
|
{
|
|
float fRatio = mv->m_flMaxSpeed / spd;
|
|
mv->m_flForwardMove *= fRatio;
|
|
mv->m_flSideMove *= fRatio;
|
|
mv->m_flUpMove *= fRatio;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (player->GetFlags() & FL_FROZEN ||
|
|
player->GetFlags() & FL_ONTRAIN ||
|
|
IsDead())
|
|
{
|
|
mv->m_flForwardMove = 0;
|
|
mv->m_flSideMove = 0;
|
|
mv->m_flUpMove = 0;
|
|
}
|
|
|
|
DecayPunchAngle();
|
|
|
|
// Take angles from command.
|
|
if (!IsDead())
|
|
{
|
|
v_angle = mv->m_vecAngles;
|
|
v_angle = v_angle + player->m_Local.m_vecPunchAngle;
|
|
|
|
// Now adjust roll angle
|
|
if (player->GetMoveType() != MOVETYPE_ISOMETRIC &&
|
|
player->GetMoveType() != MOVETYPE_NOCLIP)
|
|
{
|
|
mv->m_vecAngles[ROLL] = CalcRoll(v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat());
|
|
}
|
|
else
|
|
{
|
|
mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ];
|
|
}
|
|
mv->m_vecAngles[PITCH] = v_angle[PITCH];
|
|
mv->m_vecAngles[YAW] = v_angle[YAW];
|
|
}
|
|
else
|
|
{
|
|
mv->m_vecAngles = mv->m_vecOldAngles;
|
|
}
|
|
|
|
// Set dead player view_offset
|
|
if (IsDead())
|
|
{
|
|
player->SetViewOffset(VEC_DEAD_VIEWHEIGHT_SCALED(player));
|
|
}
|
|
|
|
// Adjust client view angles to match values used on server.
|
|
if (mv->m_vecAngles[YAW] > 180.0f)
|
|
{
|
|
mv->m_vecAngles[YAW] -= 360.0f;
|
|
}
|
|
}
|
|
|
|
void CGameMovement::ReduceTimers(void)
|
|
{
|
|
float frame_msec = 1000.0f * gpGlobals->frametime;
|
|
|
|
if (player->m_Local.m_flDucktime > 0)
|
|
{
|
|
player->m_Local.m_flDucktime -= frame_msec;
|
|
if (player->m_Local.m_flDucktime < 0)
|
|
{
|
|
player->m_Local.m_flDucktime = 0;
|
|
}
|
|
}
|
|
if (player->m_Local.m_flDuckJumpTime > 0)
|
|
{
|
|
player->m_Local.m_flDuckJumpTime -= frame_msec;
|
|
if (player->m_Local.m_flDuckJumpTime < 0)
|
|
{
|
|
player->m_Local.m_flDuckJumpTime = 0;
|
|
}
|
|
}
|
|
if (player->m_Local.m_flJumpTime > 0)
|
|
{
|
|
player->m_Local.m_flJumpTime -= frame_msec;
|
|
if (player->m_Local.m_flJumpTime < 0)
|
|
{
|
|
player->m_Local.m_flJumpTime = 0;
|
|
}
|
|
}
|
|
if (player->m_flSwimSoundTime > 0)
|
|
{
|
|
player->m_flSwimSoundTime -= frame_msec;
|
|
if (player->m_flSwimSoundTime < 0)
|
|
{
|
|
player->m_flSwimSoundTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *pMove -
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::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;
|
|
|
|
// StartTrackPredictionErrors should have set this
|
|
Assert(player == pPlayer);
|
|
player = pPlayer;
|
|
|
|
mv = pMove;
|
|
mv->m_flMaxSpeed = pPlayer->GetPlayerMaxSpeed();
|
|
|
|
// CheckV( player->CurrentCommandNumber(), "StartPos", mv->GetAbsOrigin() );
|
|
|
|
DiffPrint("start %f %f %f", mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z);
|
|
|
|
// Run the command.
|
|
PlayerMove();
|
|
|
|
FinishMove();
|
|
|
|
DiffPrint("end %f %f %f", mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z);
|
|
|
|
// CheckV( player->CurrentCommandNumber(), "EndPos", mv->GetAbsOrigin() );
|
|
|
|
//This is probably not needed, but just in case.
|
|
gpGlobals->frametime = flStoreFrametime;
|
|
|
|
// player = NULL;
|
|
}
|
|
|
|
void CGameMovement::StartTrackPredictionErrors(CBasePlayer *pPlayer)
|
|
{
|
|
player = pPlayer;
|
|
|
|
#if PREDICTION_ERROR_CHECK_LEVEL > 0
|
|
StartCommand(CBaseEntity::IsServer(), player->CurrentCommandNumber());
|
|
#endif
|
|
}
|
|
|
|
void CGameMovement::FinishTrackPredictionErrors(CBasePlayer *pPlayer)
|
|
{
|
|
#if PREDICTION_ERROR_CHECK_LEVEL > 0
|
|
Assert(player == pPlayer);
|
|
|
|
// DiffPrint( "end %f", player->m_Local.m_vecPunchAngleVel.m_Value.x );
|
|
|
|
// Call validate at end of checking
|
|
Validate(CBaseEntity::IsServer(), player->CurrentCommandNumber());
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets ground entity
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FinishMove(void)
|
|
{
|
|
mv->m_nOldButtons = mv->m_nButtons;
|
|
}
|
|
|
|
#define PUNCH_DAMPING 9.0f // bigger number makes the response more damped, smaller is less damped
|
|
// currently the system will overshoot, with larger damping values it won't
|
|
#define PUNCH_SPRING_CONSTANT 65.0f // bigger number increases the speed at which the view corrects
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Decays the punchangle toward 0,0,0.
|
|
// Modelled as a damped spring
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::DecayPunchAngle(void)
|
|
{
|
|
if (player->m_Local.m_vecPunchAngle->LengthSqr() > 0.001 || player->m_Local.m_vecPunchAngleVel->LengthSqr() > 0.001)
|
|
{
|
|
player->m_Local.m_vecPunchAngle += player->m_Local.m_vecPunchAngleVel * gpGlobals->frametime;
|
|
float damping = 1 - (PUNCH_DAMPING * gpGlobals->frametime);
|
|
|
|
if (damping < 0)
|
|
{
|
|
damping = 0;
|
|
}
|
|
player->m_Local.m_vecPunchAngleVel *= damping;
|
|
|
|
// torsional spring
|
|
// UNDONE: Per-axis spring constant?
|
|
float springForceMagnitude = PUNCH_SPRING_CONSTANT * gpGlobals->frametime;
|
|
springForceMagnitude = clamp(springForceMagnitude, 0.f, 2.f);
|
|
player->m_Local.m_vecPunchAngleVel -= player->m_Local.m_vecPunchAngle * springForceMagnitude;
|
|
|
|
// don't wrap around
|
|
player->m_Local.m_vecPunchAngle.Init(
|
|
clamp(player->m_Local.m_vecPunchAngle->x, -89.f, 89.f),
|
|
clamp(player->m_Local.m_vecPunchAngle->y, -179.f, 179.f),
|
|
clamp(player->m_Local.m_vecPunchAngle->z, -89.f, 89.f));
|
|
}
|
|
else
|
|
{
|
|
player->m_Local.m_vecPunchAngle.Init(0, 0, 0);
|
|
player->m_Local.m_vecPunchAngleVel.Init(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::StartGravity(void)
|
|
{
|
|
float ent_gravity;
|
|
|
|
if (player->GetGravity())
|
|
ent_gravity = player->GetGravity();
|
|
else
|
|
ent_gravity = 1.0;
|
|
|
|
// Add gravity so they'll be in the correct position during movement
|
|
// yes, this 0.5 looks wrong, but it's not.
|
|
mv->m_vecVelocity[2] -= (ent_gravity * GetCurrentGravity() * 0.5 * gpGlobals->frametime);
|
|
mv->m_vecVelocity[2] += player->GetBaseVelocity()[2] * gpGlobals->frametime;
|
|
|
|
Vector temp = player->GetBaseVelocity();
|
|
temp[2] = 0;
|
|
player->SetBaseVelocity(temp);
|
|
|
|
CheckVelocity();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::CheckWaterJump(void)
|
|
{
|
|
Vector flatforward;
|
|
Vector forward;
|
|
Vector flatvelocity;
|
|
float curspeed;
|
|
|
|
AngleVectors(mv->m_vecViewAngles, &forward); // Determine movement angles
|
|
|
|
// Already water jumping.
|
|
if (player->m_flWaterJumpTime)
|
|
return;
|
|
|
|
// Don't hop out if we just jumped in
|
|
if (mv->m_vecVelocity[2] < -180)
|
|
return; // only hop out if we are moving up
|
|
|
|
// See if we are backing up
|
|
flatvelocity[0] = mv->m_vecVelocity[0];
|
|
flatvelocity[1] = mv->m_vecVelocity[1];
|
|
flatvelocity[2] = 0;
|
|
|
|
// Must be moving
|
|
curspeed = VectorNormalize(flatvelocity);
|
|
|
|
// see if near an edge
|
|
flatforward[0] = forward[0];
|
|
flatforward[1] = forward[1];
|
|
flatforward[2] = 0;
|
|
VectorNormalize(flatforward);
|
|
|
|
// Are we backing into water from steps or something? If so, don't pop forward
|
|
if (curspeed != 0.0 && (DotProduct(flatvelocity, flatforward) < 0.0))
|
|
return;
|
|
|
|
Vector vecStart;
|
|
// Start line trace at waist height (using the center of the player for this here)
|
|
vecStart = mv->GetAbsOrigin() + (GetPlayerMins() + GetPlayerMaxs()) * 0.5;
|
|
|
|
Vector vecEnd;
|
|
VectorMA(vecStart, 24.0f, flatforward, vecEnd);
|
|
|
|
trace_t tr;
|
|
TracePlayerBBox(vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr);
|
|
if (tr.fraction < 1.0) // solid at waist
|
|
{
|
|
IPhysicsObject *pPhysObj = tr.m_pEnt->VPhysicsGetObject();
|
|
if (pPhysObj)
|
|
{
|
|
if (pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD)
|
|
return;
|
|
}
|
|
|
|
vecStart.z = mv->GetAbsOrigin().z + player->GetViewOffset().z + WATERJUMP_HEIGHT;
|
|
VectorMA(vecStart, 24.0f, flatforward, vecEnd);
|
|
VectorMA(vec3_origin, -50.0f, tr.plane.normal, player->m_vecWaterJumpVel);
|
|
|
|
TracePlayerBBox(vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr);
|
|
if (tr.fraction == 1.0) // open at eye level
|
|
{
|
|
// Now trace down to see if we would actually land on a standable surface.
|
|
VectorCopy(vecEnd, vecStart);
|
|
vecEnd.z -= 1024.0f;
|
|
TracePlayerBBox(vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr);
|
|
if ((tr.fraction < 1.0f) && (tr.plane.normal.z >= 0.7))
|
|
{
|
|
mv->m_vecVelocity[2] = 256.0f; // Push up
|
|
mv->m_nOldButtons |= IN_JUMP; // Don't jump again until released
|
|
player->AddFlag(FL_WATERJUMP);
|
|
player->m_flWaterJumpTime = 2000.0f; // Do this for 2 seconds
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::WaterJump(void)
|
|
{
|
|
if (player->m_flWaterJumpTime > 10000)
|
|
player->m_flWaterJumpTime = 10000;
|
|
|
|
if (!player->m_flWaterJumpTime)
|
|
return;
|
|
|
|
player->m_flWaterJumpTime -= 1000.0f * gpGlobals->frametime;
|
|
|
|
if (player->m_flWaterJumpTime <= 0 || !player->GetWaterLevel())
|
|
{
|
|
player->m_flWaterJumpTime = 0;
|
|
player->RemoveFlag(FL_WATERJUMP);
|
|
}
|
|
|
|
mv->m_vecVelocity[0] = player->m_vecWaterJumpVel[0];
|
|
mv->m_vecVelocity[1] = player->m_vecWaterJumpVel[1];
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::WaterMove(void)
|
|
{
|
|
int i;
|
|
Vector wishvel;
|
|
float wishspeed;
|
|
Vector wishdir;
|
|
Vector start, dest;
|
|
Vector temp;
|
|
trace_t pm;
|
|
float speed, newspeed, addspeed, accelspeed;
|
|
Vector forward, right, up;
|
|
|
|
AngleVectors(mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
|
|
|
|
//
|
|
// user intentions
|
|
//
|
|
for (i = 0; i<3; i++)
|
|
{
|
|
wishvel[i] = forward[i] * mv->m_flForwardMove + right[i] * mv->m_flSideMove;
|
|
}
|
|
|
|
// if we have the jump key down, move us up as well
|
|
if (mv->m_nButtons & IN_JUMP)
|
|
{
|
|
wishvel[2] += mv->m_flClientMaxSpeed;
|
|
}
|
|
// Sinking after no other movement occurs
|
|
else if (!mv->m_flForwardMove && !mv->m_flSideMove && !mv->m_flUpMove)
|
|
{
|
|
wishvel[2] -= 60; // drift towards bottom
|
|
}
|
|
else // Go straight up by upmove amount.
|
|
{
|
|
// exaggerate upward movement along forward as well
|
|
float upwardMovememnt = mv->m_flForwardMove * forward.z * 2;
|
|
upwardMovememnt = clamp(upwardMovememnt, 0.f, mv->m_flClientMaxSpeed);
|
|
wishvel[2] += mv->m_flUpMove + upwardMovememnt;
|
|
}
|
|
|
|
// Copy it over and determine speed
|
|
VectorCopy(wishvel, wishdir);
|
|
wishspeed = VectorNormalize(wishdir);
|
|
|
|
// Cap speed.
|
|
if (wishspeed > mv->m_flMaxSpeed)
|
|
{
|
|
VectorScale(wishvel, mv->m_flMaxSpeed / wishspeed, wishvel);
|
|
wishspeed = mv->m_flMaxSpeed;
|
|
}
|
|
|
|
// Slow us down a bit.
|
|
wishspeed *= 0.8;
|
|
|
|
// Water friction
|
|
VectorCopy(mv->m_vecVelocity, temp);
|
|
speed = VectorNormalize(temp);
|
|
if (speed)
|
|
{
|
|
newspeed = speed - gpGlobals->frametime * speed * sv_friction.GetFloat() * player->m_surfaceFriction;
|
|
if (newspeed < 0.1f)
|
|
{
|
|
newspeed = 0;
|
|
}
|
|
|
|
VectorScale(mv->m_vecVelocity, newspeed / speed, mv->m_vecVelocity);
|
|
}
|
|
else
|
|
{
|
|
newspeed = 0;
|
|
}
|
|
|
|
// water acceleration
|
|
if (wishspeed >= 0.1f) // old !
|
|
{
|
|
addspeed = wishspeed - newspeed;
|
|
if (addspeed > 0)
|
|
{
|
|
VectorNormalize(wishvel);
|
|
accelspeed = sv_accelerate.GetFloat() * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
|
|
if (accelspeed > addspeed)
|
|
{
|
|
accelspeed = addspeed;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
float deltaSpeed = accelspeed * wishvel[i];
|
|
mv->m_vecVelocity[i] += deltaSpeed;
|
|
mv->m_outWishVel[i] += deltaSpeed;
|
|
}
|
|
}
|
|
}
|
|
|
|
VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
|
|
// Now move
|
|
// assume it is a stair or a slope, so press down from stepheight above
|
|
VectorMA(mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, dest);
|
|
|
|
TracePlayerBBox(mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
if (pm.fraction == 1.0f)
|
|
{
|
|
VectorCopy(dest, start);
|
|
if (player->m_Local.m_bAllowAutoMovement)
|
|
{
|
|
start[2] += player->m_Local.m_flStepSize + 1;
|
|
}
|
|
|
|
TracePlayerBBox(start, dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
|
|
if (!pm.startsolid && !pm.allsolid)
|
|
{
|
|
float stepDist = pm.endpos.z - mv->GetAbsOrigin().z;
|
|
mv->m_outStepHeight += stepDist;
|
|
// walked up the step, so just keep result and exit
|
|
mv->SetAbsOrigin(pm.endpos);
|
|
VectorSubtract(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
return;
|
|
}
|
|
|
|
// Try moving straight along out normal path.
|
|
TryPlayerMove();
|
|
}
|
|
else
|
|
{
|
|
if (!player->GetGroundEntity())
|
|
{
|
|
TryPlayerMove();
|
|
VectorSubtract(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
return;
|
|
}
|
|
|
|
StepMove(dest, pm);
|
|
}
|
|
|
|
VectorSubtract(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Does the basic move attempting to climb up step heights. It uses
|
|
// the mv->GetAbsOrigin() and mv->m_vecVelocity. It returns a new
|
|
// new mv->GetAbsOrigin(), mv->m_vecVelocity, and mv->m_outStepHeight.
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::StepMove(Vector &vecDestination, trace_t &trace)
|
|
{
|
|
Vector vecEndPos;
|
|
VectorCopy(vecDestination, vecEndPos);
|
|
|
|
// Try sliding forward both on ground and up 16 pixels
|
|
// take the move that goes farthest
|
|
Vector vecPos, vecVel;
|
|
VectorCopy(mv->GetAbsOrigin(), vecPos);
|
|
VectorCopy(mv->m_vecVelocity, vecVel);
|
|
|
|
// Slide move down.
|
|
TryPlayerMove(&vecEndPos, &trace);
|
|
|
|
// Down results.
|
|
Vector vecDownPos, vecDownVel;
|
|
VectorCopy(mv->GetAbsOrigin(), vecDownPos);
|
|
VectorCopy(mv->m_vecVelocity, vecDownVel);
|
|
|
|
// Reset original values.
|
|
mv->SetAbsOrigin(vecPos);
|
|
VectorCopy(vecVel, mv->m_vecVelocity);
|
|
|
|
// Move up a stair height.
|
|
VectorCopy(mv->GetAbsOrigin(), vecEndPos);
|
|
if (player->m_Local.m_bAllowAutoMovement)
|
|
{
|
|
vecEndPos.z += player->m_Local.m_flStepSize + DIST_EPSILON;
|
|
}
|
|
|
|
TracePlayerBBox(mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace);
|
|
if (!trace.startsolid && !trace.allsolid)
|
|
{
|
|
mv->SetAbsOrigin(trace.endpos);
|
|
}
|
|
|
|
// Slide move up.
|
|
TryPlayerMove();
|
|
|
|
// Move down a stair (attempt to).
|
|
VectorCopy(mv->GetAbsOrigin(), vecEndPos);
|
|
if (player->m_Local.m_bAllowAutoMovement)
|
|
{
|
|
vecEndPos.z -= player->m_Local.m_flStepSize + DIST_EPSILON;
|
|
}
|
|
|
|
TracePlayerBBox(mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace);
|
|
|
|
// If we are not on the ground any more then use the original movement attempt.
|
|
if (trace.plane.normal[2] < 0.7)
|
|
{
|
|
mv->SetAbsOrigin(vecDownPos);
|
|
VectorCopy(vecDownVel, mv->m_vecVelocity);
|
|
float flStepDist = mv->GetAbsOrigin().z - vecPos.z;
|
|
if (flStepDist > 0.0f)
|
|
{
|
|
mv->m_outStepHeight += flStepDist;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If the trace ended up in empty space, copy the end over to the origin.
|
|
if (!trace.startsolid && !trace.allsolid)
|
|
{
|
|
mv->SetAbsOrigin(trace.endpos);
|
|
}
|
|
|
|
// Copy this origin to up.
|
|
Vector vecUpPos;
|
|
VectorCopy(mv->GetAbsOrigin(), vecUpPos);
|
|
|
|
// decide which one went farther
|
|
float flDownDist = (vecDownPos.x - vecPos.x) * (vecDownPos.x - vecPos.x) + (vecDownPos.y - vecPos.y) * (vecDownPos.y - vecPos.y);
|
|
float flUpDist = (vecUpPos.x - vecPos.x) * (vecUpPos.x - vecPos.x) + (vecUpPos.y - vecPos.y) * (vecUpPos.y - vecPos.y);
|
|
if (flDownDist > flUpDist)
|
|
{
|
|
mv->SetAbsOrigin(vecDownPos);
|
|
VectorCopy(vecDownVel, mv->m_vecVelocity);
|
|
}
|
|
else
|
|
{
|
|
// copy z value from slide move
|
|
mv->m_vecVelocity.z = vecDownVel.z;
|
|
}
|
|
|
|
float flStepDist = mv->GetAbsOrigin().z - vecPos.z;
|
|
if (flStepDist > 0)
|
|
{
|
|
mv->m_outStepHeight += flStepDist;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::Friction(void)
|
|
{
|
|
float speed, newspeed, control;
|
|
float friction;
|
|
float drop;
|
|
|
|
// If we are in water jump cycle, don't apply friction
|
|
if (player->m_flWaterJumpTime)
|
|
return;
|
|
|
|
// Calculate speed
|
|
speed = VectorLength(mv->m_vecVelocity);
|
|
|
|
// If too slow, return
|
|
if (speed < 0.1f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
drop = 0;
|
|
|
|
// apply ground friction
|
|
if (player->GetGroundEntity() != NULL) // On an entity that is the ground
|
|
{
|
|
friction = sv_friction.GetFloat() * player->m_surfaceFriction;
|
|
|
|
// Bleed off some speed, but if we have less than the bleed
|
|
// threshold, bleed the threshold amount.
|
|
|
|
if (IsX360())
|
|
{
|
|
if (player->m_Local.m_bDucked)
|
|
{
|
|
control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
|
|
}
|
|
else
|
|
{
|
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
|
|
control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
|
|
#else
|
|
control = (speed < sv_stopspeed.GetFloat()) ? (sv_stopspeed.GetFloat() * 2.0f) : speed;
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
|
|
}
|
|
|
|
// Add the amount to the drop amount.
|
|
drop += control*friction*gpGlobals->frametime;
|
|
}
|
|
|
|
// scale the velocity
|
|
newspeed = speed - drop;
|
|
if (newspeed < 0)
|
|
newspeed = 0;
|
|
|
|
if (newspeed != speed)
|
|
{
|
|
// Determine proportion of old speed we are using.
|
|
newspeed /= speed;
|
|
// Adjust velocity according to proportion.
|
|
VectorScale(mv->m_vecVelocity, newspeed, mv->m_vecVelocity);
|
|
}
|
|
|
|
mv->m_outWishVel -= (1.f - newspeed) * mv->m_vecVelocity;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FinishGravity(void)
|
|
{
|
|
float ent_gravity;
|
|
|
|
if (player->m_flWaterJumpTime)
|
|
return;
|
|
|
|
if (player->GetGravity())
|
|
ent_gravity = player->GetGravity();
|
|
else
|
|
ent_gravity = 1.0;
|
|
|
|
// Get the correct velocity for the end of the dt
|
|
mv->m_vecVelocity[2] -= (ent_gravity * GetCurrentGravity() * gpGlobals->frametime * 0.5);
|
|
|
|
CheckVelocity();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : wishdir -
|
|
// accel -
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::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 > GetAirSpeedCap())
|
|
wishspd = GetAirSpeedCap();
|
|
|
|
// 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 CGameMovement::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
|
|
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, sv_airaccelerate.GetFloat());
|
|
|
|
// 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);
|
|
}
|
|
|
|
|
|
bool CGameMovement::CanAccelerate()
|
|
{
|
|
// Dead players don't accelerate.
|
|
if (player->pl.deadflag)
|
|
return false;
|
|
|
|
// If waterjumping, don't accelerate
|
|
if (player->m_flWaterJumpTime)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : wishdir -
|
|
// wishspeed -
|
|
// accel -
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::Accelerate(Vector& wishdir, float wishspeed, float accel)
|
|
{
|
|
int i;
|
|
float addspeed, accelspeed, currentspeed;
|
|
|
|
// This gets overridden because some games (CSPort) want to allow dead (observer) players
|
|
// to be able to move around.
|
|
if (!CanAccelerate())
|
|
return;
|
|
|
|
// See if we are changing direction a bit
|
|
currentspeed = mv->m_vecVelocity.Dot(wishdir);
|
|
|
|
// Reduce wishspeed by the amount of veer.
|
|
addspeed = wishspeed - currentspeed;
|
|
|
|
// If not going to add any speed, done.
|
|
if (addspeed <= 0)
|
|
return;
|
|
|
|
// Determine amount of accleration.
|
|
accelspeed = accel * gpGlobals->frametime * wishspeed * player->m_surfaceFriction;
|
|
|
|
// Cap at addspeed
|
|
if (accelspeed > addspeed)
|
|
accelspeed = addspeed;
|
|
|
|
// Adjust velocity.
|
|
for (i = 0; i<3; i++)
|
|
{
|
|
mv->m_vecVelocity[i] += accelspeed * wishdir[i];
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Try to keep a walking player on the ground when running down slopes etc
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::StayOnGround(void)
|
|
{
|
|
trace_t trace;
|
|
Vector start(mv->GetAbsOrigin());
|
|
Vector end(mv->GetAbsOrigin());
|
|
start.z += 2;
|
|
end.z -= player->GetStepSize();
|
|
|
|
// See how far up we can go without getting stuck
|
|
|
|
TracePlayerBBox(mv->GetAbsOrigin(), start, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace);
|
|
start = trace.endpos;
|
|
|
|
// using trace.startsolid is unreliable here, it doesn't get set when
|
|
// tracing bounding box vs. terrain
|
|
|
|
// Now trace down from a known safe position
|
|
TracePlayerBBox(start, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace);
|
|
if (trace.fraction > 0.0f && // must go somewhere
|
|
trace.fraction < 1.0f && // must hit something
|
|
!trace.startsolid && // can't be embedded in a solid
|
|
trace.plane.normal[2] >= 0.7) // can't hit a steep slope that we can't stand on anyway
|
|
{
|
|
float flDelta = fabs(mv->GetAbsOrigin().z - trace.endpos.z);
|
|
|
|
//This is incredibly hacky. The real problem is that trace returning that strange value we can't network over.
|
|
if (flDelta > 0.5f * COORD_RESOLUTION)
|
|
{
|
|
mv->SetAbsOrigin(trace.endpos);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::WalkMove(void)
|
|
{
|
|
int i;
|
|
|
|
Vector wishvel;
|
|
float spd;
|
|
float fmove, smove;
|
|
Vector wishdir;
|
|
float wishspeed;
|
|
|
|
Vector dest;
|
|
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
|
|
if (g_bMovementOptimizations)
|
|
{
|
|
if (forward[2] != 0)
|
|
{
|
|
forward[2] = 0;
|
|
VectorNormalize(forward);
|
|
}
|
|
|
|
if (right[2] != 0)
|
|
{
|
|
right[2] = 0;
|
|
VectorNormalize(right);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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 != 0.0f) && (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();
|
|
// 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);
|
|
return;
|
|
}
|
|
|
|
// first try just moving to the destination
|
|
dest[0] = mv->GetAbsOrigin()[0] + mv->m_vecVelocity[0] * gpGlobals->frametime;
|
|
dest[1] = mv->GetAbsOrigin()[1] + mv->m_vecVelocity[1] * gpGlobals->frametime;
|
|
dest[2] = mv->GetAbsOrigin()[2];
|
|
|
|
// first try moving directly to the next spot
|
|
TracePlayerBBox(mv->GetAbsOrigin(), dest, PlayerSolidMask(), 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)
|
|
{
|
|
mv->SetAbsOrigin(pm.endpos);
|
|
// 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);
|
|
|
|
StayOnGround();
|
|
return;
|
|
}
|
|
|
|
// Don't walk up stairs if not on ground.
|
|
if (oldground == NULL && player->GetWaterLevel() == 0)
|
|
{
|
|
// 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);
|
|
return;
|
|
}
|
|
|
|
// If we are jumping out of water, don't do anything more.
|
|
if (player->m_flWaterJumpTime)
|
|
{
|
|
// 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);
|
|
return;
|
|
}
|
|
|
|
StepMove(dest, pm);
|
|
|
|
// 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);
|
|
|
|
StayOnGround();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::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();
|
|
|
|
// Redetermine position vars
|
|
CategorizePosition();
|
|
|
|
// If we are on ground, no downward velocity.
|
|
if (player->GetGroundEntity() != NULL)
|
|
{
|
|
mv->m_vecVelocity[2] = 0;
|
|
}
|
|
}
|
|
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();
|
|
}
|
|
else
|
|
{
|
|
AirMove(); // Take into account movement when in air.
|
|
}
|
|
|
|
// Set final flags.
|
|
CategorizePosition();
|
|
|
|
// 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();
|
|
#if !defined( CLIENT_DLL )
|
|
player->Splash();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FullObserverMove(void)
|
|
{
|
|
int mode = player->GetObserverMode();
|
|
|
|
if (mode == OBS_MODE_IN_EYE || mode == OBS_MODE_CHASE)
|
|
{
|
|
CBaseEntity * target = player->GetObserverTarget();
|
|
|
|
if (target != NULL)
|
|
{
|
|
mv->SetAbsOrigin(target->GetAbsOrigin());
|
|
mv->m_vecViewAngles = target->GetAbsAngles();
|
|
mv->m_vecVelocity = target->GetAbsVelocity();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (mode != OBS_MODE_ROAMING)
|
|
{
|
|
// don't move in fixed or death cam mode
|
|
return;
|
|
}
|
|
|
|
if (sv_specnoclip.GetBool())
|
|
{
|
|
// roam in noclip mode
|
|
FullNoClipMove(sv_specspeed.GetFloat(), sv_specaccelerate.GetFloat());
|
|
return;
|
|
}
|
|
|
|
// do a full clipped free roam move:
|
|
|
|
Vector wishvel;
|
|
Vector forward, right, up;
|
|
Vector wishdir, wishend;
|
|
float wishspeed;
|
|
|
|
AngleVectors(mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
|
|
|
|
// Copy movement amounts
|
|
|
|
float factor = sv_specspeed.GetFloat();
|
|
|
|
if (mv->m_nButtons & IN_SPEED)
|
|
{
|
|
factor /= 2.0f;
|
|
}
|
|
|
|
float fmove = mv->m_flForwardMove * factor;
|
|
float smove = mv->m_flSideMove * factor;
|
|
|
|
VectorNormalize(forward); // Normalize remainder of vectors
|
|
VectorNormalize(right); //
|
|
|
|
for (int i = 0; i<3; i++) // Determine x and y parts of velocity
|
|
wishvel[i] = forward[i] * fmove + right[i] * smove;
|
|
wishvel[2] += mv->m_flUpMove;
|
|
|
|
VectorCopy(wishvel, wishdir); // Determine maginitude of speed of move
|
|
wishspeed = VectorNormalize(wishdir);
|
|
|
|
//
|
|
// Clamp to server defined max speed
|
|
//
|
|
|
|
float maxspeed = sv_maxvelocity.GetFloat();
|
|
|
|
|
|
if (wishspeed > maxspeed)
|
|
{
|
|
VectorScale(wishvel, mv->m_flMaxSpeed / wishspeed, wishvel);
|
|
wishspeed = maxspeed;
|
|
}
|
|
|
|
// Set pmove velocity, give observer 50% acceration bonus
|
|
Accelerate(wishdir, wishspeed, sv_specaccelerate.GetFloat());
|
|
|
|
float spd = VectorLength(mv->m_vecVelocity);
|
|
if (spd < 1.0f)
|
|
{
|
|
mv->m_vecVelocity.Init();
|
|
return;
|
|
}
|
|
|
|
float friction = sv_friction.GetFloat();
|
|
|
|
// Add the amount to the drop amount.
|
|
float drop = spd * friction * gpGlobals->frametime;
|
|
|
|
// scale the velocity
|
|
float newspeed = spd - drop;
|
|
|
|
if (newspeed < 0)
|
|
newspeed = 0;
|
|
|
|
// Determine proportion of old speed we are using.
|
|
newspeed /= spd;
|
|
|
|
VectorScale(mv->m_vecVelocity, newspeed, mv->m_vecVelocity);
|
|
|
|
CheckVelocity();
|
|
|
|
TryPlayerMove();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FullNoClipMove(float factor, float maxacceleration)
|
|
{
|
|
Vector wishvel;
|
|
Vector forward, right, up;
|
|
Vector wishdir;
|
|
float wishspeed;
|
|
float maxspeed = sv_maxspeed.GetFloat() * factor;
|
|
|
|
AngleVectors(mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
|
|
|
|
if (mv->m_nButtons & IN_SPEED)
|
|
{
|
|
factor /= 2.0f;
|
|
}
|
|
|
|
// Copy movement amounts
|
|
float fmove = mv->m_flForwardMove * factor;
|
|
float smove = mv->m_flSideMove * factor;
|
|
|
|
VectorNormalize(forward); // Normalize remainder of vectors
|
|
VectorNormalize(right); //
|
|
|
|
for (int i = 0; i<3; i++) // Determine x and y parts of velocity
|
|
wishvel[i] = forward[i] * fmove + right[i] * smove;
|
|
wishvel[2] += mv->m_flUpMove * factor;
|
|
|
|
VectorCopy(wishvel, wishdir); // Determine maginitude of speed of move
|
|
wishspeed = VectorNormalize(wishdir);
|
|
|
|
//
|
|
// Clamp to server defined max speed
|
|
//
|
|
if (wishspeed > maxspeed)
|
|
{
|
|
VectorScale(wishvel, maxspeed / wishspeed, wishvel);
|
|
wishspeed = maxspeed;
|
|
}
|
|
|
|
if (maxacceleration > 0.0)
|
|
{
|
|
// Set pmove velocity
|
|
Accelerate(wishdir, wishspeed, maxacceleration);
|
|
|
|
float spd = VectorLength(mv->m_vecVelocity);
|
|
if (spd < 1.0f)
|
|
{
|
|
mv->m_vecVelocity.Init();
|
|
return;
|
|
}
|
|
|
|
// Bleed off some speed, but if we have less than the bleed
|
|
// threshhold, bleed the theshold amount.
|
|
float control = (spd < maxspeed / 4.0) ? maxspeed / 4.0 : spd;
|
|
|
|
float friction = sv_friction.GetFloat() * player->m_surfaceFriction;
|
|
|
|
// Add the amount to the drop amount.
|
|
float drop = control * friction * gpGlobals->frametime;
|
|
|
|
// scale the velocity
|
|
float newspeed = spd - drop;
|
|
if (newspeed < 0)
|
|
newspeed = 0;
|
|
|
|
// Determine proportion of old speed we are using.
|
|
newspeed /= spd;
|
|
VectorScale(mv->m_vecVelocity, newspeed, mv->m_vecVelocity);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(wishvel, mv->m_vecVelocity);
|
|
}
|
|
|
|
// Just move ( don't clip or anything )
|
|
Vector out;
|
|
VectorMA(mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, out);
|
|
mv->SetAbsOrigin(out);
|
|
|
|
// Zero out velocity if in noaccel mode
|
|
if (maxacceleration < 0.0f)
|
|
{
|
|
mv->m_vecVelocity.Init();
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Checks to see if we should actually jump
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::PlaySwimSound()
|
|
{
|
|
MoveHelper()->StartSound(mv->GetAbsOrigin(), "Player.Swim");
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CGameMovement::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
|
|
}
|
|
|
|
// Don't allow jumping when the player is in a stasis field.
|
|
#ifndef HL2_EPISODIC
|
|
if (player->m_Local.m_bSlowMovement)
|
|
return false;
|
|
#endif
|
|
|
|
if (mv->m_nOldButtons & IN_JUMP)
|
|
return false; // don't pogo stick
|
|
|
|
// Cannot jump will in the unduck transition.
|
|
if (player->m_Local.m_bDucking && (player->GetFlags() & FL_DUCKING))
|
|
return false;
|
|
|
|
// Still updating the eye position.
|
|
if (player->m_Local.m_flDuckJumpTime > 0.0f)
|
|
return false;
|
|
|
|
|
|
// In the air now.
|
|
SetGroundEntity(NULL);
|
|
|
|
player->PlayStepSound((Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true);
|
|
|
|
MoveHelper()->PlayerSetAnimation(PLAYER_JUMP);
|
|
|
|
float flGroundFactor = 1.0f;
|
|
if (player->m_pSurfaceData)
|
|
{
|
|
flGroundFactor = player->m_pSurfaceData->game.jumpFactor;
|
|
}
|
|
|
|
float flMul;
|
|
if (g_bMovementOptimizations)
|
|
{
|
|
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
|
|
Assert(GetCurrentGravity() == 600.0f);
|
|
flMul = 160.0f; // approx. 21 units.
|
|
#else
|
|
Assert(GetCurrentGravity() == 800.0f);
|
|
flMul = 268.3281572999747f;
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
flMul = sqrt(2 * GetCurrentGravity() * GAMEMOVEMENT_JUMP_HEIGHT);
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Add a little forward velocity based on your current forward velocity - if you are not sprinting.
|
|
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
|
|
if (gpGlobals->maxClients == 1)
|
|
{
|
|
CHLMoveData *pMoveData = (CHLMoveData*)mv;
|
|
Vector vecForward;
|
|
AngleVectors(mv->m_vecViewAngles, &vecForward);
|
|
vecForward.z = 0;
|
|
VectorNormalize(vecForward);
|
|
|
|
// We give a certain percentage of the current forward movement as a bonus to the jump speed. That bonus is clipped
|
|
// to not accumulate over time.
|
|
float flSpeedBoostPerc = (!pMoveData->m_bIsSprinting && !player->m_Local.m_bDucked) ? 0.5f : 0.1f;
|
|
float flSpeedAddition = fabs(mv->m_flForwardMove * flSpeedBoostPerc);
|
|
float flMaxSpeed = mv->m_flMaxSpeed + (mv->m_flMaxSpeed * flSpeedBoostPerc);
|
|
float flNewSpeed = (flSpeedAddition + mv->m_vecVelocity.Length2D());
|
|
|
|
// If we're over the maximum, we want to only boost as much as will get us to the goal speed
|
|
if (flNewSpeed > flMaxSpeed)
|
|
{
|
|
flSpeedAddition -= flNewSpeed - flMaxSpeed;
|
|
}
|
|
|
|
if (mv->m_flForwardMove < 0.0f)
|
|
flSpeedAddition *= -1.0f;
|
|
|
|
// Add it on
|
|
VectorAdd((vecForward*flSpeedAddition), mv->m_vecVelocity, mv->m_vecVelocity);
|
|
}
|
|
#endif
|
|
|
|
FinishGravity();
|
|
|
|
CheckV(player->CurrentCommandNumber(), "CheckJump", mv->m_vecVelocity);
|
|
|
|
mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz;
|
|
mv->m_outStepHeight += 0.15f;
|
|
|
|
OnJump(mv->m_outJumpVel.z);
|
|
|
|
// Set jump time.
|
|
if (gpGlobals->maxClients == 1)
|
|
{
|
|
player->m_Local.m_flJumpTime = GAMEMOVEMENT_JUMP_TIME;
|
|
player->m_Local.m_bInDuckJump = true;
|
|
}
|
|
|
|
#if defined( HL2_DLL )
|
|
|
|
if (xc_uncrouch_on_jump.GetBool())
|
|
{
|
|
// Uncrouch when jumping
|
|
if (player->GetToggledDuckState())
|
|
{
|
|
player->ToggleDuck();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Flag that we jumped.
|
|
mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FullLadderMove()
|
|
{
|
|
CheckWater();
|
|
|
|
// Was jump button pressed? If so, set velocity to 270 away from ladder.
|
|
if (mv->m_nButtons & IN_JUMP)
|
|
{
|
|
CheckJumpButton();
|
|
}
|
|
else
|
|
{
|
|
mv->m_nOldButtons &= ~IN_JUMP;
|
|
}
|
|
|
|
// Perform the move accounting for any base velocity.
|
|
VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
TryPlayerMove();
|
|
VectorSubtract(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CGameMovement::TryPlayerMove(Vector *pFirstDest, trace_t *pFirstTrace)
|
|
{
|
|
int bumpcount, numbumps;
|
|
Vector dir;
|
|
float d;
|
|
int numplanes;
|
|
Vector planes[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->GetAbsOrigin(), 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
|
|
{
|
|
#if defined( PLAYER_GETTING_STUCK_TESTING )
|
|
trace_t foo;
|
|
TracePlayerBBox(mv->GetAbsOrigin(), mv->GetAbsOrigin(), PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo);
|
|
if (foo.startsolid || foo.fraction != 1.0f)
|
|
{
|
|
Msg("bah\n");
|
|
}
|
|
#endif
|
|
TracePlayerBBox(mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TracePlayerBBox(mv->GetAbsOrigin(), 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)
|
|
{
|
|
if (numbumps > 0 && pm.fraction == 1)
|
|
{
|
|
// There's a precision issue with terrain tracing that can cause a swept box to successfully trace
|
|
// when the end position is stuck in the triangle. Re-run the test with an uswept box to catch that
|
|
// case until the bug is fixed.
|
|
// If we detect getting stuck, don't allow the movement
|
|
trace_t stuck;
|
|
TracePlayerBBox(pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, stuck);
|
|
if (stuck.startsolid || stuck.fraction != 1.0f)
|
|
{
|
|
//Msg( "Player will become stuck!!!\n" );
|
|
VectorCopy(vec3_origin, mv->m_vecVelocity);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if defined( PLAYER_GETTING_STUCK_TESTING )
|
|
trace_t foo;
|
|
TracePlayerBBox(pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo);
|
|
if (foo.startsolid || foo.fraction != 1.0f)
|
|
{
|
|
Msg("Player will become stuck!!!\n");
|
|
}
|
|
#endif
|
|
// actually covered some distance
|
|
mv->SetAbsOrigin(pm.endpos);
|
|
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
|
|
}
|
|
|
|
// 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] > 0.7)
|
|
{
|
|
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 >= 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
|
|
//
|
|
|
|
// reflect player velocity
|
|
// Only give this a try for first impact plane because you can get yourself stuck in an acute corner by jumping in place
|
|
// and pressing forward and nobody was really using this bounce/reflection feature anyway...
|
|
if (numplanes == 1 &&
|
|
player->GetMoveType() == MOVETYPE_WALK &&
|
|
player->GetGroundEntity() == NULL)
|
|
{
|
|
for (i = 0; i < numplanes; i++)
|
|
{
|
|
if (planes[i][2] > 0.7)
|
|
{
|
|
// 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 - player->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);
|
|
dir.NormalizeInPlace();
|
|
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);
|
|
}
|
|
|
|
// Check if they slammed into a wall
|
|
float fSlamVol = 0.0f;
|
|
|
|
float fLateralStoppingAmount = primal_velocity.Length2D() - mv->m_vecVelocity.Length2D();
|
|
if (fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED * 2.0f)
|
|
{
|
|
fSlamVol = 1.0f;
|
|
}
|
|
else if (fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED)
|
|
{
|
|
fSlamVol = 0.85f;
|
|
}
|
|
|
|
PlayerRoughLandingEffects(fSlamVol);
|
|
|
|
return blocked;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determine whether or not the player is on a ladder (physprop or world).
|
|
//-----------------------------------------------------------------------------
|
|
inline bool CGameMovement::OnLadder(trace_t &trace)
|
|
{
|
|
if (trace.contents & CONTENTS_LADDER)
|
|
return true;
|
|
|
|
IPhysicsSurfaceProps *pPhysProps = MoveHelper()->GetSurfaceProps();
|
|
if (pPhysProps)
|
|
{
|
|
const surfacedata_t *pSurfaceData = pPhysProps->GetSurfaceData(trace.surface.surfaceProps);
|
|
if (pSurfaceData)
|
|
{
|
|
if (pSurfaceData->game.climbable != 0)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//=============================================================================
|
|
// HPE_BEGIN
|
|
// [sbodenbender] make ladders easier to climb in cstrike
|
|
//=============================================================================
|
|
#if defined (CSTRIKE_DLL)
|
|
ConVar sv_ladder_dampen("sv_ladder_dampen", "0.2", FCVAR_REPLICATED, "Amount to dampen perpendicular movement on a ladder", true, 0.0f, true, 1.0f);
|
|
ConVar sv_ladder_angle("sv_ladder_angle", "-0.707", FCVAR_REPLICATED, "Cos of angle of incidence to ladder perpendicular for applying ladder_dampen", true, -1.0f, true, 1.0f);
|
|
#endif
|
|
//=============================================================================
|
|
// HPE_END
|
|
//=============================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CGameMovement::LadderMove(void)
|
|
{
|
|
trace_t pm;
|
|
bool onFloor;
|
|
Vector floor;
|
|
Vector wishdir;
|
|
Vector end;
|
|
|
|
if (player->GetMoveType() == MOVETYPE_NOCLIP)
|
|
return false;
|
|
|
|
if (!GameHasLadders())
|
|
return false;
|
|
|
|
// If I'm already moving on a ladder, use the previous ladder direction
|
|
if (player->GetMoveType() == MOVETYPE_LADDER)
|
|
{
|
|
wishdir = -player->m_vecLadderNormal;
|
|
}
|
|
else
|
|
{
|
|
// otherwise, use the direction player is attempting to move
|
|
if (mv->m_flForwardMove || mv->m_flSideMove)
|
|
{
|
|
for (int i = 0; i<3; i++) // Determine x and y parts of velocity
|
|
wishdir[i] = m_vecForward[i] * mv->m_flForwardMove + m_vecRight[i] * mv->m_flSideMove;
|
|
|
|
VectorNormalize(wishdir);
|
|
}
|
|
else
|
|
{
|
|
// Player is not attempting to move, no ladder behavior
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// wishdir points toward the ladder if any exists
|
|
VectorMA(mv->GetAbsOrigin(), LadderDistance(), wishdir, end);
|
|
TracePlayerBBox(mv->GetAbsOrigin(), end, LadderMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
|
|
// no ladder in that direction, return
|
|
if (pm.fraction == 1.0f || !OnLadder(pm))
|
|
return false;
|
|
|
|
player->SetMoveType(MOVETYPE_LADDER);
|
|
player->SetMoveCollide(MOVECOLLIDE_DEFAULT);
|
|
|
|
player->m_vecLadderNormal = pm.plane.normal;
|
|
|
|
// On ladder, convert movement to be relative to the ladder
|
|
|
|
VectorCopy(mv->GetAbsOrigin(), floor);
|
|
floor[2] += GetPlayerMins()[2] - 1;
|
|
|
|
if (enginetrace->GetPointContents(floor) == CONTENTS_SOLID || player->GetGroundEntity() != NULL)
|
|
{
|
|
onFloor = true;
|
|
}
|
|
else
|
|
{
|
|
onFloor = false;
|
|
}
|
|
|
|
player->SetGravity(0);
|
|
|
|
float climbSpeed = ClimbSpeed();
|
|
|
|
float forwardSpeed = 0, rightSpeed = 0;
|
|
if (mv->m_nButtons & IN_BACK)
|
|
forwardSpeed -= climbSpeed;
|
|
|
|
if (mv->m_nButtons & IN_FORWARD)
|
|
forwardSpeed += climbSpeed;
|
|
|
|
if (mv->m_nButtons & IN_MOVELEFT)
|
|
rightSpeed -= climbSpeed;
|
|
|
|
if (mv->m_nButtons & IN_MOVERIGHT)
|
|
rightSpeed += climbSpeed;
|
|
|
|
if (mv->m_nButtons & IN_JUMP)
|
|
{
|
|
player->SetMoveType(MOVETYPE_WALK);
|
|
player->SetMoveCollide(MOVECOLLIDE_DEFAULT);
|
|
|
|
VectorScale(pm.plane.normal, 270, mv->m_vecVelocity);
|
|
}
|
|
else
|
|
{
|
|
if (forwardSpeed != 0 || rightSpeed != 0)
|
|
{
|
|
Vector velocity, perp, cross, lateral, tmp;
|
|
|
|
//ALERT(at_console, "pev %.2f %.2f %.2f - ",
|
|
// pev->velocity.x, pev->velocity.y, pev->velocity.z);
|
|
// Calculate player's intended velocity
|
|
//Vector velocity = (forward * gpGlobals->v_forward) + (right * gpGlobals->v_right);
|
|
VectorScale(m_vecForward, forwardSpeed, velocity);
|
|
VectorMA(velocity, rightSpeed, m_vecRight, velocity);
|
|
|
|
// Perpendicular in the ladder plane
|
|
VectorCopy(vec3_origin, tmp);
|
|
tmp[2] = 1;
|
|
CrossProduct(tmp, pm.plane.normal, perp);
|
|
VectorNormalize(perp);
|
|
|
|
// decompose velocity into ladder plane
|
|
float normal = DotProduct(velocity, pm.plane.normal);
|
|
|
|
// This is the velocity into the face of the ladder
|
|
VectorScale(pm.plane.normal, normal, cross);
|
|
|
|
// This is the player's additional velocity
|
|
VectorSubtract(velocity, cross, lateral);
|
|
|
|
// This turns the velocity into the face of the ladder into velocity that
|
|
// is roughly vertically perpendicular to the face of the ladder.
|
|
// NOTE: It IS possible to face up and move down or face down and move up
|
|
// because the velocity is a sum of the directional velocity and the converted
|
|
// velocity through the face of the ladder -- by design.
|
|
CrossProduct(pm.plane.normal, perp, tmp);
|
|
|
|
//=============================================================================
|
|
// HPE_BEGIN
|
|
// [sbodenbender] make ladders easier to climb in cstrike
|
|
//=============================================================================
|
|
#if defined (CSTRIKE_DLL)
|
|
// break lateral into direction along tmp (up the ladder) and direction along perp (perpendicular to ladder)
|
|
float tmpDist = DotProduct(tmp, lateral);
|
|
float perpDist = DotProduct(perp, lateral);
|
|
|
|
Vector angleVec = perp * perpDist;
|
|
angleVec += cross;
|
|
// angleVec is our desired movement in the ladder normal/perpendicular plane
|
|
VectorNormalize(angleVec);
|
|
float angleDot = DotProduct(angleVec, pm.plane.normal);
|
|
// angleDot is our angle of incidence to the laddernormal in the ladder normal/perpendicular plane
|
|
|
|
if (angleDot < sv_ladder_angle.GetFloat())
|
|
lateral = (tmp * tmpDist) + (perp * sv_ladder_dampen.GetFloat() * perpDist);
|
|
#endif // CSTRIKE_DLL
|
|
//=============================================================================
|
|
// HPE_END
|
|
//=============================================================================
|
|
|
|
VectorMA(lateral, -normal, tmp, mv->m_vecVelocity);
|
|
|
|
if (onFloor && normal > 0) // On ground moving away from the ladder
|
|
{
|
|
VectorMA(mv->m_vecVelocity, MAX_CLIMB_SPEED, pm.plane.normal, mv->m_vecVelocity);
|
|
}
|
|
//pev->velocity = lateral - (CrossProduct( trace.vecPlaneNormal, perp ) * normal);
|
|
}
|
|
else
|
|
{
|
|
mv->m_vecVelocity.Init();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : axis -
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
|
|
const char *DescribeAxis(int axis)
|
|
{
|
|
static char sz[32];
|
|
|
|
switch (axis)
|
|
{
|
|
case 0:
|
|
Q_strncpy(sz, "X", sizeof(sz));
|
|
break;
|
|
case 1:
|
|
Q_strncpy(sz, "Y", sizeof(sz));
|
|
break;
|
|
case 2:
|
|
default:
|
|
Q_strncpy(sz, "Z", sizeof(sz));
|
|
break;
|
|
}
|
|
|
|
return sz;
|
|
}
|
|
#else
|
|
const char *DescribeAxis(int axis);
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::CheckVelocity(void)
|
|
{
|
|
int i;
|
|
|
|
//
|
|
// bound velocity
|
|
//
|
|
|
|
Vector org = mv->GetAbsOrigin();
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
// See if it's bogus.
|
|
if (IS_NAN(mv->m_vecVelocity[i]))
|
|
{
|
|
DevMsg(1, "PM Got a NaN velocity %s\n", DescribeAxis(i));
|
|
mv->m_vecVelocity[i] = 0;
|
|
}
|
|
|
|
if (IS_NAN(org[i]))
|
|
{
|
|
DevMsg(1, "PM Got a NaN origin on %s\n", DescribeAxis(i));
|
|
org[i] = 0;
|
|
mv->SetAbsOrigin(org);
|
|
}
|
|
|
|
// Bound it.
|
|
if (mv->m_vecVelocity[i] > sv_maxvelocity.GetFloat())
|
|
{
|
|
DevMsg(1, "PM Got a velocity too high on %s\n", DescribeAxis(i));
|
|
mv->m_vecVelocity[i] = sv_maxvelocity.GetFloat();
|
|
}
|
|
else if (mv->m_vecVelocity[i] < -sv_maxvelocity.GetFloat())
|
|
{
|
|
DevMsg(1, "PM Got a velocity too low on %s\n", DescribeAxis(i));
|
|
mv->m_vecVelocity[i] = -sv_maxvelocity.GetFloat();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::AddGravity(void)
|
|
{
|
|
float ent_gravity;
|
|
|
|
if (player->m_flWaterJumpTime)
|
|
return;
|
|
|
|
if (player->GetGravity())
|
|
ent_gravity = player->GetGravity();
|
|
else
|
|
ent_gravity = 1.0;
|
|
|
|
// Add gravity incorrectly
|
|
mv->m_vecVelocity[2] -= (ent_gravity * GetCurrentGravity() * gpGlobals->frametime);
|
|
mv->m_vecVelocity[2] += player->GetBaseVelocity()[2] * gpGlobals->frametime;
|
|
Vector temp = player->GetBaseVelocity();
|
|
temp[2] = 0;
|
|
player->SetBaseVelocity(temp);
|
|
|
|
CheckVelocity();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : push -
|
|
// Output : trace_t
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::PushEntity(Vector& push, trace_t *pTrace)
|
|
{
|
|
Vector end;
|
|
|
|
VectorAdd(mv->GetAbsOrigin(), push, end);
|
|
TracePlayerBBox(mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, *pTrace);
|
|
mv->SetAbsOrigin(pTrace->endpos);
|
|
|
|
// So we can run impact function afterwards.
|
|
// If
|
|
if (pTrace->fraction < 1.0 && !pTrace->allsolid)
|
|
{
|
|
MoveHelper()->AddToTouched(*pTrace, mv->m_vecVelocity);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : in -
|
|
// normal -
|
|
// out -
|
|
// overbounce -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CGameMovement::ClipVelocity(Vector& in, Vector& normal, Vector& out, float overbounce)
|
|
{
|
|
float backoff;
|
|
float change;
|
|
float angle;
|
|
int i, blocked;
|
|
|
|
angle = normal[2];
|
|
|
|
blocked = 0x00; // Assume unblocked.
|
|
if (angle > 0) // If the plane that is blocking us has a positive z component, then assume it's a floor.
|
|
blocked |= 0x01; //
|
|
if (!angle) // If the plane has no Z, it is vertical (wall/step)
|
|
blocked |= 0x02; //
|
|
|
|
|
|
// Determine how far along plane to slide based on incoming direction.
|
|
backoff = DotProduct(in, normal) * overbounce;
|
|
|
|
for (i = 0; i<3; i++)
|
|
{
|
|
change = normal[i] * backoff;
|
|
out[i] = in[i] - change;
|
|
}
|
|
|
|
// iterate once to make sure we aren't still moving through the plane
|
|
float adjust = DotProduct(out, normal);
|
|
if (adjust < 0.0f)
|
|
{
|
|
out -= (normal * adjust);
|
|
// Msg( "Adjustment = %lf\n", adjust );
|
|
}
|
|
|
|
// Return blocking flags.
|
|
return blocked;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Computes roll angle for a certain movement direction and velocity
|
|
// Input : angles -
|
|
// velocity -
|
|
// rollangle -
|
|
// rollspeed -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float CGameMovement::CalcRoll(const QAngle &angles, const Vector &velocity, float rollangle, float rollspeed)
|
|
{
|
|
float sign;
|
|
float side;
|
|
float value;
|
|
Vector forward, right, up;
|
|
|
|
AngleVectors(angles, &forward, &right, &up);
|
|
|
|
side = DotProduct(velocity, right);
|
|
sign = side < 0 ? -1 : 1;
|
|
side = fabs(side);
|
|
value = rollangle;
|
|
if (side < rollspeed)
|
|
{
|
|
side = side * value / rollspeed;
|
|
}
|
|
else
|
|
{
|
|
side = value;
|
|
}
|
|
return side*sign;
|
|
}
|
|
|
|
#define CHECKSTUCK_MINTIME 0.05 // Don't check again too quickly.
|
|
|
|
#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
|
|
Vector rgv3tStuckTable[54];
|
|
#else
|
|
extern Vector rgv3tStuckTable[54];
|
|
#endif
|
|
|
|
#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CreateStuckTable(void)
|
|
{
|
|
float x, y, z;
|
|
int idx;
|
|
int i;
|
|
float zi[3];
|
|
static int firsttime = 1;
|
|
|
|
if (!firsttime)
|
|
return;
|
|
|
|
firsttime = 0;
|
|
|
|
memset(rgv3tStuckTable, 0, sizeof(rgv3tStuckTable));
|
|
|
|
idx = 0;
|
|
// Little Moves.
|
|
x = y = 0;
|
|
// Z moves
|
|
for (z = -0.125; z <= 0.125; z += 0.125)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
x = z = 0;
|
|
// Y moves
|
|
for (y = -0.125; y <= 0.125; y += 0.125)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
y = z = 0;
|
|
// X moves
|
|
for (x = -0.125; x <= 0.125; x += 0.125)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
|
|
// Remaining multi axis nudges.
|
|
for (x = -0.125; x <= 0.125; x += 0.250)
|
|
{
|
|
for (y = -0.125; y <= 0.125; y += 0.250)
|
|
{
|
|
for (z = -0.125; z <= 0.125; z += 0.250)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Big Moves.
|
|
x = y = 0;
|
|
zi[0] = 0.0f;
|
|
zi[1] = 1.0f;
|
|
zi[2] = 6.0f;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
// Z moves
|
|
z = zi[i];
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
|
|
x = z = 0;
|
|
|
|
// Y moves
|
|
for (y = -2.0f; y <= 2.0f; y += 2.0)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
y = z = 0;
|
|
// X moves
|
|
for (x = -2.0f; x <= 2.0f; x += 2.0f)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
|
|
// Remaining multi axis nudges.
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
z = zi[i];
|
|
|
|
for (x = -2.0f; x <= 2.0f; x += 2.0f)
|
|
{
|
|
for (y = -2.0f; y <= 2.0f; y += 2.0)
|
|
{
|
|
rgv3tStuckTable[idx][0] = x;
|
|
rgv3tStuckTable[idx][1] = y;
|
|
rgv3tStuckTable[idx][2] = z;
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
Assert(idx < sizeof(rgv3tStuckTable) / sizeof(rgv3tStuckTable[0]));
|
|
}
|
|
#else
|
|
extern void CreateStuckTable(void);
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : nIndex -
|
|
// server -
|
|
// offset -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int GetRandomStuckOffsets(CBasePlayer *pPlayer, Vector& offset)
|
|
{
|
|
// Last time we did a full
|
|
int idx;
|
|
idx = pPlayer->m_StuckLast++;
|
|
|
|
VectorCopy(rgv3tStuckTable[idx % 54], offset);
|
|
|
|
return (idx % 54);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : nIndex -
|
|
// server -
|
|
//-----------------------------------------------------------------------------
|
|
void ResetStuckOffsets(CBasePlayer *pPlayer)
|
|
{
|
|
pPlayer->m_StuckLast = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &input -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CGameMovement::CheckStuck(void)
|
|
{
|
|
Vector base;
|
|
Vector offset;
|
|
Vector test;
|
|
EntityHandle_t hitent;
|
|
int idx;
|
|
float fTime;
|
|
trace_t traceresult;
|
|
|
|
CreateStuckTable();
|
|
|
|
hitent = TestPlayerPosition(mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, traceresult);
|
|
if (hitent == INVALID_ENTITY_HANDLE)
|
|
{
|
|
ResetStuckOffsets(player);
|
|
return 0;
|
|
}
|
|
|
|
// Deal with stuckness...
|
|
#ifndef DEDICATED
|
|
if (developer.GetBool())
|
|
{
|
|
bool isServer = player->IsServer();
|
|
engine->Con_NPrintf(isServer, "%s stuck on object %i/%s",
|
|
isServer ? "server" : "client",
|
|
hitent.GetEntryIndex(), MoveHelper()->GetName(hitent));
|
|
}
|
|
#endif
|
|
|
|
VectorCopy(mv->GetAbsOrigin(), base);
|
|
|
|
//
|
|
// Deal with precision error in network.
|
|
//
|
|
// World or BSP model
|
|
if (!player->IsServer())
|
|
{
|
|
if (MoveHelper()->IsWorldEntity(hitent))
|
|
{
|
|
int nReps = 0;
|
|
ResetStuckOffsets(player);
|
|
do
|
|
{
|
|
GetRandomStuckOffsets(player, offset);
|
|
VectorAdd(base, offset, test);
|
|
|
|
if (TestPlayerPosition(test, COLLISION_GROUP_PLAYER_MOVEMENT, traceresult) == INVALID_ENTITY_HANDLE)
|
|
{
|
|
ResetStuckOffsets(player);
|
|
mv->SetAbsOrigin(test);
|
|
return 0;
|
|
}
|
|
nReps++;
|
|
} while (nReps < 54);
|
|
}
|
|
}
|
|
|
|
// Only an issue on the client.
|
|
idx = player->IsServer() ? 0 : 1;
|
|
|
|
fTime = engine->Time();
|
|
// Too soon?
|
|
if (m_flStuckCheckTime[player->entindex()][idx] >= fTime - CHECKSTUCK_MINTIME)
|
|
{
|
|
return 1;
|
|
}
|
|
m_flStuckCheckTime[player->entindex()][idx] = fTime;
|
|
|
|
MoveHelper()->AddToTouched(traceresult, mv->m_vecVelocity);
|
|
GetRandomStuckOffsets(player, offset);
|
|
VectorAdd(base, offset, test);
|
|
|
|
if (TestPlayerPosition(test, COLLISION_GROUP_PLAYER_MOVEMENT, traceresult) == INVALID_ENTITY_HANDLE)
|
|
{
|
|
ResetStuckOffsets(player);
|
|
mv->SetAbsOrigin(test);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : bool
|
|
//-----------------------------------------------------------------------------
|
|
bool CGameMovement::InWater(void)
|
|
{
|
|
return (player->GetWaterLevel() > WL_Feet);
|
|
}
|
|
|
|
|
|
void CGameMovement::ResetGetPointContentsCache()
|
|
{
|
|
for (int slot = 0; slot < MAX_PC_CACHE_SLOTS; ++slot)
|
|
{
|
|
for (int i = 0; i < MAX_PLAYERS; ++i)
|
|
{
|
|
m_CachedGetPointContents[i][slot] = -9999;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int CGameMovement::GetPointContentsCached(const Vector &point, int slot)
|
|
{
|
|
if (g_bMovementOptimizations)
|
|
{
|
|
Assert(player);
|
|
Assert(slot >= 0 && slot < MAX_PC_CACHE_SLOTS);
|
|
|
|
int idx = player->entindex() - 1;
|
|
|
|
if (m_CachedGetPointContents[idx][slot] == -9999 || point.DistToSqr(m_CachedGetPointContentsPoint[idx][slot]) > 1)
|
|
{
|
|
m_CachedGetPointContents[idx][slot] = enginetrace->GetPointContents(point);
|
|
m_CachedGetPointContentsPoint[idx][slot] = point;
|
|
}
|
|
|
|
return m_CachedGetPointContents[idx][slot];
|
|
}
|
|
else
|
|
{
|
|
return enginetrace->GetPointContents(point);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &input -
|
|
// Output : bool
|
|
//-----------------------------------------------------------------------------
|
|
bool CGameMovement::CheckWater(void)
|
|
{
|
|
Vector point;
|
|
int cont;
|
|
|
|
Vector vPlayerMins = GetPlayerMins();
|
|
Vector vPlayerMaxs = GetPlayerMaxs();
|
|
|
|
// Pick a spot just above the players feet.
|
|
point[0] = mv->GetAbsOrigin()[0] + (vPlayerMins[0] + vPlayerMaxs[0]) * 0.5;
|
|
point[1] = mv->GetAbsOrigin()[1] + (vPlayerMins[1] + vPlayerMaxs[1]) * 0.5;
|
|
point[2] = mv->GetAbsOrigin()[2] + vPlayerMins[2] + 1;
|
|
|
|
// Assume that we are not in water at all.
|
|
player->SetWaterLevel(WL_NotInWater);
|
|
player->SetWaterType(CONTENTS_EMPTY);
|
|
|
|
// Grab point contents.
|
|
cont = GetPointContentsCached(point, 0);
|
|
|
|
// Are we under water? (not solid and not empty?)
|
|
if (cont & MASK_WATER)
|
|
{
|
|
// Set water type
|
|
player->SetWaterType(cont);
|
|
|
|
// We are at least at level one
|
|
player->SetWaterLevel(WL_Feet);
|
|
|
|
// Now check a point that is at the player hull midpoint.
|
|
point[2] = mv->GetAbsOrigin()[2] + (vPlayerMins[2] + vPlayerMaxs[2])*0.5;
|
|
cont = GetPointContentsCached(point, 1);
|
|
// If that point is also under water...
|
|
if (cont & MASK_WATER)
|
|
{
|
|
// Set a higher water level.
|
|
player->SetWaterLevel(WL_Waist);
|
|
|
|
// Now check the eye position. (view_ofs is relative to the origin)
|
|
point[2] = mv->GetAbsOrigin()[2] + player->GetViewOffset()[2];
|
|
cont = GetPointContentsCached(point, 2);
|
|
if (cont & MASK_WATER)
|
|
player->SetWaterLevel(WL_Eyes); // In over our eyes
|
|
}
|
|
|
|
// Adjust velocity based on water current, if any.
|
|
if (cont & MASK_CURRENT)
|
|
{
|
|
Vector v;
|
|
VectorClear(v);
|
|
if (cont & CONTENTS_CURRENT_0)
|
|
v[0] += 1;
|
|
if (cont & CONTENTS_CURRENT_90)
|
|
v[1] += 1;
|
|
if (cont & CONTENTS_CURRENT_180)
|
|
v[0] -= 1;
|
|
if (cont & CONTENTS_CURRENT_270)
|
|
v[1] -= 1;
|
|
if (cont & CONTENTS_CURRENT_UP)
|
|
v[2] += 1;
|
|
if (cont & CONTENTS_CURRENT_DOWN)
|
|
v[2] -= 1;
|
|
|
|
// BUGBUG -- this depends on the value of an unspecified enumerated type
|
|
// The deeper we are, the stronger the current.
|
|
Vector temp;
|
|
VectorMA(player->GetBaseVelocity(), 50.0*player->GetWaterLevel(), v, temp);
|
|
player->SetBaseVelocity(temp);
|
|
}
|
|
}
|
|
|
|
// if we just transitioned from not in water to in water, record the time it happened
|
|
if ((WL_NotInWater == m_nOldWaterLevel) && (player->GetWaterLevel() > WL_NotInWater))
|
|
{
|
|
m_flWaterEntryTime = gpGlobals->curtime;
|
|
}
|
|
|
|
return (player->GetWaterLevel() > WL_Feet);
|
|
}
|
|
|
|
void CGameMovement::SetGroundEntity(trace_t *pm)
|
|
{
|
|
CBaseEntity *newGround = pm ? pm->m_pEnt : NULL;
|
|
|
|
CBaseEntity *oldGround = player->GetGroundEntity();
|
|
Vector vecBaseVelocity = player->GetBaseVelocity();
|
|
|
|
if (!oldGround && newGround)
|
|
{
|
|
// Subtract ground velocity at instant we hit ground jumping
|
|
vecBaseVelocity -= newGround->GetAbsVelocity();
|
|
vecBaseVelocity.z = newGround->GetAbsVelocity().z;
|
|
}
|
|
else if (oldGround && !newGround)
|
|
{
|
|
// Add in ground velocity at instant we started jumping
|
|
vecBaseVelocity += oldGround->GetAbsVelocity();
|
|
vecBaseVelocity.z = oldGround->GetAbsVelocity().z;
|
|
}
|
|
|
|
player->SetBaseVelocity(vecBaseVelocity);
|
|
player->SetGroundEntity(newGround);
|
|
|
|
// If we are on something...
|
|
|
|
if (newGround)
|
|
{
|
|
CategorizeGroundSurface(*pm);
|
|
|
|
// Then we are not in water jump sequence
|
|
player->m_flWaterJumpTime = 0;
|
|
|
|
// Standing on an entity other than the world, so signal that we are touching something.
|
|
if (!pm->DidHitWorld())
|
|
{
|
|
MoveHelper()->AddToTouched(*pm, mv->m_vecVelocity);
|
|
}
|
|
|
|
mv->m_vecVelocity.z = 0.0f;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Traces the player's collision bounds in quadrants, looking for a plane that
|
|
// can be stood upon (normal's z >= 0.7f). Regardless of success or failure,
|
|
// replace the fraction and endpos with the original ones, so we don't try to
|
|
// move the player down to the new floor and get stuck on a leaning wall that
|
|
// the original trace hit first.
|
|
//-----------------------------------------------------------------------------
|
|
void TracePlayerBBoxForGround(const Vector& start, const Vector& end, const Vector& minsSrc,
|
|
const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask,
|
|
int collisionGroup, trace_t& pm)
|
|
{
|
|
VPROF("TracePlayerBBoxForGround");
|
|
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Traces the player's collision bounds in quadrants, looking for a plane that
|
|
// can be stood upon (normal's z >= 0.7f). Regardless of success or failure,
|
|
// replace the fraction and endpos with the original ones, so we don't try to
|
|
// move the player down to the new floor and get stuck on a leaning wall that
|
|
// the original trace hit first.
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::TryTouchGroundInQuadrants(const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm)
|
|
{
|
|
VPROF("CGameMovement::TryTouchGroundInQuadrants");
|
|
|
|
Vector mins, maxs;
|
|
Vector minsSrc = GetPlayerMins();
|
|
Vector maxsSrc = GetPlayerMaxs();
|
|
|
|
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);
|
|
TryTouchGround(start, end, mins, maxs, fMask, 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;
|
|
TryTouchGround(start, end, mins, maxs, fMask, 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);
|
|
TryTouchGround(start, end, mins, maxs, fMask, 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);
|
|
TryTouchGround(start, end, mins, maxs, fMask, 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 CGameMovement::CategorizePosition(void)
|
|
{
|
|
Vector point;
|
|
trace_t pm;
|
|
|
|
// Reset this each time we-recategorize, otherwise we have bogus friction when we jump into water and plunge downward really quickly
|
|
player->m_surfaceFriction = 1.0f;
|
|
|
|
// 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;
|
|
|
|
float flOffset = 2.0f;
|
|
|
|
point[0] = mv->GetAbsOrigin()[0];
|
|
point[1] = mv->GetAbsOrigin()[1];
|
|
point[2] = mv->GetAbsOrigin()[2] - flOffset;
|
|
|
|
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.
|
|
#define NON_JUMP_VELOCITY 140.0f
|
|
|
|
float zvel = mv->m_vecVelocity[2];
|
|
bool bMovingUp = zvel > 0.0f;
|
|
bool bMovingUpRapidly = zvel > NON_JUMP_VELOCITY;
|
|
float flGroundEntityVelZ = 0.0f;
|
|
if (bMovingUpRapidly)
|
|
{
|
|
// Tracker 73219, 75878: ywb 8/2/07
|
|
// After save/restore (and maybe at other times), we can get a case where we were saved on a lift and
|
|
// after restore we'll have a high local velocity due to the lift making our abs velocity appear high.
|
|
// We need to account for standing on a moving ground object in that case in order to determine if we really
|
|
// are moving away from the object we are standing on at too rapid a speed. Note that CheckJump already sets
|
|
// ground entity to NULL, so this wouldn't have any effect unless we are moving up rapidly not from the jump button.
|
|
CBaseEntity *ground = player->GetGroundEntity();
|
|
if (ground)
|
|
{
|
|
flGroundEntityVelZ = ground->GetAbsVelocity().z;
|
|
bMovingUpRapidly = (zvel - flGroundEntityVelZ) > NON_JUMP_VELOCITY;
|
|
}
|
|
}
|
|
|
|
// Was on ground, but now suddenly am not
|
|
if (bMovingUpRapidly ||
|
|
(bMovingUp && player->GetMoveType() == MOVETYPE_LADDER))
|
|
{
|
|
SetGroundEntity(NULL);
|
|
}
|
|
else
|
|
{
|
|
// Try and move down.
|
|
TryTouchGround(bumpOrigin, point, GetPlayerMins(), GetPlayerMaxs(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
|
|
// Was on ground, but now suddenly am not. If we hit a steep plane, we are not on ground
|
|
if (!pm.m_pEnt || 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
|
|
TryTouchGroundInQuadrants(bumpOrigin, point, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm);
|
|
|
|
if (!pm.m_pEnt || pm.plane.normal[2] < 0.7)
|
|
{
|
|
SetGroundEntity(NULL);
|
|
// 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);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetGroundEntity(&pm); // Otherwise, point to index of ent under us.
|
|
}
|
|
|
|
#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
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determine if the player has hit the ground while falling, apply
|
|
// damage, and play the appropriate impact sound.
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::CheckFalling(void)
|
|
{
|
|
// this function really deals with landing, not falling, so early out otherwise
|
|
if (player->GetGroundEntity() == NULL || player->m_Local.m_flFallVelocity <= 0)
|
|
return;
|
|
|
|
if (!IsDead() && player->m_Local.m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHOLD)
|
|
{
|
|
bool bAlive = true;
|
|
float fvol = 0.5;
|
|
|
|
if (player->GetWaterLevel() > 0)
|
|
{
|
|
// They landed in water.
|
|
}
|
|
else
|
|
{
|
|
// Scale it down if we landed on something that's floating...
|
|
if (player->GetGroundEntity()->IsFloating())
|
|
{
|
|
player->m_Local.m_flFallVelocity -= PLAYER_LAND_ON_FLOATING_OBJECT;
|
|
}
|
|
|
|
//
|
|
// They hit the ground.
|
|
//
|
|
if (player->GetGroundEntity()->GetAbsVelocity().z < 0.0f)
|
|
{
|
|
// Player landed on a descending object. Subtract the velocity of the ground entity.
|
|
player->m_Local.m_flFallVelocity += player->GetGroundEntity()->GetAbsVelocity().z;
|
|
player->m_Local.m_flFallVelocity = MAX(0.1f, player->m_Local.m_flFallVelocity);
|
|
}
|
|
|
|
if (player->m_Local.m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED)
|
|
{
|
|
//
|
|
// If they hit the ground going this fast they may take damage (and die).
|
|
//
|
|
bAlive = MoveHelper()->PlayerFallingDamage();
|
|
fvol = 1.0;
|
|
}
|
|
else if (player->m_Local.m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED / 2)
|
|
{
|
|
fvol = 0.85;
|
|
}
|
|
else if (player->m_Local.m_flFallVelocity < PLAYER_MIN_BOUNCE_SPEED)
|
|
{
|
|
fvol = 0;
|
|
}
|
|
}
|
|
|
|
PlayerRoughLandingEffects(fvol);
|
|
|
|
if (bAlive)
|
|
{
|
|
MoveHelper()->PlayerSetAnimation(PLAYER_WALK);
|
|
}
|
|
}
|
|
|
|
// let any subclasses know that the player has landed and how hard
|
|
OnLand(player->m_Local.m_flFallVelocity);
|
|
|
|
//
|
|
// Clear the fall velocity so the impact doesn't happen again.
|
|
//
|
|
player->m_Local.m_flFallVelocity = 0;
|
|
}
|
|
|
|
void CGameMovement::PlayerRoughLandingEffects(float fvol)
|
|
{
|
|
if (fvol > 0.0)
|
|
{
|
|
//
|
|
// Play landing sound right away.
|
|
player->m_flStepSoundTime = 400;
|
|
|
|
// Play step sound for current texture.
|
|
player->PlayStepSound((Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, fvol, true);
|
|
|
|
//
|
|
// Knock the screen around a little bit, temporary effect.
|
|
//
|
|
player->m_Local.m_vecPunchAngle.Set(ROLL, player->m_Local.m_flFallVelocity * 0.013);
|
|
|
|
if (player->m_Local.m_vecPunchAngle[PITCH] > 8)
|
|
{
|
|
player->m_Local.m_vecPunchAngle.Set(PITCH, 8);
|
|
}
|
|
|
|
#if !defined( CLIENT_DLL )
|
|
player->RumbleEffect((fvol > 0.85f) ? (RUMBLE_FALL_LONG) : (RUMBLE_FALL_SHORT), 0, RUMBLE_FLAGS_NONE);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Use for ease-in, ease-out style interpolation (accel/decel) Used by ducking code.
|
|
// Input : value -
|
|
// scale -
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float CGameMovement::SplineFraction(float value, float scale)
|
|
{
|
|
float valueSquared;
|
|
|
|
value = scale * value;
|
|
valueSquared = value * value;
|
|
|
|
// Nice little ease-in, ease-out spline-like curve
|
|
return 3 * valueSquared - 2 * valueSquared * value;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Determine if crouch/uncrouch caused player to get stuck in world
|
|
// Input : direction -
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FixPlayerCrouchStuck(bool upward)
|
|
{
|
|
EntityHandle_t hitent;
|
|
int i;
|
|
Vector test;
|
|
trace_t dummy;
|
|
|
|
int direction = upward ? 1 : 0;
|
|
|
|
hitent = TestPlayerPosition(mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy);
|
|
if (hitent == INVALID_ENTITY_HANDLE)
|
|
return;
|
|
|
|
VectorCopy(mv->GetAbsOrigin(), test);
|
|
for (i = 0; i < 36; i++)
|
|
{
|
|
Vector org = mv->GetAbsOrigin();
|
|
org.z += direction;
|
|
mv->SetAbsOrigin(org);
|
|
hitent = TestPlayerPosition(mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy);
|
|
if (hitent == INVALID_ENTITY_HANDLE)
|
|
return;
|
|
}
|
|
|
|
mv->SetAbsOrigin(test); // Failed
|
|
}
|
|
|
|
bool CGameMovement::CanUnduck()
|
|
{
|
|
int i;
|
|
trace_t trace;
|
|
Vector newOrigin;
|
|
|
|
VectorCopy(mv->GetAbsOrigin(), newOrigin);
|
|
|
|
if (player->GetGroundEntity() != NULL)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
newOrigin[i] += (VEC_DUCK_HULL_MIN_SCALED(player)[i] - VEC_HULL_MIN_SCALED(player)[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If in air an letting go of crouch, make sure we can offset origin to make
|
|
// up for uncrouching
|
|
Vector hullSizeNormal = VEC_HULL_MAX_SCALED(player) - VEC_HULL_MIN_SCALED(player);
|
|
Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED(player) - VEC_DUCK_HULL_MIN_SCALED(player);
|
|
Vector viewDelta = (hullSizeNormal - hullSizeCrouch);
|
|
viewDelta.Negate();
|
|
VectorAdd(newOrigin, viewDelta, newOrigin);
|
|
}
|
|
|
|
bool saveducked = player->m_Local.m_bDucked;
|
|
player->m_Local.m_bDucked = false;
|
|
TracePlayerBBox(mv->GetAbsOrigin(), newOrigin, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace);
|
|
player->m_Local.m_bDucked = saveducked;
|
|
if (trace.startsolid || (trace.fraction != 1.0f))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Stop ducking
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FinishUnDuck(void)
|
|
{
|
|
int i;
|
|
trace_t trace;
|
|
Vector newOrigin;
|
|
|
|
VectorCopy(mv->GetAbsOrigin(), newOrigin);
|
|
|
|
if (player->GetGroundEntity() != NULL)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
newOrigin[i] += (VEC_DUCK_HULL_MIN_SCALED(player)[i] - VEC_HULL_MIN_SCALED(player)[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If in air an letting go of crouch, make sure we can offset origin to make
|
|
// up for uncrouching
|
|
Vector hullSizeNormal = VEC_HULL_MAX_SCALED(player) - VEC_HULL_MIN_SCALED(player);
|
|
Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED(player) - VEC_DUCK_HULL_MIN_SCALED(player);
|
|
Vector viewDelta = (hullSizeNormal - hullSizeCrouch);
|
|
viewDelta.Negate();
|
|
VectorAdd(newOrigin, viewDelta, newOrigin);
|
|
}
|
|
|
|
player->m_Local.m_bDucked = false;
|
|
player->RemoveFlag(FL_DUCKING);
|
|
player->m_Local.m_bDucking = false;
|
|
player->m_Local.m_bInDuckJump = false;
|
|
player->SetViewOffset(GetPlayerViewOffset(false));
|
|
player->m_Local.m_flDucktime = 0;
|
|
|
|
mv->SetAbsOrigin(newOrigin);
|
|
|
|
#ifdef CLIENT_DLL
|
|
#ifdef STAGING_ONLY
|
|
if (debug_latch_reset_onduck.GetBool())
|
|
{
|
|
player->ResetLatched();
|
|
}
|
|
#else
|
|
player->ResetLatched();
|
|
#endif
|
|
#endif // CLIENT_DLL
|
|
|
|
// Recategorize position since ducking can change origin
|
|
CategorizePosition();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::UpdateDuckJumpEyeOffset(void)
|
|
{
|
|
if (player->m_Local.m_flDuckJumpTime != 0.0f)
|
|
{
|
|
float flDuckMilliseconds = MAX(0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDuckJumpTime);
|
|
float flDuckSeconds = flDuckMilliseconds / GAMEMOVEMENT_DUCK_TIME;
|
|
if (flDuckSeconds > TIME_TO_UNDUCK)
|
|
{
|
|
player->m_Local.m_flDuckJumpTime = 0.0f;
|
|
SetDuckedEyeOffset(0.0f);
|
|
}
|
|
else
|
|
{
|
|
float flDuckFraction = SimpleSpline(1.0f - (flDuckSeconds / TIME_TO_UNDUCK));
|
|
SetDuckedEyeOffset(flDuckFraction);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FinishUnDuckJump(trace_t &trace)
|
|
{
|
|
Vector vecNewOrigin;
|
|
VectorCopy(mv->GetAbsOrigin(), vecNewOrigin);
|
|
|
|
// Up for uncrouching.
|
|
Vector hullSizeNormal = VEC_HULL_MAX_SCALED(player) - VEC_HULL_MIN_SCALED(player);
|
|
Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED(player) - VEC_DUCK_HULL_MIN_SCALED(player);
|
|
Vector viewDelta = (hullSizeNormal - hullSizeCrouch);
|
|
|
|
float flDeltaZ = viewDelta.z;
|
|
viewDelta.z *= trace.fraction;
|
|
flDeltaZ -= viewDelta.z;
|
|
|
|
player->RemoveFlag(FL_DUCKING);
|
|
player->m_Local.m_bDucked = false;
|
|
player->m_Local.m_bDucking = false;
|
|
player->m_Local.m_bInDuckJump = false;
|
|
player->m_Local.m_flDucktime = 0.0f;
|
|
player->m_Local.m_flDuckJumpTime = 0.0f;
|
|
player->m_Local.m_flJumpTime = 0.0f;
|
|
|
|
Vector vecViewOffset = GetPlayerViewOffset(false);
|
|
vecViewOffset.z -= flDeltaZ;
|
|
player->SetViewOffset(vecViewOffset);
|
|
|
|
VectorSubtract(vecNewOrigin, viewDelta, vecNewOrigin);
|
|
mv->SetAbsOrigin(vecNewOrigin);
|
|
|
|
// Recategorize position since ducking can change origin
|
|
CategorizePosition();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Finish ducking
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FinishDuck(void)
|
|
{
|
|
if (player->GetFlags() & FL_DUCKING)
|
|
return;
|
|
|
|
player->AddFlag(FL_DUCKING);
|
|
player->m_Local.m_bDucked = true;
|
|
player->m_Local.m_bDucking = false;
|
|
|
|
player->SetViewOffset(GetPlayerViewOffset(true));
|
|
|
|
// HACKHACK - Fudge for collision bug - no time to fix this properly
|
|
if (player->GetGroundEntity() != NULL)
|
|
{
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
Vector org = mv->GetAbsOrigin();
|
|
org[i] -= (VEC_DUCK_HULL_MIN_SCALED(player)[i] - VEC_HULL_MIN_SCALED(player)[i]);
|
|
mv->SetAbsOrigin(org);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Vector hullSizeNormal = VEC_HULL_MAX_SCALED(player) - VEC_HULL_MIN_SCALED(player);
|
|
Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED(player) - VEC_DUCK_HULL_MIN_SCALED(player);
|
|
Vector viewDelta = (hullSizeNormal - hullSizeCrouch);
|
|
Vector out;
|
|
VectorAdd(mv->GetAbsOrigin(), viewDelta, out);
|
|
mv->SetAbsOrigin(out);
|
|
|
|
#ifdef CLIENT_DLL
|
|
#ifdef STAGING_ONLY
|
|
if (debug_latch_reset_onduck.GetBool())
|
|
{
|
|
player->ResetLatched();
|
|
}
|
|
#else
|
|
player->ResetLatched();
|
|
#endif
|
|
#endif // CLIENT_DLL
|
|
}
|
|
|
|
// See if we are stuck?
|
|
FixPlayerCrouchStuck(true);
|
|
|
|
// Recategorize position since ducking can change origin
|
|
CategorizePosition();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::StartUnDuckJump(void)
|
|
{
|
|
player->AddFlag(FL_DUCKING);
|
|
player->m_Local.m_bDucked = true;
|
|
player->m_Local.m_bDucking = false;
|
|
|
|
player->SetViewOffset(GetPlayerViewOffset(true));
|
|
|
|
Vector hullSizeNormal = VEC_HULL_MAX_SCALED(player) - VEC_HULL_MIN_SCALED(player);
|
|
Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED(player) - VEC_DUCK_HULL_MIN_SCALED(player);
|
|
Vector viewDelta = (hullSizeNormal - hullSizeCrouch);
|
|
Vector out;
|
|
VectorAdd(mv->GetAbsOrigin(), viewDelta, out);
|
|
mv->SetAbsOrigin(out);
|
|
|
|
// See if we are stuck?
|
|
FixPlayerCrouchStuck(true);
|
|
|
|
// Recategorize position since ducking can change origin
|
|
CategorizePosition();
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : duckFraction -
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::SetDuckedEyeOffset(float duckFraction)
|
|
{
|
|
Vector vDuckHullMin = GetPlayerMins(true);
|
|
Vector vStandHullMin = GetPlayerMins(false);
|
|
|
|
float fMore = (vDuckHullMin.z - vStandHullMin.z);
|
|
|
|
Vector vecDuckViewOffset = GetPlayerViewOffset(true);
|
|
Vector vecStandViewOffset = GetPlayerViewOffset(false);
|
|
Vector temp = player->GetViewOffset();
|
|
temp.z = ((vecDuckViewOffset.z - fMore) * duckFraction) +
|
|
(vecStandViewOffset.z * (1 - duckFraction));
|
|
player->SetViewOffset(temp);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Crop the speed of the player when ducking and on the ground.
|
|
// Input: bInDuck - is the player already ducking
|
|
// bInAir - is the player in air
|
|
// NOTE: Only crop player speed once.
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::HandleDuckingSpeedCrop(void)
|
|
{
|
|
if (!(m_iSpeedCropped & SPEED_CROPPED_DUCK) && (player->GetFlags() & FL_DUCKING) && (player->GetGroundEntity() != NULL))
|
|
{
|
|
float frac = 0.33333333f;
|
|
mv->m_flForwardMove *= frac;
|
|
mv->m_flSideMove *= frac;
|
|
mv->m_flUpMove *= frac;
|
|
m_iSpeedCropped |= SPEED_CROPPED_DUCK;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Check to see if we are in a situation where we can unduck jump.
|
|
//-----------------------------------------------------------------------------
|
|
bool CGameMovement::CanUnDuckJump(trace_t &trace)
|
|
{
|
|
// Trace down to the stand position and see if we can stand.
|
|
Vector vecEnd(mv->GetAbsOrigin());
|
|
vecEnd.z -= 36.0f; // This will have to change if bounding hull change!
|
|
TracePlayerBBox(mv->GetAbsOrigin(), vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace);
|
|
if (trace.fraction < 1.0f)
|
|
{
|
|
// Find the endpoint.
|
|
vecEnd.z = mv->GetAbsOrigin().z + (-36.0f * trace.fraction);
|
|
|
|
// Test a normal hull.
|
|
trace_t traceUp;
|
|
bool bWasDucked = player->m_Local.m_bDucked;
|
|
player->m_Local.m_bDucked = false;
|
|
TracePlayerBBox(vecEnd, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceUp);
|
|
player->m_Local.m_bDucked = bWasDucked;
|
|
if (!traceUp.startsolid)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: See if duck button is pressed and do the appropriate things
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::Duck(void)
|
|
{
|
|
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"
|
|
|
|
// Check to see if we are in the air.
|
|
bool bInAir = (player->GetGroundEntity() == NULL);
|
|
bool bInDuck = (player->GetFlags() & FL_DUCKING) ? true : false;
|
|
bool bDuckJump = (player->m_Local.m_flJumpTime > 0.0f);
|
|
bool bDuckJumpTime = (player->m_Local.m_flDuckJumpTime > 0.0f);
|
|
|
|
if (mv->m_nButtons & IN_DUCK)
|
|
{
|
|
mv->m_nOldButtons |= IN_DUCK;
|
|
}
|
|
else
|
|
{
|
|
mv->m_nOldButtons &= ~IN_DUCK;
|
|
}
|
|
|
|
// Handle death.
|
|
if (IsDead())
|
|
return;
|
|
|
|
// Slow down ducked players.
|
|
HandleDuckingSpeedCrop();
|
|
|
|
// If the player is holding down the duck button, the player is in duck transition, ducking, or duck-jumping.
|
|
if ((mv->m_nButtons & IN_DUCK) || player->m_Local.m_bDucking || bInDuck || bDuckJump)
|
|
{
|
|
// DUCK
|
|
if ((mv->m_nButtons & IN_DUCK) || bDuckJump)
|
|
{
|
|
// XBOX SERVER ONLY
|
|
#if !defined(CLIENT_DLL)
|
|
if (IsX360() && buttonsPressed & IN_DUCK)
|
|
{
|
|
// Hinting logic
|
|
if (player->GetToggledDuckState() && player->m_nNumCrouches < NUM_CROUCH_HINTS)
|
|
{
|
|
UTIL_HudHintText(player, "#Valve_Hint_Crouch");
|
|
player->m_nNumCrouches++;
|
|
}
|
|
}
|
|
#endif
|
|
// Have the duck button pressed, but the player currently isn't in the duck position.
|
|
if ((buttonsPressed & IN_DUCK) && !bInDuck && !bDuckJump && !bDuckJumpTime)
|
|
{
|
|
player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME;
|
|
player->m_Local.m_bDucking = true;
|
|
}
|
|
|
|
// The player is in duck transition and not duck-jumping.
|
|
if (player->m_Local.m_bDucking && !bDuckJump && !bDuckJumpTime)
|
|
{
|
|
float flDuckMilliseconds = MAX(0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDucktime);
|
|
float flDuckSeconds = flDuckMilliseconds * 0.001f;
|
|
|
|
// Finish in duck transition when transition time is over, in "duck", in air.
|
|
if ((flDuckSeconds > TIME_TO_DUCK) || bInDuck || bInAir)
|
|
{
|
|
FinishDuck();
|
|
}
|
|
else
|
|
{
|
|
// Calc parametric time
|
|
float flDuckFraction = SimpleSpline(flDuckSeconds / TIME_TO_DUCK);
|
|
SetDuckedEyeOffset(flDuckFraction);
|
|
}
|
|
}
|
|
|
|
if (bDuckJump)
|
|
{
|
|
// Make the bounding box small immediately.
|
|
if (!bInDuck)
|
|
{
|
|
StartUnDuckJump();
|
|
}
|
|
else
|
|
{
|
|
// Check for a crouch override.
|
|
if (!(mv->m_nButtons & IN_DUCK))
|
|
{
|
|
trace_t trace;
|
|
if (CanUnDuckJump(trace))
|
|
{
|
|
FinishUnDuckJump(trace);
|
|
player->m_Local.m_flDuckJumpTime = (GAMEMOVEMENT_TIME_TO_UNDUCK * (1.0f - trace.fraction)) + GAMEMOVEMENT_TIME_TO_UNDUCK_INV;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// UNDUCK (or attempt to...)
|
|
else
|
|
{
|
|
if (player->m_Local.m_bInDuckJump)
|
|
{
|
|
// Check for a crouch override.
|
|
if (!(mv->m_nButtons & IN_DUCK))
|
|
{
|
|
trace_t trace;
|
|
if (CanUnDuckJump(trace))
|
|
{
|
|
FinishUnDuckJump(trace);
|
|
|
|
if (trace.fraction < 1.0f)
|
|
{
|
|
player->m_Local.m_flDuckJumpTime = (GAMEMOVEMENT_TIME_TO_UNDUCK * (1.0f - trace.fraction)) + GAMEMOVEMENT_TIME_TO_UNDUCK_INV;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
player->m_Local.m_bInDuckJump = false;
|
|
}
|
|
}
|
|
|
|
if (bDuckJumpTime)
|
|
return;
|
|
|
|
// Try to unduck unless automovement is not allowed
|
|
// NOTE: When not onground, you can always unduck
|
|
if (player->m_Local.m_bAllowAutoMovement || bInAir || player->m_Local.m_bDucking)
|
|
{
|
|
// We released the duck button, we aren't in "duck" and we are not in the air - start unduck transition.
|
|
if ((buttonsReleased & IN_DUCK))
|
|
{
|
|
if (bInDuck && !bDuckJump)
|
|
{
|
|
player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME;
|
|
}
|
|
else if (player->m_Local.m_bDucking && !player->m_Local.m_bDucked)
|
|
{
|
|
// Invert time if release before fully ducked!!!
|
|
float unduckMilliseconds = 1000.0f * TIME_TO_UNDUCK;
|
|
float duckMilliseconds = 1000.0f * TIME_TO_DUCK;
|
|
float elapsedMilliseconds = GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_flDucktime;
|
|
|
|
float fracDucked = elapsedMilliseconds / duckMilliseconds;
|
|
float remainingUnduckMilliseconds = fracDucked * unduckMilliseconds;
|
|
|
|
player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME - unduckMilliseconds + remainingUnduckMilliseconds;
|
|
}
|
|
}
|
|
|
|
|
|
// Check to see if we are capable of unducking.
|
|
if (CanUnduck())
|
|
{
|
|
// or unducking
|
|
if ((player->m_Local.m_bDucking || player->m_Local.m_bDucked))
|
|
{
|
|
float flDuckMilliseconds = MAX(0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDucktime);
|
|
float flDuckSeconds = flDuckMilliseconds * 0.001f;
|
|
|
|
// Finish ducking immediately if duck time is over or not on ground
|
|
if (flDuckSeconds > TIME_TO_UNDUCK || (bInAir && !bDuckJump))
|
|
{
|
|
FinishUnDuck();
|
|
}
|
|
else
|
|
{
|
|
// Calc parametric time
|
|
float flDuckFraction = SimpleSpline(1.0f - (flDuckSeconds / TIME_TO_UNDUCK));
|
|
SetDuckedEyeOffset(flDuckFraction);
|
|
player->m_Local.m_bDucking = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Still under something where we can't unduck, so make sure we reset this timer so
|
|
// that we'll unduck once we exit the tunnel, etc.
|
|
if (player->m_Local.m_flDucktime != GAMEMOVEMENT_DUCK_TIME)
|
|
{
|
|
SetDuckedEyeOffset(1.0f);
|
|
player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME;
|
|
player->m_Local.m_bDucked = true;
|
|
player->m_Local.m_bDucking = false;
|
|
player->AddFlag(FL_DUCKING);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// HACK: (jimd 5/25/2006) we have a reoccuring bug (#50063 in Tracker) where the player's
|
|
// view height gets left at the ducked height while the player is standing, but we haven't
|
|
// been able to repro it to find the cause. It may be fixed now due to a change I'm
|
|
// also making in UpdateDuckJumpEyeOffset but just in case, this code will sense the
|
|
// problem and restore the eye to the proper position. It doesn't smooth the transition,
|
|
// but it is preferable to leaving the player's view too low.
|
|
//
|
|
// If the player is still alive and not an observer, check to make sure that
|
|
// his view height is at the standing height.
|
|
else if (!IsDead() && !player->IsObserver() && !player->IsInAVehicle())
|
|
{
|
|
if ((player->m_Local.m_flDuckJumpTime == 0.0f) && (fabs(player->GetViewOffset().z - GetPlayerViewOffset(false).z) > 0.1))
|
|
{
|
|
// we should rarely ever get here, so assert so a coder knows when it happens
|
|
Assert(0);
|
|
DevMsg(1, "Restoring player view height\n");
|
|
|
|
// set the eye height to the non-ducked height
|
|
SetDuckedEyeOffset(0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
static ConVar sv_optimizedmovement("sv_optimizedmovement", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::PlayerMove(void)
|
|
{
|
|
VPROF("CGameMovement::PlayerMove");
|
|
|
|
CheckParameters();
|
|
|
|
// clear output applied velocity
|
|
mv->m_outWishVel.Init();
|
|
mv->m_outJumpVel.Init();
|
|
|
|
MoveHelper()->ResetTouchList(); // Assume we don't touch anything
|
|
|
|
ReduceTimers();
|
|
|
|
AngleVectors(mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp); // Determine movement angles
|
|
|
|
// Always try and unstick us unless we are using a couple of the movement modes
|
|
if (player->GetMoveType() != MOVETYPE_NOCLIP &&
|
|
player->GetMoveType() != MOVETYPE_NONE &&
|
|
player->GetMoveType() != MOVETYPE_ISOMETRIC &&
|
|
player->GetMoveType() != MOVETYPE_OBSERVER &&
|
|
!player->pl.deadflag)
|
|
{
|
|
if (CheckInterval(STUCK))
|
|
{
|
|
if (CheckStuck())
|
|
{
|
|
// Can't move, we're stuck
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now that we are "unstuck", see where we are (player->GetWaterLevel() and type, player->GetGroundEntity()).
|
|
if (player->GetMoveType() != MOVETYPE_WALK ||
|
|
mv->m_bGameCodeMovedPlayer ||
|
|
!sv_optimizedmovement.GetBool())
|
|
{
|
|
CategorizePosition();
|
|
}
|
|
else
|
|
{
|
|
if (mv->m_vecVelocity.z > 250.0f)
|
|
{
|
|
SetGroundEntity(NULL);
|
|
}
|
|
}
|
|
|
|
// Store off the starting water level
|
|
m_nOldWaterLevel = player->GetWaterLevel();
|
|
|
|
// If we are not on ground, store off how fast we are moving down
|
|
if (player->GetGroundEntity() == NULL)
|
|
{
|
|
player->m_Local.m_flFallVelocity = -mv->m_vecVelocity[2];
|
|
}
|
|
|
|
m_nOnLadder = 0;
|
|
|
|
player->UpdateStepSound(player->m_pSurfaceData, mv->GetAbsOrigin(), mv->m_vecVelocity);
|
|
|
|
UpdateDuckJumpEyeOffset();
|
|
Duck();
|
|
|
|
// Don't run ladder code if dead on on a train
|
|
if (!player->pl.deadflag && !(player->GetFlags() & FL_ONTRAIN))
|
|
{
|
|
// If was not on a ladder now, but was on one before,
|
|
// get off of the ladder
|
|
|
|
// TODO: this causes lots of weirdness.
|
|
//bool bCheckLadder = CheckInterval( LADDER );
|
|
//if ( bCheckLadder || player->GetMoveType() == MOVETYPE_LADDER )
|
|
{
|
|
if (!LadderMove() &&
|
|
(player->GetMoveType() == MOVETYPE_LADDER))
|
|
{
|
|
// Clear ladder stuff unless player is dead or riding a train
|
|
// It will be reset immediately again next frame if necessary
|
|
player->SetMoveType(MOVETYPE_WALK);
|
|
player->SetMoveCollide(MOVECOLLIDE_DEFAULT);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
Msg("%i, %i, %s, player = %8x, move type = %2i, ground entity = %8x, velocity = (%f %f %f)\n",
|
|
player->CurrentCommandNumber(),
|
|
player->m_nTickBase,
|
|
player->IsServer() ? "SERVER" : "CLIENT",
|
|
player,
|
|
player->GetMoveType(),
|
|
player->GetGroundEntity(),
|
|
mv->m_vecVelocity[0], mv->m_vecVelocity[1], mv->m_vecVelocity[2]);
|
|
|
|
#endif
|
|
|
|
// Handle movement modes.
|
|
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:
|
|
FullWalkMove();
|
|
break;
|
|
|
|
case MOVETYPE_ISOMETRIC:
|
|
//IsometricMove();
|
|
// Could also try: FullTossMove();
|
|
FullWalkMove();
|
|
break;
|
|
|
|
case MOVETYPE_OBSERVER:
|
|
FullObserverMove(); // clips against world&players
|
|
break;
|
|
|
|
default:
|
|
DevMsg(1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer());
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Performs the collision resolution for fliers.
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::PerformFlyCollisionResolution(trace_t &pm, Vector &move)
|
|
{
|
|
Vector base;
|
|
float vel;
|
|
float backoff;
|
|
|
|
switch (player->GetMoveCollide())
|
|
{
|
|
case MOVECOLLIDE_FLY_CUSTOM:
|
|
// Do nothing; the velocity should have been modified by touch
|
|
// FIXME: It seems wrong for touch to modify velocity
|
|
// given that it can be called in a number of places
|
|
// where collision resolution do *not* in fact occur
|
|
|
|
// Should this ever occur for players!?
|
|
Assert(0);
|
|
break;
|
|
|
|
case MOVECOLLIDE_FLY_BOUNCE:
|
|
case MOVECOLLIDE_DEFAULT:
|
|
{
|
|
if (player->GetMoveCollide() == MOVECOLLIDE_FLY_BOUNCE)
|
|
backoff = 2.0 - player->m_surfaceFriction;
|
|
else
|
|
backoff = 1;
|
|
|
|
ClipVelocity(mv->m_vecVelocity, pm.plane.normal, mv->m_vecVelocity, backoff);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Invalid collide type!
|
|
Assert(0);
|
|
break;
|
|
}
|
|
|
|
// stop if on ground
|
|
if (pm.plane.normal[2] > 0.7)
|
|
{
|
|
base.Init();
|
|
if (mv->m_vecVelocity[2] < GetCurrentGravity() * gpGlobals->frametime)
|
|
{
|
|
// we're rolling on the ground, add static friction.
|
|
SetGroundEntity(&pm);
|
|
mv->m_vecVelocity[2] = 0;
|
|
}
|
|
|
|
vel = DotProduct(mv->m_vecVelocity, mv->m_vecVelocity);
|
|
|
|
// Con_DPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] );
|
|
|
|
if (vel < (30 * 30) || (player->GetMoveCollide() != MOVECOLLIDE_FLY_BOUNCE))
|
|
{
|
|
SetGroundEntity(&pm);
|
|
mv->m_vecVelocity.Init();
|
|
}
|
|
else
|
|
{
|
|
VectorScale(mv->m_vecVelocity, (1.0 - pm.fraction) * gpGlobals->frametime * 0.9, move);
|
|
PushEntity(move, &pm);
|
|
}
|
|
VectorSubtract(mv->m_vecVelocity, base, mv->m_vecVelocity);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::FullTossMove(void)
|
|
{
|
|
trace_t pm;
|
|
Vector move;
|
|
|
|
CheckWater();
|
|
|
|
// add velocity if player is moving
|
|
if ((mv->m_flForwardMove != 0.0f) || (mv->m_flSideMove != 0.0f) || (mv->m_flUpMove != 0.0f))
|
|
{
|
|
Vector forward, right, up;
|
|
float fmove, smove;
|
|
Vector wishdir, wishvel;
|
|
float wishspeed;
|
|
int i;
|
|
|
|
AngleVectors(mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
|
|
|
|
// Copy movement amounts
|
|
fmove = mv->m_flForwardMove;
|
|
smove = mv->m_flSideMove;
|
|
|
|
VectorNormalize(forward); // Normalize remainder of vectors.
|
|
VectorNormalize(right); //
|
|
|
|
for (i = 0; i<3; i++) // Determine x and y parts of velocity
|
|
wishvel[i] = forward[i] * fmove + right[i] * smove;
|
|
|
|
wishvel[2] += mv->m_flUpMove;
|
|
|
|
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
|
|
Accelerate(wishdir, wishspeed, sv_accelerate.GetFloat());
|
|
}
|
|
|
|
if (mv->m_vecVelocity[2] > 0)
|
|
{
|
|
SetGroundEntity(NULL);
|
|
}
|
|
|
|
// If on ground and not moving, return.
|
|
if (player->GetGroundEntity() != NULL)
|
|
{
|
|
if (VectorCompare(player->GetBaseVelocity(), vec3_origin) &&
|
|
VectorCompare(mv->m_vecVelocity, vec3_origin))
|
|
return;
|
|
}
|
|
|
|
CheckVelocity();
|
|
|
|
// add gravity
|
|
if (player->GetMoveType() == MOVETYPE_FLYGRAVITY)
|
|
{
|
|
AddGravity();
|
|
}
|
|
|
|
// move origin
|
|
// Base velocity is not properly accounted for since this entity will move again after the bounce without
|
|
// taking it into account
|
|
VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
|
|
CheckVelocity();
|
|
|
|
VectorScale(mv->m_vecVelocity, gpGlobals->frametime, move);
|
|
VectorSubtract(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
|
|
|
|
PushEntity(move, &pm); // Should this clear basevelocity
|
|
|
|
CheckVelocity();
|
|
|
|
if (pm.allsolid)
|
|
{
|
|
// entity is trapped in another solid
|
|
SetGroundEntity(&pm);
|
|
mv->m_vecVelocity.Init();
|
|
return;
|
|
}
|
|
|
|
if (pm.fraction != 1)
|
|
{
|
|
PerformFlyCollisionResolution(pm, move);
|
|
}
|
|
|
|
// check for in water
|
|
CheckWater();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: TF2 commander mode movement logic
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#pragma warning (disable : 4701)
|
|
|
|
void CGameMovement::IsometricMove(void)
|
|
{
|
|
int i;
|
|
Vector wishvel;
|
|
float fmove, smove;
|
|
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;
|
|
|
|
// No up / down movement
|
|
forward[2] = 0;
|
|
right[2] = 0;
|
|
|
|
VectorNormalize(forward); // Normalize remainder of vectors
|
|
VectorNormalize(right); //
|
|
|
|
for (i = 0; i<3; i++) // Determine x and y parts of velocity
|
|
wishvel[i] = forward[i] * fmove + right[i] * smove;
|
|
//wishvel[2] += mv->m_flUpMove;
|
|
|
|
Vector out;
|
|
VectorMA(mv->GetAbsOrigin(), gpGlobals->frametime, wishvel, out);
|
|
mv->SetAbsOrigin(out);
|
|
|
|
// Zero out the velocity so that we don't accumulate a huge downward velocity from
|
|
// gravity, etc.
|
|
mv->m_vecVelocity.Init();
|
|
}
|
|
|
|
#pragma warning (default : 4701)
|
|
|
|
|
|
bool CGameMovement::GameHasLadders() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Traces player movement + position
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::TracePlayerBBox(const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm)
|
|
{
|
|
VPROF("CGameMovement::TracePlayerBBox");
|
|
|
|
Ray_t ray;
|
|
ray.Init(start, end, GetPlayerMins(), GetPlayerMaxs());
|
|
UTIL_TraceRay(ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm);
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: overridded by game classes to limit results (to standable objects for example)
|
|
//-----------------------------------------------------------------------------
|
|
void CGameMovement::TryTouchGround(const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, unsigned int fMask, int collisionGroup, trace_t& pm)
|
|
{
|
|
VPROF("CGameMovement::TryTouchGround");
|
|
|
|
Ray_t ray;
|
|
ray.Init(start, end, mins, maxs);
|
|
UTIL_TraceRay(ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm);
|
|
}
|
|
|