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