1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // pmove.c
22 //
23 
24 #include "cg_local.h"
25 
26 // all of the locals will be zeroed before each pmove, just to make damn sure
27 // we don't have any differences when running on client or server
28 
29 typedef struct pMoveLocal_s {
30 	vec3_t			origin;			// full float precision
31 	vec3_t			velocity;		// full float precision
32 
33 	vec3_t			forward, right, up;
34 	float			frameTime;
35 
36 	cBspSurface_t	*groundSurface;
37 	int				groundContents;
38 
39 	svec3_t			previousOrigin;
40 	qBool			ladder;
41 } pMoveLocal_t;
42 
43 static pMoveNew_t	*pm;
44 static pMoveLocal_t	pml;
45 
46 static float	pmAirAcceleration = 0;
47 
48 #define STEPSIZE			18
49 #define MIN_STEP_NORMAL		0.7f		// can't step up onto very steep slopes
50 #define MAX_CLIP_PLANES		5
51 
52 // movement parameters
53 #define PM_STOPSPEED		100.0f
54 #define PM_MAXSPEED			300.0f
55 #define PM_DUCKSPEED		100.0f
56 #define PM_ACCELERATE		10.0f
57 #define PM_WATERACCELERATE	10.0f
58 #define PM_FRICTION			6.0f
59 #define PM_WATERFRICTION	1.0f
60 #define PM_WATERSPEED		400.0f
61 
62 /*
63 ==================
64 PM_ClipVelocity
65 
66 Slide off of the impacting object
67 returns the blocked flags (1 = floor, 2 = step / wall)
68 ==================
69 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)70 static void PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
71 {
72 	float	backoff;
73 	float	change;
74 	int		i;
75 
76 	backoff = DotProduct (in, normal) * overbounce;
77 
78 	for (i=0 ; i<3 ; i++) {
79 		change = normal[i]*backoff;
80 		out[i] = in[i] - change;
81 		if ((out[i] > -LARGE_EPSILON) && (out[i] < LARGE_EPSILON))
82 			out[i] = 0;
83 	}
84 }
85 
86 
87 /*
88 ==================
89 PM_StepSlideMove
90 
91 Each intersection will try to step over the obstruction instead of
92 sliding along it.
93 
94 Returns a new origin, velocity, and contact entity
95 Does not modify any world state?
96 
97 Returns qTrue if going onto a step.
98 ==================
99 */
PM_StepSlideMove_(void)100 static qBool PM_StepSlideMove_ (void)
101 {
102 	int			bumpcount, numbumps;
103 	vec3_t		dir;
104 	float		d;
105 	int			numPlanes;
106 	vec3_t		planes[MAX_CLIP_PLANES];
107 	vec3_t		primal_velocity;
108 	int			i, j;
109 	trace_t		trace;
110 	vec3_t		end;
111 	float		time_left;
112 	qBool		step = qFalse;
113 
114 	numbumps = 4;
115 
116 	Vec3Copy (pml.velocity, primal_velocity);
117 	numPlanes = 0;
118 
119 	time_left = pml.frameTime;
120 
121 	for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) {
122 		end[0] = pml.origin[0] + time_left * pml.velocity[0];
123 		end[1] = pml.origin[1] + time_left * pml.velocity[1];
124 		end[2] = pml.origin[2] + time_left * pml.velocity[2];
125 
126 		trace = pm->trace (pml.origin, pm->mins, pm->maxs, end);
127 
128 		if (trace.allSolid) {
129 			// Entity is trapped in another solid
130 			pml.velocity[2] = 0;	// Don't build up falling damage
131 			return 0;
132 		}
133 
134 		if (trace.fraction > 0) {
135 			// Actually covered some distance
136 			Vec3Copy (trace.endPos, pml.origin);
137 			numPlanes = 0;
138 		}
139 
140 		if (trace.fraction == 1)
141 			 break;		// Moved the entire distance
142 
143 		// Save entity for contact
144 		if (pm->numTouch < MAXTOUCH && trace.ent) {
145 			pm->touchEnts[pm->numTouch] = trace.ent;
146 			pm->numTouch++;
147 		}
148 
149 		if (trace.plane.normal[2] < MIN_STEP_NORMAL)
150 			step = qTrue;	// Step
151 
152 		time_left -= time_left * trace.fraction;
153 
154 		// Slide along this plane
155 		if (numPlanes >= MAX_CLIP_PLANES) {
156 			// This shouldn't really happen
157 			Vec3Clear (pml.velocity);
158 			break;
159 		}
160 
161 		Vec3Copy (trace.plane.normal, planes[numPlanes]);
162 		numPlanes++;
163 
164 		// modify original_velocity so it parallels all of the clip planes
165 		for (i=0 ; i<numPlanes ; i++) {
166 			PM_ClipVelocity (pml.velocity, planes[i], pml.velocity, 1.01f);
167 			for (j=0 ; j<numPlanes ; j++) {
168 				if (j != i) {
169 					if (DotProduct (pml.velocity, planes[j]) < 0)
170 						break;	// not ok
171 				}
172 			}
173 			if (j == numPlanes)
174 				break;
175 		}
176 
177 		if (i != numPlanes) {
178 			// Go along this plane
179 		}
180 		else {
181 			// Go along the crease
182 			if (numPlanes != 2) {
183 				Vec3Clear (pml.velocity);
184 				break;
185 			}
186 			CrossProduct (planes[0], planes[1], dir);
187 			d = DotProduct (dir, pml.velocity);
188 			Vec3Scale (dir, d, pml.velocity);
189 		}
190 
191 		/*
192 		** if velocity is against the original velocity, stop dead
193 		** to avoid tiny occilations in sloping corners
194 		*/
195 		if (DotProduct (pml.velocity, primal_velocity) <= 0) {
196 			Vec3Clear (pml.velocity);
197 			break;
198 		}
199 	}
200 
201 	if (pm->state.pmTime)
202 		Vec3Copy (primal_velocity, pml.velocity);
203 
204 	return step;
205 }
206 
PM_StepSlideMove(void)207 static void PM_StepSlideMove (void)
208 {
209 	vec3_t		start_o, start_v;
210 	vec3_t		down_o, down_v;
211 	trace_t		trace;
212 	float		down_dist, up_dist;
213 	vec3_t		up, down;
214 	qBool		step;
215 
216 	Vec3Copy (pml.origin, start_o);
217 	Vec3Copy (pml.velocity, start_v);
218 
219 	step = PM_StepSlideMove_ ();
220 
221 	Vec3Copy (pml.origin, down_o);
222 	Vec3Copy (pml.velocity, down_v);
223 
224 	Vec3Copy (start_o, up);
225 	up[2] += STEPSIZE;
226 
227 	trace = pm->trace (up, pm->mins, pm->maxs, up);
228 	if (trace.allSolid)
229 		return;	// Can't step up
230 
231 	// Try sliding above
232 	Vec3Copy (up, pml.origin);
233 	Vec3Copy (start_v, pml.velocity);
234 
235 	PM_StepSlideMove_ ();
236 
237 	// Push down the final amount
238 	Vec3Copy (pml.origin, down);
239 	down[2] -= STEPSIZE;
240 	trace = pm->trace (pml.origin, pm->mins, pm->maxs, down);
241 	if (!trace.allSolid)
242 		Vec3Copy (trace.endPos, pml.origin);
243 
244 	Vec3Copy (pml.origin, up);
245 
246 	// Decide which one went farther
247 	down_dist = (down_o[0] - start_o[0])*(down_o[0] - start_o[0])
248 		+ (down_o[1] - start_o[1])*(down_o[1] - start_o[1]);
249 	up_dist = (up[0] - start_o[0])*(up[0] - start_o[0])
250 		+ (up[1] - start_o[1])*(up[1] - start_o[1]);
251 
252 	if (down_dist > up_dist || trace.plane.normal[2] < MIN_STEP_NORMAL) {
253 		Vec3Copy (down_o, pml.origin);
254 		Vec3Copy (down_v, pml.velocity);
255 		return;
256 	}
257 
258 	if (step || trace.plane.normal[2] == 1)
259 		pm->step = qTrue;
260 
261 	// !! Special case !!
262 	// if we were walking along a plane, then we need to copy the Z over
263 	pml.velocity[2] = down_v[2];
264 }
265 
266 
267 /*
268 ==================
269 PM_Friction
270 
271 Handles both ground friction and water friction
272 ==================
273 */
PM_Friction(void)274 static void PM_Friction (void)
275 {
276 	float	*vel;
277 	float	speed, newspeed, control;
278 	float	friction;
279 	float	drop;
280 
281 	vel = pml.velocity;
282 
283 	speed = Vec3Length (vel);
284 	if (speed < 1) {
285 		vel[0] = 0;
286 		vel[1] = 0;
287 		return;
288 	}
289 
290 	drop = 0;
291 
292 	// Apply ground friction
293 	if ((pm->groundEntity && pml.groundSurface && !(pml.groundSurface->flags & SURF_TEXINFO_SLICK)) || pml.ladder) {
294 		friction = PM_FRICTION;
295 		control = (speed < PM_STOPSPEED) ? PM_STOPSPEED : speed;
296 		drop += control*friction*pml.frameTime;
297 	}
298 
299 	// Apply water friction
300 	if (pm->waterLevel && !pml.ladder)
301 		drop += speed*PM_WATERFRICTION*pm->waterLevel*pml.frameTime;
302 
303 	// Scale the velocity
304 	newspeed = speed - drop;
305 	if (newspeed < 0)
306 		newspeed = 0;
307 
308 	newspeed /= speed;
309 
310 	vel[0] = vel[0] * newspeed;
311 	vel[1] = vel[1] * newspeed;
312 	vel[2] = vel[2] * newspeed;
313 }
314 
315 
316 /*
317 ==============
318 PM_Accelerate
319 
320 Handles user intended acceleration
321 ==============
322 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)323 static void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel)
324 {
325 	float		addspeed, accelspeed, currentspeed;
326 
327 	currentspeed = DotProduct (pml.velocity, wishdir);
328 	addspeed = wishspeed - currentspeed;
329 	if (addspeed <= 0)
330 		return;
331 
332 	accelspeed = accel*pml.frameTime*wishspeed;
333 	if (accelspeed > addspeed)
334 		accelspeed = addspeed;
335 
336 	pml.velocity[0] += accelspeed*wishdir[0];
337 	pml.velocity[1] += accelspeed*wishdir[1];
338 	pml.velocity[2] += accelspeed*wishdir[2];
339 }
340 
341 
342 /*
343 ==============
344 PM_AirAccelerate
345 
346 Handles user intended air acceleration
347 ==============
348 */
PM_AirAccelerate(vec3_t wishdir,float wishspeed,float accel)349 static void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel)
350 {
351 	float		addspeed, accelspeed;
352 	float		currentspeed, wishspd = wishspeed;
353 
354 	if (wishspd > 30)
355 		wishspd = 30;
356 	currentspeed = DotProduct (pml.velocity, wishdir);
357 	addspeed = wishspd - currentspeed;
358 	if (addspeed <= 0)
359 		return;
360 
361 	accelspeed = accel * wishspeed * pml.frameTime;
362 	if (accelspeed > addspeed)
363 		accelspeed = addspeed;
364 
365 	pml.velocity[0] += accelspeed*wishdir[0];
366 	pml.velocity[1] += accelspeed*wishdir[1];
367 	pml.velocity[2] += accelspeed*wishdir[2];
368 }
369 
370 
371 /*
372 =============
373 PM_AddCurrents
374 =============
375 */
PM_AddCurrents(vec3_t wishvel)376 static void PM_AddCurrents (vec3_t	wishvel)
377 {
378 	vec3_t	v;
379 	float	s;
380 
381 	// Account for ladders
382 	if (pml.ladder && fabs (pml.velocity[2]) <= 200) {
383 		if ((pm->viewAngles[PITCH] <= -15) && (pm->cmd.forwardMove > 0))
384 			wishvel[2] = 200;
385 		else if ((pm->viewAngles[PITCH] >= 15) && (pm->cmd.forwardMove > 0))
386 			wishvel[2] = -200;
387 		else if (pm->cmd.upMove > 0)
388 			wishvel[2] = 200;
389 		else if (pm->cmd.upMove < 0)
390 			wishvel[2] = -200;
391 		else
392 			wishvel[2] = 0;
393 
394 		// Limit horizontal speed when on a ladder
395 		if (wishvel[0] < -25)
396 			wishvel[0] = -25;
397 		else if (wishvel[0] > 25)
398 			wishvel[0] = 25;
399 
400 		if (wishvel[1] < -25)
401 			wishvel[1] = -25;
402 		else if (wishvel[1] > 25)
403 			wishvel[1] = 25;
404 	}
405 
406 	// Add water currents
407 	if (pm->waterType & MASK_CURRENT) {
408 		Vec3Clear (v);
409 
410 		if (pm->waterType & CONTENTS_CURRENT_0)		v[0] += 1;
411 		if (pm->waterType & CONTENTS_CURRENT_90)	v[1] += 1;
412 		if (pm->waterType & CONTENTS_CURRENT_180)	v[0] -= 1;
413 		if (pm->waterType & CONTENTS_CURRENT_270)	v[1] -= 1;
414 		if (pm->waterType & CONTENTS_CURRENT_UP)	v[2] += 1;
415 		if (pm->waterType & CONTENTS_CURRENT_DOWN)	v[2] -= 1;
416 
417 		s = PM_WATERSPEED;
418 		if ((pm->waterLevel == 1) && (pm->groundEntity))
419 			s /= 2;
420 
421 		Vec3MA (wishvel, s, v, wishvel);
422 	}
423 
424 	// Add conveyor belt velocities
425 	if (pm->groundEntity) {
426 		Vec3Clear (v);
427 
428 		if (pml.groundContents & CONTENTS_CURRENT_0)	v[0] += 1;
429 		if (pml.groundContents & CONTENTS_CURRENT_90)	v[1] += 1;
430 		if (pml.groundContents & CONTENTS_CURRENT_180)	v[0] -= 1;
431 		if (pml.groundContents & CONTENTS_CURRENT_270)	v[1] -= 1;
432 		if (pml.groundContents & CONTENTS_CURRENT_UP)	v[2] += 1;
433 		if (pml.groundContents & CONTENTS_CURRENT_DOWN)	v[2] -= 1;
434 
435 		Vec3MA (wishvel, 100 /* pm->groundEntity->speed */, v, wishvel);
436 	}
437 }
438 
439 
440 /*
441 ===================
442 PM_WaterMove
443 ===================
444 */
PM_WaterMove(void)445 static void PM_WaterMove (void)
446 {
447 	vec3_t	wishvel, wishdir;
448 	float	wishspeed;
449 
450 	// User intentions
451 	wishvel[0] = pml.forward[0]*pm->cmd.forwardMove + pml.right[0]*pm->cmd.sideMove;
452 	wishvel[1] = pml.forward[1]*pm->cmd.forwardMove + pml.right[1]*pm->cmd.sideMove;
453 	wishvel[2] = pml.forward[2]*pm->cmd.forwardMove + pml.right[2]*pm->cmd.sideMove;
454 
455 	if (!pm->cmd.forwardMove && !pm->cmd.sideMove && !pm->cmd.upMove)
456 		wishvel[2] -= 60;		// Drift towards bottom
457 	else
458 		wishvel[2] += pm->cmd.upMove;
459 
460 	PM_AddCurrents (wishvel);
461 
462 	Vec3Copy (wishvel, wishdir);
463 	wishspeed = VectorNormalizef (wishdir, wishdir);
464 
465 	if (wishspeed > PM_MAXSPEED) {
466 		Vec3Scale (wishvel, PM_MAXSPEED/wishspeed, wishvel);
467 		wishspeed = PM_MAXSPEED;
468 	}
469 	wishspeed *= 0.5f;
470 
471 	PM_Accelerate (wishdir, wishspeed, PM_WATERACCELERATE);
472 
473 	PM_StepSlideMove ();
474 }
475 
476 
477 /*
478 ===================
479 PM_AirMove
480 ===================
481 */
PM_AirMove(void)482 static void PM_AirMove (void)
483 {
484 	vec3_t		wishvel, wishdir;
485 	float		fmove, smove;
486 	float		wishspeed;
487 	float		maxspeed;
488 
489 	fmove = pm->cmd.forwardMove;
490 	smove = pm->cmd.sideMove;
491 
492 	wishvel[0] = pml.forward[0]*fmove + pml.right[0]*smove;
493 	wishvel[1] = pml.forward[1]*fmove + pml.right[1]*smove;
494 	wishvel[2] = 0;
495 
496 	PM_AddCurrents (wishvel);
497 
498 	Vec3Copy (wishvel, wishdir);
499 	wishspeed = VectorNormalizef (wishdir, wishdir);
500 
501 	// Clamp to server defined max speed
502 	maxspeed = (pm->state.pmFlags & PMF_DUCKED) ? PM_DUCKSPEED : PM_MAXSPEED;
503 
504 	if (wishspeed > maxspeed) {
505 		Vec3Scale (wishvel, maxspeed/wishspeed, wishvel);
506 		wishspeed = maxspeed;
507 	}
508 
509 	if (pml.ladder) {
510 		PM_Accelerate (wishdir, wishspeed, PM_ACCELERATE);
511 		if (!wishvel[2]) {
512 			if (pml.velocity[2] > 0) {
513 				pml.velocity[2] -= pm->state.gravity * pml.frameTime;
514 				if (pml.velocity[2] < 0)
515 					pml.velocity[2]  = 0;
516 			}
517 			else {
518 				pml.velocity[2] += pm->state.gravity * pml.frameTime;
519 				if (pml.velocity[2] > 0)
520 					pml.velocity[2]  = 0;
521 			}
522 		}
523 
524 		PM_StepSlideMove ();
525 	}
526 	else if (pm->groundEntity) {
527 		// Walking on ground
528 		pml.velocity[2] = 0; //!!! this is before the accel
529 		PM_Accelerate (wishdir, wishspeed, PM_ACCELERATE);
530 
531 		if (pm->state.gravity > 0)
532 			pml.velocity[2] = 0;
533 		else
534 			pml.velocity[2] -= pm->state.gravity * pml.frameTime;
535 
536 		if (!pml.velocity[0] && !pml.velocity[1])
537 			return;
538 		PM_StepSlideMove ();
539 	}
540 	else {
541 		// Not on ground, so little effect on velocity
542 		if (pmAirAcceleration)
543 			PM_AirAccelerate (wishdir, wishspeed, PM_ACCELERATE);
544 		else
545 			PM_Accelerate (wishdir, wishspeed, 1);
546 
547 		// Add gravity
548 		pml.velocity[2] -= pm->state.gravity * pml.frameTime;
549 		PM_StepSlideMove ();
550 	}
551 }
552 
553 
554 /*
555 =============
556 PM_CatagorizePosition
557 =============
558 */
PM_CatagorizePosition(void)559 static void PM_CatagorizePosition (void)
560 {
561 	vec3_t		point;
562 	int			cont;
563 	trace_t		trace;
564 	float		sample1;
565 	float		sample2;
566 
567 	// If the player hull point one unit down is solid, the player is on ground
568 	// see if standing on something solid
569 	point[0] = pml.origin[0];
570 	point[1] = pml.origin[1];
571 	point[2] = pml.origin[2] - 0.25;
572 
573 	if (pml.velocity[2] > 180) {
574 		pm->state.pmFlags &= ~PMF_ON_GROUND;
575 		pm->groundEntity = NULL;
576 	}
577 	else {
578 		trace = pm->trace (pml.origin, pm->mins, pm->maxs, point);
579 		pml.groundSurface = trace.surface;
580 		pml.groundContents = trace.contents;
581 
582 		if (!trace.ent || (trace.plane.normal[2] < MIN_STEP_NORMAL && !trace.startSolid)) {
583 			pm->groundEntity = NULL;
584 			pm->state.pmFlags &= ~PMF_ON_GROUND;
585 		}
586 		else {
587 			pm->groundEntity = trace.ent;
588 
589 			// Hitting solid ground will end a waterjump
590 			if (pm->state.pmFlags & PMF_TIME_WATERJUMP) {
591 				pm->state.pmFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT);
592 				pm->state.pmTime = 0;
593 			}
594 
595 			if (!(pm->state.pmFlags & PMF_ON_GROUND)) {
596 				// Just hit the ground
597 				pm->state.pmFlags |= PMF_ON_GROUND;
598 				// Don't do landing time if we were just going down a slope
599 				if (pml.velocity[2] < -200 && !pm->strafeHack) {
600 					pm->state.pmFlags |= PMF_TIME_LAND;
601 					// Don't allow another jump for a little while
602 					if (pml.velocity[2] < -400)
603 						pm->state.pmTime = 25;
604 					else
605 						pm->state.pmTime = 18;
606 				}
607 			}
608 		}
609 
610 		if (pm->numTouch < MAXTOUCH && trace.ent) {
611 			pm->touchEnts[pm->numTouch] = trace.ent;
612 			pm->numTouch++;
613 		}
614 	}
615 
616 	// Get waterLevel, accounting for ducking
617 	pm->waterLevel = 0;
618 	pm->waterType = 0;
619 
620 	sample2 = pm->viewHeight - pm->mins[2];
621 	sample1 = sample2 / 2;
622 
623 	point[2] = pml.origin[2] + pm->mins[2] + 1;
624 	cont = pm->pointContents (point);
625 
626 	if (cont & MASK_WATER) {
627 		pm->waterType = cont;
628 		pm->waterLevel = 1;
629 		point[2] = pml.origin[2] + pm->mins[2] + sample1;
630 		cont = pm->pointContents (point);
631 		if (cont & MASK_WATER) {
632 			pm->waterLevel = 2;
633 			point[2] = pml.origin[2] + pm->mins[2] + sample2;
634 			cont = pm->pointContents (point);
635 			if (cont & MASK_WATER)
636 				pm->waterLevel = 3;
637 		}
638 	}
639 }
640 
641 
642 /*
643 =============
644 PM_CheckJump
645 =============
646 */
PM_CheckJump(void)647 static void PM_CheckJump (void)
648 {
649 	if (pm->state.pmFlags & PMF_TIME_LAND) {
650 		// Hasn't been long enough since landing to jump again
651 		return;
652 	}
653 
654 	if (pm->cmd.upMove < 10) {
655 		// Not holding jump
656 		pm->state.pmFlags &= ~PMF_JUMP_HELD;
657 		return;
658 	}
659 
660 	// Must wait for jump to be released
661 	if (pm->state.pmFlags & PMF_JUMP_HELD)
662 		return;
663 
664 	if (pm->state.pmType == PMT_DEAD)
665 		return;
666 
667 	if (pm->waterLevel >= 2) {
668 		// Swimming, not jumping
669 		pm->groundEntity = NULL;
670 
671 		if (pml.velocity[2] <= -300)
672 			return;
673 
674 		if (pm->waterType == CONTENTS_WATER)
675 			pml.velocity[2] = 100;
676 		else if (pm->waterType == CONTENTS_SLIME)
677 			pml.velocity[2] = 80;
678 		else
679 			pml.velocity[2] = 50;
680 		return;
681 	}
682 
683 	if (pm->groundEntity == NULL)
684 		return;		// In air, so no effect
685 
686 	pm->state.pmFlags |= PMF_JUMP_HELD;
687 
688 	pm->groundEntity = NULL;
689 	pml.velocity[2] += 270;
690 	if (pml.velocity[2] < 270)
691 		pml.velocity[2] = 270;
692 
693 	// This is so hacky my eyes hurt just looking at it
694 	if (!cgi.Cvar_GetIntegerValue ("dedicated") && glm_jumppred->floatVal && !cgi.Com_ServerState () && cg.currGameMod == GAME_MOD_GLOOM) {
695 		switch (cg.gloomClassType) {
696 		case GLM_HATCHLING:	pml.velocity[2] *= 1.5f;	break;
697 		case GLM_DRONE:		pml.velocity[2] *= 1.4f;	break;
698 		case GLM_KAMIKAZE:	pml.velocity[2] *= 1.4f;	break;
699 		case GLM_STINGER:	pml.velocity[2] *= 1.35f;	break;
700 		case GLM_GUARDIAN:	pml.velocity[2] *= 1.2f;	break;
701 		case GLM_STALKER:	pml.velocity[2] *= 0.5f;	break;
702 		case GLM_BREEDER:	pml.velocity[2] *= 0.7f;	break;
703 		case GLM_HT:		pml.velocity[2] *= 0.8f;	break;
704 		case GLM_COMMANDO:	pml.velocity[2] *= 1.2f;	break;
705 		case GLM_EXTERM:	pml.velocity[2] *= 0.9f;	break;
706 		case GLM_MECH:		pml.velocity[2] *= 0.4f;	break;
707 		case GLM_WRAITH:	pml.velocity[2] *= 1.4f;	break;
708 
709 		case GLM_GRUNT:
710 		case GLM_ST:
711 		case GLM_ENGINEER:
712 		case GLM_BIOTECH:
713 		case GLM_DEFAULT:
714 		case GLM_OBSERVER:
715 		default:
716 			break;
717 		}
718 	}
719 }
720 
721 
722 /*
723 =============
724 PM_CheckSpecialMovement
725 =============
726 */
PM_CheckSpecialMovement(void)727 static void PM_CheckSpecialMovement (void)
728 {
729 	vec3_t	spot;
730 	int		cont;
731 	vec3_t	flatforward;
732 	trace_t	trace;
733 
734 	if (pm->state.pmTime)
735 		return;
736 
737 	pml.ladder = qFalse;
738 
739 	// Check for ladder
740 	flatforward[0] = pml.forward[0];
741 	flatforward[1] = pml.forward[1];
742 	flatforward[2] = 0;
743 	VectorNormalizef (flatforward, flatforward);
744 
745 	Vec3MA (pml.origin, 1, flatforward, spot);
746 	trace = pm->trace (pml.origin, pm->mins, pm->maxs, spot);
747 	if ((trace.fraction < 1) && (trace.contents & CONTENTS_LADDER))
748 		pml.ladder = qTrue;
749 
750 	// Check for water jump
751 	if (pm->waterLevel != 2)
752 		return;
753 
754 	Vec3MA (pml.origin, 30, flatforward, spot);
755 	spot[2] += 4;
756 	cont = pm->pointContents (spot);
757 	if (!(cont & CONTENTS_SOLID))
758 		return;
759 
760 	spot[2] += 16;
761 	cont = pm->pointContents (spot);
762 	if (cont)
763 		return;
764 
765 	// Jump out of water
766 	Vec3Scale (flatforward, 50, pml.velocity);
767 	pml.velocity[2] = 350;
768 
769 	pm->state.pmFlags |= PMF_TIME_WATERJUMP;
770 	pm->state.pmTime = 255;
771 }
772 
773 
774 /*
775 ===============
776 PM_FlyMove
777 ===============
778 */
PM_FlyMove(qBool doClip)779 static void PM_FlyMove (qBool doClip)
780 {
781 	float	speed, drop, friction, control, newspeed;
782 	float	currentspeed, addspeed, accelspeed;
783 	vec3_t	wishvel, wishdir, end;
784 	float	fmove, smove, wishspeed;
785 	trace_t	trace;
786 
787 	pm->viewHeight = 22;
788 
789 	// friction
790 	speed = Vec3Length (pml.velocity);
791 	if (speed < 1)
792 		Vec3Clear (pml.velocity);
793 	else {
794 		drop = 0;
795 
796 		friction = PM_FRICTION*1.5;	// extra friction
797 		control = (speed < PM_STOPSPEED) ? PM_STOPSPEED : speed;
798 		drop += control*friction*pml.frameTime;
799 
800 		// scale the velocity
801 		newspeed = speed - drop;
802 		if (newspeed < 0)
803 			newspeed = 0;
804 		newspeed /= speed;
805 
806 		Vec3Scale (pml.velocity, newspeed, pml.velocity);
807 	}
808 
809 	// accelerate
810 	fmove = pm->cmd.forwardMove;
811 	smove = pm->cmd.sideMove;
812 
813 	VectorNormalizef (pml.forward, pml.forward);
814 	VectorNormalizef (pml.right, pml.right);
815 
816 	wishvel[0] = pml.forward[0]*fmove + pml.right[0]*smove;
817 	wishvel[1] = pml.forward[1]*fmove + pml.right[1]*smove;
818 	wishvel[2] = pml.forward[2]*fmove + pml.right[2]*smove + pm->cmd.upMove;
819 
820 	Vec3Copy (wishvel, wishdir);
821 	wishspeed = VectorNormalizef (wishdir, wishdir);
822 
823 	// clamp to server defined max speed
824 	if (wishspeed > PM_MAXSPEED) {
825 		Vec3Scale (wishvel, PM_MAXSPEED/wishspeed, wishvel);
826 		wishspeed = PM_MAXSPEED;
827 	}
828 
829 	currentspeed = DotProduct (pml.velocity, wishdir);
830 	addspeed = wishspeed - currentspeed;
831 	if (addspeed <= 0)
832 		return;
833 
834 	accelspeed = PM_ACCELERATE*pml.frameTime*wishspeed;
835 	if (accelspeed > addspeed)
836 		accelspeed = addspeed;
837 
838 	pml.velocity[0] += accelspeed*wishdir[0];
839 	pml.velocity[1] += accelspeed*wishdir[1];
840 	pml.velocity[2] += accelspeed*wishdir[2];
841 
842 	if (doClip) {
843 		end[0] = pml.origin[0] + pml.frameTime * pml.velocity[0];
844 		end[1] = pml.origin[1] + pml.frameTime * pml.velocity[1];
845 		end[2] = pml.origin[2] + pml.frameTime * pml.velocity[2];
846 
847 		trace = pm->trace (pml.origin, pm->mins, pm->maxs, end);
848 
849 		Vec3Copy (trace.endPos, pml.origin);
850 	}
851 	else {
852 		// move
853 		Vec3MA (pml.origin, pml.frameTime, pml.velocity, pml.origin);
854 	}
855 }
856 
857 
858 /*
859 ==============
860 PM_CheckDuck
861 
862 Sets mins, maxs, and pm->viewHeight
863 ==============
864 */
PM_CheckDuck(void)865 static void PM_CheckDuck (void)
866 {
867 	trace_t	trace;
868 
869 	pm->mins[0] = -16;
870 	pm->mins[1] = -16;
871 
872 	pm->maxs[0] = 16;
873 	pm->maxs[1] = 16;
874 
875 	if (pm->state.pmType == PMT_GIB) {
876 		pm->mins[2] = 0;
877 		pm->maxs[2] = 16;
878 		pm->viewHeight = 8;
879 		return;
880 	}
881 
882 	pm->mins[2] = -24;
883 
884 	if (pm->state.pmType == PMT_DEAD) {
885 		pm->state.pmFlags |= PMF_DUCKED;
886 	}
887 	else if (pm->cmd.upMove < 0 && (pm->state.pmFlags & PMF_ON_GROUND)) {
888 		// duck
889 		pm->state.pmFlags |= PMF_DUCKED;
890 	}
891 	else {
892 		// stand up if possible
893 		if (pm->state.pmFlags & PMF_DUCKED) {
894 			// try to stand up
895 			pm->maxs[2] = 32;
896 			trace = pm->trace (pml.origin, pm->mins, pm->maxs, pml.origin);
897 			if (!trace.allSolid)
898 				pm->state.pmFlags &= ~PMF_DUCKED;
899 		}
900 	}
901 
902 	if (pm->state.pmFlags & PMF_DUCKED) {
903 		pm->maxs[2] = 4;
904 		pm->viewHeight = -2;
905 	}
906 	else {
907 		pm->maxs[2] = 32;
908 		pm->viewHeight = 22;
909 	}
910 }
911 
912 
913 /*
914 ==============
915 PM_DeadMove
916 ==============
917 */
PM_DeadMove(void)918 static void PM_DeadMove (void)
919 {
920 	float	forward;
921 
922 	if (!pm->groundEntity)
923 		return;
924 
925 	// extra friction
926 	forward = Vec3Length (pml.velocity);
927 	forward -= 20;
928 	if (forward <= 0)
929 		Vec3Clear (pml.velocity);
930 	else {
931 		VectorNormalizef (pml.velocity, pml.velocity);
932 		Vec3Scale (pml.velocity, forward, pml.velocity);
933 	}
934 }
935 
936 
937 /*
938 ================
939 PM_GoodPosition
940 ================
941 */
PM_GoodPosition(void)942 static qBool PM_GoodPosition (void)
943 {
944 	trace_t	trace;
945 	vec3_t	origin, end;
946 
947 	if (pm->state.pmType == PMT_SPECTATOR)
948 		return qTrue;
949 
950 	origin[0] = end[0] = pm->state.origin[0]*(1.0f/8.0f);
951 	origin[1] = end[1] = pm->state.origin[1]*(1.0f/8.0f);
952 	origin[2] = end[2] = pm->state.origin[2]*(1.0f/8.0f);
953 	trace = pm->trace (origin, pm->mins, pm->maxs, end);
954 
955 	return !trace.allSolid;
956 }
957 
958 
959 /*
960 ================
961 PM_SnapPosition
962 
963 On exit, the origin will have a value that is pre-quantized to the (1.0f/8.0f)
964 precision of the network channel and in a valid position.
965 ================
966 */
PM_SnapPosition(void)967 static void PM_SnapPosition (void)
968 {
969 	int		sign[3];
970 	int		i, j, bits;
971 	int16	base[3];
972 	// try all single bits first
973 	static int jitterbits[8] = {0,4,1,2,3,5,6,7};
974 
975 	// snap velocity to eigths
976 	pm->state.velocity[0] = (int)(pml.velocity[0]*8);
977 	pm->state.velocity[1] = (int)(pml.velocity[1]*8);
978 	pm->state.velocity[2] = (int)(pml.velocity[2]*8);
979 
980 	for (i=0 ; i<3 ; i++) {
981 		if (pml.origin[i] >= 0)
982 			sign[i] = 1;
983 		else
984 			sign[i] = -1;
985 		pm->state.origin[i] = (int)(pml.origin[i]*8);
986 		if (pm->state.origin[i]*(1.0f/8.0f) == pml.origin[i])
987 			sign[i] = 0;
988 	}
989 	Vec3Copy (pm->state.origin, base);
990 
991 	// try all combinations
992 	for (j=0 ; j<8 ; j++) {
993 		bits = jitterbits[j];
994 		Vec3Copy (base, pm->state.origin);
995 		for (i=0 ; i<3 ; i++)
996 			if (bits & (1<<i))
997 				pm->state.origin[i] += sign[i];
998 
999 		if (PM_GoodPosition ())
1000 			return;
1001 	}
1002 
1003 	// go back to the last position
1004 	Vec3Copy (pml.previousOrigin, pm->state.origin);
1005 }
1006 
1007 
1008 /*
1009 ================
1010 PM_InitialSnapPosition
1011 ================
1012 */
PM_InitialSnapPosition(void)1013 static void PM_InitialSnapPosition (void)
1014 {
1015 	int			x, y, z;
1016 	int16		base[3];
1017 	static int	offset[3] = { 0, -1, 1 };
1018 
1019 	Vec3Copy (pm->state.origin, base);
1020 
1021 	for (z=0 ; z<3 ; z++) {
1022 		pm->state.origin[2] = base[2] + offset[z];
1023 		for (y=0 ; y<3 ; y++) {
1024 			pm->state.origin[1] = base[1] + offset[y];
1025 			for (x=0 ; x<3 ; x++) {
1026 				pm->state.origin[0] = base[0] + offset[x];
1027 				if (PM_GoodPosition ()) {
1028 					pml.origin[0] = pm->state.origin[0]*(1.0f/8.0f);
1029 					pml.origin[1] = pm->state.origin[1]*(1.0f/8.0f);
1030 					pml.origin[2] = pm->state.origin[2]*(1.0f/8.0f);
1031 					Vec3Copy (pm->state.origin, pml.previousOrigin);
1032 					return;
1033 				}
1034 			}
1035 		}
1036 	}
1037 
1038 	Com_DevPrintf (PRNT_WARNING, "Bad InitialSnapPosition\n");
1039 }
1040 
1041 
1042 /*
1043 ================
1044 PM_ClampAngles
1045 ================
1046 */
PM_ClampAngles(void)1047 static void PM_ClampAngles (void)
1048 {
1049 	int16	temp;
1050 	int		i;
1051 
1052 	if (pm->state.pmFlags & PMF_TIME_TELEPORT) {
1053 		pm->viewAngles[YAW] = SHORT2ANGLE (pm->cmd.angles[YAW] + pm->state.deltaAngles[YAW]);
1054 		pm->viewAngles[PITCH] = 0;
1055 		pm->viewAngles[ROLL] = 0;
1056 	}
1057 	else {
1058 		// circularly clamp the angles with deltas
1059 		for (i=0 ; i<3 ; i++) {
1060 			temp = pm->cmd.angles[i] + pm->state.deltaAngles[i];
1061 			pm->viewAngles[i] = SHORT2ANGLE(temp);
1062 		}
1063 
1064 		// don't let the player look up or down more than 90 degrees
1065 		if ((pm->viewAngles[PITCH] > 89) && (pm->viewAngles[PITCH] < 180))
1066 			pm->viewAngles[PITCH] = 89;
1067 		else if ((pm->viewAngles[PITCH] < 271) && (pm->viewAngles[PITCH] >= 180))
1068 			pm->viewAngles[PITCH] = 271;
1069 	}
1070 
1071 	Angles_Vectors (pm->viewAngles, pml.forward, pml.right, pml.up);
1072 }
1073 
1074 
1075 /*
1076 ================
1077 Pmove
1078 
1079 Can be called by either the server or the client
1080 ================
1081 */
Pmove(pMoveNew_t * pMove,float airAcceleration)1082 void Pmove (pMoveNew_t *pMove, float airAcceleration)
1083 {
1084 	pm = pMove;
1085 	pmAirAcceleration = airAcceleration;
1086 
1087 	// clear results
1088 	pm->numTouch = 0;
1089 	Vec3Clear (pm->viewAngles);
1090 	pm->viewHeight = 0;
1091 	pm->groundEntity = 0;
1092 	pm->waterType = 0;
1093 	pm->waterLevel = 0;
1094 	pm->step = qFalse;
1095 
1096 	// clear all pmove local vars
1097 	memset (&pml, 0, sizeof (pml));
1098 
1099 	// convert origin and velocity to float values
1100 	pml.origin[0] = pm->state.origin[0]*(1.0f/8.0f);
1101 	pml.origin[1] = pm->state.origin[1]*(1.0f/8.0f);
1102 	pml.origin[2] = pm->state.origin[2]*(1.0f/8.0f);
1103 
1104 	pml.velocity[0] = pm->state.velocity[0]*(1.0f/8.0f);
1105 	pml.velocity[1] = pm->state.velocity[1]*(1.0f/8.0f);
1106 	pml.velocity[2] = pm->state.velocity[2]*(1.0f/8.0f);
1107 
1108 	// save old org in case we get stuck
1109 	Vec3Copy (pm->state.origin, pml.previousOrigin);
1110 
1111 	pml.frameTime = pm->cmd.msec * 0.001;
1112 
1113 	PM_ClampAngles ();
1114 
1115 	if (pm->state.pmType == PMT_SPECTATOR) {
1116 		pml.frameTime *= pm->multiplier;
1117 		PM_FlyMove (qFalse);
1118 		PM_SnapPosition ();
1119 		return;
1120 	}
1121 
1122 	if (pm->state.pmType >= PMT_DEAD) {
1123 		pm->cmd.forwardMove = 0;
1124 		pm->cmd.sideMove = 0;
1125 		pm->cmd.upMove = 0;
1126 	}
1127 
1128 	if (pm->state.pmType == PMT_FREEZE)
1129 		return;		// no movement at all
1130 
1131 	// set mins, maxs, and viewHeight
1132 	PM_CheckDuck ();
1133 
1134 	if (pm->snapInitial)
1135 		PM_InitialSnapPosition ();
1136 
1137 	// set groundEntity, waterType, and waterLevel
1138 	PM_CatagorizePosition ();
1139 
1140 	if (pm->state.pmType == PMT_DEAD)
1141 		PM_DeadMove ();
1142 
1143 	PM_CheckSpecialMovement ();
1144 
1145 	// drop timing counter
1146 	if (pm->state.pmTime) {
1147 		int		msec;
1148 
1149 		msec = pm->cmd.msec >> 3;
1150 		if (!msec)
1151 			msec = 1;
1152 		if (msec >= pm->state.pmTime) {
1153 			pm->state.pmFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT);
1154 			pm->state.pmTime = 0;
1155 		}
1156 		else
1157 			pm->state.pmTime -= msec;
1158 	}
1159 
1160 	if (pm->state.pmFlags & PMF_TIME_TELEPORT) {
1161 		// teleport pause stays exactly in place
1162 	}
1163 	else if (pm->state.pmFlags & PMF_TIME_WATERJUMP) {
1164 		// waterjump has no control, but falls
1165 		pml.velocity[2] -= pm->state.gravity * pml.frameTime;
1166 		if (pml.velocity[2] < 0) {
1167 			// cancel as soon as we are falling down again
1168 			pm->state.pmFlags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT);
1169 			pm->state.pmTime = 0;
1170 		}
1171 
1172 		PM_StepSlideMove ();
1173 	}
1174 	else {
1175 		PM_CheckJump ();
1176 
1177 		PM_Friction ();
1178 
1179 		if (pm->waterLevel >= 2) {
1180 			PM_WaterMove ();
1181 		}
1182 		else {
1183 			vec3_t	angles;
1184 
1185 			Vec3Copy(pm->viewAngles, angles);
1186 			if (angles[PITCH] > 180)
1187 				angles[PITCH] = angles[PITCH] - 360;
1188 			angles[PITCH] /= 3;
1189 
1190 			Angles_Vectors (angles, pml.forward, pml.right, pml.up);
1191 
1192 			PM_AirMove ();
1193 		}
1194 	}
1195 
1196 	// set groundEntity, waterType, and waterLevel for final spot
1197 	PM_CatagorizePosition ();
1198 
1199 	PM_SnapPosition ();
1200 }
1201