djcev.com

//

Git Repos / fte_dogmode / commit 5ef6e0e

Commit: 5ef6e0ec89d8c603c0b78d8ba341ac277ad156f8
Parent: 1de5850fad8e5b8687b3fe3adf431d30cca2283c
Author: Cameron Vanderzanden, 2023-11-07 16:07
Committer: Cameron Vanderzanden, 2023-11-07 16:07

Commit Message

Extended the Nuclide slidemove

Having played with it a little bit I found some issues with the
Nuclide SDK's slidemove function. In particular it has difficulty
with complex movements going up stairs (notably wallrunning) and
issues with moves that interact with the crease of two planes.

This is due in part to the fact that Nuclide's move runs only one
loop and doesn't store an array of planes (making it both faster
and more simple). After much testing I added the slide-along-crease
function back in with a set of conditions suitable for most
interactions. It's definitely not perfect and I will probably
revisit it in the future.

Also fixed wallrunning into stairs by clipping velocity to the stair
forward move if/when that forward move hits something.

There's still room for improvement here, I've already encountered
some errors in play (that I have no idea how to fix).

Change List

?File Add Del
M qc/pmove.qc +958 -937

Diff qc/pmove.qc

diff --git a/qc/pmove.qc b/qc/pmove.qc
index d0713a7..c876f44 100644
--- a/qc/pmove.qc
+++ b/qc/pmove.qc
@@ -78,57 +78,22 @@ float() PM_Nudge =
};

//======================================================================
-// PM_ClipVelocity
-//----------------------------------------------------------------------
-#define STOP_EPSILON 0.125
-#if 0
-vector(vector vel, vector normal, float ob, float oneside) PM_ClipVelocity =
-{
- local float backoff, backoffz;
-
- backoff = vel * normal;
- backoffz = 0;
-
- if (backoff < 0)
- backoff *= ob;
- else
- if (oneside)
- // backoff = 0;
- backoff *= -0.001;
- else
- backoff /= ob;
-
- vel -= normal * backoff;
-
- if (vel_x > -STOP_EPSILON && vel_x < STOP_EPSILON)
- vel_x = 0;
- if (vel_y > -STOP_EPSILON && vel_y < STOP_EPSILON)
- vel_y = 0;
- if (vel_z > -STOP_EPSILON && vel_z < STOP_EPSILON)
- vel_z = 0;
-
- return vel;
-};
-#endif
-
-//======================================================================
// PM_Rebound
//
// Alternative ClipVelocity that does ground checking.
// For use with Eukara / Nuclide move.
//----------------------------------------------------------------------
+#define STOP_EPSILON 0.125
void(vector normal, float ob, float oneside) PM_Rebound =
{
- local float backoff, backoffz;
+ local float backoff;

backoff = self.velocity * normal;
- backoffz = 0;

if (backoff < 0)
backoff *= ob;
else
if (oneside)
- // backoff = 0;
backoff *= -0.001;
else
backoff /= ob;
@@ -159,29 +124,23 @@ void(vector normal, float ob, float oneside) PM_Rebound =
}
};

-#define PM_SLIDE_NUMBUMPS 4
-#define PM_SLIDE_MAX_CLIP_PLANES 5
//======================================================================
-// PM_Q1FlyMove
+// PM_NuclideMove
//
-// Based on Quake 1's SV_FlyMove function (found in id's engine, QuakeSpasm,
-// and others). Has the disadvantage of using an array of vectors, and
-// is slow (I assume) in QuakeC. -- CEV
+// Updates origin, moves the player through the world. Similar to Q3
+// SlideMove and Q1 FlyMove. This version applies gravity and restores
+// velocity based on a timer check (both features of Quake 3's pmove).
+//
+// Based on code from the Nuclide SDK (presumably by Eukara), specifically
+// the function PMoveCustom_Move found in the file pmove_custom.qc.
+// This in turn appears to be based on a similar function in CSQCTest.
//----------------------------------------------------------------------
-#if 0
-int(float dogravity, float onesided) PM_Q1FlyMove =
+#define PM_SLIDE_NUMBUMPS 5
+void(float dogravity, float onesided) PM_NuclideMove =
{
- vector planes[PM_SLIDE_MAX_CLIP_PLANES];
- vector dir, end, new_v, start_v;
- int blocked, bumpcount, numplanes, i, j;
- float d, ent_grav, grav, time_left;
-
- blocked = numplanes = 0;
-
- // initialize all these vectors
- for (i = 0; i < PM_SLIDE_MAX_CLIP_PLANES; i++)
- planes[i] = '0 0 0';
- dir = end = new_v = '0 0 0';
+ vector end, previous_plane, saved_plane, start_o, start_v;
+ float dostep, ent_grav, grav, stepsize, time_left;
+ int i, start_onground;

if (dogravity)
{
@@ -190,17 +149,33 @@ int(float dogravity, float onesided) PM_Q1FlyMove =
else
ent_grav = 1.0;
grav = ent_grav * autocvar(sv_gravity, 800) * input_timelength;
- // we'll do half of it now, half later -- CEV
+ // Half now, half later. Apparently affects framerate
+ // dependence. -- CEV
self.velocity_z -= grav * 0.5;
}

- // set start_v after gravity check above
+ start_o = self.origin;
start_v = self.velocity;
+ start_onground = (self.pmove_flags & PMF_ONGROUND);

- for (bumpcount = 0, time_left = input_timelength;
- time_left > 0 && bumpcount < PM_SLIDE_NUMBUMPS; bumpcount++)
+ dostep = TRUE;
+ if (!(autocvar(pm_airstep, TRUE)) && !(self.pmove_flags & PMF_ONGROUND))
+ // Borrowing FTE's pm_airstep cvar here. If false, don't
+ // step up while in the air (changes stairs) -- CEV
+ dostep = FALSE;
+
+ if (autocvar(pm_nostep, FALSE))
+ // no stepping. useful for testing -- CEV
+ dostep = FALSE;
+
+ // we need to bounce off surfaces (in order to slide along them),
+ // so we need at 2 attempts -- comment from Nuclide
+ // I've changed this to a larger number of attempts (from 3 to 5)
+ // to accommodate more complex plane interactions -- CEV
+ i = PM_SLIDE_NUMBUMPS;
+ for (time_left = input_timelength; time_left > 0 && i; i--)
{
- end = self.origin + self.velocity * time_left;
+ end = self.origin + (self.velocity * time_left);
tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);

if (trace_allsolid || trace_startsolid)
@@ -210,1122 +185,1168 @@ int(float dogravity, float onesided) PM_Q1FlyMove =
continue;

// nah, we're stuck. don't build up falling damage
- // but allow sideways acceleration
+ // but allow sideways acceleration -- CEV
#ifdef SSQC
- dprint ("PM_Q1FlyMove: entity trapped in a solid\n");
+ dprint ("PM_NuclideMove: entity trapped in a solid\n");
#endif
self.velocity_z = 0;
- return 3;
+ break;
}

- if (trace_fraction >= 0.001)
- self.origin = trace_endpos;
+ self.origin = trace_endpos;

if (trace_fraction >= 1.0f)
+ // made the whole move -- CEV
break;

- if (trace_plane_normal_z > 0.7)
- {
- blocked |= 1;
- self.groundnormal = trace_plane_normal;
- self.groundentity = trace_ent;
- }
-
- if (!(trace_plane_normal_z))
- blocked |= 2;
-
- PM_DoTouch (trace_ent);
+ saved_plane = trace_plane_normal;
time_left -= time_left * trace_fraction;

- if (numplanes >= PM_SLIDE_MAX_CLIP_PLANES)
+ // integrated StepSlideMove -- CEV
+ // only attempt to step if there's time left, dostep is
+ // true, and we encountered something like a vertical plane
+ if (time_left && dostep && saved_plane_z >= 0 &&
+ saved_plane_z <= 0.7f)
{
- // this shouldn't really happen
- if (cvar("developer"))
- {
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: numplanes >= max: ");
- dprint (ftos(numplanes));
- dprint ("\n");
- #endif
- }
- self.velocity = '0 0 0';
- blocked = 7;
- break;
- }
-
- planes[numplanes] = trace_plane_normal;
- numplanes++;
+ // first: move up
+ trace_endpos = self.origin;
+ trace_endpos_z += PM_STEPHEIGHT;
+ tracebox (self.origin, self.mins, self.maxs,
+ trace_endpos, FALSE, self);
+ stepsize = trace_endpos_z - self.origin_z;

- for (i = 0; i < numplanes; i++)
- {
- // slide along the plane
- new_v = PM_ClipVelocity (self.velocity,
- planes[i], PM_OVERCLIP, onesided);
+ // second: move forward
+ end = trace_endpos + (self.velocity * time_left);
+ end_z = trace_endpos_z;
+ tracebox (trace_endpos, self.mins, self.maxs,
+ end, FALSE, self);

- for (j = 0; j < numplanes; j++)
- if (j != i)
- // not ok (the comment says)
- if ((new_v * planes[j]) < 0)
- break;
+ if (trace_fraction >= 1.0f)
+ {
+ // forward move did not hit anything
+ local float fwd_fraction = trace_fraction;
+ local vector fwd_plane = trace_plane_normal;

- if (j == numplanes)
- break;
- }
+ // third: move down
+ end = trace_endpos;
+ end_z -= stepsize;
+ tracebox (trace_endpos, self.mins, self.maxs,
+ end, FALSE, self);

- if (i == numplanes)
- {
- if (numplanes != 2)
+ if (trace_fraction < 1.0f &&
+ trace_plane_normal_z > 0.7f)
+ {
+ // "good ground", accept the step move
+ self.origin = trace_endpos;
+ time_left -= time_left * trace_fraction;
+ saved_plane = trace_plane_normal;
+ }
+ }
+ else if (trace_plane_normal_z == 0)
{
- #ifdef SSQC
- dprint ("PM_Q1FlyMove: stopping dead\n");
- #endif
- self.velocity = '0 0 0';
- blocked = 7;
- break;
+ // forward move hit something, so clip to
+ // it. solves issues with complex movement
+ // on stairs -- CEV
+ time_left -= time_left * trace_fraction;
+ saved_plane = trace_plane_normal;
}
-
- // slide along the crease
- dir = crossproduct (planes[0], planes[1]);
- dir = normalize (dir);
- d = (dir * self.velocity);
- new_v = dir * d;
}

- self.velocity = new_v;
-
- if ((self.velocity * start_v) <= 0)
+ // Slide along the crease of previous plane and current plane
+ // if previous plane is not the same as current plane AND
+ // our velocity interacts with the previous plane OR
+ // our velocity interacts with the current plane AND
+ // current plane is an acute vertical angle -- CEV
+ //
+ // TODO CEV: this is *definitely* wrong, but solves some
+ // immediate problems
+ if (previous_plane && previous_plane != saved_plane &&
+ (!(self.velocity * previous_plane >= 0) ||
+ (!(self.velocity * saved_plane >= 0) &&
+ saved_plane_z < 0)))
{
#ifdef SSQC
- dprint ("PM_Q1FlyMove: turned against orig vel\n");
+ dprint ("PM_NuclideMove: sliding along a crease...\n");
#endif
- self.velocity = '0 0 0';
- break;
+ end = crossproduct (previous_plane, saved_plane);
+ end = normalize (end);
+ self.velocity = end * (end * self.velocity);
}
+
+ // clip velocity to the plane from before (saved_plane)
+ // then touch trace_ent. This may have tracking problems
+ // (trace_ent might not correspond to saved_plane) -- CEV
+ PM_Rebound (saved_plane, PM_OVERCLIP, onesided);
+ PM_DoTouch (trace_ent);
+ previous_plane = saved_plane;
+ }
+
+ // if we hit the bump limit, zero out velocity to prevent
+ // oscillations in movement (especially in corners) -- CEV
+ if (i == 0)
+ {
+ #ifdef SSQC
+ dprint ("PM_NuclideMove: hit the bump limit\n");
+ #endif
+ self.velocity = '0 0 0';
}

// wallclip / wall skim timer check
- if ( // (self.primal_speed > PM_MAXSPEED) &&
- (self.velocity != '0 0 0') &&
- (self.jump_time) &&
- (!(self.pmove_flags & PMF_WATERJUMP)) &&
+ // in order: velocity is non-zero, we're within WALLCLIP_WINDOW,
+ // we haven't just teleported, we're not waterjumping, we're not
+ // in the ground boost state, and we lost speed during the move -- CEV
+ if (self.velocity && self.jump_time > (time - PM_WALLCLIP_WINDOW) &&
(self.teleport_time <= (time - 0.1)) &&
+ (!(self.pmove_flags & PMF_WATERJUMP)) &&
(self.boost_time <= time - PM_BOOST_WINDOW) &&
- (self.jump_time > (time - PM_WALLCLIP_WINDOW)) &&
(vlen(start_v) > vlen(self.velocity)))
{
#ifdef SSQC
- dprint ("PM_Q1FlyMove: restoring velocity...\n");
+ dprint ("PM_NuclideMove: wallclip, restoring velocity...\n");
#endif
- self.velocity = [start_v_x, start_v_y, self.velocity_z];
+ // take whichever Z velocity is lower; fixes steep ramps -- CEV
+ if (start_v_z > self.velocity_z)
+ self.velocity = [start_v_x, start_v_y, self.velocity_z];
+ else
+ self.velocity = start_v;
}

// final gravity check here
if (dogravity)
self.velocity_z -= grav * 0.5;

- /* for debugging -- CEV
+ // manage the "boost" timer -- CEV
+ // this seems like an overly complicated check -- CEV
+ if (!start_onground && (self.pmove_flags & PMF_ONGROUND) &&
+ (start_v_z > self.velocity_z) &&
+ (self.velocity_z < 1) &&
+ (self.origin_z - start_o_z > 0))
+ {
+ if (!self.boost_time &&
+ self.boost_time <= time - PM_BOOST_WINDOW)
+ self.boost_time = time;
+ /*
+ if (cvar("developer"))
+ {
+ // more debugging -- CEV
+ #ifdef SSQC
+ local float stepsize2;
+ stepsize2 = self.origin_z - start_o_z;
+ dprint (sprintf("PM_NuclideMove: step up: %g, %g\n",
+ stepsize, stepsize2));
+ #endif
+ }
+ */
+ }
+
+ /*
#ifdef SSQC
- dprint ("PM_Q1FlyMove: numplanes ");
- dprint (ftos(numplanes));
- dprint ("\n");
+ if (cvar("developer"))
+ // for debugging purposes -- CEV
+ dprint (sprintf("PM_NuclideMove: %g bumps\n", 5 - i));
#endif
*/
-
- return blocked;
};
-#endif

-//======================================================================
-// PM_StepSlideMove
-//
-// Based on the StepSlideMove function found in the Quake III sourcecode.
-// Calls PM_Q1FlyMove several times. This works, but is slow and has
-// some tracking issues (probably my fault). -- CEV
//----------------------------------------------------------------------
-#if 0
-void(float dogravity, float onesided) PM_StepSlideMove =
+void(float friction) PM_Friction =
{
- vector start_o, start_v, first_o, first_v;
- vector up, down;
- float stepsize;
- int clip, first_clip;
- int start_onground;
-
- clip = first_clip = 0;
- start_o = self.origin;
- start_v = self.velocity;
- start_onground = (self.pmove_flags & PMF_ONGROUND);
+ float control, newspeed, speed;

- // first try let's go
- first_clip = PM_Q1FlyMove (dogravity, onesided);
+ speed = vlen(self.velocity);

- if (!(first_clip & 2) && !(first_clip & 8))
+ if (speed < 1)
{
- // we got where we wanted to go right away
- // #ifdef SSQC
- // dprint ("PM_StepSlideMove: accepting first move\n");
- // #endif
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
+ self.velocity = '0 0 0';
return;
}
+
+ // calculate what their new speed should be
+ control = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
+ newspeed = speed - control * friction * input_timelength;

- if (self.movetype != MOVETYPE_WALK)
- // gibbed by a trigger?
- return;
-
- if (autocvar(pm_nostep, FALSE))
- // no stepping. useful for testing -- CEV
- return;
-
- if (!(autocvar(pm_airstep, TRUE)) && !(self.pmove_flags & PMF_ONGROUND))
- // Borrowing FTE's pm_airstep cvar here. If false, don't
- // step up while in the air (changes stairs) -- CEV
- return;
-
- first_o = self.origin;
- first_v = self.velocity;
-
- self.origin = start_o;
- self.velocity = start_v;
-
- up = start_o;
- up_z += PM_STEPHEIGHT;
- tracebox (start_o, self.mins, self.maxs, up, FALSE, self);
+ // and slow them
+ if (newspeed < 0)
+ newspeed = 0;
+ self.velocity = self.velocity * (newspeed / speed);
+};

- if (trace_allsolid)
+//----------------------------------------------------------------------
+void(entity ground_e, vector ground_v, int docheck) PM_SetOnground =
+{
+ // look for ground with tracebox if requested
+ if (docheck)
{
- #ifdef SSQC
- dprint ("PM_StepSlideMove: can't step up\n");
- #endif
- self.origin = first_o;
- self.velocity = first_v;
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
+ tracebox (self.origin, self.mins, self.maxs,
+ self.origin - PM_GROUNDDIST_V, FALSE, self);
+ // only onground if we hit it, it faces upwards,
+ // and we're actually moving towards it
+ // if (trace_fraction < 1 && trace_plane_normal_z > 0.7 &&
+ // self.velocity * trace_plane_normal < 0.01)
+ if (trace_fraction < 1 && trace_plane_normal_z > 0.7)
+ {
+ // on ground
+ self.groundentity = trace_ent;
+ self.groundnormal = trace_plane_normal;
+ }
else
- self.pmove_flags &= ~PMF_ONGROUND;
- return;
+ {
+ self.groundentity = __NULL__;
+ self.groundnormal = __NULL__;
+ }
}
-
- // try slidemove from this position (in air)
- stepsize = trace_endpos_z - start_o_z;
- self.origin = trace_endpos;
- self.velocity = start_v;
- clip = PM_Q1FlyMove (dogravity, onesided);
-
- // push down the final amount
- down = self.origin;
- down_z -= stepsize;
- tracebox (self.origin, self.mins, self.maxs, down, FALSE, self);
-
- if (trace_allsolid)
+ else
{
- #ifdef SSQC
- dprint ("PM_StepSlideMove: second move is in a solid\n");
- #endif
- self.origin = first_o;
- self.velocity = first_v;
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
- return;
+ self.groundentity = ground_e;
+ self.groundnormal = ground_v;
}

- if (!trace_plane_normal_z || trace_plane_normal_z > 0.7)
+ // assume we're on ground if we have a groundnormal field -- CEV
+ // is there a situation where groundnormal could be '0 0 0'? -- CEV
+ if (self.groundnormal != __NULL__)
{
- // we're on 'good ground', accept the second slidemove
- // #ifdef SSQC
- // dprint ("PM_StepSlideMove: accepting second slidemove\n");
- // #endif
- self.origin = trace_endpos;
- if (trace_fraction < 1.0)
- {
- // clip to step
- self.velocity = PM_ClipVelocity (self.velocity,
- trace_plane_normal, PM_OVERCLIP, onesided);
- }
- if (trace_plane_normal_z > 0.7)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
+ // on ground
+ if (!(self.pmove_flags & PMF_ONGROUND))
+ // transitioning from in-air to onground
+ self.land_time = time;
+ self.pmove_flags |= PMF_ONGROUND;
+ self.pmove_flags &= ~PMF_WALLJUMP;
}
else
{
- // not on 'good ground', revert to first slidemove
- // see the similar section of SV_WalkMove
+ // not on ground
+ if (self.pmove_flags & PMF_ONGROUND)
+ self.land_time = 0;
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+
+ if (self.pmove_flags & PMF_ONGROUND)
+ self.flags |= FL_ONGROUND;
+ else
+ self.flags &= ~FL_ONGROUND;
+};
+
+//======================================================================
+// PM_CategorizePosition
+// Based on similarly-named function in the GPL2 purecsqc pmove.qc
+//----------------------------------------------------------------------
+void() PM_CategorizePosition =
+{
+ vector p;
+ float pc;
+
+ if (self.movetype == MOVETYPE_NOCLIP)
+ // noclip is never on ground
+ PM_SetOnground (__NULL__, __NULL__, FALSE);
+ else
+ // do a trace to check
+ PM_SetOnground (__NULL__, __NULL__, TRUE);
+
+ // clear doublejumped if outside of DOUBLEJUMP_COOLDOWN
+ if ((self.pmove_flags & PMF_DOUBLEJUMPED)
+ && self.jump_time < (time - PM_DOUBLEJUMP_COOLDOWN))
+ {
// #ifdef SSQC
- // dprint ("PM_StepSlideMove: reverting to first move\n");
+ // dprint ("PM_Categorize: clear FL_DOUBLEJUMPED\n");
// #endif
- self.origin = first_o;
- self.velocity = first_v;
- if (first_clip & 1)
- self.pmove_flags |= PMF_ONGROUND;
- else
- self.pmove_flags &= ~PMF_ONGROUND;
+ self.pmove_flags &= ~PMF_DOUBLEJUMPED;
}

- // this is a complicated check.
- // if we started in the air and we're now on the ground
- // and we stepped up and we lost some Z velocity then we've
- // clipped to ground on a step up
- if (!start_onground && (self.pmove_flags & PMF_ONGROUND) &&
- (start_v_z > self.velocity_z) &&
- (self.velocity_z < 1) &&
- (self.origin_z - start_o_z > 0))
+ // check water levels
+ p = self.origin;
+ p_z = self.origin_z + self.mins_z + 1;
+ pc = pointcontents (p);
+ if (pc < CONTENT_SOLID)
{
- if (!self.boost_time &&
- self.boost_time <= time - PM_BOOST_WINDOW)
- self.boost_time = time;
-
- if (cvar("developer"))
+ self.watertype = pc;
+ p_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
+ if (pointcontents(p) < CONTENT_SOLID)
{
- #ifdef SSQC
- stepsize = self.origin_z - start_o_z;
- dprint (sprintf("PM_StepSlideMove: step up: %g\n",
- stepsize));
- #endif
+ p_z = self.origin_z + self.maxs_z;
+ if (pointcontents(p) < CONTENT_SOLID)
+ self.waterlevel = 3;
+ else
+ self.waterlevel = 2;
+ }
+ else
+ {
+ self.waterlevel = 1;
}
}
+ else
+ {
+ self.watertype = CONTENT_EMPTY;
+ self.waterlevel = 0;
+ }
};
-#endif

-//======================================================================
-// PM_NuclideMove
-//
-// Updates origin, moves the player through the world. Similar to Q3
-// SlideMove and Q1 FlyMove. This version applies gravity and restores
-// velocity based on a timer check (both features of Quake 3's pmove).
-//
-// Based on code from the Nuclide SDK (presumably by Eukara), specifically
-// the function PMoveCustom_Move found in the file pmove_custom.qc.
-// This in turn appears to be based on a similar function in CSQCTest.
//----------------------------------------------------------------------
-void(float dogravity, float onesided) PM_NuclideMove =
+void(vector wishdir, float wishspeed, float accel) PM_Accelerate =
{
- vector end, saved_plane, start_o, start_v;
- float dostep, ent_grav, grav, stepsize, time_left;
- int i, start_onground;
+ float addspeed, accelspeed, curspeed;

- if (dogravity)
- {
- if (self.gravity)
- ent_grav = self.gravity;
- else
- ent_grav = 1.0;
- grav = ent_grav * autocvar(sv_gravity, 800) * input_timelength;
- // Half now, half later. Apparently affects framerate
- // dependence. -- CEV
- self.velocity_z -= grav * 0.5;
- }
+ curspeed = self.velocity * wishdir;
+ addspeed = wishspeed - curspeed;

- start_o = self.origin;
- start_v = self.velocity;
- start_onground = (self.pmove_flags & PMF_ONGROUND);
+ if (addspeed <= 0)
+ return;

- dostep = TRUE;
- if (autocvar(pm_nostep, FALSE))
- // no stepping. useful for testing -- CEV
- dostep = FALSE;
+ accelspeed = accel * input_timelength * wishspeed;
+ if (accelspeed > addspeed) accelspeed = addspeed;

- if (!(autocvar(pm_airstep, TRUE)) && !(self.pmove_flags & PMF_ONGROUND))
- // Borrowing FTE's pm_airstep cvar here. If false, don't
- // step up while in the air (changes stairs) -- CEV
- dostep = FALSE;
+ self.velocity = self.velocity + accelspeed * wishdir;
+};

- // we need to bounce off surfaces (in order to slide along them),
- // so we need at 2 attempts -- comment from Nuclide
- // I've changed this to a larger number of attempts (from 3 to 5)
- // to accommodate more complex plane interactions -- CEV
- i = PM_SLIDE_NUMBUMPS + 1;
- for (time_left = input_timelength; time_left > 0 && i; i--)
- {
- end = self.origin + (self.velocity * time_left);
- tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);
+//----------------------------------------------------------------------
+void(vector wishvel, float wishspeed, float accel) PM_Q1AirAccelerate =
+{
+ float addspeed, wishspd, accelspeed, currentspeed;

- if (trace_allsolid || trace_startsolid)
- {
- // entity is trapped in a solid; attempt to nudge out
- if (PM_Nudge())
- continue;
+ currentspeed = self.velocity * wishvel;

- // nah, we're stuck. don't build up falling damage
- // but allow sideways acceleration -- CEV
- #ifdef SSQC
- dprint ("PM_NuclideMove: entity trapped in a solid\n");
- #endif
- self.velocity_z = 0;
- break;
- }
+ wishvel = normalize (wishvel);
+ wishspd = vlen (wishvel);
+ if (wishspd > PM_MAXAIRSPEED) wishspd = PM_MAXAIRSPEED;

- self.origin = trace_endpos;
+ addspeed = wishspd - currentspeed;
+ if (addspeed <= 0)
+ return;

- if (trace_fraction >= 1.0f)
- {
- setorigin (self, self.origin);
- break;
- }
+ accelspeed = accel * input_timelength * wishspeed;
+ if (accelspeed > addspeed) accelspeed = addspeed;

- saved_plane = trace_plane_normal;
- time_left -= time_left * trace_fraction;
+ self.velocity = self.velocity + accelspeed * wishvel;
+};

- // integrated StepSlideMove -- CEV
- if (time_left && dostep)
- {
- // step up if we can
- // first: move up
- trace_endpos = self.origin;
- trace_endpos_z += PM_STEPHEIGHT;
- tracebox (self.origin, self.mins, self.maxs,
- trace_endpos, FALSE, self);
- stepsize = trace_endpos_z - self.origin_z;
+//======================================================================
+// PM_AirControl
+// CPM-like Air Control. Based on Xonotic and an old experiment. -- CEV
+//----------------------------------------------------------------------
+void(vector wishdir, float wishspeed, float accel) PM_AirControl =
+{
+ local float dot, xyspeed, zspeed;

- local float roof_fraction = trace_fraction;
- local vector roof_plane_normal = trace_plane_normal;
+ zspeed = self.velocity_z;
+ self.velocity_z = 0;

- // second: move forward
- end = trace_endpos + (self.velocity * time_left);
- end_z = trace_endpos_z;
- tracebox (trace_endpos, self.mins, self.maxs,
- end, FALSE, self);
+ PM_Accelerate (wishdir, wishspeed, accel);

- if (trace_fraction == 1.0f)
- {
- local float fwd_fraction = trace_fraction;
- local vector fwd_plane = trace_plane_normal;
+ xyspeed = vlen (self.velocity);
+ self.velocity = normalize (self.velocity);
+ dot = self.velocity * wishdir;

- // third: move down
- end = trace_endpos;
- end_z -= stepsize;
- tracebox (trace_endpos, self.mins, self.maxs,
- end, FALSE, self);
+ if (dot > 0)
+ {
+ self.velocity = normalize (self.velocity * xyspeed +
+ wishdir * PM_AIRACCELTURN);
+ }

- if (trace_fraction < 1.0f &&
- trace_plane_normal_z > 0.7f)
- {
- time_left -= time_left * fwd_fraction;
-
- // we hit the ceiling; clip velocity
- if (roof_fraction < 1.0f)
- PM_Rebound (roof_plane_normal,
- PM_OVERCLIP, onesided);
-
- // clip velocity to either the down
- // or forward move if either hit
- // something
- if (trace_fraction < 1.0f)
- PM_Rebound (trace_plane_normal,
- PM_OVERCLIP, onesided);
- else if (fwd_fraction < 1.0f)
- PM_Rebound (fwd_plane,
- PM_OVERCLIP, onesided);
-
- // accept the down move
- self.origin = trace_endpos;
- continue;
- }
- }
- }
+ self.velocity = self.velocity * xyspeed;
+ self.velocity_z = zspeed;
+};

- // stepping not requested or did not succeed.
- // clip velocity to the plane from the outermost loop
- // (saved_plane) then touch trace_ent.
- // This may have tracking problems (trace_ent might
- // not correspond to saved_plane) -- CEV
- PM_Rebound (saved_plane, PM_OVERCLIP, onesided);
- PM_DoTouch (trace_ent);
+//----------------------------------------------------------------------
+void() PM_Jump =
+{
+ // are we already waterjumping?
+ if (self.pmove_flags & PMF_WATERJUMP)
+ return;
+
+ // are we noclipping?
+ if (self.movetype == MOVETYPE_NOCLIP)
+ return;
+
+ // don't pogo
+ if (self.pmove_flags & PMF_JUMP_HELD)
+ return;
+
+ // don't let the player jump more often than JUMP_WINDOW -- CEV
+ /*
+ if ((self.jump_time) && (self.jump_time > time - PM_JUMP_WINDOW))
+ return;
+ */
+
+ // make sure we get at least jumpspeed upwards from
+ // the ground plane by clamping it first.
+ if (self.groundnormal && (self.velocity * self.groundnormal < 0))
+ {
+ // #ifdef SSQC
+ // dprint("PM_Jump: clamping to ground\n");
+ // #endif
+ self.velocity -= self.groundnormal *
+ (self.velocity * self.groundnormal);
}

- // wallclip / wall skim timer check
- // in order: velocity is non-zero, we're within WALLCLIP_WINDOW,
- // we haven't just teleported, we're not waterjumping, and we
- // lost speed during the move -- CEV
- if (self.velocity && self.jump_time > (time - PM_WALLCLIP_WINDOW) &&
- (self.teleport_time <= (time - 0.1)) &&
- (!(self.pmove_flags & PMF_WATERJUMP)) &&
- (vlen(start_v) > vlen(self.velocity)))
+ // make sure we get at least the indicated jump speed
+ // this changes the behavior of downward ramps -- CEV
+ if (self.velocity_z < 0)
+ self.velocity_z = 0;
+
+ // teleport jumps; allow a larger (+0.2) window to account for
+ // time to travel thru teleporter. -- CEV
+ if ((self.jump_time > time - PM_TELEJUMP_WINDOW) &&
+ (self.teleport_time > time - (PM_TELEJUMP_WINDOW + 0.2)))
{
#ifdef SSQC
- dprint ("PM_NuclideMove: wallclip, restoring velocity...\n");
+ dprint ("PM_Jump: telejump ");
+ dprint (ftos(self.velocity_z));
+ dprint (", ");
#endif
- // take whichever Z velocity is lower; fixes steep ramps -- CEV
- if (start_v_z > self.velocity_z)
- self.velocity = [start_v_x, start_v_y, self.velocity_z];
- else
- self.velocity = start_v;
+ self.velocity_z += PM_TELEJUMPSPEED;
+ if !(self.pmove_flags & PMF_DOUBLEJUMPED)
+ self.pmove_flags |= PMF_DOUBLEJUMPED;
}
-
- // final gravity check here
- if (dogravity)
- self.velocity_z -= grav * 0.5;
-
- // manage the "boost" timer -- CEV
- // this seems like an overly complicated check -- CEV
- if (!start_onground && (self.pmove_flags & PMF_ONGROUND) &&
- (start_v_z > self.velocity_z) &&
- (self.velocity_z < 1) &&
- (self.origin_z - start_o_z > 0))
+ // A doublejump is two jumps within 400ms, usually +50% Z velocity.
+ else if (self.jump_time > (time - PM_DOUBLEJUMP_WINDOW))
{
- if (!self.boost_time &&
- self.boost_time <= time - PM_BOOST_WINDOW)
- self.boost_time = time;
- /*
- if (cvar("developer"))
+ if (self.boost_time && self.boost_time >
+ (time - PM_BOOST_WINDOW))
{
- // more debugging -- CEV
#ifdef SSQC
- local float stepsize2;
- stepsize2 = self.origin_z - start_o_z;
- dprint (sprintf("PM_NuclideMove: step up: %g, %g\n",
- stepsize, stepsize2));
+ dprint ("PM_Jump: stairjump ");
#endif
}
- */
+ else
+ {
+ #ifdef SSQC
+ dprint ("PM_Jump: doublejump ");
+ #endif
+ }
+ #ifdef SSQC
+ dprint (ftos(self.velocity_z));
+ dprint (", ");
+ #endif
+ if (self.pmove_flags & PMF_DOUBLEJUMPED)
+ {
+ self.velocity_z += PM_TRIPLEJUMPSPEED;
+ }
+ else
+ {
+ self.velocity_z += PM_DOUBLEJUMPSPEED;
+ self.pmove_flags |= PMF_DOUBLEJUMPED;
+ }
+ }
+ // normal jump
+ else
+ {
+ #ifdef SSQC
+ dprint ("PM_Jump: jump ");
+ dprint (ftos(self.velocity_z));
+ dprint (", ");
+ #endif
+ self.velocity_z += PM_JUMPSPEED;
}

- /*
+ // report Z velocity
#ifdef SSQC
- if (cvar("developer"))
- // for debugging purposes -- CEV
- dprint (sprintf("PM_NuclideMove: %i bumps\n", 5 - i));
+ dprint (ftos(self.velocity_z));
+ dprint ("\n");
#endif
- */
+
+ // clear flags
+ PM_SetOnground (__NULL__, __NULL__, FALSE);
+ self.pmove_flags |= PMF_JUMP_HELD;
+ // timers
+ self.jump_time = time;
+ if (self.boost_time)
+ {
+ // #ifdef SSQC
+ // dprint ("PM_Jump: clearing boost_time\n");
+ // #endif
+ self.boost_time = 0;
+ }
};

//----------------------------------------------------------------------
-void(float friction) PM_Friction =
+void(vector checkdir) PM_WallJump =
{
- float control, newspeed, speed;
+ // is our upward speed greater than walljump speed?
+ if (self.velocity_z > PM_WALLJUMPSPEED)
+ return;

- speed = vlen(self.velocity);
+ // are we already waterjumping?
+ if (self.pmove_flags & PMF_WATERJUMP)
+ return;

- if (speed < 1)
- {
- self.velocity = '0 0 0';
+ // are we already walljumping?
+ if (self.pmove_flags & PMF_WALLJUMP)
return;
- }
-
- // calculate what their new speed should be
- control = speed < PM_STOPSPEED ? PM_STOPSPEED : speed;
- newspeed = speed - control * friction * input_timelength;

- // and slow them
- if (newspeed < 0)
- newspeed = 0;
- self.velocity = self.velocity * (newspeed / speed);
-};
+ // are we noclipping?
+ if (self.movetype == MOVETYPE_NOCLIP)
+ return;

-//----------------------------------------------------------------------
-void(entity ground_e, vector ground_v, int docheck) PM_SetOnground =
-{
- // look for ground with tracebox if requested
- if (docheck)
+ // don't pogo
+ if (self.pmove_flags & PMF_JUMP_HELD)
+ return;
+
+ // don't let the player jump more often than WALLJUMP_WINDOW -- CEV
+ if ((self.jump_time) && (self.jump_time > time - PM_WALLJUMP_WINDOW))
+ return;
+
+ // TODO CEV
+ return;
+
+ // don't walljump if within 32 units of ground
+ tracebox (self.origin, self.mins, self.maxs,
+ self.origin - PM_WALLDIST_V, FALSE, self);
+ if (trace_fraction < 1) return;
+
+ tracebox (self.origin, self.mins, self.maxs,
+ self.origin + self.velocity * 100, FALSE, self);
+ if (trace_fraction < 1 && trace_plane_normal_z < 0.2 &&
+ (vlen(self.origin - trace_endpos) < 10))
{
- tracebox (self.origin, self.mins, self.maxs,
- self.origin - PM_GROUNDDIST_V, FALSE, self);
- // only onground if we hit it, it faces upwards,
- // and we're actually moving towards it
- // if (trace_fraction < 1 && trace_plane_normal_z > 0.7 &&
- // self.velocity * trace_plane_normal < 0.01)
- if (trace_fraction < 1 && trace_plane_normal_z > 0.7)
+ #ifdef SSQC
+ dprint (sprintf("PM_WallJump: walljump dist %g\n",
+ vlen(self.origin - trace_endpos)));
+ #endif
+ // player jumping sound
+ #ifdef SSQC
+ sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
+ #endif
+ // modify X and Y velocity
+ self.velocity_x += trace_plane_normal_x * PM_WALLJUMPFORCE;
+ self.velocity_y += trace_plane_normal_y * PM_WALLJUMPFORCE;
+ // now modify Z
+ if (self.jump_time > (time - PM_DOUBLEJUMP_WINDOW))
{
- // on ground
- self.groundentity = trace_ent;
- self.groundnormal = trace_plane_normal;
+ self.velocity_z += PM_DOUBLEJUMPSPEED;
}
+ else if (self.velocity_z < (PM_WALLJUMPSPEED / 0.5))
+ self.velocity_z = PM_WALLJUMPSPEED;
else
- {
- self.groundentity = __NULL__;
- self.groundnormal = __NULL__;
- }
- }
- else
- {
- self.groundentity = ground_e;
- self.groundnormal = ground_v;
+ self.velocity_z += PM_WALLJUMPSPEED;
+ // set walljump flag
+ self.pmove_flags |= PMF_WALLJUMP;
+ // clear misc flags & ground fields
+ self.pmove_flags |= PMF_JUMP_HELD;
+ self.button2 = 0;
+ self.groundnormal = __NULL__;
+ self.groundentity = __NULL__;
+ // set jump timer
+ self.jump_time = time;
}
+};

- // assume we're on ground if we have a groundnormal field -- CEV
- // is there a situation where groundnormal could be '0 0 0'? -- CEV
- if (self.groundnormal != __NULL__)
- {
- // on ground
- if (!(self.pmove_flags & PMF_ONGROUND))
- // transitioning from in-air to onground
- self.land_time = time;
- self.pmove_flags |= PMF_ONGROUND;
- self.pmove_flags &= ~PMF_WALLJUMP;
- }
- else
+//----------------------------------------------------------------------
+void() PM_WaterJump =
+{
+ // can't be waterjumping if we're on ground
+ if ((self.pmove_flags & PMF_WATERJUMP) &&
+ (((time > self.teleport_time) && !(self.waterlevel)) ||
+ (self.pmove_flags & PMF_ONGROUND)))
{
- // not on ground
- if (self.pmove_flags & PMF_ONGROUND)
- self.land_time = 0;
- self.pmove_flags &= ~PMF_ONGROUND;
+ #ifdef SSQC
+ dprint ("PM_WaterJump: clearing FL_WATERJUMP\n");
+ #endif
+ self.pmove_flags &= ~PMF_WATERJUMP;
+ self.flags &= ~FL_WATERJUMP;
+ self.teleport_time = 0;
}
-
- if (self.pmove_flags & PMF_ONGROUND)
- self.flags |= FL_ONGROUND;
- else
- self.flags &= ~FL_ONGROUND;
};

-//======================================================================
-// PM_CategorizePosition
-// Based on similarly-named function in the GPL2 purecsqc pmove.qc
//----------------------------------------------------------------------
-void() PM_CategorizePosition =
+int() PM_WalkAccelerate =
{
- vector p;
- float pc;
+ vector forward, right, up;
+ vector wishvel, wishdir, wishang;
+ float wishspeed;
+ int onesided;

- if (self.movetype == MOVETYPE_NOCLIP)
- // noclip is never on ground
- PM_SetOnground (__NULL__, __NULL__, FALSE);
- else
- // do a trace to check
- PM_SetOnground (__NULL__, __NULL__, TRUE);
+ onesided = FALSE;

- // clear doublejumped if outside of DOUBLEJUMP_COOLDOWN
- if ((self.pmove_flags & PMF_DOUBLEJUMPED)
- && self.jump_time < (time - PM_DOUBLEJUMP_COOLDOWN))
- {
- // #ifdef SSQC
- // dprint ("PM_Categorize: clear FL_DOUBLEJUMPED\n");
- // #endif
- self.pmove_flags &= ~PMF_DOUBLEJUMPED;
- }
+ makevectors (input_angles);

- // check water levels
- p = self.origin;
- p_z = self.origin_z + self.mins_z + 1;
- pc = pointcontents (p);
- if (pc < CONTENT_SOLID)
+ forward = v_forward;
+ right = v_right;
+ up = v_up;
+
+ forward_z = 0;
+ right_z = 0;
+ forward = normalize (forward);
+ right = normalize (right);
+
+ wishvel = forward * input_movevalues_x + right * input_movevalues_y;
+ if (self.movetype != MOVETYPE_WALK)
+ wishvel_z = input_movevalues_z;
+
+ wishspeed = vlen (wishvel);
+ wishdir = normalize (wishvel);
+
+ if (wishspeed > PM_MAXSPEED)
+ wishspeed = PM_MAXSPEED;
+
+ if (input_buttons & 2)
+ // +jump was pressed
+ if (self.pmove_flags & PMF_ONGROUND)
+ PM_Jump ();
+ // else
+ // PM_WallJump (right);
+
+ if (self.pmove_flags & PMF_ONGROUND)
{
- self.watertype = pc;
- p_z = self.origin_z + (self.mins_z + self.maxs_z) * 0.5;
- if (pointcontents(p) < CONTENT_SOLID)
+ if (self.boost_time > (time - PM_BOOST_WINDOW))
{
- p_z = self.origin_z + self.maxs_z;
- if (pointcontents(p) < CONTENT_SOLID)
- self.waterlevel = 3;
- else
- self.waterlevel = 2;
+ PM_Friction (PM_BOOSTFRICTION);
+ PM_Accelerate (wishdir, wishspeed, PM_BOOSTACCEL);
}
else
{
- self.waterlevel = 1;
+ PM_Friction (PM_FRICTION);
+ PM_Accelerate (wishdir, wishspeed, PM_GROUNDACCEL);
}
}
else
{
- self.watertype = CONTENT_EMPTY;
- self.waterlevel = 0;
- }
-};
-
-//----------------------------------------------------------------------
-void(vector wishdir, float wishspeed, float accel) PM_Accelerate =
-{
- float addspeed, accelspeed, curspeed;
+ // Quake 3 strafejumping if requesting both X and Y
+ // -- CEV
+ if (input_movevalues_x && input_movevalues_y)
+ {
+ PM_Accelerate (wishdir, wishspeed, PM_AIRACCELQ3);
+ }
+ else
+ {
+ // I'm a little afraid doing this is expensive
+ // CPU-wise but it seems to be the right way.
+ // this is inspired by the old Slide mod -- CEV

- curspeed = self.velocity * wishdir;
- addspeed = wishspeed - curspeed;
+ // calculate the difference between the angle
+ // we're currently moving and the angle the
+ // user is requesting to move
+ wishang = vectoangles (wishdir) -
+ vectoangles (self.velocity);
+ wishang_y = anglemod (wishang_y);

- if (addspeed <= 0)
- return;
+ // limit wishang_y to a range of 0-180 (roughly)
+ if (wishang_y >= 180)
+ wishang_y = fabs (wishang_y - 360);

- accelspeed = accel * input_timelength * wishspeed;
- if (accelspeed > addspeed) accelspeed = addspeed;
+ /*
+ #ifdef SSQC
+ dprint ("PM_WalkAccelerate: wishang_y ");
+ dprint (ftos(wishang_y));
+ dprint ("\n");
+ #endif
+ */

- self.velocity = self.velocity + accelspeed * wishdir;
+ // Q1 air control when requesting movement within
+ // 45 to 135 degrees of current movement -- CEV
+ if ((wishang_y > 45) && (wishang_y < 135) &&
+ self.primal_speed >= 180)
+ {
+ PM_Q1AirAccelerate (wishvel, wishspeed,
+ PM_AIRACCEL);
+ }
+ // +direction style air control otherwise -- CEV
+ else
+ {
+ if (wishang_y >= 135)
+ // we're slowing down / stopping
+ PM_AirControl (wishdir, wishspeed,
+ PM_AIRACCELBACK);
+ else
+ PM_AirControl (wishdir, wishspeed,
+ PM_AIRACCELFWD);
+ }
+ }
+ }
+ return onesided;
};

//----------------------------------------------------------------------
-void(vector wishvel, float wishspeed, float accel) PM_Q1AirAccelerate =
+void(float scale) PM_NoClipAccelerate =
{
- float addspeed, wishspd, accelspeed, currentspeed;
-
- currentspeed = self.velocity * wishvel;
+ vector wishdir;
+ float wishspeed;

- wishvel = normalize (wishvel);
- wishspd = vlen (wishvel);
- if (wishspd > PM_MAXAIRSPEED) wishspd = PM_MAXAIRSPEED;
+ makevectors (input_angles);

- addspeed = wishspd - currentspeed;
- if (addspeed <= 0)
- return;
+ wishdir = v_forward * input_movevalues_x +
+ v_right * input_movevalues_y +
+ v_up * input_movevalues_z;

- accelspeed = accel * input_timelength * wishspeed;
- if (accelspeed > addspeed) accelspeed = addspeed;
+ if (input_buttons & 2)
+ // should be water:100, slime:80, lava:50, but lets
+ // just bake smartjump in here instead (we don't have
+ // the client's cl_upspeed value in ssqc though).
+ wishdir_z = max (PM_MAXSPEED, wishdir_z);
+ else if (wishdir == '0 0 0' && scale < 1)
+ wishdir_z = -PM_WATERSINKSPEED;

- self.velocity = self.velocity + accelspeed * wishvel;
+ wishspeed = vlen (wishdir) * scale;
+ wishdir = normalize (wishdir);
+
+ PM_Friction (PM_FRICTION);
+ PM_Accelerate (wishdir, wishspeed, PM_GROUNDACCEL);
};

//======================================================================
-// PM_AirControl
-// CPM-like Air Control. Based on Xonotic and an old experiment. -- CEV
+// PM_Move -- PMOVE entrypoint -- CEV
//----------------------------------------------------------------------
-void(vector wishdir, float wishspeed, float accel) PM_AirControl =
+void(entity target) PM_Move =
{
- local float dot, xyspeed, zspeed;
-
- zspeed = self.velocity_z;
- self.velocity_z = 0;
-
- PM_Accelerate (wishdir, wishspeed, accel);
-
- xyspeed = vlen (self.velocity);
- self.velocity = normalize (self.velocity);
- dot = self.velocity * wishdir;
-
- if (dot > 0)
- {
- self.velocity = normalize (self.velocity * xyspeed +
- wishdir * PM_AIRACCELTURN);
- }
+ entity oldself;
+ float dogravity, onesided;

- self.velocity = self.velocity * xyspeed;
- self.velocity_z = zspeed;
-};
+ oldself = self;
+ self = target;
+ onesided = FALSE;

-//----------------------------------------------------------------------
-void() PM_Jump =
-{
- // are we already waterjumping?
- if (self.pmove_flags & PMF_WATERJUMP)
- return;
+ PM_Nudge ();

- // are we noclipping?
- if (self.movetype == MOVETYPE_NOCLIP)
- return;
+ if (!(input_buttons & 2))
+ self.pmove_flags &= ~PMF_JUMP_HELD;

- // don't pogo
- if (self.pmove_flags & PMF_JUMP_HELD)
- return;
+ PM_CategorizePosition ();
+ PM_WaterJump ();

- // don't let the player jump more often than JUMP_WINDOW -- CEV
- /*
- if ((self.jump_time) && (self.jump_time > time - PM_JUMP_WINDOW))
- return;
- */
+ // save velocity + speed here -- CEV
+ self.primal_velocity = self.velocity;
+ self.primal_speed = vlen ([self.velocity_x, self.velocity_y, 0]);

- // make sure we get at least jumpspeed upwards from
- // the ground plane by clamping it first.
- if (self.groundnormal && (self.velocity * self.groundnormal < 0))
+ if (self.boost_time && self.boost_time <= time - PM_BOOST_WINDOW)
{
+ // Clear the boost timer if it's set
// #ifdef SSQC
- // dprint("PM_Jump: clamping to ground\n");
+ // dprint ("PM_Move: clearing boost_time\n");
// #endif
- self.velocity -= self.groundnormal *
- (self.velocity * self.groundnormal);
+ self.boost_time = 0;
}

- // make sure we get at least the indicated jump speed
- // this changes the behavior of downward ramps -- CEV
- if (self.velocity_z < 0)
- self.velocity_z = 0;
-
- // teleport jumps; allow a larger (+0.2) window to account for
- // time to travel thru teleporter. -- CEV
- if ((self.jump_time > time - PM_TELEJUMP_WINDOW) &&
- (self.teleport_time > time - (PM_TELEJUMP_WINDOW + 0.2)))
- {
- #ifdef SSQC
- dprint ("PM_Jump: telejump ");
- dprint (ftos(self.velocity_z));
- dprint (", ");
- #endif
- self.velocity_z += PM_TELEJUMPSPEED;
- if !(self.pmove_flags & PMF_DOUBLEJUMPED)
- self.pmove_flags |= PMF_DOUBLEJUMPED;
- }
- // A doublejump is two jumps within 400ms, usually +50% Z velocity.
- else if (self.jump_time > (time - PM_DOUBLEJUMP_WINDOW))
+ if (input_timelength < 0)
{
- if (self.boost_time && self.boost_time >
- (time - PM_BOOST_WINDOW))
- {
- #ifdef SSQC
- dprint ("PM_Jump: stairjump ");
- #endif
- }
- else
- {
- #ifdef SSQC
- dprint ("PM_Jump: doublejump ");
- #endif
- }
- #ifdef SSQC
- dprint (ftos(self.velocity_z));
- dprint (", ");
- #endif
- if (self.pmove_flags & PMF_DOUBLEJUMPED)
- {
- self.velocity_z += PM_TRIPLEJUMPSPEED;
- }
- else
- {
- self.velocity_z += PM_DOUBLEJUMPSPEED;
- self.pmove_flags |= PMF_DOUBLEJUMPED;
- }
+ dprint (sprintf("PM_Move: returning, input_timelength: %g\n",
+ input_timelength));
+ self = oldself;
+ return;
}
- // normal jump
- else
+
+ switch (self.movetype)
{
- #ifdef SSQC
- dprint ("PM_Jump: jump ");
- dprint (ftos(self.velocity_z));
- dprint (", ");
- #endif
- self.velocity_z += PM_JUMPSPEED;
- }
+ case MOVETYPE_WALK:
+ if (self.waterlevel >= 2)
+ {
+ PM_NoClipAccelerate (0.7);
+ // water friction
+ self.velocity -= 0.8 * self.waterlevel *
+ input_timelength * self.velocity;
+ }
+ else
+ {
+ onesided = PM_WalkAccelerate ();
+ }

- // report Z velocity
- #ifdef SSQC
- dprint (ftos(self.velocity_z));
- dprint ("\n");
- #endif
+ // onesided if outside DOUBLEJUMP_WINDOW time
+ if (self.jump_time <= (time - PM_DOUBLEJUMP_WINDOW))
+ onesided = TRUE;

- // clear flags
- PM_SetOnground (__NULL__, __NULL__, FALSE);
- self.pmove_flags |= PMF_JUMP_HELD;
- // timers
- self.jump_time = time;
- if (self.boost_time)
- {
- // #ifdef SSQC
- // dprint ("PM_Jump: clearing boost_time\n");
- // #endif
- self.boost_time = 0;
- }
-};
+ // don't stick to the floor when stepping up if we've
+ // doublejumped recently
+ if (self.pmove_flags & PMF_DOUBLEJUMPED)
+ onesided = TRUE;

-//----------------------------------------------------------------------
-void(vector checkdir) PM_WallJump =
-{
- // is our upward speed greater than walljump speed?
- if (self.velocity_z > PM_WALLJUMPSPEED)
- return;
+ // don't stick to the floor when jumping out of water
+ if ((self.pmove_flags & PMF_WATERJUMP) ||
+ (self.pmove_flags & PMF_WALLJUMP))
+ onesided = TRUE;

- // are we already waterjumping?
- if (self.pmove_flags & PMF_WATERJUMP)
- return;
+ // apply gravity when in the air
+ dogravity = !(self.pmove_flags & PMF_ONGROUND);

- // are we already walljumping?
- if (self.pmove_flags & PMF_WALLJUMP)
- return;
+ // apply gravity when we're on a ramp
+ if (self.groundnormal && self.groundnormal_z < 1)
+ dogravity = TRUE;

- // are we noclipping?
- if (self.movetype == MOVETYPE_NOCLIP)
- return;
+ PM_NuclideMove (dogravity, onesided);
+ break;

- // don't pogo
- if (self.pmove_flags & PMF_JUMP_HELD)
- return;
+ case MOVETYPE_FLY:
+ PM_NoClipAccelerate (1.0);
+ PM_NuclideMove (FALSE, TRUE);
+ break;

- // don't let the player jump more often than WALLJUMP_WINDOW -- CEV
- if ((self.jump_time) && (self.jump_time > time - PM_WALLJUMP_WINDOW))
- return;
+ case MOVETYPE_NOCLIP:
+ PM_NoClipAccelerate (1.0);
+ self.origin += self.velocity * input_timelength;
+ break;

- // TODO CEV
- return;
+ case MOVETYPE_NONE:
+ break;
+ }

- // don't walljump if within 32 units of ground
- tracebox (self.origin, self.mins, self.maxs,
- self.origin - PM_WALLDIST_V, FALSE, self);
- if (trace_fraction < 1) return;
-
- tracebox (self.origin, self.mins, self.maxs,
- self.origin + self.velocity * 100, FALSE, self);
- if (trace_fraction < 1 && trace_plane_normal_z < 0.2 &&
- (vlen(self.origin - trace_endpos) < 10))
- {
- #ifdef SSQC
- dprint (sprintf("PM_WallJump: walljump dist %g\n",
- vlen(self.origin - trace_endpos)));
- #endif
- // player jumping sound
- #ifndef CSQC
- sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM);
- #endif
- // modify X and Y velocity
- self.velocity_x += trace_plane_normal_x * PM_WALLJUMPFORCE;
- self.velocity_y += trace_plane_normal_y * PM_WALLJUMPFORCE;
- // now modify Z
- if (self.jump_time > (time - PM_DOUBLEJUMP_WINDOW))
- {
- self.velocity_z += PM_DOUBLEJUMPSPEED;
- }
- else if (self.velocity_z < (PM_WALLJUMPSPEED / 0.5))
- self.velocity_z = PM_WALLJUMPSPEED;
- else
- self.velocity_z += PM_WALLJUMPSPEED;
- // set walljump flag
- self.pmove_flags |= PMF_WALLJUMP;
- // clear misc flags & ground fields
- self.pmove_flags |= PMF_JUMP_HELD;
- self.button2 = 0;
- self.groundnormal = __NULL__;
- self.groundentity = __NULL__;
- // set jump timer
- self.jump_time = time;
- }
-};
+ if (self.groundentity)
+ PM_DoTouch (self.groundentity);
+ PM_SetOnground (self.groundentity, self.groundnormal, TRUE);
+ touchtriggers (self);
+ self = oldself;
+};

//----------------------------------------------------------------------
-void() PM_WaterJump =
-{
- // can't be waterjumping if we're on ground
- if ((self.pmove_flags & PMF_WATERJUMP) &&
- (((time > self.teleport_time) && !(self.waterlevel)) ||
- (self.pmove_flags & PMF_ONGROUND)))
- {
- #ifdef SSQC
- dprint ("PM_WaterJump: clearing FL_WATERJUMP\n");
- #endif
- self.pmove_flags &= ~PMF_WATERJUMP;
- self.flags &= ~FL_WATERJUMP;
- self.teleport_time = 0;
- }
-};
+// Code graveyard below. Saved here for reference purposes. -- CEV
+//----------------------------------------------------------------------

+//======================================================================
+// PM_ClipVelocity
//----------------------------------------------------------------------
-int() PM_WalkAccelerate =
+#if 0
+vector(vector vel, vector normal, float ob, float oneside) PM_ClipVelocity =
{
- vector forward, right, up;
- vector wishvel, wishdir, wishang;
- float wishspeed;
- int onesided;
+ local float backoff;

- onesided = FALSE;
+ backoff = vel * normal;

- makevectors (input_angles);
+ if (backoff < 0)
+ backoff *= ob;
+ else
+ if (oneside)
+ // backoff = 0;
+ backoff *= -0.001;
+ else
+ backoff /= ob;

- forward = v_forward;
- right = v_right;
- up = v_up;
+ vel -= normal * backoff;

- forward_z = 0;
- right_z = 0;
- forward = normalize (forward);
- right = normalize (right);
+ if (vel_x > -STOP_EPSILON && vel_x < STOP_EPSILON)
+ vel_x = 0;
+ if (vel_y > -STOP_EPSILON && vel_y < STOP_EPSILON)
+ vel_y = 0;
+ if (vel_z > -STOP_EPSILON && vel_z < STOP_EPSILON)
+ vel_z = 0;

- wishvel = forward * input_movevalues_x + right * input_movevalues_y;
- if (self.movetype != MOVETYPE_WALK)
- wishvel_z = input_movevalues_z;
+ return vel;
+};
+#endif

- wishspeed = vlen (wishvel);
- wishdir = normalize (wishvel);
+//======================================================================
+// PM_Q1FlyMove
+//
+// Based on Quake 1's SV_FlyMove function (found in id's engine, QuakeSpasm,
+// and others). Has the disadvantage of using an array of vectors, and
+// is slow (I assume) in QuakeC. -- CEV
+//----------------------------------------------------------------------
+#if 0
+#define PM_SLIDE_MAX_CLIP_PLANES 5
+int(float dogravity, float onesided) PM_Q1FlyMove =
+{
+ vector planes[PM_SLIDE_MAX_CLIP_PLANES];
+ vector dir, end, new_v, start_v;
+ int blocked, bumpcount, numplanes, i, j;
+ float d, ent_grav, grav, time_left;

- if (wishspeed > PM_MAXSPEED)
- wishspeed = PM_MAXSPEED;
+ blocked = numplanes = 0;

- if (input_buttons & 2)
- // +jump was pressed
- if (self.pmove_flags & PMF_ONGROUND)
- PM_Jump ();
- // else
- // PM_WallJump (right);
+ // initialize all these vectors
+ for (i = 0; i < PM_SLIDE_MAX_CLIP_PLANES; i++)
+ planes[i] = '0 0 0';
+ dir = end = new_v = '0 0 0';

- if (self.pmove_flags & PMF_ONGROUND)
+ if (dogravity)
{
- if (self.boost_time > (time - PM_BOOST_WINDOW))
- {
- PM_Friction (PM_BOOSTFRICTION);
- PM_Accelerate (wishdir, wishspeed, PM_BOOSTACCEL);
- }
+ if (self.gravity)
+ ent_grav = self.gravity;
else
- {
- PM_Friction (PM_FRICTION);
- PM_Accelerate (wishdir, wishspeed, PM_GROUNDACCEL);
- }
+ ent_grav = 1.0;
+ grav = ent_grav * autocvar(sv_gravity, 800) * input_timelength;
+ // we'll do half of it now, half later -- CEV
+ self.velocity_z -= grav * 0.5;
}
- else
+
+ // set start_v after gravity check above
+ start_v = self.velocity;
+
+ for (bumpcount = 0, time_left = input_timelength;
+ time_left > 0 && bumpcount < PM_SLIDE_NUMBUMPS; bumpcount++)
{
- // Quake 3 strafejumping if requesting both X and Y
- // -- CEV
- if (input_movevalues_x && input_movevalues_y)
+ end = self.origin + self.velocity * time_left;
+ tracebox (self.origin, self.mins, self.maxs, end, FALSE, self);
+
+ if (trace_allsolid || trace_startsolid)
{
- PM_Accelerate (wishdir, wishspeed, PM_AIRACCELQ3);
+ // entity is trapped in a solid; attempt to nudge out
+ if (PM_Nudge())
+ continue;
+
+ // nah, we're stuck. don't build up falling damage
+ // but allow sideways acceleration
+ #ifdef SSQC
+ dprint ("PM_Q1FlyMove: entity trapped in a solid\n");
+ #endif
+ self.velocity_z = 0;
+ return 3;
}
- else
- {
- // I'm a little afraid doing this is expensive
- // CPU-wise but it seems to be the right way.
- // this is inspired by the old Slide mod -- CEV

- // calculate the difference between the angle
- // we're currently moving and the angle the
- // user is requesting to move
- wishang = vectoangles (wishdir) -
- vectoangles (self.velocity);
- wishang_y = anglemod (wishang_y);
+ if (trace_fraction >= 0.001)
+ self.origin = trace_endpos;

- // limit wishang_y to a range of 0-180 (roughly)
- if (wishang_y >= 180)
- wishang_y = fabs (wishang_y - 360);
+ if (trace_fraction >= 1.0f)
+ break;

- /*
- #ifdef SSQC
- dprint ("PM_WalkAccelerate: wishang_y ");
- dprint (ftos(wishang_y));
- dprint ("\n");
- #endif
- */
+ if (trace_plane_normal_z > 0.7)
+ {
+ blocked |= 1;
+ self.groundnormal = trace_plane_normal;
+ self.groundentity = trace_ent;
+ }

- // Q1 air control when requesting movement within
- // 45 to 135 degrees of current movement -- CEV
- if ((wishang_y > 45) && (wishang_y < 135) &&
- self.primal_speed >= 180)
+ if (!(trace_plane_normal_z))
+ blocked |= 2;
+
+ PM_DoTouch (trace_ent);
+ time_left -= time_left * trace_fraction;
+
+ if (numplanes >= PM_SLIDE_MAX_CLIP_PLANES)
+ {
+ // this shouldn't really happen
+ if (cvar("developer"))
{
- PM_Q1AirAccelerate (wishvel, wishspeed,
- PM_AIRACCEL);
+ #ifdef SSQC
+ dprint ("PM_Q1FlyMove: numplanes >= max: ");
+ dprint (ftos(numplanes));
+ dprint ("\n");
+ #endif
}
- // +direction style air control otherwise -- CEV
- else
+ self.velocity = '0 0 0';
+ blocked = 7;
+ break;
+ }
+
+ planes[numplanes] = trace_plane_normal;
+ numplanes++;
+
+ for (i = 0; i < numplanes; i++)
+ {
+ // slide along the plane
+ new_v = PM_ClipVelocity (self.velocity,
+ planes[i], PM_OVERCLIP, onesided);
+
+ for (j = 0; j < numplanes; j++)
+ if (j != i)
+ // not ok (the comment says)
+ if ((new_v * planes[j]) < 0)
+ break;
+
+ if (j == numplanes)
+ break;
+ }
+
+ if (i == numplanes)
+ {
+ if (numplanes != 2)
{
- if (wishang_y >= 135)
- // we're slowing down / stopping
- PM_AirControl (wishdir, wishspeed,
- PM_AIRACCELBACK);
- else
- PM_AirControl (wishdir, wishspeed,
- PM_AIRACCELFWD);
+ #ifdef SSQC
+ dprint ("PM_Q1FlyMove: stopping dead\n");
+ #endif
+ self.velocity = '0 0 0';
+ blocked = 7;
+ break;
}
+
+ // slide along the crease
+ dir = crossproduct (planes[0], planes[1]);
+ dir = normalize (dir);
+ d = (dir * self.velocity);
+ new_v = dir * d;
}
- }
- return onesided;
-};

-//----------------------------------------------------------------------
-void(float scale) PM_NoClipAccelerate =
-{
- vector wishdir;
- float wishspeed;
+ self.velocity = new_v;

- makevectors (input_angles);
+ if ((self.velocity * start_v) <= 0)
+ {
+ #ifdef SSQC
+ dprint ("PM_Q1FlyMove: turned against orig vel\n");
+ #endif
+ self.velocity = '0 0 0';
+ break;
+ }
+ }

- wishdir = v_forward * input_movevalues_x +
- v_right * input_movevalues_y +
- v_up * input_movevalues_z;
+ // wallclip / wall skim timer check
+ if ( // (self.primal_speed > PM_MAXSPEED) &&
+ (self.velocity != '0 0 0') &&
+ (self.jump_time) &&
+ (!(self.pmove_flags & PMF_WATERJUMP)) &&
+ (self.teleport_time <= (time - 0.1)) &&
+ (self.boost_time <= time - PM_BOOST_WINDOW) &&
+ (self.jump_time > (time - PM_WALLCLIP_WINDOW)) &&
+ (vlen(start_v) > vlen(self.velocity)))
+ {
+ #ifdef SSQC
+ dprint ("PM_Q1FlyMove: restoring velocity...\n");
+ #endif
+ self.velocity = [start_v_x, start_v_y, self.velocity_z];
+ }

- if (input_buttons & 2)
- // should be water:100, slime:80, lava:50, but lets
- // just bake smartjump in here instead (we don't have
- // the client's cl_upspeed value in ssqc though).
- wishdir_z = max (PM_MAXSPEED, wishdir_z);
- else if (wishdir == '0 0 0' && scale < 1)
- wishdir_z = -PM_WATERSINKSPEED;
+ // final gravity check here
+ if (dogravity)
+ self.velocity_z -= grav * 0.5;

- wishspeed = vlen (wishdir) * scale;
- wishdir = normalize (wishdir);
+ /* for debugging -- CEV
+ #ifdef SSQC
+ dprint ("PM_Q1FlyMove: numplanes ");
+ dprint (ftos(numplanes));
+ dprint ("\n");
+ #endif
+ */

- PM_Friction (PM_FRICTION);
- PM_Accelerate (wishdir, wishspeed, PM_GROUNDACCEL);
+ return blocked;
};
+#endif

//======================================================================
-// PM_Move -- PMOVE entrypoint -- CEV
+// PM_StepSlideMove
+//
+// Based on the StepSlideMove function found in the Quake III sourcecode.
+// Calls PM_Q1FlyMove several times. This works, but is slow and has
+// some tracking issues (probably my fault). -- CEV
//----------------------------------------------------------------------
-void(entity target) PM_Move =
+#if 0
+void(float dogravity, float onesided) PM_StepSlideMove =
{
- entity oldself;
- float dogravity, onesided;
-
- oldself = self;
- self = target;
- onesided = FALSE;
-
- PM_Nudge ();
-
- if (!(input_buttons & 2))
- self.pmove_flags &= ~PMF_JUMP_HELD;
+ vector start_o, start_v, first_o, first_v;
+ vector up, down;
+ float stepsize;
+ int clip, first_clip;
+ int start_onground;

- PM_CategorizePosition ();
- PM_WaterJump ();
+ clip = first_clip = 0;
+ start_o = self.origin;
+ start_v = self.velocity;
+ start_onground = (self.pmove_flags & PMF_ONGROUND);

- // save velocity + speed here -- CEV
- self.primal_velocity = self.velocity;
- self.primal_speed = vlen ([self.velocity_x, self.velocity_y, 0]);
+ // first try let's go
+ first_clip = PM_Q1FlyMove (dogravity, onesided);

- if (self.boost_time && self.boost_time <= time - PM_BOOST_WINDOW)
+ if (!(first_clip & 2) && !(first_clip & 8))
{
- // Clear the boost timer if it's set
+ // we got where we wanted to go right away
// #ifdef SSQC
- // dprint ("PM_Move: clearing boost_time\n");
+ // dprint ("PM_StepSlideMove: accepting first move\n");
// #endif
- self.boost_time = 0;
+ if (first_clip & 1)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &= ~PMF_ONGROUND;
+ return;
}

- if (input_timelength < 0)
- {
- dprint (sprintf("PM_Move: returning, input_timelength: %g\n",
- input_timelength));
- self = oldself;
+ if (self.movetype != MOVETYPE_WALK)
+ // gibbed by a trigger?
return;
- }

- switch (self.movetype)
- {
- case MOVETYPE_WALK:
- if (self.waterlevel >= 2)
- {
- PM_NoClipAccelerate (0.7);
- // water friction
- self.velocity -= 0.8 * self.waterlevel *
- input_timelength * self.velocity;
- }
- else
- {
- onesided = PM_WalkAccelerate ();
- }
+ if (autocvar(pm_nostep, FALSE))
+ // no stepping. useful for testing -- CEV
+ return;

- // onesided if outside DOUBLEJUMP_WINDOW time
- if (self.jump_time <= (time - PM_DOUBLEJUMP_WINDOW))
- onesided = TRUE;
+ if (!(autocvar(pm_airstep, TRUE)) && !(self.pmove_flags & PMF_ONGROUND))
+ // Borrowing FTE's pm_airstep cvar here. If false, don't
+ // step up while in the air (changes stairs) -- CEV
+ return;

- // don't stick to the floor when stepping up if we've
- // doublejumped recently
- if (self.pmove_flags & PMF_DOUBLEJUMPED)
- onesided = TRUE;
+ first_o = self.origin;
+ first_v = self.velocity;

- // don't stick to the floor when jumping out of water
- if ((self.pmove_flags & PMF_WATERJUMP) ||
- (self.pmove_flags & PMF_WALLJUMP))
- onesided = TRUE;
+ self.origin = start_o;
+ self.velocity = start_v;

- // apply gravity when in the air
- dogravity = !(self.pmove_flags & PMF_ONGROUND);
+ up = start_o;
+ up_z += PM_STEPHEIGHT;
+ tracebox (start_o, self.mins, self.maxs, up, FALSE, self);

- // apply gravity when we're on a ramp
- if (self.groundnormal && self.groundnormal_z < 1 &&
- // self.groundnormal_z > 0.7)
- self.groundnormal_z > 0)
- dogravity = TRUE;
+ if (trace_allsolid)
+ {
+ #ifdef SSQC
+ dprint ("PM_StepSlideMove: can't step up\n");
+ #endif
+ self.origin = first_o;
+ self.velocity = first_v;
+ if (first_clip & 1)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &= ~PMF_ONGROUND;
+ return;
+ }

- PM_NuclideMove (dogravity, onesided);
- break;
+ // try slidemove from this position (in air)
+ stepsize = trace_endpos_z - start_o_z;
+ self.origin = trace_endpos;
+ self.velocity = start_v;
+ clip = PM_Q1FlyMove (dogravity, onesided);

- case MOVETYPE_FLY:
- PM_NoClipAccelerate (1.0);
- PM_NuclideMove (FALSE, TRUE);
- break;
+ // push down the final amount
+ down = self.origin;
+ down_z -= stepsize;
+ tracebox (self.origin, self.mins, self.maxs, down, FALSE, self);

- case MOVETYPE_NOCLIP:
- PM_NoClipAccelerate (1.0);
- self.origin += self.velocity * input_timelength;
- break;
+ if (trace_allsolid)
+ {
+ #ifdef SSQC
+ dprint ("PM_StepSlideMove: second move is in a solid\n");
+ #endif
+ self.origin = first_o;
+ self.velocity = first_v;
+ if (first_clip & 1)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &= ~PMF_ONGROUND;
+ return;
+ }

- case MOVETYPE_NONE:
- break;
+ if (!trace_plane_normal_z || trace_plane_normal_z > 0.7)
+ {
+ // we're on 'good ground', accept the second slidemove
+ // #ifdef SSQC
+ // dprint ("PM_StepSlideMove: accepting second slidemove\n");
+ // #endif
+ self.origin = trace_endpos;
+ if (trace_fraction < 1.0)
+ {
+ // clip to step
+ self.velocity = PM_ClipVelocity (self.velocity,
+ trace_plane_normal, PM_OVERCLIP, onesided);
+ }
+ if (trace_plane_normal_z > 0.7)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &= ~PMF_ONGROUND;
+ }
+ else
+ {
+ // not on 'good ground', revert to first slidemove
+ // see the similar section of SV_WalkMove
+ // #ifdef SSQC
+ // dprint ("PM_StepSlideMove: reverting to first move\n");
+ // #endif
+ self.origin = first_o;
+ self.velocity = first_v;
+ if (first_clip & 1)
+ self.pmove_flags |= PMF_ONGROUND;
+ else
+ self.pmove_flags &= ~PMF_ONGROUND;
}

- if (self.groundentity)
- PM_DoTouch (self.groundentity);
- PM_SetOnground (self.groundentity, self.groundnormal, TRUE);
- touchtriggers (self);
- self = oldself;
+ // this is a complicated check.
+ // if we started in the air and we're now on the ground
+ // and we stepped up and we lost some Z velocity then we've
+ // clipped to ground on a step up
+ if (!start_onground && (self.pmove_flags & PMF_ONGROUND) &&
+ (start_v_z > self.velocity_z) &&
+ (self.velocity_z < 1) &&
+ (self.origin_z - start_o_z > 0))
+ {
+ if (!self.boost_time &&
+ self.boost_time <= time - PM_BOOST_WINDOW)
+ self.boost_time = time;
+
+ if (cvar("developer"))
+ {
+ #ifdef SSQC
+ stepsize = self.origin_z - start_o_z;
+ dprint (sprintf("PM_StepSlideMove: step up: %g\n",
+ stepsize));
+ #endif
+ }
+ }
};
+#endif
+

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