djcev.com

//

Git Repos / fte_dogmode / qc / pmove.qc

Last update to this file was on 2024-11-20 at 23:54.

Show pmove.qc

//==============================================================================
// PMOVE
//==============================================================================

// TODO CEV: improved player unstick function
// TODO CEV: varied sounds (different jump sounds)
// TODO CEV: fix telejump check (teleporter time isn't networked to CSQC)

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

//======================================================================
// fields
//======================================================================

#if defined(CSQC) || defined(SSQC)
// note: timers are expensive, require synch between server and client -- CEV
// .entity groundentity; // already defined in entvars_t
.vector groundnormal; // ground plane normal; NOT networked
.float pm_flags; // custom movement flags -- CEV
.float pm_timer; // crouchslide & jump timer -- CEV
.void() customphysics;
#endif

//======================================================================
// pmove constants (could be reworked as cvars, would add overhead) -- CEV
//======================================================================

#if defined(CSQC) || defined(SSQC)
// acceleration & friction
// 10 - 6.5265 = 3.4375
// 3.4375 / 2.0 = 1.71875
// 10 - 1.71875 = 8.28125
const float PM_AIRACCELY = 70.0f; // (320 / 30) * 6.5625; 106.666 in Q1
const float PM_AIRACCELXY = 1.0f; // for Q3 strafejumping; 1.0 in Q3; 0.85
const float PM_AIRACCELXFWD = 1.0f; // 1 feels close to Q3 / CPM; 0.85f
const float PM_AIRACCELXBACK = 2.5f; // Air stop speed in Q3? 5.0f?
const float PM_AIRACCELBASE = 32.0f; //
const float PM_AIRACCELTURN = 250.0f; // affects +fwd turning radius; 150.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_SLIDEACCELY = 70.0f; //
const float PM_SLIDEACCELXY = 1.0f; // crouchslide accel; 0.85f
const float PM_SLIDEFRICTION = 0.25f; // crouchslide friction; 1.0?
const float PM_SURFACCELY = 106.666f; // (320 / 30) * 10.0
const float PM_SURFACCELXY = 1.0f; //
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_MAXSPEED = 320.0f; // 320 always
const float PM_MAXAIRSPEED = 30.0f; // 30 for Q1 (PM_MAXSPEED / 10.666)
const float PM_MAXWISHSPEED = 400.0f; // 320, 400
const float PM_STOPSPEED = 100.0f; // used in friction calculations
const float PM_WALKSPEED = 160.0f; // walk speed
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
const float PM_STAIRJUMPSPEED = 360.0f; // 360 = 90 * 4
const float PM_TELEJUMPSPEED = 360.0f; // same as STAIRJUMPSPEED
const float PM_WALLJUMPFORCE = 90.0f; // push away from wall
const float PM_WALLJUMPGROUND = 28.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_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
#if defined(SSQC) // floor is within this distance
const float PM_MOVEFLAGS = MOVE_NORMAL;
#elif defined(CSQC)
const float PM_MOVEFLAGS = MOVE_NORMAL; // is MOVE_LAGGED supported in CSQC?
#endif

// 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
// this is currently 19 flags; max flags for a float is 23 -- CEV
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
};

// 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)
// PM_CHECKVELOCITY(vel)
float() PM_Nudge;
// PM_SETONGROUND_NOGROUND()
// PM_SETONGROUND_TRACEGROUND()
// PM_SETONGROUND()
// PM_DANCEMOVE_ADDTOUCH(ent)
// PM_DANCEMOVE_DOTOUCH(ent)
// PM_DANCEMOVE_AXIALNUDGE(plane)
// PM_DANCEMOVE_CLIP_INNER(v, pl_a, pl_b, pl_c, k)
// PM_DANCEMOVE_CLIP(v, pl_i, pl_j, pl_k, k)
// PM_DANCEMOVE_GROUNDIMPACT()
// PM_DANCEMOVE(end, start_vel, i, j, k)
void() PM_CrouchStart;
void() PM_CrouchStop;
void() PM_CrouchSlideStart;
void() PM_CrouchSlideStop;
void() 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, wish, cur, new, zspeed, dot, move_time)
// PM_MOVE_MOVETYPES_CATEGORIZEPOSITION()
// PM_MOVE_MOVETYPES_SETNOCLIPFLAGS()
// PM_MOVE_MOVETYPES_NOCLIPACCELERATE(scale, move_time)
// PM_MOVE_MOVETYPES_SWIMPREMOVE_SSQC()
// 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()
#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
local 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_CHECKVELOCITY - bound self.velocity to server maximums -- CEV
//----------------------------------------------------------------------
#define PM_CHECKVELOCITY(vel) \
{ \
vel.x = bound (-PM_MAXVELOCITY, vel.x, PM_MAXVELOCITY); \
vel.y = bound (-PM_MAXVELOCITY, vel.y, PM_MAXVELOCITY); \
vel.z = bound (-PM_MAXVELOCITY, vel.z, PM_MAXVELOCITY); \
}

//----------------------------------------------------------------------
// PM_Nudge
// from the GPL2 CSQCTest code that comes with FTEQW; is 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.pm_flags = self.pm_flags - (self.pm_flags & PM_NOGROUNDFLAGS); \
/* don't crouchslide in air -- CEV */ \
if (self.pm_flags & PMF_CROUCHSLIDE) \
{ \
if (self.pm_timer < 0) \
self.pm_timer = 0; \
PM_CrouchSlideStop (); \
} \
}

//----------------------------------------------------------------------
#define PM_SETONGROUND_TRACEGROUND() \
{ \
/* do a trace to check for ground -- CEV */ \
tracebox (self.origin, self.mins, self.maxs, \
self.origin - '0 0 0.25', PM_MOVEFLAGS, self); \
}

//----------------------------------------------------------------------
// 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.pm_flags & PMF_ONGROUND)) { \
if (self.pm_flags & PMF_CROUCH_HELD) { \
if (!(self.pm_flags & PMF_CROUCHSLIDE)) { \
if (self.conlevel < 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.pm_flags & PMF_SLIDE_STEP)) \
{ \
self.flags |= FL_ONGROUND; \
self.pm_flags |= PMF_ONGROUND; \
} \
if (self.groundnormal.z < PM_PLANE_FLAT) \
self.pm_flags |= PMF_ONRAMP; \
else if (self.pm_flags & PMF_ONRAMP) \
self.pm_flags &= ~PMF_ONRAMP; \
if (trace_surfaceflagsf & Q3SURFACEFLAG_SLICK) \
/* on a slick/icy surface -- CEV */ \
self.pm_flags |= PMF_ONSLICK; \
self.pm_flags = self.pm_flags - \
(self.pm_flags & PM_ONGROUNDFLAGS); \
} \
else if (trace_plane_normal.z > PM_PLANE_VERTICAL) \
{ \
/* on a steep slope */ \
/* set groundentity and groundnormal -- CEV */ \
self.groundentity = __NULL__; \
self.groundnormal = '0 0 0'; \
/* now do flags -- CEV */ \
self.flags &= ~FL_ONGROUND; \
self.pm_flags |= PMF_ONSLOPE; \
self.pm_flags = self.pm_flags - \
(self.pm_flags & PM_ONSLOPEFLAGS); \
self.pm_timer = 0; \
/* don't crouchslide on a slope -- CEV */ \
if (self.pm_flags & PMF_CROUCHSLIDE) \
PM_CrouchSlideStop (); \
} \
else \
{ \
/* we probably shouldn't end up here -- CEV */ \
PM_SETONGROUND_NOGROUND () \
} \
/* flag when standing on a vertically moving ent -- CEV */ \
if (trace_networkentity || trace_ent.velocity.z != 0) \
self.pm_flags |= PMF_ONMOVINGENT; \
else if (self.pm_flags & PMF_ONMOVINGENT) \
self.pm_flags &= ~PMF_ONMOVINGENT; \
} \
else \
{ \
PM_SETONGROUND_NOGROUND () \
} \
}

//----------------------------------------------------------------------
#define PM_DANCEMOVE_ADDTOUCH(ent) \
{ \
if (ent) \
{ \
if (ent.touch) \
{ \
if (ent != touched_ent3) { \
if (ent != touched_ent2) { \
if (ent != touched_ent1) \
{ \
touched_ent3 = touched_ent2; \
touched_ent2 = touched_ent1; \
touched_ent1 = ent; \
} } } \
} \
} \
}

//----------------------------------------------------------------------
#define PM_DANCEMOVE_DOTOUCH(ent) \
{ \
if (ent) \
{ \
if (ent.touch != __NULL__) \
{ \
if (ent.touch != sub_null) \
{ \
stemp = self; \
otemp = other; \
other = self; \
self = ent; \
self.touch (); \
self = stemp; \
other = otemp; \
} \
} \
} \
}

//----------------------------------------------------------------------
#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, pl_a, pl_b, pl_c, k) \
{ \
/* test if plane_b exists, is not identical to plane_a, and
* that velocity interacts with it -- CEV */ \
if (pl_b) { \
if (pl_b != pl_a) \
{ \
k = self.velocity * pl_b; \
if (k < PM_PLANE_INTERACT) \
{ \
/* clip to plane_b (the second plane) -- CEV */ \
self.velocity -= pl_b * k; \
/* test if move goes back into the first plane */ \
if (self.velocity * pl_a < 0) \
{ \
/* slide along the crease -- CEV */ \
/* ("><" is crossproduct) -- CEV */ \
v = pl_a >< pl_b; \
v = normalize (v); \
self.velocity = v * (v * self.velocity); \
/* check if we interact with a 3rd plane */ \
if (pl_c) { \
if (self.velocity * pl_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, pl_i, pl_j, pl_k, k) \
{ \
/* test if plane_i exists & if velocity interacts with it -- CEV */ \
if (pl_i) \
{ \
k = self.velocity * pl_i; \
if (k < PM_PLANE_INTERACT) \
{ \
/* clip to the plane -- CEV */ \
self.velocity -= pl_i * k; \
/* check for 2nd and 3rd plane interactions -- CEV */ \
PM_DANCEMOVE_CLIP_INNER (v, pl_i, pl_j, pl_k, k) \
PM_DANCEMOVE_CLIP_INNER (v, pl_i, pl_k, pl_j, k) \
} \
} \
}

//----------------------------------------------------------------------
// PM_DANCEMOVE_GROUNDIMPACT
// This macro is meant to be called from PM_DANCEMOVE -- CEV
//----------------------------------------------------------------------
#ifdef SSQC
#define PM_DANCEMOVE_GROUNDIMPACT() \
{ \
/* impact info (see player_postthink) -- CEV */ \
if (!(self.pm_flags & PMF_ONGROUND)) { \
if (self.velocity.z < self.jump_flag) \
{ \
self.jump_flag = self.velocity.z; \
} } \
}
#endif

#ifdef CSQC
#define PM_DANCEMOVE_GROUNDIMPACT() \
{ \
}
#endif

//----------------------------------------------------------------------
// 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
//----------------------------------------------------------------------
#define PM_DANCEMOVE(end, start_vel, i, j, k) \
{ \
local float grav = 0; \
if (self.conlevel < WATERLEVEL_WAIST) { \
if (!(self.pm_flags & PMF_ONLADDER)) { \
if (!(self.pm_flags & PMF_ONGROUND) || self.pm_flags & PMF_ONRAMP) \
{ \
if (self.gravity) \
grav = self.gravity; \
else \
grav = 1.0; \
/* world_gravity is set in worldspawn() -- CEV */ \
grav *= world_gravity * input_timelength; \
/* 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 */ \
local vector plane1, plane2; \
local float time_left = input_timelength; \
local entity stemp, otemp, touched_ent1, touched_ent2, touched_ent3; \
plane1 = plane2 = '0 0 0'; \
i = j = k = 0; \
start_vel = self.velocity; \
stemp = otemp = touched_ent1 = touched_ent2 = touched_ent3 = __NULL__; \
/* 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) \
{ \
/* self is in something else; attempt to nudge out */ \
if (PM_Nudge()) \
continue; \
else \
setorigin (self, self.oldorigin); \
/* nah, we're stuck. don't build up falling damage
* but allow sideways acceleration -- CEV */ \
dprint ("PM_DANCEMOVE: player entity stuck!"); \
dprint (sprintf(" timestamp %g\n", time)); \
self.velocity.z = 0; \
break; \
} \
/* 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.pm_flags & PMF_SLIDE_STEP) { \
if (time_left > 0) { \
if (trace_plane_normal.z <= PM_PLANE_GROUND) \
{ \
/* store the entity and plane normal from above in
* case we need to reject the step attempt -- CEV */ \
local vector first_plane = trace_plane_normal; \
local entity first_ent = trace_ent; \
local float first_frac = trace_fraction; \
local float first_net = trace_networkentity; \
j = time_left; \
/* first: move up */ \
trace_endpos.z += PM_STEPHEIGHT; \
tracebox (self.origin, self.mins, self.maxs, \
trace_endpos, PM_MOVEFLAGS, self); \
if (trace_allsolid || trace_startsolid) \
goto PM_DanceMove_RejectStep2; \
if (trace_endpos.z - self.origin.z <= 0) \
{ \
/* dprint (sprintf("PM_DANCEMOVE: A %v\n", \
trace_endpos - self.origin)); */ \
goto PM_DanceMove_RejectStep2; \
} \
local vector roof_plane = '0 0 0'; \
if (trace_fraction < 1.0f) \
{ \
PM_DANCEMOVE_ADDTOUCH (trace_ent) \
roof_plane = trace_plane_normal; \
} \
/* 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, \
PM_MOVEFLAGS, self); \
if (trace_allsolid) \
goto PM_DanceMove_RejectStep2; \
/* 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) { \
if (fabs(self.origin.x - trace_endpos.x) < PM_FLMIN) \
if (fabs(self.origin.y - trace_endpos.y) < PM_FLMIN) \
/* dprint (sprintf("PM_DANCEMOVE: B\n")); */ \
goto PM_DanceMove_RejectStep2; \
} \
local vector fwd_plane = '0 0 0'; \
/* local vector save_pos = trace_endpos; */ \
j -= j * trace_fraction; \
if (trace_fraction < 1.0f) \
{ \
PM_DANCEMOVE_ADDTOUCH (trace_ent) \
fwd_plane = trace_plane_normal; \
} \
/* third: move down; +1 to k is important -- CEV */ \
end = trace_endpos; \
end.z -= k + 1; \
tracebox (trace_endpos, self.mins, self.maxs, end, \
PM_MOVEFLAGS, self); \
/*
if (trace_endpos == save_pos) \
{ \
dprint (sprintf("PM_DANCEMOVE: D\n")); \
goto PM_DanceMove_RejectStep2; \
} \
*/ \
/* 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) \
{ \
/*
dprint (sprintf("PM_DANCEMOVE: norm %v, " \
"diff %v, time %g\n", \
trace_plane_normal, \
trace_endpos - self.origin, \
j)); \
*/ \
/* Check the plane just in case -- CEV */ \
if (trace_plane_normal.z != 0) \
{ \
dprint ("PM_DANCEMOVE: C\n"); \
goto PM_DanceMove_RejectStep2; \
} \
/* 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) \
goto PM_DanceMove_RejectStep2; \
} \
else \
{ \
/* TODO CEV we still need to reject the
* step here, don't fully understand
* the reason why -- CEV */ \
/*
dprint (sprintf("PM_DANCEMOVE: Z %v\n",\
trace_endpos - save_pos)); \
*/ \
goto PM_DanceMove_RejectStep2; \
} \
} \
else if (trace_plane_normal.z <= PM_PLANE_GROUND) \
{ \
/* this is the expected and regular
* not-good-ground step rejection -- CEV */ \
goto PM_DanceMove_RejectStep2; \
} \
if (trace_allsolid == FALSE) \
{ \
/* 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) \
plane2 = plane1 = '0 0 0'; \
if (roof_plane) { \
if (roof_plane != trace_plane_normal) \
{ \
plane1 = roof_plane; \
} } \
if (fwd_plane) { \
if (fwd_plane != trace_plane_normal) \
{ \
plane2 = plane1; \
plane1 = fwd_plane; \
} } \
self.pm_flags |= PMF_STEPPED; \
if (!(self.pm_flags & PMF_ONGROUND)) { \
if (start_vel.z <= 0) \
{ \
/* for stairjumps. -- CEV */ \
self.pm_flags |= PMF_AIRSTEPPED; \
} } \
} \
else \
{ \
/* discard the step attempt -- CEV */ \
PM_DanceMove_RejectStep2: \
trace_plane_normal = first_plane; \
/* TODO CEV rewind touched ents? */ \
trace_ent = first_ent; \
trace_fraction = first_frac; \
trace_networkentity = first_net; \
} \
} } } \
PM_DANCEMOVE_AXIALNUDGE (self.velocity) \
PM_DANCEMOVE_AXIALNUDGE (plane1) \
PM_DANCEMOVE_AXIALNUDGE (plane2) \
/* 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) \
PM_DANCEMOVE_CLIP (end, plane1, trace_plane_normal, plane2, k) \
PM_DANCEMOVE_CLIP (end, plane2, plane1, trace_plane_normal, k) \
/* store current plane and plane1 for the next pass -- CEV */ \
if (trace_plane_normal != self.groundnormal) \
{ \
plane2 = plane1; \
plane1 = trace_plane_normal; \
} \
/* stop if we've turned against original velocity -- CEV */ \
if (self.velocity * start_vel <= 0) \
self.velocity = '0 0 0'; \
/* stop the loop if velocity is now zero -- CEV */ \
if (self.velocity == '0 0 0') \
break; \
} \
/* call the touch functions of any entities we've collided with
* (as needed) -- 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.pm_flags & PMF_ONGROUND)) { \
if (self.pm_timer > PM_WALLCLIP_WINDOW) \
{ \
j = start_vel.z; \
k = self.velocity.z; \
self.velocity = start_vel; \
/* 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.pm_timer > 0) { \
if (self.velocity.z) { \
if (self.groundnormal.z > PM_PLANE_GROUND) { \
if (!(self.pm_flags & PMF_CROUCH_HELD)) { \
if (!(self.pm_flags & PMF_WALK_HELD)) { \
if (!(self.pm_flags & PMF_DOUBLEJUMPED)) \
{ \
/*
dprint (sprintf("PM_DANCEMOVE: glue %f\n", self.origin.z));
*/ \
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.pm_flags = self.pm_flags - (self.pm_flags & PM_SLIDEFLAGS); \
}

//----------------------------------------------------------------------
void() PM_CrouchStart =
{
// crouch
self.pm_flags |= 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.pm_flags &= ~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 =
{
if (self.pm_timer >= 0)
{
#ifdef SSQC
#if 0
sound (self, CHAN_AUTO, "player/slidestart.ogg",
0.4, ATTN_FEET);
#endif
sound (self, CHAN_SLIDE, "player/slide.ogg",
0.2, ATTN_FEET);
#endif
self.pm_timer = -PM_CROUCHSLIDE_TIME;
}

#ifdef SSQC
dprint (sprintf("PM_CrouchSlideStart: lesgo %g\n", self.velocity.z));
#endif

self.pm_flags |= PMF_CROUCHSLIDE;
};

//----------------------------------------------------------------------
// PM_CrouchSlideStop
//----------------------------------------------------------------------
void() PM_CrouchSlideStop =
{
self.pm_flags &= ~PMF_CROUCHSLIDE;

#ifdef SSQC
sound (self, CHAN_SLIDE, "misc/null.wav", 0.4, ATTN_FEET);
#endif
};

//----------------------------------------------------------------------
// PM_Jump
//----------------------------------------------------------------------
void() PM_Jump =
{
// are we already waterjumping, or is jump being held?
if (self.pm_flags & PMF_WATERJUMPED)
return;
if (self.pm_flags & PMF_JUMP_HELD)
return;

#ifdef SSQC
local string wav = "";
local float vol = 0;
#endif

// 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)
if (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.pm_timer > 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
// non-additive jump, though it shouldn't matter -- CEV
self.velocity.z = PM_TELEJUMPSPEED;
}
else if (self.pm_flags & PMF_ONRAMP)
{
// the groundnormal is weird - a ramp - so we
// want an additive doublejump here -- CEV
self.velocity.z += PM_DOUBLEJUMPSPEED;
}
else
{
// flat ground, don't do an additive jump -- CEV
self.velocity.z = PM_STAIRJUMPSPEED;
}

#ifdef SSQC
vol = 0.9;
wav = "player/plyrjmp8.wav";
#endif

// set the doublejump flag -- CEV
self.pm_flags |= PMF_DOUBLEJUMPED;
}
else
{
// normal jump
#ifdef SSQC
local float r2 = rint (random() * 3);
vol = 0.1;
wav = sprintf ("player/jump0%g.ogg", r2 + 1);
#endif

if (self.pm_flags & PMF_ONRAMP)
{
// do an 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.pm_flags & PMF_AIRSTEPPED)
self.velocity.z = PM_JUMPSPEED;
else if (self.pm_flags & PMF_CROUCH_HELD)
self.velocity.z = PM_JUMPSPEED;
else if (self.pm_flags & PMF_WALK_HELD)
self.velocity.z = PM_JUMPSPEED;
else if (!(self.pm_flags & PMF_STEPPED))
self.velocity.z = PM_JUMPSPEED;
#if 0
else
dprint (sprintf("PM_Jump: eating jump; %f\n",
self.origin.z));
#endif
}
}

// manage flags -- CEV
PM_SETONGROUND_NOGROUND ()
self.pm_flags |= PMF_JUMP_HELD;

if (self.pm_flags & PMF_CROUCHSLIDE)
PM_CrouchSlideStop ();

#ifdef SSQC
// player jumping sound; moved into pmove from client.qc -- CEV
if (wav != "")
sound (self, CHAN_BODY, wav, vol, ATTN_NORM);
player_footstep ();
#endif

// timers for all jumps -- CEV
self.pm_timer = PM_DOUBLEJUMP_WINDOW;
};

//----------------------------------------------------------------------
// PM_WallClimb -- climbing; checks performed in PM_WallJumpCheck -- CEV
//----------------------------------------------------------------------
void() PM_WallClimb =
{
// accelerate upwards toward a vertical ledge -- CEV
local float grav;
local 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 pm_timer so we don't stick to the floor -- CEV
self.pm_flags |= PMF_CLIMB;
self.pm_flags |= PMF_JUMP_HELD;
self.pm_timer = 0;

#if 0
dprint (sprintf("PM_WallClimb: vel.z %g\n", self.velocity.z));
#endif
};

//----------------------------------------------------------------------
// PM_WallJump -- Perform an actual walljump -- CEV
//----------------------------------------------------------------------
void() PM_WallJump =
{
#ifdef SSQC
local float vol = 0;
local string wav = "";
#endif

// 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.pm_timer > 0)
{
#ifdef SSQC
vol = 0.9;
wav = "player/plyrjmp8.wav";
#endif
self.velocity.z = PM_WALLJUMPDOUBLE;
}
else
{
#ifdef SSQC
local float r = rint (random() * 3);
vol = 0.2;
wav = sprintf ("player/jump0%g.ogg", r + 1);
#endif
self.velocity.z = PM_WALLJUMPSPEED;
}

// manage flags & fields -- CEV
self.pm_flags |= PMF_JUMP_HELD;
self.pm_flags |= PMF_WALLJUMPED;
self.pm_timer = 0;

// server-side stuff
#ifdef SSQC
player_footstep ();
if (wav != "")
sound (self, CHAN_BODY, wav, vol, ATTN_NORM);
#endif
};

//----------------------------------------------------------------------
// PM_WallJumpCheck -- Wall jumping, mantling, and climbing -- CEV
//----------------------------------------------------------------------
void() PM_WallJumpCheck =
{
// are we in water, waterjumping, or falling too fast? -- CEV
if (self.conlevel > WATERLEVEL_NONE)
return;
if (self.pm_flags & 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);
local float grounddist = self.origin.z - trace_endpos.z;
if (grounddist <= PM_WALLJUMPGROUND)
if (trace_plane_normal.z > PM_PLANE_GROUND)
return;

local float i, checks;
local vector start, end;
checks = (self.pm_flags & 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);

#if 0
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.pm_flags & PMF_JUMP_HELD)) {
if (!(self.pm_flags & 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.pm_flags & 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

local 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
self.pm_flags &= ~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.pm_timer > 0) \
temp = speed1; \
else if (speed1 < PM_STOPSPEED) \
temp = PM_STOPSPEED; \
else \
temp = speed1; \
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) \
{ \
newspeed = accel * move_time * wish; \
newspeed = min (newspeed, speed1); \
self.velocity += newspeed * dir; \
} \
}

//----------------------------------------------------------------------
#define PM_AIRCONTROL(dir, wish, cur, new, zspeed, dot, move_time) \
{ \
zspeed = self.velocity.z; \
self.velocity.z = 0; \
cur = vlen (self.velocity); \
self.velocity = normalize (self.velocity); \
dot = self.velocity * dir; \
if (dot > 0) \
{ \
new = PM_AIRACCELBASE; \
new *= bound (0, wish / PM_MAXAIRSPEED, 1); \
new *= PM_AIRACCELTURN * dot * dot * move_time; \
self.velocity = self.velocity * cur + dir * new; \
self.velocity = normalize (self.velocity); \
} \
self.velocity *= cur; \
self.velocity.z = zspeed; \
}

//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_CATEGORIZEPOSITION
// Based on similarly-named function in the GPL2 purecsqc pmove.qc
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_CATEGORIZEPOSITION() \
{ \
/* if Z velocity is greater than 180 and we're not on a ramp or
* a steep slope then assume we're not onground (an optimization
* from Warsow / Warfork) -- CEV */ \
if (self.velocity.z > 180) \
{ \
if (self.pm_flags & PMF_ONRAMP) \
{ \
PM_SETONGROUND_TRACEGROUND () \
PM_SETONGROUND () \
} \
else if (self.pm_flags & PMF_ONSLOPE) \
{ \
PM_SETONGROUND_TRACEGROUND () \
PM_SETONGROUND () \
} \
else \
{ \
PM_SETONGROUND_NOGROUND () \
} \
} \
else \
{ \
/* 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.pm_flags & PMF_CROUCHSLIDE) { \
if (self.pm_flags & PMF_ONGROUND) { \
if (self.conlevel >= WATERLEVEL_WAIST) \
{ \
if (self.pm_timer < 0) \
self.pm_timer = 0; \
PM_CrouchSlideStop (); \
} } } \
/* can't be waterjumping if we're on ground */ \
if (self.pm_flags & PMF_WATERJUMPED) { \
if (self.conlevel == WATERLEVEL_NONE || self.pm_flags & PMF_ONGROUND) \
{ \
self.pm_flags &= ~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.pm_flags & PMF_DOUBLEJUMPED) \
self.pm_flags &= ~PMF_DOUBLEJUMPED; \
if (self.pm_flags & PMF_WALLJUMPED) \
self.pm_flags &= ~PMF_WALLJUMPED; \
if (self.pm_flags & PMF_ONGROUND) \
self.pm_flags &= ~PMF_ONGROUND; \
if (self.flags & FL_ONGROUND) \
self.flags &= ~FL_ONGROUND; \
if (self.pm_flags & PMF_PUSHED) \
self.pm_flags &= ~PMF_PUSHED; \
if (self.pm_flags & PMF_CLIMB) \
self.pm_flags &= ~PMF_CLIMB; \
self.pm_timer = 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) \
}

#ifdef SSQC
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SWIMPREMOVE_SSQC() \
{ \
/* functionality copied into pmove from client.qc -- CEV */ \
if (self.conlevel >= WATERLEVEL_WAIST) { \
if (self.conlevel >= WATERLEVEL_EYES) { \
if (self.swim_time < time) \
{ \
/* play swimming sound */ \
self.swim_time = time + 1; \
if (random() < 0.5) \
sound (self, CHAN_BODY, "misc/water1.wav", \
0.6, ATTN_NORM); \
else \
sound (self, CHAN_BODY, "misc/water2.wav", \
0.6, ATTN_NORM); \
} } } \
}
#endif

#ifdef CSQC
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SWIMPREMOVE_SSQC() \
{ \
}
#endif

//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_SWIMPREMOVE -- water jump, water wading sounds -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SWIMPREMOVE(start, end, move_time) \
{ \
/* CheckWaterJump */ \
if (self.conlevel == 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.pm_flags |= PMF_WATERJUMPED; \
self.flags &= ~FL_JUMPRELEASED; \
self.pm_flags &= ~PMF_JUMP_HELD; \
/* Z velocity was 225 */ \
self.velocity.z = PM_JUMPSPEED; \
} \
} \
} \
PM_MOVE_MOVETYPES_SWIMPREMOVE_SSQC () \
if (self.pm_flags & PMF_STEPPED) \
{ \
self.pm_flags &= ~PMF_STEPPED; \
self.pm_flags &= ~PMF_AIRSTEPPED; \
} \
}

//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_SWIMACCELERATE
// Largely copied from id Software's WaterMove -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_SWIMACCELERATE(move_time) \
{ \
if (!(self.pm_flags & 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.pm_flags & 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.pm_flags & PMF_ONLADDER) \
{ \
local float zspeed = 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, zspeed, \
PM_CLIMBSPEEDMAX); \
} \
else if (input_buttons & PM_BTN_DOWN || input_movevalues.z < 0)\
{ \
/* PlayerClimbDown -- CEV */ \
self.velocity.z = bound (-PM_CLIMBSPEEDMAX, -zspeed, \
-PM_CLIMBSPEEDMIN); \
} \
else \
{ \
self.flags |= FL_JUMPRELEASED; \
self.pm_flags &= ~PMF_JUMP_HELD; \
self.velocity.z = 0; \
} \
if (self.pm_flags & PMF_ONGROUND) \
PM_SETONGROUND_NOGROUND () \
self.pm_timer = 0; \
} \
else \
{ \
if (input_buttons & PM_BTN_JUMP) \
{ \
/* +jump was pressed */ \
if (self.pm_flags & PMF_ONGROUND) \
{ \
PM_Jump (); \
} \
else if (world_walljump) \
{ \
PM_WallJumpCheck (); \
} \
} \
else \
{ \
self.flags |= FL_JUMPRELEASED; \
self.pm_flags &= ~PMF_JUMP_HELD; \
} \
if (input_buttons & PM_BTN_DOWN && \
!(self.pm_flags & PMF_CROUCHED)) \
{ \
PM_CrouchStart (); \
} \
else if (!(input_buttons & PM_BTN_DOWN) && \
self.pm_flags & PMF_CROUCHED) \
{ \
PM_CrouchStop (); \
} \
if (self.pm_flags & PMF_STEPPED) \
{ \
self.pm_flags &= ~PMF_STEPPED; \
self.pm_flags &= ~PMF_AIRSTEPPED; \
} \
} \
/* determine the user's requested movement direction / angle.
* inspired by IsMoveInDirection from Nexuiz / Xonotic; this
* is slow and could be improved -- CEV */ \
if (wishspeed) \
{ \
/* work out the requested movement direction -- CEV */ \
/* vectoyaw ignores the Z component of its input -- CEV */ \
wishyaw -= vectoyaw (input_movevalues); \
wishyaw = fabs (wishyaw - 360.0f * rint(wishyaw / 360.0f)); \
/* we don't care about 180 (backward) wishyaw; we'll test
* the dotproduct of wishvel and self.velocity later to
* determine if we're slowing down or speeding up -- CEV */ \
if (wishyaw == 180) \
wishyaw = 0; \
if (wishyaw == 0) \
{ \
/* test if the player is looking perpendicular to
* the direction they're moving in (+/- 30 degrees)
* if that's the case then alter wishyaw so the
* checks below will do Q1 air accel -- CEV */ \
temp = input_angles.y - vectoyaw(self.velocity); \
temp = fabs (temp - 360.0f * rint(temp / 360.0f)); \
wishyaw = -wishyaw; \
if (temp > 60.0f) \
if (temp < 120.0f) \
wishyaw = -90.0f; \
} \
} \
}

//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_WALKACCELERATE -- accel & friction for MOVETYPE_WALK -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_WALKACCELERATE(move_time) \
{ \
accel = friction = speed1 = temp = 0; \
/* priority and sequence of these checks is important -- CEV */ \
if (self.pm_flags & PMF_ONGROUND) \
{ \
if (self.pm_flags & PMF_ONSLICK) \
{ \
/* ice physics; friction remains 0 -- CEV */ \
wishnew = min (wishspeed, PM_MAXSPEED); \
if (wishspeed) \
accel = PM_SLICKACCEL; \
} \
else if (self.pm_flags & PMF_CROUCHSLIDE) \
{ \
/* movement while crouchsliding -- CEV */ \
wishnew = min (wishspeed, PM_CROUCHSPEED); \
/* water level adds extra friction -- CEV */ \
friction = PM_SLIDEFRICTION + self.conlevel; \
if (wishspeed) { \
if (fabs(wishyaw) == 90) \
{ \
/* Q1 air accel -- CEV */ \
/* same behavior as when in the air -- CEV */ \
accel = PM_SLIDEACCELY; \
wishnew = min (wishspeed, PM_MAXAIRSPEED); \
} \
else if (wishyaw == 0) \
{ \
/* CPM / Painkiller style control -- CEV */ \
if (wishvel * self.velocity < 0) \
accel = PM_AIRACCELXBACK; \
else \
accel = PM_AIRACCELXFWD; \
} \
else \
{ \
/* regular ground accel -- CEV */ \
accel = PM_SLIDEACCELXY; \
} } \
} \
else \
{ \
/* movement onground -- CEV */ \
if (self.pm_flags & PMF_STEPPED) \
/* changing the friction constant is sketchy;
* it might help with navigating stairs */ \
friction = PM_GROUNDFRICTION * 0.1f; \
else \
friction = PM_GROUNDFRICTION; \
if (wishspeed) \
{ \
if (self.pm_timer > 0) \
{ \
/* go directly to PM_MAXWISHSPEED
* for stairjumps -- CEV */ \
wishnew = PM_MAXWISHSPEED; \
} \
else \
{ \
if (self.pm_flags & PMF_CROUCHED) \
temp = PM_CROUCHSPEED; \
else if (self.pm_flags & PMF_WALK_HELD)\
temp = PM_WALKSPEED; \
else \
temp = PM_MAXSPEED; \
wishnew = min (wishspeed, temp); \
} \
if (self.pm_flags & PMF_STEPPED) \
accel = PM_GROUNDACCEL * 0.1f; \
else \
accel = PM_GROUNDACCEL; \
} \
} \
} \
else if (self.pm_flags & PMF_ONSLOPE) \
{ \
/* movement while surfing -- CEV */ \
if (wishspeed) \
{ \
if (wishyaw == 45 || wishyaw == 135) \
{ \
/* Q3 accel -- CEV */ \
accel = PM_SURFACCELXY; \
wishnew = min (wishspeed, PM_MAXSPEED); \
} \
else \
{ \
/* Q1 air accel -- CEV */ \
accel = PM_SURFACCELY; \
wishnew = min (wishspeed, PM_MAXAIRSPEED); \
} \
} \
} \
else if (wishspeed) \
{ \
/* movement in the air -- CEV */ \
if (fabs(wishyaw) == 90) \
{ \
/* Q1 air accel -- CEV */ \
if (wishyaw < 0) { \
if (self.speed < PM_MAXSPEED) \
{ \
/* if player is holding +fwd / +back and
* they're below MAXSPEED then nudge them
* towards wishdir -- CEV */ \
wishnew = min (wishspeed, PM_MAXSPEED); \
accel = PM_AIRACCELXY; \
PM_ACCELERATE (wishdir, wishnew, speed1, \
temp, accel, move_time) \
} } \
accel = PM_AIRACCELY; \
wishnew = min (wishspeed, PM_MAXAIRSPEED); \
} \
else \
{ \
wishnew = min (wishspeed, PM_MAXSPEED); \
/* apply a penalty to wishspeed if player is holding
* jump (simulating PM_CmdScale behavior in Quake 3)
* -- CEV */ \
if (self.pm_flags & PMF_JUMP_HELD) { \
/*
if (!(self.pm_flags & PMF_DOUBLEJUMPED)) \
{ \
*/ \
/* scale in Q3 is 2.519685 when not holding
* jump, 1.781686 when holding jump. 1.78 is
* roughly 70% of 2.51. -- CEV */ \
wishnew *= (0.7f + (0.3f * \
(1 - (self.pm_timer / \
PM_DOUBLEJUMP_WINDOW)))); \
/* } */ } \
if (wishyaw == 0) \
{ \
/* CPM / Painkiller air control -- CEV */ \
if (wishvel * self.velocity < 0) \
accel = PM_AIRACCELXBACK; \
else \
accel = PM_AIRACCELXFWD; \
} \
else \
{ \
/* X and Y in the air: strafejumping -- CEV */ \
accel = PM_AIRACCELXY; \
} \
} \
} \
if (friction > 0) \
{ \
/* standard Quake friction -- CEV */ \
PM_FRICTION (speed1, temp, friction, move_time) \
} \
if (accel) \
{ \
/* standard Quake ground accel function -- CEV */ \
PM_ACCELERATE (wishdir, wishnew, speed1, temp, accel, \
move_time) \
} \
if (wishspeed) { \
if (wishyaw == 0 || wishyaw == 180) \
{ \
PM_AIRCONTROL (wishdir, wishnew, speed1, temp, friction, \
accel, move_time) \
} } \
}

//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES_MANAGETIMER
// self.pm_timer is pos when jumping, neg when crouchsliding -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES_MANAGETIMER(adjust_time) \
{ \
if (self.pm_timer) \
{ \
if (self.pm_flags & PMF_CROUCHSLIDE) \
{ \
self.pm_timer += adjust_time; \
if (self.pm_timer >= 0) \
self.pm_timer = 0; \
} \
else \
{ \
self.pm_timer -= adjust_time; \
if (self.pm_timer <= 0) \
self.pm_timer = 0; \
} \
} \
if (self.pm_flags & PMF_CROUCHSLIDE) \
{ \
if (self.pm_timer >= 0) \
PM_CrouchSlideStop (); \
} \
if (self.pm_flags & PMF_DOUBLEJUMPED) \
{ \
if (self.pm_timer <= 0) \
self.pm_flags &= ~PMF_DOUBLEJUMPED; \
} \
}

//----------------------------------------------------------------------
// PM_MOVE_PRE -- prepare to move the player -- CEV
//----------------------------------------------------------------------
#define PM_MOVE_PRE() \
{ \
/* nudge player's origin if the server is sending low-precision
* (16 bit?) player coordinates. This can be fixed by setting
* sv_bigcoords to 1 (so the server sends float coords). -- CEV */ \
if (world_bigcoords == FALSE) \
{ \
PM_Nudge (); \
} \
/* 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)) \
{ \
self.pm_flags &= ~PMF_JUMP_HELD; \
} \
/* crouch key, crouchsliding -- CEV */ \
if (input_buttons & PM_BTN_DOWN) \
{ \
self.pm_flags |= PMF_CROUCH_HELD; \
} \
else \
{ \
self.pm_flags &= ~PMF_CROUCH_HELD; \
if (self.pm_flags & PMF_CROUCHSLIDE) \
PM_CrouchSlideStop (); \
} \
/* walk key, walking -- CEV */ \
if (input_buttons & PM_BTN_WALK) \
{ \
self.pm_flags |= PMF_WALK_HELD; \
} \
else \
{ \
self.pm_flags &= ~PMF_WALK_HELD; \
} \
}

//----------------------------------------------------------------------
// PM_MOVE_MOVETYPES
//----------------------------------------------------------------------
#define PM_MOVE_MOVETYPES() \
{ \
local vector wishdir, wishvel; \
local float speed1, temp, wishspeed, wishyaw, wishyawdiff; \
wishdir = wishvel = '0 0 0'; \
speed1 = temp = wishspeed = wishyaw = wishyawdiff = 0; \
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 (input_timelength) \
/* we're noclipping so update origin directly -- CEV */ \
self.origin += self.velocity * input_timelength; \
} \
else \
{ \
/* the following vars will be used in macros below */ \
local vector vec1, vec2; \
local float accel, friction, half_time, wishnew; \
vec1 = vec2 = '0 0 0'; \
accel = friction = wishnew = 0; \
half_time = input_timelength * 0.5f; \
if (self.movetype == MOVETYPE_WALK) \
{ \
/* work out the properties of the player's position */ \
PM_MOVE_MOVETYPES_CATEGORIZEPOSITION () \
if (self.conlevel < WATERLEVEL_WAIST) \
{ \
/* intended movement for walking/air accel */ \
/* only yaw matters here -- CEV */ \
makevectors (input_angles.y * '0 1 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 () \
PM_MOVE_MOVETYPES_WALKACCELERATE (half_time) \
} \
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, \
half_time) \
PM_MOVE_MOVETYPES_SWIMACCELERATE (half_time) \
} \
/* handle countdown timers -- CEV */ \
PM_MOVE_MOVETYPES_MANAGETIMER (half_time) \
} \
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 (half_time) \
} \
/* step if nostep is false and we're not on a ladder... */ \
if (world_nostep == FALSE) { \
if (!(self.pm_flags & PMF_ONLADDER)) \
{ \
/* ... and either airstep is true or we're onground */ \
if (world_airstep == TRUE) \
self.pm_flags |= PMF_SLIDE_STEP; \
else if (self.pm_flags & PMF_ONGROUND) \
self.pm_flags |= PMF_SLIDE_STEP; \
} } \
/* bound velocity to fixed server maximums -- CEV */ \
if (self.velocity != '0 0 0') \
{ \
PM_CHECKVELOCITY (self.velocity) \
} \
/* Do the move. Bounce, Rock, Skate, Roll -- CEV */ \
PM_DANCEMOVE (vec1, vec2, accel, friction, temp) \
/* now the back half of player acceleration -- CEV */ \
if (self.movetype == MOVETYPE_WALK) \
{ \
if (self.conlevel < WATERLEVEL_WAIST) \
{ \
PM_MOVE_MOVETYPES_WALKACCELERATE (half_time) \
} \
else \
{ \
PM_MOVE_MOVETYPES_SWIMACCELERATE (half_time) \
} \
PM_MOVE_MOVETYPES_MANAGETIMER (half_time) \
} \
else if (self.movetype == MOVETYPE_FLY) \
{ \
PM_MOVE_MOVETYPES_NOCLIPACCELERATE (half_time) \
} \
/* clear ladder flag -- CEV */ \
if (self.pm_flags & PMF_ONLADDER) \
self.pm_flags &= ~PMF_ONLADDER; \
touchtriggers (); \
base_entity_positioncontents (self); \
} \
}

//----------------------------------------------------------------------
// 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() \
{ \
if (self.velocity != '0 0 0') \
{ \
PM_CHECKVELOCITY (self.velocity) \
PM_TRUNCATEVECTORTOEIGTH (self.velocity) \
} \
self.speed = vlen ([self.velocity.x, self.velocity.y, 0]); \
setorigin (self, self.origin); \
self.oldorigin = self.origin; \
}

#endif

Return to the top of this page or return to the overview of this repo.

Log pmove.qc

Date Commit Message Author + -
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.