/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "shared/shared.h" #include "common/pmove.h" #define STEPSIZE 18 // all of the locals will be zeroed before each // pmove, just to make damn sure we don't have // any differences when running on client or server typedef struct { vec3_t origin; // full float precision vec3_t velocity; // full float precision vec3_t forward, right, up; float frametime; csurface_t *groundsurface; cplane_t groundplane; int groundcontents; vec3_t previous_origin; qboolean ladder; } pml_t; static pmove_t *pm; static pml_t pml; static pmoveParams_t *pmp; // movement parameters static const float pm_stopspeed = 100; static const float pm_duckspeed = 100; static const float pm_accelerate = 10; static const float pm_wateraccelerate = 10; static const float pm_waterspeed = 400; /* walking up a step should kill some velocity */ /* ================== PM_ClipVelocity Slide off of the impacting object returns the blocked flags (1 = floor, 2 = step / wall) ================== */ #define STOP_EPSILON 0.1 static void PM_ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i; backoff = DotProduct(in, normal) * overbounce; for (i = 0; i < 3; i++) { change = normal[i] * backoff; out[i] = in[i] - change; if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON) out[i] = 0; } } /* ================== PM_StepSlideMove Each intersection will try to step over the obstruction instead of sliding along it. Returns a new origin, velocity, and contact entity Does not modify any world state? ================== */ #define MIN_STEP_NORMAL 0.7 // can't step up onto very steep slopes #define MAX_CLIP_PLANES 5 static void PM_StepSlideMove_(void) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity; int i, j; trace_t trace; vec3_t end; float time_left; numbumps = 4; VectorCopy(pml.velocity, primal_velocity); numplanes = 0; time_left = pml.frametime; for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { for (i = 0; i < 3; i++) end[i] = pml.origin[i] + time_left * pml.velocity[i]; trace = pm->trace(pml.origin, pm->mins, pm->maxs, end); if (trace.allsolid) { // entity is trapped in another solid pml.velocity[2] = 0; // don't build up falling damage return; } if (trace.fraction > 0) { // actually covered some distance VectorCopy(trace.endpos, pml.origin); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance // save entity for contact if (pm->numtouch < MAXTOUCH && trace.ent) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } time_left -= time_left * trace.fraction; // slide along this plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorCopy(vec3_origin, pml.velocity); break; } VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i = 0; i < numplanes; i++) { PM_ClipVelocity(pml.velocity, planes[i], pml.velocity, 1.01); for (j = 0; j < numplanes; j++) if (j != i) { if (DotProduct(pml.velocity, planes[j]) < 0) break; // not ok } if (j == numplanes) break; } if (i != numplanes) { // go along this plane } else { // go along the crease if (numplanes != 2) { // Con_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorCopy(vec3_origin, pml.velocity); break; } CrossProduct(planes[0], planes[1], dir); d = DotProduct(dir, pml.velocity); VectorScale(dir, d, pml.velocity); } // // if velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct(pml.velocity, primal_velocity) <= 0) { VectorCopy(vec3_origin, pml.velocity); break; } } if (pm->s.pm_time) { VectorCopy(primal_velocity, pml.velocity); } } /* ================== PM_StepSlideMove ================== */ static void PM_StepSlideMove(void) { vec3_t start_o, start_v; vec3_t down_o, down_v; trace_t trace; float down_dist, up_dist; // vec3_t delta; vec3_t up, down; VectorCopy(pml.origin, start_o); VectorCopy(pml.velocity, start_v); PM_StepSlideMove_(); VectorCopy(pml.origin, down_o); VectorCopy(pml.velocity, down_v); VectorCopy(start_o, up); up[2] += STEPSIZE; trace = pm->trace(up, pm->mins, pm->maxs, up); if (trace.allsolid) return; // can't step up // try sliding above VectorCopy(up, pml.origin); VectorCopy(start_v, pml.velocity); PM_StepSlideMove_(); // push down the final amount VectorCopy(pml.origin, down); down[2] -= STEPSIZE; trace = pm->trace(pml.origin, pm->mins, pm->maxs, down); if (!trace.allsolid) { VectorCopy(trace.endpos, pml.origin); } VectorCopy(pml.origin, up); // decide which one went farther down_dist = (down_o[0] - start_o[0]) * (down_o[0] - start_o[0]) + (down_o[1] - start_o[1]) * (down_o[1] - start_o[1]); up_dist = (up[0] - start_o[0]) * (up[0] - start_o[0]) + (up[1] - start_o[1]) * (up[1] - start_o[1]); if (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL) { VectorCopy(down_o, pml.origin); VectorCopy(down_v, pml.velocity); return; } //!! Special case // if we were walking along a plane, then we need to copy the Z over pml.velocity[2] = down_v[2]; } /* ================== PM_Friction Handles both ground friction and water friction ================== */ static void PM_Friction(void) { float *vel; float speed, newspeed, control; float friction; float drop; vel = pml.velocity; speed = sqrt(vel[0] * vel[0] + vel[1] * vel[1] + vel[2] * vel[2]); if (speed < 1) { vel[0] = 0; vel[1] = 0; return; } drop = 0; // apply ground friction if ((pm->groundentity && pml.groundsurface && !(pml.groundsurface->flags & SURF_SLICK)) || (pml.ladder)) { friction = pmp->friction; control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control * friction * pml.frametime; } // apply water friction if (pm->waterlevel && !pml.ladder) drop += speed * pmp->waterfriction * pm->waterlevel * pml.frametime; // scale the velocity newspeed = speed - drop; if (newspeed < 0) { newspeed = 0; } newspeed /= speed; vel[0] = vel[0] * newspeed; vel[1] = vel[1] * newspeed; vel[2] = vel[2] * newspeed; } /* ============== PM_Accelerate Handles user intended acceleration ============== */ static void PM_Accelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed; currentspeed = DotProduct(pml.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = accel * pml.frametime * wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pml.velocity[i] += accelspeed * wishdir[i]; } static void PM_AirAccelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed, wishspd = wishspeed; if (wishspd > 30) wishspd = 30; currentspeed = DotProduct(pml.velocity, wishdir); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; accelspeed = accel * wishspeed * pml.frametime; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pml.velocity[i] += accelspeed * wishdir[i]; } /* ============= PM_AddCurrents ============= */ static void PM_AddCurrents(vec3_t wishvel) { vec3_t v; float s; // // account for ladders // if (pml.ladder && fabs(pml.velocity[2]) <= 200) { if ((pm->viewangles[PITCH] <= -15) && (pm->cmd.forwardmove > 0)) wishvel[2] = 200; else if ((pm->viewangles[PITCH] >= 15) && (pm->cmd.forwardmove > 0)) wishvel[2] = -200; else if (pm->cmd.upmove > 0) wishvel[2] = 200; else if (pm->cmd.upmove < 0) wishvel[2] = -200; else wishvel[2] = 0; // limit horizontal speed when on a ladder if (wishvel[0] < -25) wishvel[0] = -25; else if (wishvel[0] > 25) wishvel[0] = 25; if (wishvel[1] < -25) wishvel[1] = -25; else if (wishvel[1] > 25) wishvel[1] = 25; } // // add water currents // if (pm->watertype & MASK_CURRENT) { VectorClear(v); if (pm->watertype & CONTENTS_CURRENT_0) v[0] += 1; if (pm->watertype & CONTENTS_CURRENT_90) v[1] += 1; if (pm->watertype & CONTENTS_CURRENT_180) v[0] -= 1; if (pm->watertype & CONTENTS_CURRENT_270) v[1] -= 1; if (pm->watertype & CONTENTS_CURRENT_UP) v[2] += 1; if (pm->watertype & CONTENTS_CURRENT_DOWN) v[2] -= 1; s = pm_waterspeed; if ((pm->waterlevel == 1) && (pm->groundentity)) s /= 2; VectorMA(wishvel, s, v, wishvel); } // // add conveyor belt velocities // if (pm->groundentity) { VectorClear(v); if (pml.groundcontents & CONTENTS_CURRENT_0) v[0] += 1; if (pml.groundcontents & CONTENTS_CURRENT_90) v[1] += 1; if (pml.groundcontents & CONTENTS_CURRENT_180) v[0] -= 1; if (pml.groundcontents & CONTENTS_CURRENT_270) v[1] -= 1; if (pml.groundcontents & CONTENTS_CURRENT_UP) v[2] += 1; if (pml.groundcontents & CONTENTS_CURRENT_DOWN) v[2] -= 1; VectorMA(wishvel, 100 /* pm->groundentity->speed */, v, wishvel); } } /* =================== PM_WaterMove =================== */ static void PM_WaterMove(void) { int i; vec3_t wishvel; float wishspeed; vec3_t wishdir; // // user intentions // for (i = 0; i < 3; i++) wishvel[i] = pml.forward[i] * pm->cmd.forwardmove + pml.right[i] * pm->cmd.sidemove; if (!pm->cmd.forwardmove && !pm->cmd.sidemove && !pm->cmd.upmove) wishvel[2] -= 60; // drift towards bottom else wishvel[2] += pm->cmd.upmove; PM_AddCurrents(wishvel); VectorCopy(wishvel, wishdir); wishspeed = VectorNormalizeLen(wishdir); if (wishspeed > pmp->maxspeed) { VectorScale(wishvel, pmp->maxspeed / wishspeed, wishvel); wishspeed = pmp->maxspeed; } wishspeed *= pmp->watermult; PM_Accelerate(wishdir, wishspeed, pm_wateraccelerate); PM_StepSlideMove(); } /* =================== PM_AirMove =================== */ static void PM_AirMove(void) { int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; float maxspeed; fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; //!!!!! pitch should be 1/3 so this isn't needed??! #if 0 pml.forward[2] = 0; pml.right[2] = 0; VectorNormalize(pml.forward); VectorNormalize(pml.right); #endif for (i = 0; i < 2; i++) wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; wishvel[2] = 0; PM_AddCurrents(wishvel); VectorCopy(wishvel, wishdir); wishspeed = VectorNormalizeLen(wishdir); // // clamp to server defined max speed // maxspeed = (pm->s.pm_flags & PMF_DUCKED) ? pm_duckspeed : pmp->maxspeed; if (wishspeed > maxspeed) { VectorScale(wishvel, maxspeed / wishspeed, wishvel); wishspeed = maxspeed; } if (pml.ladder) { PM_Accelerate(wishdir, wishspeed, pm_accelerate); if (!wishvel[2]) { if (pml.velocity[2] > 0) { pml.velocity[2] -= pm->s.gravity * pml.frametime; if (pml.velocity[2] < 0) pml.velocity[2] = 0; } else { pml.velocity[2] += pm->s.gravity * pml.frametime; if (pml.velocity[2] > 0) pml.velocity[2] = 0; } } PM_StepSlideMove(); } else if (pm->groundentity) { // walking on ground pml.velocity[2] = 0; //!!! this is before the accel PM_Accelerate(wishdir, wishspeed, pm_accelerate); // PGM -- fix for negative trigger_gravity fields // pml.velocity[2] = 0; if (pm->s.gravity > 0) pml.velocity[2] = 0; else pml.velocity[2] -= pm->s.gravity * pml.frametime; // PGM if (!pml.velocity[0] && !pml.velocity[1]) return; PM_StepSlideMove(); } else { // not on ground, so little effect on velocity if (pmp->airaccelerate) PM_AirAccelerate(wishdir, wishspeed, pm_accelerate); else PM_Accelerate(wishdir, wishspeed, 1); // add gravity pml.velocity[2] -= pm->s.gravity * pml.frametime; PM_StepSlideMove(); } } /* ============= PM_CategorizePosition ============= */ static void PM_CategorizePosition(void) { vec3_t point; int cont; trace_t trace; int sample1; int sample2; // if the player hull point one unit down is solid, the player // is on ground // see if standing on something solid point[0] = pml.origin[0]; point[1] = pml.origin[1]; point[2] = pml.origin[2] - 0.25f; if (pml.velocity[2] > 180) { //!!ZOID changed from 100 to 180 (ramp accel) pm->s.pm_flags &= ~PMF_ON_GROUND; pm->groundentity = NULL; } else { trace = pm->trace(pml.origin, pm->mins, pm->maxs, point); pml.groundplane = trace.plane; pml.groundsurface = trace.surface; pml.groundcontents = trace.contents; if (!trace.ent || (trace.plane.normal[2] < 0.7 && !trace.startsolid)) { pm->groundentity = NULL; pm->s.pm_flags &= ~PMF_ON_GROUND; } else { pm->groundentity = trace.ent; // hitting solid ground will end a waterjump if (pm->s.pm_flags & PMF_TIME_WATERJUMP) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } if (!(pm->s.pm_flags & PMF_ON_GROUND)) { // just hit the ground pm->s.pm_flags |= PMF_ON_GROUND; // don't do landing time if we were just going down a slope if (pml.velocity[2] < -200 && !pmp->strafehack) { pm->s.pm_flags |= PMF_TIME_LAND; // don't allow another jump for a little while if (pml.velocity[2] < -400) pm->s.pm_time = 25; else pm->s.pm_time = 18; } } } if (pm->numtouch < MAXTOUCH && trace.ent) { pm->touchents[pm->numtouch] = trace.ent; pm->numtouch++; } } // // get waterlevel, accounting for ducking // pm->waterlevel = 0; pm->watertype = 0; sample2 = pm->viewheight - pm->mins[2]; sample1 = sample2 / 2; point[2] = pml.origin[2] + pm->mins[2] + 1; cont = pm->pointcontents(point); if (cont & MASK_WATER) { pm->watertype = cont; pm->waterlevel = 1; point[2] = pml.origin[2] + pm->mins[2] + sample1; cont = pm->pointcontents(point); if (cont & MASK_WATER) { pm->waterlevel = 2; point[2] = pml.origin[2] + pm->mins[2] + sample2; cont = pm->pointcontents(point); if (cont & MASK_WATER) pm->waterlevel = 3; } } } /* ============= PM_CheckJump ============= */ static void PM_CheckJump(void) { if (pm->s.pm_flags & PMF_TIME_LAND) { // hasn't been long enough since landing to jump again return; } if (pm->cmd.upmove < 10) { // not holding jump pm->s.pm_flags &= ~PMF_JUMP_HELD; return; } // must wait for jump to be released if (pm->s.pm_flags & PMF_JUMP_HELD) return; if (pm->s.pm_type == PM_DEAD) return; if (pm->waterlevel >= 2) { // swimming, not jumping pm->groundentity = NULL; if (pmp->waterhack) return; if (pml.velocity[2] <= -300) return; // FIXME: makes velocity dependent on client FPS, // even causes prediction misses if (pm->watertype == CONTENTS_WATER) pml.velocity[2] = 100; else if (pm->watertype == CONTENTS_SLIME) pml.velocity[2] = 80; else pml.velocity[2] = 50; return; } if (pm->groundentity == NULL) return; // in air, so no effect pm->s.pm_flags |= PMF_JUMP_HELD; pm->groundentity = NULL; pm->s.pm_flags &= ~PMF_ON_GROUND; pml.velocity[2] += 270; if (pml.velocity[2] < 270) pml.velocity[2] = 270; } /* ============= PM_CheckSpecialMovement ============= */ static void PM_CheckSpecialMovement(void) { vec3_t spot; int cont; vec3_t flatforward; trace_t trace; if (pm->s.pm_time) return; pml.ladder = qfalse; // check for ladder flatforward[0] = pml.forward[0]; flatforward[1] = pml.forward[1]; flatforward[2] = 0; VectorNormalize(flatforward); VectorMA(pml.origin, 1, flatforward, spot); trace = pm->trace(pml.origin, pm->mins, pm->maxs, spot); if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER)) pml.ladder = qtrue; // check for water jump if (pm->waterlevel != 2) return; VectorMA(pml.origin, 30, flatforward, spot); spot[2] += 4; cont = pm->pointcontents(spot); if (!(cont & CONTENTS_SOLID)) return; spot[2] += 16; cont = pm->pointcontents(spot); if (cont) return; // jump out of water VectorScale(flatforward, 50, pml.velocity); pml.velocity[2] = 350; pm->s.pm_flags |= PMF_TIME_WATERJUMP; pm->s.pm_time = 255; } /* =============== PM_FlyMove =============== */ static void PM_FlyMove(void) { float speed, drop, friction, control, newspeed; float currentspeed, addspeed, accelspeed; int i; vec3_t wishvel; float fmove, smove; vec3_t wishdir; float wishspeed; pm->viewheight = 22; // friction speed = VectorLength(pml.velocity); if (speed < 1) { VectorCopy(vec3_origin, pml.velocity); } else { drop = 0; friction = pmp->flyfriction; control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control * friction * pml.frametime; // scale the velocity newspeed = speed - drop; if (newspeed < 0) newspeed = 0; newspeed /= speed; VectorScale(pml.velocity, newspeed, pml.velocity); } // accelerate fmove = pm->cmd.forwardmove; smove = pm->cmd.sidemove; VectorNormalize(pml.forward); VectorNormalize(pml.right); for (i = 0; i < 3; i++) wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; wishvel[2] += pm->cmd.upmove; VectorCopy(wishvel, wishdir); wishspeed = VectorNormalizeLen(wishdir); // // clamp to server defined max speed // if (wishspeed > pmp->maxspeed) { VectorScale(wishvel, pmp->maxspeed / wishspeed, wishvel); wishspeed = pmp->maxspeed; } currentspeed = DotProduct(pml.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { if (!pmp->flyhack) { return; // original buggy behaviour } } else { accelspeed = pm_accelerate * pml.frametime * wishspeed; if (accelspeed > addspeed) accelspeed = addspeed; for (i = 0; i < 3; i++) pml.velocity[i] += accelspeed * wishdir[i]; } #if 0 if (doclip) { for (i = 0; i < 3; i++) end[i] = pml.origin[i] + pml.frametime * pml.velocity[i]; trace = pm->trace(pml.origin, pm->mins, pm->maxs, end); VectorCopy(trace.endpos, pml.origin); } else #endif { // move VectorMA(pml.origin, pml.frametime, pml.velocity, pml.origin); } } /* ============== PM_CheckDuck Sets mins, maxs, and pm->viewheight ============== */ static void PM_CheckDuck(void) { trace_t trace; pm->mins[0] = -16; pm->mins[1] = -16; pm->maxs[0] = 16; pm->maxs[1] = 16; if (pm->s.pm_type == PM_GIB) { pm->mins[2] = 0; pm->maxs[2] = 16; pm->viewheight = 8; return; } pm->mins[2] = -24; if (pm->s.pm_type == PM_DEAD) { pm->s.pm_flags |= PMF_DUCKED; } else if (pm->cmd.upmove < 0 && (pm->s.pm_flags & PMF_ON_GROUND)) { // duck pm->s.pm_flags |= PMF_DUCKED; } else { // stand up if possible if (pm->s.pm_flags & PMF_DUCKED) { // try to stand up pm->maxs[2] = 32; trace = pm->trace(pml.origin, pm->mins, pm->maxs, pml.origin); if (!trace.allsolid) pm->s.pm_flags &= ~PMF_DUCKED; } } if (pm->s.pm_flags & PMF_DUCKED) { pm->maxs[2] = 4; pm->viewheight = -2; } else { pm->maxs[2] = 32; pm->viewheight = 22; } } /* ============== PM_DeadMove ============== */ static void PM_DeadMove(void) { float forward; if (!pm->groundentity) return; // extra friction forward = VectorLength(pml.velocity); forward -= 20; if (forward <= 0) { VectorClear(pml.velocity); } else { VectorNormalize(pml.velocity); VectorScale(pml.velocity, forward, pml.velocity); } } static qboolean PM_GoodPosition(void) { trace_t trace; vec3_t origin, end; int i; if (pm->s.pm_type == PM_SPECTATOR) return qtrue; for (i = 0; i < 3; i++) origin[i] = end[i] = pm->s.origin[i] * 0.125; trace = pm->trace(origin, pm->mins, pm->maxs, end); return !trace.allsolid; } /* ================ PM_SnapPosition On exit, the origin will have a value that is pre-quantized to the 0.125 precision of the network channel and in a valid position. ================ */ static void PM_SnapPosition(void) { int sign[3]; int i, j, bits; short base[3]; // try all single bits first static const byte jitterbits[8] = {0, 4, 1, 2, 3, 5, 6, 7}; // snap velocity to eigths for (i = 0; i < 3; i++) pm->s.velocity[i] = (int)(pml.velocity[i] * 8); for (i = 0; i < 3; i++) { if (pml.origin[i] >= 0) sign[i] = 1; else sign[i] = -1; pm->s.origin[i] = (int)(pml.origin[i] * 8); if (pm->s.origin[i] * 0.125f == pml.origin[i]) sign[i] = 0; } VectorCopy(pm->s.origin, base); // try all combinations for (j = 0; j < 8; j++) { bits = jitterbits[j]; VectorCopy(base, pm->s.origin); for (i = 0; i < 3; i++) if (bits & (1 << i)) pm->s.origin[i] += sign[i]; if (PM_GoodPosition()) return; } // go back to the last position VectorCopy(pml.previous_origin, pm->s.origin); } #if 0 void PM_HackedSnapPosition(void) { int x, y, z; short base[3]; static const int offset[3] = { 0, -1, 1 }; int i; // snap velocity to eigths for (i = 0; i < 3; i++) pm->s.velocity[i] = Q_rint(pml.velocity[i] * 8); for (i = 0; i < 3; i++) pm->s.origin[i] = Q_rint(pml.origin[i] * 8); VectorCopy(pm->s.origin, base); for (z = 0; z < 3; z++) { pm->s.origin[2] = base[2] + offset[z]; for (y = 0; y < 3; y++) { pm->s.origin[1] = base[1] + offset[y]; for (x = 0; x < 3; x++) { pm->s.origin[0] = base[0] + offset[x]; if (PM_GoodPosition()) { pml.origin[0] = pm->s.origin[0] * 0.125; pml.origin[1] = pm->s.origin[1] * 0.125; pml.origin[2] = pm->s.origin[2] * 0.125; VectorCopy(pm->s.origin, pml.previous_origin); return; } } } } VectorCopy(pml.previous_origin, pm->s.origin); } #endif /* ================ PM_InitialSnapPosition ================ */ static void PM_InitialSnapPosition(void) { int x, y, z; short base[3]; static const short offset[3] = { 0, -1, 1 }; VectorCopy(pm->s.origin, base); for (z = 0; z < 3; z++) { pm->s.origin[2] = base[2] + offset[z]; for (y = 0; y < 3; y++) { pm->s.origin[1] = base[1] + offset[y]; for (x = 0; x < 3; x++) { pm->s.origin[0] = base[0] + offset[x]; if (PM_GoodPosition()) { pml.origin[0] = pm->s.origin[0] * 0.125; pml.origin[1] = pm->s.origin[1] * 0.125; pml.origin[2] = pm->s.origin[2] * 0.125; VectorCopy(pm->s.origin, pml.previous_origin); return; } } } } } /* ================ PM_ClampAngles ================ */ static void PM_ClampAngles(void) { short temp; int i; if (pm->s.pm_flags & PMF_TIME_TELEPORT) { pm->viewangles[YAW] = SHORT2ANGLE(pm->cmd.angles[YAW] + pm->s.delta_angles[YAW]); pm->viewangles[PITCH] = 0; pm->viewangles[ROLL] = 0; } else { // circularly clamp the angles with deltas for (i = 0; i < 3; i++) { temp = pm->cmd.angles[i] + pm->s.delta_angles[i]; pm->viewangles[i] = SHORT2ANGLE(temp); } // don't let the player look up or down more than 90 degrees if (pm->viewangles[PITCH] > 89 && pm->viewangles[PITCH] < 180) pm->viewangles[PITCH] = 89; else if (pm->viewangles[PITCH] < 271 && pm->viewangles[PITCH] >= 180) pm->viewangles[PITCH] = 271; } AngleVectors(pm->viewangles, pml.forward, pml.right, pml.up); } /* ================ Pmove Can be called by either the server or the client ================ */ void Pmove(pmove_t *pmove, pmoveParams_t *params) { pm = pmove; pmp = params; // clear results pm->numtouch = 0; VectorClear(pm->viewangles); pm->viewheight = 0; pm->groundentity = NULL; pm->watertype = 0; pm->waterlevel = 0; // clear all pmove local vars memset(&pml, 0, sizeof(pml)); // convert origin and velocity to float values VectorScale(pm->s.origin, 0.125f, pml.origin); VectorScale(pm->s.velocity, 0.125f, pml.velocity); // save old org in case we get stuck VectorCopy(pm->s.origin, pml.previous_origin); PM_ClampAngles(); if (pm->s.pm_type == PM_SPECTATOR) { pml.frametime = pmp->speedmult * pm->cmd.msec * 0.001f; PM_FlyMove(); PM_SnapPosition(); return; } pml.frametime = pm->cmd.msec * 0.001f; if (pm->s.pm_type >= PM_DEAD) { pm->cmd.forwardmove = 0; pm->cmd.sidemove = 0; pm->cmd.upmove = 0; } if (pm->s.pm_type == PM_FREEZE) return; // no movement at all // set mins, maxs, and viewheight PM_CheckDuck(); if (pm->snapinitial) PM_InitialSnapPosition(); // set groundentity, watertype, and waterlevel PM_CategorizePosition(); if (pm->s.pm_type == PM_DEAD) PM_DeadMove(); PM_CheckSpecialMovement(); // drop timing counter if (pm->s.pm_time) { int msec; msec = pm->cmd.msec >> 3; if (!msec) msec = 1; if (msec >= pm->s.pm_time) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } else pm->s.pm_time -= msec; } if (pm->s.pm_flags & PMF_TIME_TELEPORT) { // teleport pause stays exactly in place } else if (pm->s.pm_flags & PMF_TIME_WATERJUMP) { // waterjump has no control, but falls pml.velocity[2] -= pm->s.gravity * pml.frametime; if (pml.velocity[2] < 0) { // cancel as soon as we are falling down again pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } PM_StepSlideMove(); } else { PM_CheckJump(); PM_Friction(); if (pm->waterlevel >= 2) PM_WaterMove(); else { vec3_t angles; VectorCopy(pm->viewangles, angles); if (angles[PITCH] > 180) angles[PITCH] = angles[PITCH] - 360; angles[PITCH] /= 3; AngleVectors(angles, pml.forward, pml.right, pml.up); PM_AirMove(); } } // set groundentity, watertype, and waterlevel for final spot PM_CategorizePosition(); PM_SnapPosition(); } void PmoveInit(pmoveParams_t *pmp) { // set up default pmove parameters memset(pmp, 0, sizeof(*pmp)); pmp->speedmult = 1; pmp->watermult = 0.5f; pmp->maxspeed = 300; pmp->friction = 6; pmp->waterfriction = 1; pmp->flyfriction = 9; } void PmoveEnableQW(pmoveParams_t *pmp) { pmp->qwmode = qtrue; pmp->watermult = 0.7f; pmp->maxspeed = 320; //pmp->upspeed = (sv_qwmod->integer > 1) ? 310 : 350; pmp->friction = 4; pmp->waterfriction = 4; pmp->airaccelerate = qtrue; }