Git Repos / fte_dogmode / qc / pmove.qc
Last update to this file was on 2025-08-13 at 05:20.
Show pmove.qc
//==============================================================================
// PMOVE
//==============================================================================
// TODO CEV: improved player unstick function
//======================================================================
// globals
//======================================================================
#if defined(CSQC) || defined (SSQC)
// the following are all managed by FTEQW -- CEV
float input_buttons; // buttons pressed by the client
// float input_impulse;
float input_timelength; // frame / tic time
vector input_angles; // +x = DOWN
vector input_movevalues; // movement requested by client
#endif
//======================================================================
// pmove constants (could be reworked as cvars, would add overhead) -- CEV
//======================================================================
#if defined(CSQC) || defined(SSQC)
// acceleration & friction
const float PM_AIRACCELBACK = 2.5f; // Air stop speed in Q3? 5.0f? 2.5f?
const float PM_AIRACCELFWD = 1.0f; // 1 feels close to Q3 / CPM
const float PM_AIRACCELSIDE = 70.0f; // (320 / 30) * 6.5625; 106.666 in Q1
const float PM_AIRACCELTURN = 6.5625f; // magic value; see PM_AIRCONTROL -- CEV
const float PM_EDGEFRICTION = 2.0f; // ???
const float PM_GROUNDACCEL = 10.0f; // 10 is Q1 & VQ3, 15 is CPM
const float PM_GROUNDFRICTION = 6.0f; // 4 for Q1, 6 for Q3, 8 for (old?) CPM
const float PM_SLICKACCEL = 10.0f; // accel while on ice / slick floor
const float PM_SLIDEACCELFWD = 10.0f; // crouchslide accel
const float PM_SLIDEACCELSIDE = 70.0f; // crouchslide accel; 70.0f
const float PM_SLIDEFRICTION = 0.4f; // crouchslide friction; 6.0 / 15.0
const float PM_SURFACCEL = 1.0f; //
const float PM_SURFACCELSIDE = 106.666f;// (320 / 30) * 10.0
const float PM_WATERACCEL = 10.0f; // water acceleration
const float PM_WATERFRICTION = 4.0f; // friction in water
// horizontal speeds (mostly)
const float PM_CROUCHSPEED = 80.0f; // ???
const float PM_CROUCHSCALE = 0.2f; // 400 * 0.2 = 80ups
const float PM_MAXSPEED = 320.0f; // 320 always
const float PM_MAXAIRSPEED = 30.0f; // 30 for Q1 (PM_MAXSPEED / 10.666)
const float PM_MAXBOOSTSPEED = 409.0f; // see PM_MaxCircleGroundSpeed
const float PM_MAXSLIDESPEED = 320.0f; //
const float PM_STOPSPEED = 100.0f; // used in friction calculations
const float PM_WALKSPEED = 160.0f; // walk speed
const float PM_WALKSCALE = 0.4f; // 400 * 0.4 = 160ups
const float PM_WATERMAXSPEED = 224.0f; // id1 224; 320 * 0.7
const float PM_WATERSINKSPEED = 60.0f;
// vertical speeds (mostly)
const float PM_GRAVITY = 800.0f; // superseded by world_gravity global
const float PM_CLIMBSPEEDMIN = 45.0f; // min ladder & climbing speed
const float PM_CLIMBSPEEDMAX = 160.0f; // max climbing speed; Rubicon2 160
const float PM_CLIMBACCEL = 45.0f; // affected by CLIMBSCALE
const float PM_JUMPSPEED = 270.0f; // standard jump Z velocity; 90 * 3
const float PM_DOUBLEJUMPSPEED = 270.0f;// 270 * 1.5 in CPM?
#if 0
const float PM_SHORTJUMPSPEED = 180.0f; // 180 = 90 * 2
const float PM_SHORTJUMPXYSPEED = 400.0f;
const float PM_SHORTJUMPTHRESH = 340.0f;// shortjump when horiz speed under this
#endif
const float PM_STAIRJUMPSPEED = 360.0f; // 360 = 90 * 4
const float PM_TELEJUMPSPEED = 405.0f; // 360; 405? (270 * 1.5) in CPM
const float PM_WALLJUMPFORCE = 90.0f; // push away from wall
const float PM_WALLJUMPGROUND = 32.0f; // distance from ground to allow WJ
const float PM_WALLJUMPLIMIT = -180.f; // no walljump if Z vel below this
const float PM_WALLJUMPSPEED = 270.0f; // same as JUMPSPEED
const float PM_WALLJUMPDOUBLE = 360.0f; // same as STAIRJUMPSPEED
// timing (in seconds)
const float PM_CROUCHSLIDE_TIME = 1.0f; // crouchslide total duration
const float PM_DOUBLEJUMP_WINDOW = 0.4f;// 2 jumps in this time is a double
const float PM_TELEJUMP_WINDOW = 0.4f; // duration to perform a telejump
const float PM_WALLCLIP_WINDOW = 0.15f; // 0.4 - 0.25
// button mapping (should be moved elsewhere, somewhere more global)
const float PM_BTN_ATTACK = INPUT_BUTTON0;
const float PM_BTN_DOWN = INPUT_BUTTON8;// crouch key
const float PM_BTN_JUMP = INPUT_BUTTON2;// jump key
const float PM_BTN_GRAB = INPUT_BUTTON4;// grab key
const float PM_BTN_WALK = INPUT_BUTTON7;// walk key
// misc
const float PM_CLIMBSCALE = 0.75; // scale XY vel by this while climbing
const float PM_FLMIN = 0.125f; // 1.0 / 8.0; minimum float precision
const float PM_MAXVELOCITY = 4095.0f; // 32768 / 8.0; vel networked as short
const float PM_OVERCLIP = 1.0f; // Q3 OVERCLIP is 1.001f, Q1 1.0f
const float PM_STEPHEIGHT = 18.0f; // 18 for Q1, 22 for later games?
const float PM_PLANE_INTERACT = 0.0f; //
const float PM_PLANE_FLAT = 1.0f; // flat horizontal
const float PM_PLANE_GROUND = 0.7f; // above this is ONGROUND
const float PM_PLANE_VERTICAL = 0.0f; // straight up vertical
const vector PM_TELEDROP = '0 0 -64'; // drop teleporter exit to floor if
// floor is within this distance
const float PM_MOVEFLAGS = MOVE_NORMAL;
#define PM_PHYSFPS 1/125
// water level
const float WATERLEVEL_NONE = 0;
const float WATERLEVEL_FEET = 1;
const float WATERLEVEL_WAIST = 2;
const float WATERLEVEL_EYES = 3;
// Player Sizes
const vector PM_STAND_MIN = VEC_HULL_MIN;
const vector PM_STAND_MAX = VEC_HULL_MAX;
const vector PM_CROUCH_MIN = '-16 -16 -24';
const vector PM_CROUCH_MAX = '16 16 16'; // Q3 crouch MAX_z is 16 -- CEV
// Player View Offset
// Q1 Ranger's view offset is lower than Q3; is Q1 Ranger canonically shorter?
const vector PM_STAND_VIEWOFS = '0 0 22'; // Q3 default 0 0 26 -- CEV
const vector PM_CROUCH_VIEWOFS = '0 0 8'; // Q3 crouch 0 0 12 -- CEV
// pmove_flags is used by the engine; FTE defines two constants:
// PMF_JUMP_HELD = 1, PMF_LADDER = 2. So those two constants should
// be first (and in that order) in our enum below. -- CEV
typedef enumflags
{
PMF_JUMP_HELD, // player is holding the jump key
PMF_ONLADDER, // entity is on a ladder
PMF_ONGROUND, // entity is on ground
PMF_ONRAMP, // entity is on a ramp
PMF_ONSLOPE, // entity is on a steep slope
PMF_ONSLICK, // entity is on a slick (icy) surface
PMF_ONMOVINGENT, // entity is standing on a networked ent
PMF_CROUCH_HELD, // player is holding the crouch key
PMF_CROUCHED, // entity is crouching
PMF_CROUCHSLIDE, // entity is crouch sliding
PMF_WALK_HELD, // player is holding the walk key
PMF_STEPPED, // entity has stepped up
PMF_AIRSTEPPED, // entity has airstepped
PMF_DOUBLEJUMPED, // entity has doublejumped
PMF_WALLJUMPED, // entity has walljumped
PMF_WATERJUMPED, // entity has waterjumped
PMF_CLIMB, // entity is climbing
PMF_PUSHED, // entity moved by a trigger_push
PMF_SLIDE_STEP, // slidemove hint to traverse steps
PMF_UNUSED1, // unused flags
PMF_UNUSED2,
PMF_UNUSED3,
PMF_UNUSED4 // 23rd flag
} player_movement_flags;
// remove these flags when landing on ground -- CEV
const float PM_ONGROUNDFLAGS = PMF_ONSLOPE | PMF_CLIMB | PMF_PUSHED |
PMF_WALLJUMPED;
// remove these flags when landing on a steep slope -- CEV
const float PM_ONSLOPEFLAGS = PMF_ONSLICK | PMF_ONRAMP | PMF_ONGROUND;
// remove these flags when not on ground -- CEV
const float PM_NOGROUNDFLAGS = PMF_ONGROUND | PMF_ONRAMP | PMF_ONSLICK |
PMF_ONSLOPE | PMF_ONMOVINGENT;
// don't transmit these flags from server to client -- CEV
const float PM_SLIDEFLAGS = PMF_SLIDE_STEP;
#endif
//======================================================================
// forward declarations
//======================================================================
#if 0
vector(vector vel, vector normal, float overbounce) PM_ClipVelocity;
float(float maxspeed, float a, float c, float t) PM_MaxCircleGroundSpeed;
#endif
#if defined(CSQC) || defined (SSQC)
// PM_TRUNCATEVECTORTOEIGTH(vec)
float() PM_Nudge;
// PM_SETONGROUND_NOGROUND()
// PM_SETONGROUND_TRACEGROUND()
// PM_SETONGROUND_ONMOVINGENT()
// PM_SETONGROUND()
// PM_DANCEMOVE_ADDTOUCH(ent)
// PM_DANCEMOVE_DOTOUCH(ent)
// PM_DANCEMOVE_STEP_DOWN(startvel, end, j, k, FL)
// PM_DANCEMOVE_STEP_FORWARD(startvel, end, j, k, FL)
// PM_DANCEMOVE_STEP_UP(startvel, end, j, k, FL)
// PM_DANCEMOVE_AXIALNUDGE(plane)
// PM_DANCEMOVE_CLIP_INNER(v, p_a, p_b, p_c, k)
// PM_DANCEMOVE_CLIP(v, p_i, p_j, p_k, k)
// PM_DANCEMOVE_GROUNDIMPACT()
void(float movetime) PM_DanceMove;
void() PM_CrouchStart;
void() PM_CrouchStop;
void() PM_CrouchSlideStart;
void() PM_CrouchSlideStop;
// PM_JUMP()
void() PM_WallClimb;
void() PM_WallJump;
void() PM_WallJumpCheck;
// PM_FRICTION(speed1, temp, friction, move_time)
// PM_ACCELERATE(dir, wish, speed1, newspeed, accel, move_time)
// PM_AIRCONTROL(dir, pushdir, newspeed, turn, move_time)
// PM_MOVE_MOVETYPES_CATEGORIZEPOSITION()
// PM_MOVE_MOVETYPES_SETNOCLIPFLAGS()
// PM_MOVE_MOVETYPES_NOCLIPACCELERATE(scale, move_time)
// PM_MOVE_MOVETYPES_SWIMPREMOVE(start, end, move_time)
// PM_MOVE_MOVETYPES_SWIMACCELERATE(move_time)
// PM_MOVE_MOVETYPES_WALKPREMOVE()
// PM_MOVE_MOVETYPES_WALKACCELERATE(move_time)
// PM_MOVE_MOVETYPES_MANAGETIMER(adjust_time)
// PM_MOVE_PRE()
// PM_MOVE_MOVETYPES()
// PM_MOVE_POST_FALLDAMAGE()
// PM_MOVE_POST()
#endif
//------------------------------------------------------------------------------
#if 0
//----------------------------------------------------------------------
// PM_ClipVelocity -- slide off the impacting surface -- CEV
//----------------------------------------------------------------------
vector(vector vel, vector normal, float overbounce) PM_ClipVelocity =
{
// the same implementation used in Quake 3 -- CEV
var float backoff = vel * normal;
if (backoff < 0)
backoff *= overbounce;
else
backoff /= overbounce;
vel -= normal * backoff;
return vel;
};
//----------------------------------------------------------------------
// PM_MaxCircleGroundSpeed
//
// circle jump calculations (in python, probably wrong on my [CEV's] part):
// 320 * math.sqrt((A * (2 - A * (1 / T))) / (c * (2 - c * (1 / T))))
// where A = accel (10.0), c = friction (4.0), T = framerate (q3 125, q1 77?)
// this equation is from the article "Circle-Jump Theory" by injx, found here:
// https://github.com/5xp/strafe-theory
//
// Max ground speed for accel 10, friction 6, T 125, w 320 (vq3) is ~409
// Max ground speed for accel 15, friction 6, T 125, w 320 (CPM) is ~497
// accel 10.000, friction 6, T 125, wishspeed 380 = 486
// accel 10.000, friction 6, T 125, wishspeed 400 = 512
//----------------------------------------------------------------------
float(float maxspeed, float a, float f, float t) PM_MaxCircleGroundSpeed =
{
return maxspeed * sqrt ((a * (2.0 - a * (1.0 / t))) /
(f * (2.0 - f * (1.0 / t))));
};
#endif
#if defined(CSQC) || defined (SSQC)
//----------------------------------------------------------------------
// PM_TRUNCATEVECTORTOEIGTH - truncate a vector to 0.125 precision -- CEV
//----------------------------------------------------------------------
#define PM_TRUNCATEVECTORTOEIGTH(vec) \
/* { */ \
vec.x = (floor(vec.x * 8 + (1.0 / 16))) * PM_FLMIN; \
vec.y = (floor(vec.y * 8 + (1.0 / 16))) * PM_FLMIN; \
vec.z = (floor(vec.z * 8 + (1.0 / 16))) * PM_FLMIN; \
/* } */
//----------------------------------------------------------------------
// PM_Nudge
// from the GPL2 CSQCTest code that comes with FTEQW; may be called often
// to nudge player origin due to float/network precision errors -- CEV
//----------------------------------------------------------------------
float() PM_Nudge =
{
vector test, org;
test = org = self.origin;
// check current position.
tracebox (org, self.mins, self.maxs, org, PM_MOVEFLAGS, self);
if (!trace_startsolid)
return TRUE;
// truncate to network accuracy
PM_TRUNCATEVECTORTOEIGTH (org)
test = org;
static float offsets[] = {0, -1./8, 1./8, -2./8, 2./8};
for (float z = 0; z < offsets.length; z++)
{
test.z = org.z + offsets[z];
for (float y = 0; y < offsets.length; y++)
{
test.y = org.y + offsets[y];
for (float x = 0; x < offsets.length; x++)
{
test.x = org.x + offsets[x];
tracebox (test, self.mins, self.maxs, test,
PM_MOVEFLAGS, self);
if (!trace_startsolid)
{
// okay, that'll do
self.origin = test;
return TRUE;
}
}
}
}
self.origin = org;
return FALSE;
};
//----------------------------------------------------------------------
#define PM_SETONGROUND_NOGROUND() \
/* { */ \
/* not on ground, clear pmove flags & fields -- CEV */ \
self.groundentity = __NULL__; \
self.groundnormal = '0 0 0'; \
self.flags &= ~FL_ONGROUND; \
self.moveflags = self.moveflags - (self.moveflags & PM_NOGROUNDFLAGS); \
/* don't crouchslide in air -- CEV */ \
if (self.moveflags & PMF_CROUCHSLIDE) \
{ \
/* TODO CEV */ \
PM_CrouchSlideStop (); \
} \
/* } */
//----------------------------------------------------------------------
#define PM_SETONGROUND_TRACEGROUND() \
/* { */ \
/* do a trace to check for ground -- CEV */ \
if (self.moveflags & PMF_ONMOVINGENT) \
{ \
/* trace further when on a vertically moving ent -- CEV */ \
tracebox (self.origin, self.mins, self.maxs, \
self.origin - '0 0 4', PM_MOVEFLAGS, self); \
} \
else \
{ \
/* '0 0 0.25' works but '0 0 1' seems more reliable -- CEV */ \
tracebox (self.origin, self.mins, self.maxs, \
self.origin - '0 0 1', PM_MOVEFLAGS, self); \
} \
/* } */
//----------------------------------------------------------------------
// PM_SETONGROUND_ONMOVINGENT -- set flag & glue the player to any
// vertically moving PUSH entity they're standing on -- CEV
//----------------------------------------------------------------------
#ifdef SSQC
#define PM_SETONGROUND_ONMOVINGENT() \
/* { */ \
/* flag when standing on a moving ent -- CEV */ \
if (trace_ent.movetype == MOVETYPE_PUSH) \
{ \
if (trace_ent.origin - trace_ent.origin_net) \
{ \
self.moveflags |= PMF_ONMOVINGENT; \
/*
dprint (sprintf("PM_SETONGROUND_ONMOVINGENT: found " \
" %s moving at %v\n", trace_ent.classname, \
trace_ent.velocity)); \
*/ \
} \
} \
else if (self.moveflags & PMF_ONMOVINGENT) \
{ \
self.moveflags &= ~PMF_ONMOVINGENT; \
} \
/* } */
#endif
#ifdef CSQC
#define PM_SETONGROUND_ONMOVINGENT() \
{ \
}
#endif
//----------------------------------------------------------------------
// PM_SETONGROUND -- manage ground flags and fields -- CEV
//----------------------------------------------------------------------
#define PM_SETONGROUND() \
/* { */ \
/* onground if we hit something & it faces upwards */ \
if (trace_fraction < 1.0f) \
{ \
if (trace_plane_normal.z > PM_PLANE_GROUND) \
{ \
/* on ground -- CEV */ \
/* start crouchsliding if: we just landed, we're
* holding crouch, we aren't already sliding, we're
* not in water, and we're going faster than run
* speed -- CEV */ \
if (!(self.moveflags & PMF_ONGROUND)) { \
if (self.moveflags & PMF_CROUCH_HELD) { \
if (!(self.moveflags & PMF_CROUCHSLIDE)) { \
if (self.waterlevel < WATERLEVEL_WAIST) { \
if (self.speed > PM_MAXSPEED) \
{ \
PM_CrouchSlideStart (); \
} } } } } \
/* set groundentity and groundnormal -- CEV */ \
self.groundentity = trace_ent; \
self.groundnormal = trace_plane_normal; \
/* now manage flags -- CEV */ \
if (!(self.moveflags & PMF_SLIDE_STEP)) \
{ \
self.flags |= FL_ONGROUND; \
self.moveflags |= PMF_ONGROUND; \
} \
if (self.groundnormal.z < PM_PLANE_FLAT) \
self.moveflags |= PMF_ONRAMP; \
else if (self.moveflags & PMF_ONRAMP) \
self.moveflags &= ~PMF_ONRAMP; \
if (trace_surfaceflagsf & Q3SURFACEFLAG_SLICK) \
/* on a slick/icy surface -- CEV */ \
self.moveflags |= PMF_ONSLICK; \
self.moveflags = self.moveflags - \
(self.moveflags & PM_ONGROUNDFLAGS); \
} \
else if (trace_plane_normal.z > PM_PLANE_VERTICAL) \
{ \
/* on a steep slope */ \
/* clear groundentity and groundnormal -- CEV */ \
self.groundentity = __NULL__; \
self.groundnormal = '0 0 0'; \
/* now do flags -- CEV */ \
self.flags &= ~FL_ONGROUND; \
self.moveflags |= PMF_ONSLOPE; \
self.moveflags = self.moveflags - \
(self.moveflags & PM_ONSLOPEFLAGS); \
self.movetimer = 0; \
/* don't crouchslide on a slope -- CEV */ \
if (self.moveflags & PMF_CROUCHSLIDE) \
PM_CrouchSlideStop (); \
} \
else \
{ \
/* we probably shouldn't end up here -- CEV */ \
PM_SETONGROUND_NOGROUND () \
} \
PM_SETONGROUND_ONMOVINGENT () \
/* hack Z vel when standing on an entity with a touch
* function to make sure DANCEMOVE runs (and calls touch
* functions) later -- CEV */ \
/*
if (trace_ent.touch && trace_ent.touch != sub_null) \
{ \
if (self.velocity.z == 0) \
{ \
self.velocity.z = -1; \
} \
} \
*/ \
} \
else \
{ \
PM_SETONGROUND_NOGROUND () \
} \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_ADDTOUCH(ent) \
/* { */ \
if (ent && ent.touch) \
{ \
if (ent != touched_ent3 && \
ent != touched_ent2 && \
ent != touched_ent1) \
{ \
touched_ent3 = touched_ent2; \
touched_ent2 = touched_ent1; \
touched_ent1 = ent; \
} \
} \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_DOTOUCH(ent) \
/* { */ \
if (ent && ent.touch != __NULL__ && ent.touch != sub_null) \
{ \
stemp = self; \
otemp = other; \
other = self; \
self = ent; \
self.touch (); \
self = stemp; \
other = otemp; \
} \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_STEP_REWIND() \
/* { */ \
/* discard the step attempt -- CEV */ \
trace_plane_normal = first_plane; \
/* TODO CEV rewind touched ents? */ \
trace_ent = first_ent; \
trace_fraction = first_frac; \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_STEP_DOWN(startvel, end, j, k, FL) \
/* { */ \
/* third: move down; +1 to k is important -- CEV */ \
end = trace_endpos; \
end.z -= k + 1; \
tracebox (trace_endpos, self.mins, self.maxs, end, FL, self); \
if (trace_allsolid) \
{ \
goto PM_DanceMove_RejectStep3; \
} \
else \
{ \
/*
if (trace_endpos == save_pos) \
{ \
dprint (sprintf("PM_DANCEMOVE: D\n")); \
goto PM_DanceMove_RejectStep3; \
} \
*/ \
/* The down move will sometimes hit nothing (frac 1).
* In this case trace_plane_normal is unreliable (again
* we hit nothing) so do some extra checks to determine
* if we keep the step move -- CEV */ \
if (trace_fraction == 1.0f) \
{ \
/* Check the plane just in case -- CEV */ \
if (trace_plane_normal.z != 0) \
{ \
dprint ("PM_DANCEMOVE: C\n"); \
goto PM_DanceMove_RejectStep3; \
} \
/* If we haven't moved and first_plane is not
* vertical then reject the attempt -- CEV */ \
if (trace_endpos == self.origin) \
{ \
if (first_plane.z != 0.0f) \
{ \
dprint ("PM_DANCEMOVE: E\n"); \
goto PM_DanceMove_RejectStep3; \
} \
} \
else \
{ \
/* dprint (sprintf("PM_DANCEMOVE: Z %g\n", \
self.velocity.z)); */ \
if (self.velocity.z < 0) \
{ \
goto PM_DanceMove_RejectStep3; \
} \
} \
} \
else if (trace_plane_normal.z <= PM_PLANE_GROUND) \
{ \
/* this is the expected and regular
* not-good-ground step rejection -- CEV */ \
goto PM_DanceMove_RejectStep3; \
} \
/* accept the stepped move. update time_left,
* origin, touched ents, and stored planes,
* then update flags -- CEV */ \
time_left = j; \
self.origin = trace_endpos; \
if (trace_ent.touch) \
{ \
PM_DANCEMOVE_ADDTOUCH (trace_ent) \
} \
if (roof_plane && roof_plane != trace_plane_normal && \
roof_plane != plane1 && roof_plane != plane2) \
{ \
plane2 = plane1; \
plane1 = roof_plane; \
} \
if (fwd_plane && fwd_plane != trace_plane_normal && \
fwd_plane != plane1 && fwd_plane != plane2) \
{ \
plane2 = plane1; \
plane1 = fwd_plane; \
} \
self.moveflags |= PMF_STEPPED; \
if (!(self.moveflags & PMF_ONGROUND) && startvel.z <= 0) \
{ \
/* for stairjumps -- CEV */ \
self.moveflags |= PMF_AIRSTEPPED; \
} \
} \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_STEP_FORWARD(startvel, end, j, k, FL) \
/* { */ \
/* second: move forward */ \
k = trace_endpos.z - self.origin.z; \
end = trace_endpos + (self.velocity * j); \
end.z = trace_endpos.z; \
tracebox (trace_endpos, self.mins, self.maxs, end, FL, self); \
if (trace_allsolid) \
{ \
goto PM_DanceMove_RejectStep3; \
} \
/* if we hit the same plane we're trying to step over
* and we made no progress in the move then reject
* the step attempt -- CEV */ \
if (trace_plane_normal == first_plane && \
fabs(self.origin.x - trace_endpos.x) < PM_FLMIN && \
fabs(self.origin.y - trace_endpos.y) < PM_FLMIN) \
{ \
/* dprint (sprintf("PM_DANCEMOVE: B\n")); */ \
goto PM_DanceMove_RejectStep3; \
} \
var vector fwd_plane = '0 0 0'; \
/* var vector save_pos = trace_endpos; */ \
j -= j * trace_fraction; \
if (trace_fraction < 1.0f) \
{ \
PM_DANCEMOVE_ADDTOUCH (trace_ent) \
fwd_plane = trace_plane_normal; \
} \
PM_DANCEMOVE_STEP_DOWN (startvel, end, j, k, FL) \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_STEP_UP(startvel, end, j, k, FL) \
/* { */ \
/* store ent and norm if we need to reject the step attempt -- CEV */ \
var vector first_plane = trace_plane_normal; \
var entity first_ent = trace_ent; \
var float first_frac = trace_fraction; \
j = time_left; \
/* first: move up */ \
trace_endpos.z += PM_STEPHEIGHT; \
tracebox (self.origin, self.mins, self.maxs, trace_endpos, FL, self); \
if (!trace_allsolid && !trace_startsolid) \
{ \
if (trace_endpos.z - self.origin.z <= 0) \
{ \
/* dprint (sprintf("PM_DANCEMOVE: A %v\n", \
trace_endpos - self.origin)); */ \
goto PM_DanceMove_RejectStep3; \
} \
var vector roof_plane = '0 0 0'; \
if (trace_fraction < 1.0f) \
{ \
PM_DANCEMOVE_ADDTOUCH (trace_ent) \
roof_plane = trace_plane_normal; \
} \
PM_DANCEMOVE_STEP_FORWARD (startvel, end, j, k, FL) \
} \
else \
{ \
PM_DanceMove_RejectStep3: \
PM_DANCEMOVE_STEP_REWIND () \
} \
/* } */
//----------------------------------------------------------------------
#define PM_DANCEMOVE_AXIALNUDGE(plane) \
/* { */ \
if (plane) \
{ \
if (trace_plane_normal * plane > 0.99f) \
{ \
self.velocity += trace_plane_normal; \
} \
} \
/* } */
//----------------------------------------------------------------------
// PM_DANCEMOVE_CLIP_INNER
// This macro is meant to be called from PM_DANCEMOVE_CLIP -- CEV
//----------------------------------------------------------------------
#define PM_DANCEMOVE_CLIP_INNER(v, p_a, p_b, p_c, k) \
/* { */ \
/* if velocity interacts with plane_b -- CEV */ \
k = self.velocity * p_b; \
if (k < PM_PLANE_INTERACT) \
{ \
/* clip to plane_b -- CEV */ \
self.velocity -= p_b * k; \
/* test if move goes back into the first plane */ \
if (self.velocity * p_a < 0.0f) \
{ \
/* slide along the crease -- CEV */ \
/* ("><" is crossproduct) -- CEV */ \
v = p_a >< p_b; \
self.velocity = v * (v * self.velocity); \
/* check if we interact with a 3rd plane */ \
if (self.velocity * p_c < 0) \
{ \
/* stop when 3 planes interact */ \
self.velocity = '0 0 0'; \
break; \
} \
} \
} \
/* } */
//----------------------------------------------------------------------
// PM_DANCEMOVE_CLIP
// This macro is meant to be called from PM_DANCEMOVE -- CEV
//----------------------------------------------------------------------
#define PM_DANCEMOVE_CLIP(v, p_i, p_j, p_k, k) \
/* { */ \
/* test if velocity interacts with first plane -- CEV */ \
k = self.velocity * p_i; \
if (k < PM_PLANE_INTERACT) \
{ \
/* clip to the plane -- CEV */ \
self.velocity -= p_i * k; \
/* check for 2nd and 3rd plane interactions -- CEV */ \
PM_DANCEMOVE_CLIP_INNER (v, p_i, p_j, p_k, k) \
PM_DANCEMOVE_CLIP_INNER (v, p_i, p_k, p_j, k) \
} \
else \
{ \
/* if no interaction with first then check the 2nd -- CEV */ \
k = self.velocity * p_j; \
if (k < PM_PLANE_INTERACT) \
{ \
self.velocity -= p_j * k; \
PM_DANCEMOVE_CLIP_INNER (v, p_j, p_i, p_k, k) \
PM_DANCEMOVE_CLIP_INNER (v, p_j, p_k, p_i, k) \
} \
else \
{ \
/* now the third stored plane -- CEV */ \
k = self.velocity * p_k; \
if (k < PM_PLANE_INTERACT) \
{ \
self.velocity -= p_k * k; \
PM_DANCEMOVE_CLIP_INNER (v, p_k, p_j, p_i, k) \
PM_DANCEMOVE_CLIP_INNER (v, p_k, p_i, p_j, k) \
} \
} \
} \
/* } */
//----------------------------------------------------------------------
// PM_DANCEMOVE_GROUNDIMPACT
// This macro is meant to be called from PM_DANCEMOVE -- CEV
//----------------------------------------------------------------------
#define PM_DANCEMOVE_GROUNDIMPACT() \
/* { */ \
/* impact info (see player_postthink) -- CEV */ \
if (!(self.moveflags & PMF_ONGROUND)) { \
if (self.velocity.z < self.jump_flag) \
{ \
self.jump_flag = self.velocity.z; \
} } \
/* } */
//----------------------------------------------------------------------
// PM_DANCEMOVE
//
// Updates origin according to velocity, moves the player through the world.
// Same as Q3 SlideMove and Q1 FlyMove. This version handles steps, applies
// gravity, and restores velocity based on a timer check (those latter two
// are features of Q3's SlideMove).
//
// Based on code from the Nuclide SDK (presumably by Eukara), specifically
// the function PMoveCustom_Move found in the file pmove_custom.qc. That
// in turn appears to be based on a similar function in the CSQCTest code
// that comes with FTEQW. -- CEV
//----------------------------------------------------------------------
void(float move_time) PM_DanceMove =
{
var float grav = 0;
if (self.waterlevel < WATERLEVEL_WAIST &&
!(self.moveflags & PMF_ONLADDER) &&
(!(self.moveflags & PMF_ONGROUND) ||
self.moveflags & PMF_ONRAMP))
{
if (self.gravity)
grav = self.gravity;
else
grav = 1.0;
/* world_gravity is set in worldspawn() -- CEV */
grav *= world_gravity * move_time;
/* Half now, half later. Apparently affects
* framerate dependence. -- CEV */
self.velocity.z -= grav * 0.5f;
}
if (self.velocity == '0 0 0')
/* we aren't moving so skip to clearing flags -- CEV */
goto PM_DanceMove_ClearFlags2;
/* declare and initialize variables -- CEV */
var vector end, plane1, plane2, startvel;
var float i, j, k;
var float time_left = move_time;
var entity stemp, otemp, touched_ent1, touched_ent2, touched_ent3;
plane1 = plane2 = '0 0 0';
startvel = self.velocity;
stemp = otemp = touched_ent1 = touched_ent2 = touched_ent3 = __NULL__;
/* slide along the groundplane if neccesary -- CEV */
if (self.groundnormal)
{
k = self.velocity * self.groundnormal;
if (k < PM_PLANE_INTERACT)
{
self.velocity -= self.groundnormal * k;
}
}
/* attempt at most 4 moves, stopping early if time_left runs out,
* clipping velocity as we go -- CEV */
for (i = 0; time_left > 0 && i < 4; i++)
{
/* set our destination & test the move -- CEV */
end = self.origin + (self.velocity * time_left);
tracebox (self.origin, self.mins, self.maxs, end,
PM_MOVEFLAGS, self);
if (trace_allsolid && trace_startsolid)
{
/* self is in something else; attempt to nudge out */
if (PM_Nudge())
continue;
dprint (sprintf("PM_DANCEMOVE: trapped in a solid %s"
"! time %g\n", trace_ent.classname, time));
break;
}
if (trace_fraction > 0)
/* accept the move -- CEV */
self.origin = trace_endpos;
if (trace_fraction >= 1.0f)
/* no obstructions, made the whole move -- CEV */
break;
/* save trace ent for later then reduce time_left -- CEV */
PM_DANCEMOVE_ADDTOUCH (trace_ent)
time_left -= time_left * trace_fraction;
/* integrated StepSlideMove from Nuclide / CSQCTest
* only attempt to step if requested and there's time left
* and we hit something like a vertical plane -- CEV */
if (self.moveflags & PMF_SLIDE_STEP && time_left > 0 &&
trace_plane_normal.z >= PM_PLANE_VERTICAL &&
trace_plane_normal.z <= PM_PLANE_GROUND)
{
/* this next macro starts a chain of macros that will
* complete (or abandon) the step attempt -- CEV */
PM_DANCEMOVE_STEP_UP (startvel, end, j, k, PM_MOVEFLAGS)
}
/* we've found a ground plane so call PM_SETONGROUND -- CEV */
if (trace_plane_normal.z > PM_PLANE_GROUND)
{
PM_DANCEMOVE_GROUNDIMPACT ()
PM_SETONGROUND ()
}
/* check stored planes and clip velocity as needed -- CEV */
PM_DANCEMOVE_CLIP (end, trace_plane_normal, plane1, plane2, k)
/* store current plane and plane1 for the next pass -- CEV */
if (trace_plane_normal != plane1 &&
trace_plane_normal != plane2 &&
trace_plane_normal != self.groundnormal)
{
plane2 = plane1;
plane1 = trace_plane_normal;
}
/* stop if we've turned against original velocity -- CEV */
if (self.velocity * startvel <= 0)
self.velocity = '0 0 0';
/* stop the loop if velocity is now zero -- CEV */
if (self.velocity == '0 0 0')
break;
}
/* call touch () on any entities we've collided with -- CEV */
PM_DANCEMOVE_DOTOUCH (touched_ent3)
PM_DANCEMOVE_DOTOUCH (touched_ent2)
PM_DANCEMOVE_DOTOUCH (touched_ent1)
/* if we're not onground and the timer is greater than
* PM_WALLCLIP_WINDOW (within 250ms of pressing jump)
* then restore velocity (to skim past walls) -- CEV */
if (!(self.moveflags & PMF_ONGROUND) &&
self.movetimer > PM_WALLCLIP_WINDOW)
{
j = startvel.z;
k = self.velocity.z;
self.velocity = startvel;
/* take min of vel_z so headbump doublejumps work -- CEV */
if (fabs(j) > fabs(k))
self.velocity.z = k;
else
self.velocity.z = j;
}
/* final gravity check here -- CEV */
if (grav)
self.velocity.z -= grav * 0.5f;
/* clip to the ground plane under certain complex conditions (this
* is done to recreate / simulate Q3/CPM stair behavior) -- CEV */
if (self.movetimer > 0 && self.velocity.z &&
self.groundnormal.z > PM_PLANE_GROUND &&
!(self.moveflags & PMF_DOUBLEJUMPED))
{
k = self.velocity * self.groundnormal;
self.velocity -= self.groundnormal * k;
}
/*
if (i > 0)
dprint (sprintf("PM_DanceMove: move complete, "
"i %g, time_left %g, velocity.z %g\n",
i, time_left, self.velocity.z));
*/
PM_DanceMove_ClearFlags2:
/* clear slide hint flags -- CEV */
self.moveflags = self.moveflags - (self.moveflags & PM_SLIDEFLAGS);
};
//----------------------------------------------------------------------
void() PM_CrouchStart =
{
// crouch
self.moveflags |= PMF_CROUCHED;
setsize (self, PM_CROUCH_MIN, PM_CROUCH_MAX);
#ifdef CSQC
// transition to crouch view offset (if not already in progress) -- CEV
if (self.entnum == player_localentnum) {
if (self.view_ofs != PM_CROUCH_VIEWOFS)
{
view_crouch_old = PM_STAND_VIEWOFS.z;
view_crouch_finished = time + PLAYER_CROUCH_SMOOTH;
} }
#endif
self.view_ofs = PM_CROUCH_VIEWOFS;
};
//----------------------------------------------------------------------
void() PM_CrouchStop =
{
// uncrouch if we're clear to stand
tracebox (self.origin, PM_STAND_MIN, PM_STAND_MAX, self.origin,
PM_MOVEFLAGS, self);
if (trace_startsolid == FALSE) {
if (trace_allsolid == FALSE)
{
self.moveflags &= ~PMF_CROUCHED;
setsize (self, PM_STAND_MIN, PM_STAND_MAX);
#ifdef CSQC
// transition to stand view offset (like above) -- CEV
if (self.entnum == player_localentnum) {
if (self.view_ofs != PM_STAND_VIEWOFS)
{
view_crouch_old = PM_CROUCH_VIEWOFS.z;
view_crouch_finished = time + PLAYER_CROUCH_SMOOTH;
} }
#endif
self.view_ofs = PM_STAND_VIEWOFS;
} }
};
//----------------------------------------------------------------------
// PM_CrouchSlideStart
// Crouchslide behavior is based on Rapha's Quake Champions movement
// tutorial found here: https://www.youtube.com/watch?v=95spyl1LRTc&t=1039s
// because I've never played QC (or Q4). Please note that it doesn't
// work exactly as described in that video. -- CEV
//----------------------------------------------------------------------
void() PM_CrouchSlideStart =
{
#ifdef DEBUG
dprint (sprintf("PM_CrouchSlideStart: lesgo %g, timer %g\n",
self.velocity.z, self.movetimer));
#endif
if (self.movetimer >= 0)
{
player_sound (snd_player_slide_00, TRUE);
self.movetimer = PM_CROUCHSLIDE_TIME * -1;
}
self.moveflags |= PMF_CROUCHSLIDE;
};
//----------------------------------------------------------------------
// PM_CrouchSlideStop
//----------------------------------------------------------------------
void() PM_CrouchSlideStop =
{
self.moveflags &= ~PMF_CROUCHSLIDE;
if (self.movetimer >= 0)
player_sound (snd_player_slide_silent, TRUE);
};
//----------------------------------------------------------------------
// PM_JUMP
//----------------------------------------------------------------------
#define PM_JUMP() \
/* { */ \
/* are we already waterjumping, or is jump being held? */ \
if (!(self.moveflags & PMF_WATERJUMPED) && \
!(self.moveflags & PMF_JUMP_HELD)) \
{ \
var sound_info_t snd = snd_empty; \
/* make sure we get at least jumpspeed upwards from the
* ground plane by clipping velocity to it. necessary
* for rampjumps. -- CEV */ \
if (self.groundnormal.z > PM_PLANE_GROUND && \
self.velocity * self.groundnormal < 0) \
{ \
self.velocity -= self.groundnormal * \
(self.velocity * self.groundnormal); \
} \
/* this changes the behavior of downward ramps -- CEV */ \
if (self.velocity.z < 0) \
self.velocity.z = 0; \
if (self.movetimer > 0) \
{ \
/* speed boost based on time since last jump -- CEV */ \
/* first: scale amount -- CEV */ \
accel = (PM_DOUBLEJUMP_WINDOW - self.movetimer) * \
(1.0f / PM_DOUBLEJUMP_WINDOW); \
/* 2nd: scale * ideal circlejump -- CEV */ \
accel = PM_MAXSPEED + (accel * \
(PM_MAXBOOSTSPEED - PM_MAXSPEED)); \
/* only apply if it would increase speed -- CEV */ \
if (accel > self.speed && self.speed > PM_WALKSPEED) \
{ \
/* don't apply boost to Z velocity -- CEV */ \
friction = self.velocity.z; \
self.velocity.z = 0; \
self.velocity = normalize (self.velocity); \
self.velocity *= accel; \
self.velocity.z = friction; \
friction = 0; \
} \
accel = 0; \
/* it may be useful in the future (for a tricks
* mode, etc) to distinguish between different
* jump types -- CEV */ \
if (self.teleport_time > time - \
(PM_TELEJUMP_WINDOW + 0.2)) \
{ \
/* a teleport jump: allow a larger (+0.2)
* window to account for time to travel
* thru teleporter -- CEV */ \
self.velocity.z = PM_TELEJUMPSPEED; \
} \
else if (self.moveflags & PMF_ONRAMP) \
{ \
/* the groundnormal is weird - a ramp - so we
* want an additive doublejump here -- CEV */ \
self.velocity.z += PM_DOUBLEJUMPSPEED; \
} \
else \
{ \
/* flat ground, no additive jump -- CEV */ \
self.velocity.z = PM_STAIRJUMPSPEED; \
} \
snd = snd_player_jump_00; \
/* set the doublejump flag -- CEV */ \
self.moveflags |= PMF_DOUBLEJUMPED; \
} \
/*
else if (input_movevalues.y && input_movevalues.x == 0 && \
self.speed <= PM_SHORTJUMPTHRESH) \
{ \
var float r1 = rint (random() * 3); \
switch (r1) \
{ \
case 0: snd = snd_player_jump_01; break; \
case 1: snd = snd_player_jump_02; break; \
case 2: snd = snd_player_jump_03; break; \
case 3: snd = snd_player_jump_04; break; \
} \
self.velocity = wishdir * PM_SHORTJUMPXYSPEED; \
if (self.moveflags & PMF_ONRAMP) \
self.velocity.z += PM_SHORTJUMPSPEED; \
else \
self.velocity.z = PM_SHORTJUMPSPEED; \
} \
*/ \
else \
{ \
/* normal jump */ \
var float r2 = rint (random() * 3); \
switch (r2) \
{ \
case 0: snd = snd_player_jump_01; break; \
case 1: snd = snd_player_jump_02; break; \
case 2: snd = snd_player_jump_03; break; \
case 3: snd = snd_player_jump_04; break; \
} \
if (self.moveflags & PMF_ONRAMP) \
{ \
/* additive jump on non-flat ground -- CEV */ \
self.velocity.z += PM_JUMPSPEED; \
} \
else \
{ \
/* ignore the jump input if the player has
* stepped up within the last frame (for
* CPM stairjumps) -- CEV */ \
if (self.moveflags & PMF_AIRSTEPPED) \
self.velocity.z = PM_JUMPSPEED; \
else if (!(self.moveflags & PMF_STEPPED)) \
self.velocity.z = PM_JUMPSPEED; \
/*
else \
dprint (sprintf("PM_Jump: eating " \
"jump; %f\n", self.origin.z)); \
*/ \
} \
} \
/* manage flags -- CEV */ \
PM_SETONGROUND_NOGROUND () \
self.moveflags |= PMF_JUMP_HELD; \
if (self.moveflags & PMF_CROUCHSLIDE) \
PM_CrouchSlideStop (); \
/* player jumping sound -- CEV */ \
if (snd.wav != snd_empty.wav) \
player_sound (snd, TRUE); \
player_footstep (); \
/*
dprint (sprintf("PM_Jump: Z vel %g, time %g\n", \
self.velocity.z, time)); \
*/ \
/* timers for all jumps -- CEV */ \
if (self.movetimer >= 0) \
self.movetimer = PM_DOUBLEJUMP_WINDOW; \
} \
/* } */
//----------------------------------------------------------------------
// PM_WallClimb -- climbing; checks performed in PM_WallJumpCheck -- CEV
//----------------------------------------------------------------------
void() PM_WallClimb =
{
// accelerate upwards toward a vertical ledge -- CEV
var float grav;
var float zspeed = self.velocity.z;
// counteract gravity with additional Z velocity -- CEV
if (self.gravity)
grav = self.gravity;
else
grav = 1.0f;
grav *= world_gravity * input_timelength;
zspeed += grav + PM_CLIMBACCEL;
self.velocity *= PM_CLIMBSCALE;
self.velocity.z = bound (PM_CLIMBSPEEDMIN, zspeed, PM_CLIMBSPEEDMAX);
// reset movetimer so we don't stick to the floor -- CEV
self.moveflags |= PMF_CLIMB;
self.moveflags |= PMF_JUMP_HELD;
if (self.movetimer > 0)
self.movetimer = 0;
#ifdef DEBUG
dprint (sprintf("PM_WallClimb: vel.z %g\n", self.velocity.z));
#endif
};
//----------------------------------------------------------------------
// PM_WallJump -- Perform an actual walljump -- CEV
//----------------------------------------------------------------------
void() PM_WallJump =
{
var sound_info_t snd = snd_empty;
// bounce off the wall plane if Z velocity is positive -- CEV
// if we bounce off the wall plane when Z vel is negative it
// introduces a scenario where a player can fall off a ledge
// and then (for just a frame or two) jump off the wall
// behind them to gain additional XY speed. You can still
// walljump from that floor/wall you fell from now but you
// won't gain horizontal speed. (found this while attempting
// to circlejump over the center gap in CPM22) -- CEV
if (self.velocity.z > 0)
self.velocity += trace_plane_normal * PM_WALLJUMPFORCE;
if (self.movetimer > 0)
{
snd = snd_player_jump_00;
}
else
{
var float r3 = rint (random() * 3);
switch (r3)
{
case 0: snd = snd_player_jump_01; break;
case 1: snd = snd_player_jump_02; break;
case 2: snd = snd_player_jump_03; break;
case 3: snd = snd_player_jump_04; break;
}
}
// additive if not falling -- CEV
if (self.movetimer > 0)
{
if (self.velocity.z > 0)
self.velocity.z = max (PM_WALLJUMPDOUBLE,
self.velocity.z + PM_WALLJUMPSPEED);
else
self.velocity.z = PM_WALLJUMPDOUBLE;
}
else
{
if (self.velocity.z > 0)
self.velocity.z += PM_WALLJUMPSPEED;
else
self.velocity.z = PM_WALLJUMPSPEED;
}
#ifdef DEBUG
dprint (sprintf("PM_WallJump: z vel %g\n", self.velocity.z));
#endif
// manage flags & fields -- CEV
self.moveflags |= PMF_JUMP_HELD;
self.moveflags |= PMF_WALLJUMPED;
if (self.movetimer > 0)
self.movetimer = 0;
if (snd.wav != snd_empty.wav)
player_sound (snd, TRUE);
player_footstep ();
};
//----------------------------------------------------------------------
// PM_WallJumpCheck -- Wall jumping, mantling, and climbing -- CEV
//----------------------------------------------------------------------
void() PM_WallJumpCheck =
{
// are we in water, waterjumping, or falling too fast? -- CEV
if (self.waterlevel > WATERLEVEL_NONE)
return;
if (self.moveflags & PMF_WATERJUMPED)
return;
if (self.velocity.z < PM_WALLJUMPLIMIT)
return;
// are we within PM_WALLJUMPGROUND units of the floor? -- CEV
tracebox (self.origin, self.mins, self.maxs, self.origin - '0 0 64',
PM_MOVEFLAGS, self);
var float grounddist = self.origin.z - trace_endpos.z;
if (grounddist <= PM_WALLJUMPGROUND)
if (trace_plane_normal.z > PM_PLANE_GROUND)
return;
var float i, checks;
var vector start, end;
checks = (self.moveflags & PMF_WALLJUMPED ? 1 : 4);
start = end = self.origin;
for (i = 0; i < checks; i++)
{
// I've borrowed a little logic from Quake Champions Classic
// here, shoutout to RhapsodyInGeek, check out their good work
// https://github.com/Quake-Champions-Classic/mod
// v_forward and v_right should still be those set by the
// makevectors call from PM_Move -- CEV
if (i < 2)
if (input_movevalues.x == 0)
continue;
else
// check further when +back -- CEV
if (i == 0)
end = v_forward * 10;
else if (i == 1)
end = v_forward * 25;
if (i > 1)
if (input_movevalues.y == 0)
continue;
else
end = v_right * 10;
if (i == 1 || i == 3)
end = end - end * 2;
end = start + end;
// this ends up firing *a lot* when hopping through a map
// normally (specifically when releasing jump button after
// a jump has registered). traceline would be better here
// except it complicates wall climbing. -- CEV
tracebox (start, PM_CROUCH_MIN, PM_CROUCH_MAX, end,
MOVE_NOMONSTERS | PM_MOVEFLAGS, self);
#ifdef DEBUG
dprint (sprintf("PM_WallJumpCheck: frac %g, norm.z %g, dist "
"%g\n", trace_fraction, trace_plane_normal.z,
vlen(self.origin - trace_endpos)));
#endif
// in order: we hit something and it's vaguely vertical -- CEV
if (trace_fraction < 1.0f) {
if (trace_plane_normal.z <= PM_PLANE_GROUND) {
if (trace_plane_normal.z >= PM_PLANE_VERTICAL)
{
if (!(self.moveflags & PMF_JUMP_HELD)) {
if (!(self.moveflags & PMF_WALLJUMPED)) {
if (self.velocity * trace_plane_normal > 0)
{
// moving away from the wall, not holding
// jump, and haven't walljumped -- CEV
PM_WallJump ();
break;
} } }
if (i == 0) {
if (self.moveflags & PMF_JUMP_HELD) {
if (self.speed <= PM_MAXSPEED) {
if (fabs(self.velocity.z) < 90.0f) {
if (grounddist > 42)
{
// holding jump and moving *into* the wall
// at a rate below MAXSPEED with a low Z
// velocity and at least 43 units away from
// the ground; attempt to climb -- CEV
var vector tpn_ang;
tpn_ang = vectoangles (trace_plane_normal);
// must be facing the impacted surface -- CEV
if (angledif(input_angles.y, tpn_ang.y) <= 150)
continue;
// we're looking for empty space, for a trace
// to not hit anything, so use tracelines
// below -- CEV
// horizontal forward at least 64u off floor
start.z += 22;
traceline (start, start + v_forward * 30,
MOVE_NOMONSTERS | PM_MOVEFLAGS, self);
if (trace_fraction >= 1.0f)
{
// Ranger's fingies can't grip a
// vertical surface -- CEV
traceline (trace_endpos,
trace_endpos - '0 0 64',
MOVE_NOMONSTERS | PM_MOVEFLAGS,
self);
if (trace_plane_normal.z >
PM_PLANE_GROUND)
{
PM_WallClimb ();
break;
}
}
// horizontal forward at least 96u off floor
start.z += 32;
traceline (start, start + v_forward * 30,
MOVE_NOMONSTERS | PM_MOVEFLAGS, self);
if (trace_fraction >= 1.0f)
{
traceline (trace_endpos,
trace_endpos - '0 0 64',
MOVE_NOMONSTERS | PM_MOVEFLAGS,
self);
if (trace_plane_normal.z >
PM_PLANE_GROUND)
{
PM_WallClimb ();
break;
}
}
start = self.origin;
} } } } }
} } }
// player can't hold on to thin air -- CEV
// TODO CEV
// self.moveflags &= ~PMF_CLIMB;
}
};
//----------------------------------------------------------------------
#define PM_FRICTION(speed1, temp, friction, move_time) \
/* { */ \
speed1 = vlen (self.velocity); \
if (speed1 < PM_FLMIN) \
{ \
self.velocity = '0 0 0'; \
} \
else \
{ \
/* calculate what their new speed should be & slow them */ \
if (self.movetimer > 0) \
temp = speed1; \
else if (speed1 < PM_STOPSPEED) \
temp = PM_STOPSPEED; \
else \
temp = speed1; \
/* edge friction -- commented out currently -- CEV */ \
/* trace from Nuclide, file pmove_custom.qc -- CEV */ \
/*
if (self.moveflags & PMF_ONGROUND) \
{ \
vec1 = self.origin + normalize(self.velocity) * \
16 + [0, 0, 1] * self.mins.z; \
traceline (vec1, vec1 + [0, 0, -34], TRUE, self); \
if (trace_fraction == 1.0) \
temp *= friction * PM_EDGEFRICTION * move_time; \
else \
temp *= friction * move_time; \
temp = speed1 - temp; \
} \
else \
{ \
*/ \
temp = speed1 - temp * friction * move_time; \
/*
} \
*/ \
if (temp <= 0) \
self.velocity = '0 0 0'; \
else \
self.velocity *= temp / speed1; \
} \
/* } */
//----------------------------------------------------------------------
#define PM_ACCELERATE(dir, wish, speed1, newspeed, accel, move_time) \
/* { */ \
speed1 = wish - (self.velocity * dir); \
if (speed1 > 0) \
{ \
/* TODO CEV */ \
/* multiply by MAXSPEED instead of wish so we don't slow
* down while crouchwalking along a wall -- CEV */ \
if (self.velocity * dir < 0) \
{ \
newspeed = accel * move_time * wish; \
} \
else \
{ \
newspeed = accel * move_time * PM_MAXSPEED; \
} \
newspeed = min (newspeed, speed1); \
self.velocity += newspeed * dir; \
} \
/* } */
//----------------------------------------------------------------------
// this is based on the unused disabled "proper way" to do acceleration
// in Quake 3 Arena; see code/game/bg_pmove.c line 260 of the GPL Quake
// 3 source release -- CEV
//
// this macro is different in that it does no acceleration (and does not
// reference wishspeed). Instead it changes movement direction and may
// reduce movement speed. Use PM_ACCELERATE first to accel. -- CEV
//
// inspired by zweek's "how does airstrafing ACTUALLY work?" youtube video
// (available here: https://www.youtube.com/watch?v=gRqoXy-0d84) -- CEV
//
// arguments are: wishdir, vec1, float1, float2, move_time
//----------------------------------------------------------------------
#define PM_AIRCONTROL(dir, pushdir, newspeed, turn, move_time) \
/* { */ \
/* self.velocity.z is already 0 -- CEV */ \
self.speed = vlen (self.velocity); \
/* scale wishdir up to self.speed; cheers zweek, see
* https://www.youtube.com/watch?v=gRqoXy-0d84&t=1492s
* for more information -- CEV */ \
pushdir = (dir * self.speed) - self.velocity; \
newspeed = turn * move_time * self.speed; \
newspeed = min (newspeed, vlen(pushdir)); \
/*
dprint (sprintf("PM_AIRCONTROL: vlen pushdir %g, newspeed %g, " \
"self.speed %g\n", vlen(pushdir), newspeed, self.speed)); \
*/ \
pushdir = normalize (pushdir); \
self.velocity += pushdir * newspeed; \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_CATEGORIZEPOSITION
// Based on similarly-named function in the GPL2 purecsqc pmove.qc
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_CATEGORIZEPOSITION() \
/* { */ \
/* look for ground, manage flags & fields -- CEV */ \
/* setting onground here before the acceleration functions
* turns out to be faster (in my testing) than relying on
* DanceMove to correctly track ground state -- CEV */ \
PM_SETONGROUND_TRACEGROUND () \
PM_SETONGROUND () \
/* set waterlevel and watertype -- CEV */ \
base_entity_positioncontents (self); \
/* don't crouchslide in water -- CEV */ \
if (self.moveflags & PMF_CROUCHSLIDE) \
{ \
if (self.moveflags & PMF_ONGROUND) \
{ \
if (self.waterlevel >= WATERLEVEL_WAIST) \
{ \
/* TODO CEV */ \
PM_CrouchSlideStop (); \
} \
} \
} \
/* can't be waterjumping if we're on ground */ \
if (self.moveflags & PMF_WATERJUMPED) { \
if (self.waterlevel == WATERLEVEL_NONE || self.moveflags & PMF_ONGROUND)\
{ \
self.moveflags &= ~PMF_WATERJUMPED; \
self.flags &= ~FL_WATERJUMP; \
} } \
/* } */
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SETNOCLIPFLAGS() \
/* { */ \
/* noclip is never on ground */ \
if (self.groundentity != __NULL__) \
self.groundentity = __NULL__; \
if (self.groundnormal != '0 0 0') \
self.groundnormal = '0 0 0'; \
if (self.moveflags & PMF_DOUBLEJUMPED) \
self.moveflags &= ~PMF_DOUBLEJUMPED; \
if (self.moveflags & PMF_WALLJUMPED) \
self.moveflags &= ~PMF_WALLJUMPED; \
if (self.moveflags & PMF_ONGROUND) \
self.moveflags &= ~PMF_ONGROUND; \
if (self.flags & FL_ONGROUND) \
self.flags &= ~FL_ONGROUND; \
if (self.moveflags & PMF_PUSHED) \
self.moveflags &= ~PMF_PUSHED; \
if (self.moveflags & PMF_CLIMB) \
self.moveflags &= ~PMF_CLIMB; \
/* self.movetimer = 0; */ \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_NOCLIPACCELERATE -- for flying / noclip -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_NOCLIPACCELERATE(move_time) \
/* { */ \
if (input_buttons & PM_BTN_JUMP) \
{ \
/* smartjump */ \
wishvel.z = max (PM_MAXSPEED, wishvel.z); \
} \
else if (input_buttons & PM_BTN_DOWN) \
{ \
/* smartcrouch */ \
wishvel.z = min (-PM_MAXSPEED, wishvel.z); \
} \
wishspeed = vlen (wishvel); \
wishdir = normalize (wishvel); \
PM_FRICTION (speed1, temp, PM_GROUNDFRICTION, move_time) \
PM_ACCELERATE (wishdir, wishspeed, speed1, temp, PM_GROUNDACCEL, \
move_time) \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_SWIMPREMOVE -- water jump, water wading sounds -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SWIMPREMOVE(start, end, move_time) \
/* { */ \
/* CheckWaterJump */ \
if (self.waterlevel == WATERLEVEL_WAIST) \
{ \
start = self.origin; \
start.z = start.z + 8; \
/* use wishvel instead of v_forward -- CEV */ \
end = [wishvel.x, wishvel.y, 0]; \
end = normalize (end); \
traceline (start, (start + end * 24), TRUE, self); \
if (trace_fraction < 1) \
{ \
/* solid at the waist */ \
start.z = start.z + self.maxs.z - 8; \
self.movedir = trace_plane_normal * -50; \
traceline (start, (start + end * 24), TRUE, self); \
if (trace_fraction == 1) \
{ \
/* open at eye level */ \
self.flags |= FL_WATERJUMP; \
self.moveflags |= PMF_WATERJUMPED; \
self.flags |= FL_JUMPRELEASED; \
self.moveflags |= PMF_JUMP_HELD; \
/* Z velocity was 225 */ \
self.velocity.z = PM_JUMPSPEED; \
} \
} \
} \
/* functionality copied into pmove from client.qc -- CEV */ \
if (self.waterlevel >= WATERLEVEL_WAIST) { \
if (self.waterlevel >= WATERLEVEL_EYES) { \
if (self.swim_time < time) \
{ \
/* play swimming sound */ \
self.swim_time = time + 1; \
if (random() < 0.5) \
player_sound (snd_player_water_01, TRUE); \
else \
player_sound (snd_player_water_02, TRUE); \
} } } \
if (self.moveflags & PMF_STEPPED) \
{ \
self.moveflags &= ~PMF_STEPPED; \
self.moveflags &= ~PMF_AIRSTEPPED; \
} \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_SWIMACCELERATE
// Largely copied from id Software's WaterMove -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SWIMACCELERATE(move_time) \
/* { */ \
if (!(self.moveflags & PMF_WATERJUMPED)) \
{ \
if (input_buttons & PM_BTN_JUMP) \
/* smartjump */ \
wishvel.z = max (PM_MAXSPEED, wishvel.z); \
else if (input_buttons & PM_BTN_DOWN) \
/* smartcrouch */ \
wishvel.z = min (-PM_MAXSPEED, wishvel.z); \
else if (input_movevalues == '0 0 0') \
/* drift towards bottom -- CEV */ \
wishvel.z -= PM_WATERSINKSPEED; \
} \
wishspeed = vlen (wishvel); \
wishspeed = min (wishspeed, PM_WATERMAXSPEED); \
wishdir = normalize (wishvel); \
if (!(self.moveflags & PMF_WATERJUMPED)) \
{ \
PM_FRICTION (speed1, temp, PM_WATERFRICTION, move_time) \
} \
PM_ACCELERATE (wishdir, wishspeed, speed1, temp, PM_WATERACCEL, \
move_time) \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_WALKPREMOVE -- crouching, jumping, ladders -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_WALKPREMOVE() \
/* { */ \
if (self.moveflags & PMF_ONLADDER) \
{ \
var float znew = fabs(self.velocity.z) + PM_CLIMBACCEL; \
self.velocity *= PM_CLIMBSCALE; \
self.speed = vlen ([self.velocity.x, self.velocity.y, 0]); \
if (input_buttons & PM_BTN_JUMP || input_movevalues.z > 0) \
{ \
/* PlayerClimb -- johnfitz */ \
self.velocity.z = bound (PM_CLIMBSPEEDMIN, znew, \
PM_CLIMBSPEEDMAX); \
} \
else if (input_buttons & PM_BTN_DOWN || input_movevalues.z < 0)\
{ \
/* PlayerClimbDown -- CEV */ \
self.velocity.z = bound (-PM_CLIMBSPEEDMAX, -znew, \
-PM_CLIMBSPEEDMIN); \
} \
else \
{ \
self.flags |= FL_JUMPRELEASED; \
self.moveflags &= ~PMF_JUMP_HELD; \
self.velocity.z = 0; \
} \
if (self.moveflags & PMF_ONGROUND) \
{ \
PM_SETONGROUND_NOGROUND () \
} \
/* self.movetimer = 0; */ \
} \
else \
{ \
if (input_buttons & PM_BTN_JUMP) \
{ \
/* +jump was pressed */ \
if (self.moveflags & PMF_ONGROUND) \
{ \
PM_JUMP () \
} \
else if (world_walljump) \
{ \
PM_WallJumpCheck (); \
} \
} \
else \
{ \
self.flags |= FL_JUMPRELEASED; \
self.moveflags &= ~PMF_JUMP_HELD; \
} \
if (input_buttons & PM_BTN_DOWN && \
!(self.moveflags & PMF_CROUCHED)) \
{ \
PM_CrouchStart (); \
} \
else if (!(input_buttons & PM_BTN_DOWN) && \
self.moveflags & PMF_CROUCHED) \
{ \
PM_CrouchStop (); \
} \
} \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_WALKACCELERATE -- accel & friction for MOVETYPE_WALK -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_WALKACCELERATE(move_time) \
/* { */ \
accel = friction = speed1 = temp = turn = 0; \
/* priority and sequence of these checks is important -- CEV */ \
/* first: friction -- CEV */ \
if (self.moveflags & PMF_ONGROUND) \
{ \
if (self.moveflags & PMF_CROUCHSLIDE) \
{ \
/* water level adds extra friction -- CEV */ \
friction = PM_SLIDEFRICTION + self.waterlevel; \
} \
else \
{ \
if (self.moveflags & PMF_STEPPED) \
friction = PM_GROUNDFRICTION * 0.1f; \
else \
friction = PM_GROUNDFRICTION; \
} \
} \
/* second: accel & wishspeed limiting -- CEV */ \
if (wishspeed) \
{ \
if ((!(self.moveflags & PMF_ONGROUND)) || \
self.moveflags & PMF_CROUCHSLIDE) \
{ \
/* movement in the air & crouchsliding -- CEV */ \
if (self.moveflags & PMF_CROUCHED) \
{ \
wishspeed *= PM_CROUCHSCALE; \
if (wishspeed > PM_CROUCHSPEED) \
wishspeed = PM_CROUCHSPEED; \
} \
else \
{ \
if (wishspeed > PM_MAXSPEED) \
wishspeed = PM_MAXSPEED; \
} \
/* penalty when holding jump (PM_CmdScale) -- CEV */ \
if (self.moveflags & PMF_JUMP_HELD) \
{ \
wishspeed *= (0.7f + (0.4f * (1 - \
(self.movetimer / \
PM_DOUBLEJUMP_WINDOW)))); \
} \
/* Chovelyoukai / Lumia's "differential strafing" */ \
/* angle of the dot of wishdir and velocity -- CEV */ \
temp = vlen (wishdir) * self.speed; \
temp = acos (wishdir * self.velocity / temp); \
/* handle NaN -- CEV */ \
if (temp != temp) \
temp = 0; \
else \
temp *= M_RAD2DEG; \
/* optimal angle for Q1 accel at cur. speed -- CEV */ \
/* approaches 90 deg as speed increases -- CEV */ \
turn = acos (PM_MAXAIRSPEED / self.speed); \
/* it's important to *not* handle NaN here -- CEV */ \
turn *= M_RAD2DEG; \
if (temp > turn) \
{ \
/* Q1 style air acceleration */ \
if (self.moveflags & PMF_CROUCHSLIDE) \
accel = PM_SLIDEACCELSIDE; \
else \
accel = PM_AIRACCELSIDE; \
if (wishspeed > PM_MAXAIRSPEED) \
wishspeed = PM_MAXAIRSPEED; \
turn = 0; \
} \
else \
{ \
/* Q3 style air acceleration */ \
if (wishvel * self.velocity < 0) \
{ \
if (self.moveflags & PMF_CROUCHSLIDE) \
accel = PM_SLIDEACCELFWD; \
else \
accel = PM_AIRACCELBACK; \
turn = 0; \
} \
else \
{ \
if (self.moveflags & PMF_CROUCHSLIDE) \
accel = PM_SLIDEACCELFWD; \
else \
accel = PM_AIRACCELFWD; \
/* when holding +fwd/+back -- CEV */ \
if (input_movevalues.x && \
input_movevalues.y == 0) \
{ \
/* +fwd air control -- CEV */ \
turn = PM_AIRACCELTURN; \
if (temp > 35.0f) \
{ \
/* turn fades to 0
* between 35 and 45
* degrees -- CEV */ \
turn *= (1 + ((35.0 - \
temp)/10.0)); \
if (turn < 0) \
turn = 0; \
} \
} \
else \
{ \
turn = 0; \
} \
} \
} \
} \
else if (self.moveflags & PMF_ONSLOPE) \
{ \
/* movement while surfing -- CEV */ \
accel = PM_SURFACCELSIDE; \
wishspeed = min (wishspeed, PM_MAXAIRSPEED); \
} \
else \
{ \
/* onground -- CEV */ \
if (self.moveflags & PMF_ONSLICK) \
{ \
/* ice physics -- CEV */ \
accel = PM_SLICKACCEL; \
if (wishspeed > PM_MAXSPEED) \
wishspeed = PM_MAXSPEED; \
} \
else \
{ \
if (self.moveflags & PMF_CROUCHED) \
{ \
wishspeed *= PM_CROUCHSCALE; \
if (wishspeed > PM_CROUCHSPEED) \
wishspeed = PM_CROUCHSPEED; \
} \
else if (self.moveflags & PMF_WALK_HELD) \
{ \
wishspeed *= PM_WALKSCALE; \
if (wishspeed > PM_WALKSPEED) \
wishspeed = PM_WALKSPEED; \
} \
else \
{ \
if (wishspeed > PM_MAXSPEED) \
wishspeed = PM_MAXSPEED; \
} \
if (self.moveflags & PMF_STEPPED) \
accel = PM_GROUNDACCEL * 0.1f; \
else \
accel = PM_GROUNDACCEL; \
} \
} \
} \
if (friction > 0) \
{ \
/* standard Quake friction -- CEV */ \
PM_FRICTION (speed1, temp, friction, move_time) \
} \
if (accel > 0) \
{ \
/* standard Quake ground accel function -- CEV */ \
PM_ACCELERATE (wishdir, wishspeed, speed1, temp, accel, \
move_time) \
} \
if (turn > 0) \
{ \
/* +fwd air control; no control when braking -- CEV */ \
PM_AIRCONTROL (wishdir, vec1, speed1, turn, move_time) \
} \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_MANAGETIMER
// self.movetimer is pos when jumping, neg when crouchsliding -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_MANAGETIMER(adjust_time) \
/* { */ \
if (self.movetimer) \
{ \
if (self.movetimer < 0) \
{ \
self.movetimer += adjust_time; \
if (self.movetimer > 0) \
self.movetimer = 0; \
} \
else \
{ \
self.movetimer -= adjust_time; \
if (self.movetimer < 0) \
self.movetimer = 0; \
} \
} \
if (self.moveflags & PMF_CROUCHSLIDE) \
{ \
if (self.movetimer >= 0) \
PM_CrouchSlideStop (); \
} \
if (self.moveflags & PMF_DOUBLEJUMPED) \
{ \
if (self.movetimer <= 0) \
self.moveflags &= ~PMF_DOUBLEJUMPED; \
} \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_PRE -- prepare to move the player -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_PRE() \
/* { */ \
/* truncate velocity to network precision -- CEV */ \
if (self.velocity != '0 0 0') \
{ \
PM_TRUNCATEVECTORTOEIGTH (self.velocity); \
} \
/* clear JUMP_HELD if it's set and the user isn't pressing jump */ \
if (!(input_buttons & PM_BTN_JUMP)) \
{ \
if (self.moveflags & PMF_JUMP_HELD) \
{ \
self.moveflags &= ~PMF_JUMP_HELD; \
} \
} \
/* crouch key, crouchsliding -- CEV */ \
if (input_buttons & PM_BTN_DOWN) \
{ \
self.moveflags |= PMF_CROUCH_HELD; \
} \
else \
{ \
self.moveflags &= ~PMF_CROUCH_HELD; \
if (self.moveflags & PMF_CROUCHSLIDE) \
PM_CrouchSlideStop (); \
} \
/* walk key, walking -- CEV */ \
if (input_buttons & PM_BTN_WALK) \
{ \
self.moveflags |= PMF_WALK_HELD; \
} \
else \
{ \
self.moveflags &= ~PMF_WALK_HELD; \
} \
/* in case engine code has found ground -- CEV */ \
if (self.flags & FL_ONGROUND) \
{ \
if (!(self.moveflags & PMF_ONGROUND)) \
{ \
self.moveflags |= PMF_ONGROUND; \
} \
} \
/* } */
//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES() \
/* { */ \
var vector vec1, vec2, wishdir, wishvel; \
var float accel, friction, speed1, temp, turn, wishspeed; \
vec1 = vec2 = wishdir = wishvel = '0 0 0'; \
accel = friction = speed1 = temp = turn = wishspeed = 0; \
var float itl = input_timelength; \
self.speed = vlen ([self.velocity.x, self.velocity.y, 0]); \
if (self.movetype == MOVETYPE_NOCLIP) \
{ \
PM_MOVE_MOVETYPES_SETNOCLIPFLAGS () \
makevectors (input_angles); \
wishvel = v_forward * input_movevalues.x; \
wishvel += v_right * input_movevalues.y; \
wishvel += v_up * input_movevalues.z; \
PM_MOVE_MOVETYPES_NOCLIPACCELERATE (itl) \
/* update origin directly -- CEV */ \
self.origin += self.velocity * itl; \
} \
else if (self.movetype == MOVETYPE_TOSS || \
self.movetype == MOVETYPE_BOUNCE) \
{ \
/* we should only get here when dead -- CEV */ \
if (self.health <= 0) \
{ \
input_movevalues = '0 0 0'; \
input_buttons = 0; \
self.button2 = 0; \
} \
/* let the engine handle BOUNCE and TOSS -- CEV */ \
runstandardplayerphysics (self); \
/* might as well copy ONGROUND state to moveflags -- CEV */ \
if (self.flags & FL_ONGROUND) \
{ \
if (!(self.moveflags & PMF_ONGROUND)) \
{ \
self.moveflags |= PMF_ONGROUND; \
} \
} \
} \
else \
{ \
/* the following vars will be used in macros below */ \
if (self.movetype == MOVETYPE_WALK) \
{ \
/* work out the properties of the player's position */ \
PM_MOVE_MOVETYPES_CATEGORIZEPOSITION () \
if (self.waterlevel < WATERLEVEL_WAIST) \
{ \
/* intended movement for walking/air accel */ \
/* only yaw matters here -- CEV */ \
makevectors (input_angles.y * '0 1 0'); \
v_forward.z = v_right.z = 0; \
wishvel = v_forward * input_movevalues.x + \
v_right * input_movevalues.y; \
wishvel.z = 0; \
wishdir = normalize (wishvel); \
wishspeed = vlen (wishvel); \
/* walking & air acceleration -- CEV */ \
PM_MOVE_MOVETYPES_WALKPREMOVE () \
/* don't factor in Z velocity when running
* ground/air accel functions -- CEV */ \
var float zsave = self.velocity.z; \
self.velocity.z = 0; \
PM_MOVE_MOVETYPES_WALKACCELERATE (itl) \
self.velocity.z = zsave; \
} \
else \
{ \
/* work out intended movement for swimming */ \
makevectors (input_angles); \
wishvel = v_forward * input_movevalues.x + \
v_right * input_movevalues.y + \
v_up * input_movevalues.z; \
wishdir = normalize (wishvel); \
wishspeed = vlen (wishvel); \
/* swim acceleration -- CEV */ \
PM_MOVE_MOVETYPES_SWIMPREMOVE (vec1, vec2, itl) \
PM_MOVE_MOVETYPES_SWIMACCELERATE (itl) \
} \
/* handle countdown timers -- CEV */ \
PM_MOVE_MOVETYPES_MANAGETIMER (itl) \
} \
else if (self.movetype == MOVETYPE_FLY) \
{ \
PM_MOVE_MOVETYPES_SETNOCLIPFLAGS () \
makevectors (input_angles); \
wishvel = v_forward * input_movevalues.x; \
wishvel += v_right * input_movevalues.y; \
wishvel += v_up * input_movevalues.z; \
PM_MOVE_MOVETYPES_NOCLIPACCELERATE (itl) \
} \
/* step if nostep is false and we're not on a ladder... */ \
if (world_nostep == FALSE) { \
if (!(self.moveflags & PMF_ONLADDER)) \
{ \
/* ... and either airstep is true or we're onground */ \
if (world_airstep == TRUE) \
self.moveflags |= PMF_SLIDE_STEP; \
else if (self.moveflags & PMF_ONGROUND) \
self.moveflags |= PMF_SLIDE_STEP; \
} } \
/* clear stepped flags -- CEV */ \
if (self.moveflags & PMF_STEPPED) \
{ \
self.moveflags &= ~PMF_STEPPED; \
self.moveflags &= ~PMF_AIRSTEPPED; \
} \
/* bound velocity to fixed server maximums -- CEV */ \
if (self.velocity != '0 0 0') \
{ \
BASE_ENTITY_CHECKVELOCITY (self.velocity) \
} \
/* Do the move. Bounce, Rock, Skate, Roll -- CEV */ \
PM_DanceMove (itl); \
/* clear ladder flag -- CEV */ \
if (self.moveflags & PMF_ONLADDER) \
self.moveflags &= ~PMF_ONLADDER; \
setorigin (self, self.origin); \
touchtriggers (self); \
base_entity_positioncontents (self); \
} \
/* } */
#ifdef SSQC
#define PM_MOVE_POST_FALLDAMAGE() \
/* { */ \
self.deathtype = "falling"; \
t_damage2 (self, world, world, 5); \
self.deathtype = ""; \
/* } */
#endif
#ifdef CSQC
#define PM_MOVE_POST_FALLDAMAGE() \
/* { */ \
/* } */
#endif
//----------------------------------------------------------------------
// PM_MOVE_POST -- handle any after-move operations -- CEV
//
// If we have velocity then restrict it to server maximums and truncate
// it to network precision -- CEV
// Then udpate speed, origin, and oldorigin -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_POST() \
/* { */ \
/* in case engine code has found ground -- CEV */ \
if (self.moveflags & PMF_ONGROUND) \
{ \
if (!(self.flags & FL_ONGROUND)) \
{ \
self.flags |= FL_ONGROUND; \
} \
} \
/* check to see if player landed and play landing sound */ \
if (self.jump_flag < -300 && self.health > 0) \
{ \
if (self.watertype == CONTENT_WATER) \
{ \
player_sound (snd_player_jumpwater, TRUE); \
} \
else if (self.jump_flag < -650) \
{ \
PM_MOVE_POST_FALLDAMAGE () \
player_sound (snd_player_land_02, TRUE); \
} \
else \
{ \
player_sound (snd_player_land_01, TRUE); \
} \
self.pain_finished = time + 0.5; \
self.jump_flag = 0; \
} \
if (self.velocity != '0 0 0') \
{ \
BASE_ENTITY_CHECKVELOCITY (self.velocity) \
PM_TRUNCATEVECTORTOEIGTH (self.velocity) \
} \
self.speed = vlen ([self.velocity.x, self.velocity.y, 0]); \
/* } */
#endif
Return to the top of this page or return to the overview of this repo.
Log pmove.qc
Date | Commit Message | Author | + | - |
---|---|---|---|---|
2025-08-13 | Another big commit. Item changes, field rework, etc. | cev | +765 | -836 |
2025-03-30 | Big commit. Entity networking, etc. | cev | +690 | -473 |
2024-11-20 | pmove refactor into prepoc macros, view bobbing | cev | +1314 | -1337 |
2024-09-17 | Ice, improved stairs, other movement changes | cev | +791 | -698 |
2024-07-17 | pmove changes, smooth crouching | cev | +745 | -689 |
2024-07-03 | pmove changes and fixes, improved climbing | cev | +795 | -1002 |
2024-06-26 | pmove fixes, GL now a faux tribolt, wall climbing | cev | +392 | -666 |
2024-06-15 | Major update, committing as-is, will have bugs | cev | +623 | -230 |
2024-04-12 | Moveable gibs, heads, some bugfixes | cev | +18 | -6 |
2024-04-08 | Registered monsters, projectile bugfixes | cev | +1 | -1 |
2024-04-05 | Player footsteps, shareware monsters, misc? | cev | +9 | -9 |
2024-03-24 | 2nd pass refactor, rework QC class structure | cev | +272 | -223 |
2024-02-27 | Bullet projectile, pmove changes, misc | cev | +1267 | -1561 |
2024-02-18 | Client/player, projectiles, entrypoints refactor | cev | +2220 | |
2023-12-09 | Start OO / class-based refactor, work on items | cev | -2139 | |
2023-12-02 | More refactoring & moving, begin adding mdls & snd | cev | +2 | -1 |
2023-11-27 | Code reorg, minor movement changes, misc | cev | +103 | -31 |
2023-11-20 | changes to movement, build environment, file reorg | cev | +491 | -300 |
2023-11-16 | pmove bug fixes, moved q3 compat code, cleanup | cev | +167 | -123 |
2023-11-13 | Clean up timers, more bugs in pmove & prediction | cev | +189 | -175 |
2023-11-11 | pmove ladders & water move, fix a prediction error | cev | +368 | -223 |
2023-11-09 | SlideMove, accel, and timer changes in pmove | cev | +400 | -349 |
2023-11-08 | Refinements to NuclideMove, step smoothing change | cev | +358 | -89 |
2023-11-07 | Extended the Nuclide slidemove | cev | +958 | -937 |
2023-11-06 | First draft client prediction, pmove changes, misc | cev | +627 | -247 |
2023-10-14 | Q3 jumppads (from Nexuiz), Refactors & removals | cev | +69 | -305 |
2023-10-13 | Seperate accel and decel values for air control | cev | +10 | -3 |
2023-10-13 | New movement code based on SV_RunClientCommand | cev | +1180 |
Return to the top of this page or return to the overview of this repo.