1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 //
23 // bg_pmove.c -- both games player movement code
24 // takes a playerstate and a usercmd as input and returns a modifed playerstate
25 
26 #include "../qcommon/q_shared.h"
27 #include "bg_public.h"
28 #include "bg_local.h"
29 
30 pmove_t		*pm;
31 pml_t		pml;
32 
33 // movement parameters
34 float	pm_stopspeed = 100.0f;
35 float	pm_duckScale = 0.25f;
36 float	pm_swimScale = 0.50f;
37 float	pm_wadeScale = 0.70f;
38 
39 float	pm_accelerate = 10.0f;
40 float	pm_airaccelerate = 1.0f;
41 float	pm_wateraccelerate = 4.0f;
42 float	pm_flyaccelerate = 8.0f;
43 
44 float	pm_friction = 6.0f;
45 float	pm_waterfriction = 1.0f;
46 float	pm_flightfriction = 3.0f;
47 float	pm_spectatorfriction = 5.0f;
48 
49 int		c_pmove = 0;
50 
51 
52 /*
53 ===============
54 PM_AddEvent
55 
56 ===============
57 */
PM_AddEvent(int newEvent)58 void PM_AddEvent( int newEvent ) {
59 	BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
60 }
61 
62 /*
63 ===============
64 PM_AddTouchEnt
65 ===============
66 */
PM_AddTouchEnt(int entityNum)67 void PM_AddTouchEnt( int entityNum ) {
68 	int		i;
69 
70 	if ( entityNum == ENTITYNUM_WORLD ) {
71 		return;
72 	}
73 	if ( pm->numtouch == MAXTOUCH ) {
74 		return;
75 	}
76 
77 	// see if it is already added
78 	for ( i = 0 ; i < pm->numtouch ; i++ ) {
79 		if ( pm->touchents[ i ] == entityNum ) {
80 			return;
81 		}
82 	}
83 
84 	// add it
85 	pm->touchents[pm->numtouch] = entityNum;
86 	pm->numtouch++;
87 }
88 
89 /*
90 ===================
91 PM_StartTorsoAnim
92 ===================
93 */
PM_StartTorsoAnim(int anim)94 static void PM_StartTorsoAnim( int anim ) {
95 	if ( pm->ps->pm_type >= PM_DEAD ) {
96 		return;
97 	}
98 	pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
99 		| anim;
100 }
PM_StartLegsAnim(int anim)101 static void PM_StartLegsAnim( int anim ) {
102 	if ( pm->ps->pm_type >= PM_DEAD ) {
103 		return;
104 	}
105 	if ( pm->ps->legsTimer > 0 ) {
106 		return;		// a high priority animation is running
107 	}
108 	pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
109 		| anim;
110 }
111 
PM_ContinueLegsAnim(int anim)112 static void PM_ContinueLegsAnim( int anim ) {
113 	if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) {
114 		return;
115 	}
116 	if ( pm->ps->legsTimer > 0 ) {
117 		return;		// a high priority animation is running
118 	}
119 	PM_StartLegsAnim( anim );
120 }
121 
PM_ContinueTorsoAnim(int anim)122 static void PM_ContinueTorsoAnim( int anim ) {
123 	if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) {
124 		return;
125 	}
126 	if ( pm->ps->torsoTimer > 0 ) {
127 		return;		// a high priority animation is running
128 	}
129 	PM_StartTorsoAnim( anim );
130 }
131 
PM_ForceLegsAnim(int anim)132 static void PM_ForceLegsAnim( int anim ) {
133 	pm->ps->legsTimer = 0;
134 	PM_StartLegsAnim( anim );
135 }
136 
137 
138 /*
139 ==================
140 PM_ClipVelocity
141 
142 Slide off of the impacting surface
143 ==================
144 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)145 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
146 	float	backoff;
147 	float	change;
148 	int		i;
149 
150 	backoff = DotProduct (in, normal);
151 
152 	if ( backoff < 0 ) {
153 		backoff *= overbounce;
154 	} else {
155 		backoff /= overbounce;
156 	}
157 
158 	for ( i=0 ; i<3 ; i++ ) {
159 		change = normal[i]*backoff;
160 		out[i] = in[i] - change;
161 	}
162 }
163 
164 
165 /*
166 ==================
167 PM_Friction
168 
169 Handles both ground friction and water friction
170 ==================
171 */
PM_Friction(void)172 static void PM_Friction( void ) {
173 	vec3_t	vec;
174 	float	*vel;
175 	float	speed, newspeed, control;
176 	float	drop;
177 
178 	vel = pm->ps->velocity;
179 
180 	VectorCopy( vel, vec );
181 	if ( pml.walking ) {
182 		vec[2] = 0;	// ignore slope movement
183 	}
184 
185 	speed = VectorLength(vec);
186 	if (speed < 1) {
187 		vel[0] = 0;
188 		vel[1] = 0;		// allow sinking underwater
189 		// FIXME: still have z friction underwater?
190 		return;
191 	}
192 
193 	drop = 0;
194 
195 	// apply ground friction
196 	if ( pm->waterlevel <= 1 ) {
197 		if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
198 			// if getting knocked back, no friction
199 			if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
200 				control = speed < pm_stopspeed ? pm_stopspeed : speed;
201 				drop += control*pm_friction*pml.frametime;
202 			}
203 		}
204 	}
205 
206 	// apply water friction even if just wading
207 	if ( pm->waterlevel ) {
208 		drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
209 	}
210 
211 	// apply flying friction
212 	if ( pm->ps->powerups[PW_FLIGHT]) {
213 		drop += speed*pm_flightfriction*pml.frametime;
214 	}
215 
216 	if ( pm->ps->pm_type == PM_SPECTATOR) {
217 		drop += speed*pm_spectatorfriction*pml.frametime;
218 	}
219 
220 	// scale the velocity
221 	newspeed = speed - drop;
222 	if (newspeed < 0) {
223 		newspeed = 0;
224 	}
225 	newspeed /= speed;
226 
227 	vel[0] = vel[0] * newspeed;
228 	vel[1] = vel[1] * newspeed;
229 	vel[2] = vel[2] * newspeed;
230 }
231 
232 
233 /*
234 ==============
235 PM_Accelerate
236 
237 Handles user intended acceleration
238 ==============
239 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)240 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {
241 #if 1
242 	// q2 style
243 	int			i;
244 	float		addspeed, accelspeed, currentspeed;
245 
246 	currentspeed = DotProduct (pm->ps->velocity, wishdir);
247 	addspeed = wishspeed - currentspeed;
248 	if (addspeed <= 0) {
249 		return;
250 	}
251 	accelspeed = accel*pml.frametime*wishspeed;
252 	if (accelspeed > addspeed) {
253 		accelspeed = addspeed;
254 	}
255 
256 	for (i=0 ; i<3 ; i++) {
257 		pm->ps->velocity[i] += accelspeed*wishdir[i];
258 	}
259 #else
260 	// proper way (avoids strafe jump maxspeed bug), but feels bad
261 	vec3_t		wishVelocity;
262 	vec3_t		pushDir;
263 	float		pushLen;
264 	float		canPush;
265 
266 	VectorScale( wishdir, wishspeed, wishVelocity );
267 	VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
268 	pushLen = VectorNormalize( pushDir );
269 
270 	canPush = accel*pml.frametime*wishspeed;
271 	if (canPush > pushLen) {
272 		canPush = pushLen;
273 	}
274 
275 	VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
276 #endif
277 }
278 
279 
280 
281 /*
282 ============
283 PM_CmdScale
284 
285 Returns the scale factor to apply to cmd movements
286 This allows the clients to use axial -127 to 127 values for all directions
287 without getting a sqrt(2) distortion in speed.
288 ============
289 */
PM_CmdScale(usercmd_t * cmd)290 static float PM_CmdScale( usercmd_t *cmd ) {
291 	int		max;
292 	float	total;
293 	float	scale;
294 
295 	max = abs( cmd->forwardmove );
296 	if ( abs( cmd->rightmove ) > max ) {
297 		max = abs( cmd->rightmove );
298 	}
299 	if ( abs( cmd->upmove ) > max ) {
300 		max = abs( cmd->upmove );
301 	}
302 	if ( !max ) {
303 		return 0;
304 	}
305 
306 	total = sqrt( cmd->forwardmove * cmd->forwardmove
307 		+ cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
308 	scale = (float)pm->ps->speed * max / ( 127.0 * total );
309 
310 	return scale;
311 }
312 
313 
314 /*
315 ================
316 PM_SetMovementDir
317 
318 Determine the rotation of the legs reletive
319 to the facing dir
320 ================
321 */
PM_SetMovementDir(void)322 static void PM_SetMovementDir( void ) {
323 	if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
324 		if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
325 			pm->ps->movementDir = 0;
326 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
327 			pm->ps->movementDir = 1;
328 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
329 			pm->ps->movementDir = 2;
330 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
331 			pm->ps->movementDir = 3;
332 		} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
333 			pm->ps->movementDir = 4;
334 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
335 			pm->ps->movementDir = 5;
336 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
337 			pm->ps->movementDir = 6;
338 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
339 			pm->ps->movementDir = 7;
340 		}
341 	} else {
342 		// if they aren't actively going directly sideways,
343 		// change the animation to the diagonal so they
344 		// don't stop too crooked
345 		if ( pm->ps->movementDir == 2 ) {
346 			pm->ps->movementDir = 1;
347 		} else if ( pm->ps->movementDir == 6 ) {
348 			pm->ps->movementDir = 7;
349 		}
350 	}
351 }
352 
353 
354 /*
355 =============
356 PM_CheckJump
357 =============
358 */
PM_CheckJump(void)359 static qboolean PM_CheckJump( void ) {
360 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
361 		return qfalse;		// don't allow jump until all buttons are up
362 	}
363 
364 	if ( pm->cmd.upmove < 10 ) {
365 		// not holding jump
366 		return qfalse;
367 	}
368 
369 	// must wait for jump to be released
370 	if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
371 		// clear upmove so cmdscale doesn't lower running speed
372 		pm->cmd.upmove = 0;
373 		return qfalse;
374 	}
375 
376 	pml.groundPlane = qfalse;		// jumping away
377 	pml.walking = qfalse;
378 	pm->ps->pm_flags |= PMF_JUMP_HELD;
379 
380 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
381 	pm->ps->velocity[2] = JUMP_VELOCITY;
382 	PM_AddEvent( EV_JUMP );
383 
384 	if ( pm->cmd.forwardmove >= 0 ) {
385 		PM_ForceLegsAnim( LEGS_JUMP );
386 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
387 	} else {
388 		PM_ForceLegsAnim( LEGS_JUMPB );
389 		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
390 	}
391 
392 	return qtrue;
393 }
394 
395 /*
396 =============
397 PM_CheckWaterJump
398 =============
399 */
PM_CheckWaterJump(void)400 static qboolean	PM_CheckWaterJump( void ) {
401 	vec3_t	spot;
402 	int		cont;
403 	vec3_t	flatforward;
404 
405 	if (pm->ps->pm_time) {
406 		return qfalse;
407 	}
408 
409 	// check for water jump
410 	if ( pm->waterlevel != 2 ) {
411 		return qfalse;
412 	}
413 
414 	flatforward[0] = pml.forward[0];
415 	flatforward[1] = pml.forward[1];
416 	flatforward[2] = 0;
417 	VectorNormalize (flatforward);
418 
419 	VectorMA (pm->ps->origin, 30, flatforward, spot);
420 	spot[2] += 4;
421 	cont = pm->pointcontents (spot, pm->ps->clientNum );
422 	if ( !(cont & CONTENTS_SOLID) ) {
423 		return qfalse;
424 	}
425 
426 	spot[2] += 16;
427 	cont = pm->pointcontents (spot, pm->ps->clientNum );
428 	if ( cont ) {
429 		return qfalse;
430 	}
431 
432 	// jump out of water
433 	VectorScale (pml.forward, 200, pm->ps->velocity);
434 	pm->ps->velocity[2] = 350;
435 
436 	pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
437 	pm->ps->pm_time = 2000;
438 
439 	return qtrue;
440 }
441 
442 //============================================================================
443 
444 
445 /*
446 ===================
447 PM_WaterJumpMove
448 
449 Flying out of the water
450 ===================
451 */
PM_WaterJumpMove(void)452 static void PM_WaterJumpMove( void ) {
453 	// waterjump has no control, but falls
454 
455 	PM_StepSlideMove( qtrue );
456 
457 	pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
458 	if (pm->ps->velocity[2] < 0) {
459 		// cancel as soon as we are falling down again
460 		pm->ps->pm_flags &= ~PMF_ALL_TIMES;
461 		pm->ps->pm_time = 0;
462 	}
463 }
464 
465 /*
466 ===================
467 PM_WaterMove
468 
469 ===================
470 */
PM_WaterMove(void)471 static void PM_WaterMove( void ) {
472 	int		i;
473 	vec3_t	wishvel;
474 	float	wishspeed;
475 	vec3_t	wishdir;
476 	float	scale;
477 	float	vel;
478 
479 	if ( PM_CheckWaterJump() ) {
480 		PM_WaterJumpMove();
481 		return;
482 	}
483 #if 0
484 	// jump = head for surface
485 	if ( pm->cmd.upmove >= 10 ) {
486 		if (pm->ps->velocity[2] > -300) {
487 			if ( pm->watertype == CONTENTS_WATER ) {
488 				pm->ps->velocity[2] = 100;
489 			} else if (pm->watertype == CONTENTS_SLIME) {
490 				pm->ps->velocity[2] = 80;
491 			} else {
492 				pm->ps->velocity[2] = 50;
493 			}
494 		}
495 	}
496 #endif
497 	PM_Friction ();
498 
499 	scale = PM_CmdScale( &pm->cmd );
500 	//
501 	// user intentions
502 	//
503 	if ( !scale ) {
504 		wishvel[0] = 0;
505 		wishvel[1] = 0;
506 		wishvel[2] = -60;		// sink towards bottom
507 	} else {
508 		for (i=0 ; i<3 ; i++)
509 			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
510 
511 		wishvel[2] += scale * pm->cmd.upmove;
512 	}
513 
514 	VectorCopy (wishvel, wishdir);
515 	wishspeed = VectorNormalize(wishdir);
516 
517 	if ( wishspeed > pm->ps->speed * pm_swimScale ) {
518 		wishspeed = pm->ps->speed * pm_swimScale;
519 	}
520 
521 	PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
522 
523 	// make sure we can go up slopes easily under water
524 	if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
525 		vel = VectorLength(pm->ps->velocity);
526 		// slide along the ground plane
527 		PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
528 			pm->ps->velocity, OVERCLIP );
529 
530 		VectorNormalize(pm->ps->velocity);
531 		VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
532 	}
533 
534 	PM_SlideMove( qfalse );
535 }
536 
537 #ifdef MISSIONPACK
538 /*
539 ===================
540 PM_InvulnerabilityMove
541 
542 Only with the invulnerability powerup
543 ===================
544 */
PM_InvulnerabilityMove(void)545 static void PM_InvulnerabilityMove( void ) {
546 	pm->cmd.forwardmove = 0;
547 	pm->cmd.rightmove = 0;
548 	pm->cmd.upmove = 0;
549 	VectorClear(pm->ps->velocity);
550 }
551 #endif
552 
553 /*
554 ===================
555 PM_FlyMove
556 
557 Only with the flight powerup
558 ===================
559 */
PM_FlyMove(void)560 static void PM_FlyMove( void ) {
561 	int		i;
562 	vec3_t	wishvel;
563 	float	wishspeed;
564 	vec3_t	wishdir;
565 	float	scale;
566 
567 	// normal slowdown
568 	PM_Friction ();
569 
570 	scale = PM_CmdScale( &pm->cmd );
571 	//
572 	// user intentions
573 	//
574 	if ( !scale ) {
575 		wishvel[0] = 0;
576 		wishvel[1] = 0;
577 		wishvel[2] = 0;
578 	} else {
579 		for (i=0 ; i<3 ; i++) {
580 			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
581 		}
582 
583 		wishvel[2] += scale * pm->cmd.upmove;
584 	}
585 
586 	VectorCopy (wishvel, wishdir);
587 	wishspeed = VectorNormalize(wishdir);
588 
589 	PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
590 
591 	PM_StepSlideMove( qfalse );
592 }
593 
594 
595 /*
596 ===================
597 PM_AirMove
598 
599 ===================
600 */
PM_AirMove(void)601 static void PM_AirMove( void ) {
602 	int			i;
603 	vec3_t		wishvel;
604 	float		fmove, smove;
605 	vec3_t		wishdir;
606 	float		wishspeed;
607 	float		scale;
608 	usercmd_t	cmd;
609 
610 	PM_Friction();
611 
612 	fmove = pm->cmd.forwardmove;
613 	smove = pm->cmd.rightmove;
614 
615 	cmd = pm->cmd;
616 	scale = PM_CmdScale( &cmd );
617 
618 	// set the movementDir so clients can rotate the legs for strafing
619 	PM_SetMovementDir();
620 
621 	// project moves down to flat plane
622 	pml.forward[2] = 0;
623 	pml.right[2] = 0;
624 	VectorNormalize (pml.forward);
625 	VectorNormalize (pml.right);
626 
627 	for ( i = 0 ; i < 2 ; i++ ) {
628 		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
629 	}
630 	wishvel[2] = 0;
631 
632 	VectorCopy (wishvel, wishdir);
633 	wishspeed = VectorNormalize(wishdir);
634 	wishspeed *= scale;
635 
636 	// not on ground, so little effect on velocity
637 	PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
638 
639 	// we may have a ground plane that is very steep, even
640 	// though we don't have a groundentity
641 	// slide along the steep plane
642 	if ( pml.groundPlane ) {
643 		PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
644 			pm->ps->velocity, OVERCLIP );
645 	}
646 
647 #if 0
648 	//ZOID:  If we are on the grapple, try stair-stepping
649 	//this allows a player to use the grapple to pull himself
650 	//over a ledge
651 	if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
652 		PM_StepSlideMove ( qtrue );
653 	else
654 		PM_SlideMove ( qtrue );
655 #endif
656 
657 	PM_StepSlideMove ( qtrue );
658 }
659 
660 /*
661 ===================
662 PM_GrappleMove
663 
664 ===================
665 */
PM_GrappleMove(void)666 static void PM_GrappleMove( void ) {
667 	vec3_t vel, v;
668 	float vlen;
669 
670 	VectorScale(pml.forward, -16, v);
671 	VectorAdd(pm->ps->grapplePoint, v, v);
672 	VectorSubtract(v, pm->ps->origin, vel);
673 	vlen = VectorLength(vel);
674 	VectorNormalize( vel );
675 
676 	if (vlen <= 100)
677 		VectorScale(vel, 10 * vlen, vel);
678 	else
679 		VectorScale(vel, 800, vel);
680 
681 	VectorCopy(vel, pm->ps->velocity);
682 
683 	pml.groundPlane = qfalse;
684 }
685 
686 /*
687 ===================
688 PM_WalkMove
689 
690 ===================
691 */
PM_WalkMove(void)692 static void PM_WalkMove( void ) {
693 	int			i;
694 	vec3_t		wishvel;
695 	float		fmove, smove;
696 	vec3_t		wishdir;
697 	float		wishspeed;
698 	float		scale;
699 	usercmd_t	cmd;
700 	float		accelerate;
701 	float		vel;
702 
703 	if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
704 		// begin swimming
705 		PM_WaterMove();
706 		return;
707 	}
708 
709 
710 	if ( PM_CheckJump () ) {
711 		// jumped away
712 		if ( pm->waterlevel > 1 ) {
713 			PM_WaterMove();
714 		} else {
715 			PM_AirMove();
716 		}
717 		return;
718 	}
719 
720 	PM_Friction ();
721 
722 	fmove = pm->cmd.forwardmove;
723 	smove = pm->cmd.rightmove;
724 
725 	cmd = pm->cmd;
726 	scale = PM_CmdScale( &cmd );
727 
728 	// set the movementDir so clients can rotate the legs for strafing
729 	PM_SetMovementDir();
730 
731 	// project moves down to flat plane
732 	pml.forward[2] = 0;
733 	pml.right[2] = 0;
734 
735 	// project the forward and right directions onto the ground plane
736 	PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
737 	PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
738 	//
739 	VectorNormalize (pml.forward);
740 	VectorNormalize (pml.right);
741 
742 	for ( i = 0 ; i < 3 ; i++ ) {
743 		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
744 	}
745 	// when going up or down slopes the wish velocity should Not be zero
746 //	wishvel[2] = 0;
747 
748 	VectorCopy (wishvel, wishdir);
749 	wishspeed = VectorNormalize(wishdir);
750 	wishspeed *= scale;
751 
752 	// clamp the speed lower if ducking
753 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
754 		if ( wishspeed > pm->ps->speed * pm_duckScale ) {
755 			wishspeed = pm->ps->speed * pm_duckScale;
756 		}
757 	}
758 
759 	// clamp the speed lower if wading or walking on the bottom
760 	if ( pm->waterlevel ) {
761 		float	waterScale;
762 
763 		waterScale = pm->waterlevel / 3.0;
764 		waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
765 		if ( wishspeed > pm->ps->speed * waterScale ) {
766 			wishspeed = pm->ps->speed * waterScale;
767 		}
768 	}
769 
770 	// when a player gets hit, they temporarily lose
771 	// full control, which allows them to be moved a bit
772 	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
773 		accelerate = pm_airaccelerate;
774 	} else {
775 		accelerate = pm_accelerate;
776 	}
777 
778 	PM_Accelerate (wishdir, wishspeed, accelerate);
779 
780 	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
781 	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
782 
783 	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
784 		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
785 	} else {
786 		// don't reset the z velocity for slopes
787 //		pm->ps->velocity[2] = 0;
788 	}
789 
790 	vel = VectorLength(pm->ps->velocity);
791 
792 	// slide along the ground plane
793 	PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
794 		pm->ps->velocity, OVERCLIP );
795 
796 	// don't decrease velocity when going up or down a slope
797 	VectorNormalize(pm->ps->velocity);
798 	VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
799 
800 	// don't do anything if standing still
801 	if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
802 		return;
803 	}
804 
805 	PM_StepSlideMove( qfalse );
806 
807 	//Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
808 
809 }
810 
811 
812 /*
813 ==============
814 PM_DeadMove
815 ==============
816 */
PM_DeadMove(void)817 static void PM_DeadMove( void ) {
818 	float	forward;
819 
820 	if ( !pml.walking ) {
821 		return;
822 	}
823 
824 	// extra friction
825 
826 	forward = VectorLength (pm->ps->velocity);
827 	forward -= 20;
828 	if ( forward <= 0 ) {
829 		VectorClear (pm->ps->velocity);
830 	} else {
831 		VectorNormalize (pm->ps->velocity);
832 		VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
833 	}
834 }
835 
836 
837 /*
838 ===============
839 PM_NoclipMove
840 ===============
841 */
PM_NoclipMove(void)842 static void PM_NoclipMove( void ) {
843 	float	speed, drop, friction, control, newspeed;
844 	int			i;
845 	vec3_t		wishvel;
846 	float		fmove, smove;
847 	vec3_t		wishdir;
848 	float		wishspeed;
849 	float		scale;
850 
851 	pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
852 
853 	// friction
854 
855 	speed = VectorLength (pm->ps->velocity);
856 	if (speed < 1)
857 	{
858 		VectorCopy (vec3_origin, pm->ps->velocity);
859 	}
860 	else
861 	{
862 		drop = 0;
863 
864 		friction = pm_friction*1.5;	// extra friction
865 		control = speed < pm_stopspeed ? pm_stopspeed : speed;
866 		drop += control*friction*pml.frametime;
867 
868 		// scale the velocity
869 		newspeed = speed - drop;
870 		if (newspeed < 0)
871 			newspeed = 0;
872 		newspeed /= speed;
873 
874 		VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
875 	}
876 
877 	// accelerate
878 	scale = PM_CmdScale( &pm->cmd );
879 
880 	fmove = pm->cmd.forwardmove;
881 	smove = pm->cmd.rightmove;
882 
883 	for (i=0 ; i<3 ; i++)
884 		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
885 	wishvel[2] += pm->cmd.upmove;
886 
887 	VectorCopy (wishvel, wishdir);
888 	wishspeed = VectorNormalize(wishdir);
889 	wishspeed *= scale;
890 
891 	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
892 
893 	// move
894 	VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
895 }
896 
897 //============================================================================
898 
899 /*
900 ================
901 PM_FootstepForSurface
902 
903 Returns an event number apropriate for the groundsurface
904 ================
905 */
PM_FootstepForSurface(void)906 static int PM_FootstepForSurface( void ) {
907 	if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) {
908 		return 0;
909 	}
910 	if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) {
911 		return EV_FOOTSTEP_METAL;
912 	}
913 	return EV_FOOTSTEP;
914 }
915 
916 
917 /*
918 =================
919 PM_CrashLand
920 
921 Check for hard landings that generate sound events
922 =================
923 */
PM_CrashLand(void)924 static void PM_CrashLand( void ) {
925 	float		delta;
926 	float		dist;
927 	float		vel, acc;
928 	float		t;
929 	float		a, b, c, den;
930 
931 	// decide which landing animation to use
932 	if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
933 		PM_ForceLegsAnim( LEGS_LANDB );
934 	} else {
935 		PM_ForceLegsAnim( LEGS_LAND );
936 	}
937 
938 	pm->ps->legsTimer = TIMER_LAND;
939 
940 	// calculate the exact velocity on landing
941 	dist = pm->ps->origin[2] - pml.previous_origin[2];
942 	vel = pml.previous_velocity[2];
943 	acc = -pm->ps->gravity;
944 
945 	a = acc / 2;
946 	b = vel;
947 	c = -dist;
948 
949 	den =  b * b - 4 * a * c;
950 	if ( den < 0 ) {
951 		return;
952 	}
953 	t = (-b - sqrt( den ) ) / ( 2 * a );
954 
955 	delta = vel + t * acc;
956 	delta = delta*delta * 0.0001;
957 
958 	// ducking while falling doubles damage
959 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
960 		delta *= 2;
961 	}
962 
963 	// never take falling damage if completely underwater
964 	if ( pm->waterlevel == 3 ) {
965 		return;
966 	}
967 
968 	// reduce falling damage if there is standing water
969 	if ( pm->waterlevel == 2 ) {
970 		delta *= 0.25;
971 	}
972 	if ( pm->waterlevel == 1 ) {
973 		delta *= 0.5;
974 	}
975 
976 	if ( delta < 1 ) {
977 		return;
978 	}
979 
980 	// create a local entity event to play the sound
981 
982 	// SURF_NODAMAGE is used for bounce pads where you don't ever
983 	// want to take damage or play a crunch sound
984 	if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )  {
985 		if ( delta > 60 ) {
986 			PM_AddEvent( EV_FALL_FAR );
987 		} else if ( delta > 40 ) {
988 			// this is a pain grunt, so don't play it if dead
989 			if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
990 				PM_AddEvent( EV_FALL_MEDIUM );
991 			}
992 		} else if ( delta > 7 ) {
993 			PM_AddEvent( EV_FALL_SHORT );
994 		} else {
995 			PM_AddEvent( PM_FootstepForSurface() );
996 		}
997 	}
998 
999 	// start footstep cycle over
1000 	pm->ps->bobCycle = 0;
1001 }
1002 
1003 /*
1004 =============
1005 PM_CheckStuck
1006 =============
1007 */
1008 /*
1009 void PM_CheckStuck(void) {
1010 	trace_t trace;
1011 
1012 	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask);
1013 	if (trace.allsolid) {
1014 		//int shit = qtrue;
1015 	}
1016 }
1017 */
1018 
1019 /*
1020 =============
1021 PM_CorrectAllSolid
1022 =============
1023 */
PM_CorrectAllSolid(trace_t * trace)1024 static int PM_CorrectAllSolid( trace_t *trace ) {
1025 	int			i, j, k;
1026 	vec3_t		point;
1027 
1028 	if ( pm->debugLevel ) {
1029 		Com_Printf("%i:allsolid\n", c_pmove);
1030 	}
1031 
1032 	// jitter around
1033 	for (i = -1; i <= 1; i++) {
1034 		for (j = -1; j <= 1; j++) {
1035 			for (k = -1; k <= 1; k++) {
1036 				VectorCopy(pm->ps->origin, point);
1037 				point[0] += (float) i;
1038 				point[1] += (float) j;
1039 				point[2] += (float) k;
1040 				pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1041 				if ( !trace->allsolid ) {
1042 					point[0] = pm->ps->origin[0];
1043 					point[1] = pm->ps->origin[1];
1044 					point[2] = pm->ps->origin[2] - 0.25;
1045 
1046 					pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1047 					pml.groundTrace = *trace;
1048 					return qtrue;
1049 				}
1050 			}
1051 		}
1052 	}
1053 
1054 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
1055 	pml.groundPlane = qfalse;
1056 	pml.walking = qfalse;
1057 
1058 	return qfalse;
1059 }
1060 
1061 
1062 /*
1063 =============
1064 PM_GroundTraceMissed
1065 
1066 The ground trace didn't hit a surface, so we are in freefall
1067 =============
1068 */
PM_GroundTraceMissed(void)1069 static void PM_GroundTraceMissed( void ) {
1070 	trace_t		trace;
1071 	vec3_t		point;
1072 
1073 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
1074 		// we just transitioned into freefall
1075 		if ( pm->debugLevel ) {
1076 			Com_Printf("%i:lift\n", c_pmove);
1077 		}
1078 
1079 		// if they aren't in a jumping animation and the ground is a ways away, force into it
1080 		// if we didn't do the trace, the player would be backflipping down staircases
1081 		VectorCopy( pm->ps->origin, point );
1082 		point[2] -= 64;
1083 
1084 		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1085 		if ( trace.fraction == 1.0 ) {
1086 			if ( pm->cmd.forwardmove >= 0 ) {
1087 				PM_ForceLegsAnim( LEGS_JUMP );
1088 				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1089 			} else {
1090 				PM_ForceLegsAnim( LEGS_JUMPB );
1091 				pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1092 			}
1093 		}
1094 	}
1095 
1096 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
1097 	pml.groundPlane = qfalse;
1098 	pml.walking = qfalse;
1099 }
1100 
1101 
1102 /*
1103 =============
1104 PM_GroundTrace
1105 =============
1106 */
PM_GroundTrace(void)1107 static void PM_GroundTrace( void ) {
1108 	vec3_t		point;
1109 	trace_t		trace;
1110 
1111 	point[0] = pm->ps->origin[0];
1112 	point[1] = pm->ps->origin[1];
1113 	point[2] = pm->ps->origin[2] - 0.25;
1114 
1115 	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
1116 	pml.groundTrace = trace;
1117 
1118 	// do something corrective if the trace starts in a solid...
1119 	if ( trace.allsolid ) {
1120 		if ( !PM_CorrectAllSolid(&trace) )
1121 			return;
1122 	}
1123 
1124 	// if the trace didn't hit anything, we are in free fall
1125 	if ( trace.fraction == 1.0 ) {
1126 		PM_GroundTraceMissed();
1127 		pml.groundPlane = qfalse;
1128 		pml.walking = qfalse;
1129 		return;
1130 	}
1131 
1132 	// check if getting thrown off the ground
1133 	if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
1134 		if ( pm->debugLevel ) {
1135 			Com_Printf("%i:kickoff\n", c_pmove);
1136 		}
1137 		// go into jump animation
1138 		if ( pm->cmd.forwardmove >= 0 ) {
1139 			PM_ForceLegsAnim( LEGS_JUMP );
1140 			pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1141 		} else {
1142 			PM_ForceLegsAnim( LEGS_JUMPB );
1143 			pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1144 		}
1145 
1146 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
1147 		pml.groundPlane = qfalse;
1148 		pml.walking = qfalse;
1149 		return;
1150 	}
1151 
1152 	// slopes that are too steep will not be considered onground
1153 	if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) {
1154 		if ( pm->debugLevel ) {
1155 			Com_Printf("%i:steep\n", c_pmove);
1156 		}
1157 		// FIXME: if they can't slide down the slope, let them
1158 		// walk (sharp crevices)
1159 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
1160 		pml.groundPlane = qtrue;
1161 		pml.walking = qfalse;
1162 		return;
1163 	}
1164 
1165 	pml.groundPlane = qtrue;
1166 	pml.walking = qtrue;
1167 
1168 	// hitting solid ground will end a waterjump
1169 	if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
1170 	{
1171 		pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
1172 		pm->ps->pm_time = 0;
1173 	}
1174 
1175 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
1176 		// just hit the ground
1177 		if ( pm->debugLevel ) {
1178 			Com_Printf("%i:Land\n", c_pmove);
1179 		}
1180 
1181 		PM_CrashLand();
1182 
1183 		// don't do landing time if we were just going down a slope
1184 		if ( pml.previous_velocity[2] < -200 ) {
1185 			// don't allow another jump for a little while
1186 			pm->ps->pm_flags |= PMF_TIME_LAND;
1187 			pm->ps->pm_time = 250;
1188 		}
1189 	}
1190 
1191 	pm->ps->groundEntityNum = trace.entityNum;
1192 
1193 	// don't reset the z velocity for slopes
1194 //	pm->ps->velocity[2] = 0;
1195 
1196 	PM_AddTouchEnt( trace.entityNum );
1197 }
1198 
1199 
1200 /*
1201 =============
1202 PM_SetWaterLevel	FIXME: avoid this twice?  certainly if not moving
1203 =============
1204 */
PM_SetWaterLevel(void)1205 static void PM_SetWaterLevel( void ) {
1206 	vec3_t		point;
1207 	int			cont;
1208 	int			sample1;
1209 	int			sample2;
1210 
1211 	//
1212 	// get waterlevel, accounting for ducking
1213 	//
1214 	pm->waterlevel = 0;
1215 	pm->watertype = 0;
1216 
1217 	point[0] = pm->ps->origin[0];
1218 	point[1] = pm->ps->origin[1];
1219 	point[2] = pm->ps->origin[2] + MINS_Z + 1;
1220 	cont = pm->pointcontents( point, pm->ps->clientNum );
1221 
1222 	if ( cont & MASK_WATER ) {
1223 		sample2 = pm->ps->viewheight - MINS_Z;
1224 		sample1 = sample2 / 2;
1225 
1226 		pm->watertype = cont;
1227 		pm->waterlevel = 1;
1228 		point[2] = pm->ps->origin[2] + MINS_Z + sample1;
1229 		cont = pm->pointcontents (point, pm->ps->clientNum );
1230 		if ( cont & MASK_WATER ) {
1231 			pm->waterlevel = 2;
1232 			point[2] = pm->ps->origin[2] + MINS_Z + sample2;
1233 			cont = pm->pointcontents (point, pm->ps->clientNum );
1234 			if ( cont & MASK_WATER ){
1235 				pm->waterlevel = 3;
1236 			}
1237 		}
1238 	}
1239 
1240 }
1241 
1242 /*
1243 ==============
1244 PM_CheckDuck
1245 
1246 Sets mins, maxs, and pm->ps->viewheight
1247 ==============
1248 */
PM_CheckDuck(void)1249 static void PM_CheckDuck (void)
1250 {
1251 	trace_t	trace;
1252 
1253 	if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
1254 		if ( pm->ps->pm_flags & PMF_INVULEXPAND ) {
1255 			// invulnerability sphere has a 42 units radius
1256 			VectorSet( pm->mins, -42, -42, -42 );
1257 			VectorSet( pm->maxs, 42, 42, 42 );
1258 		}
1259 		else {
1260 			VectorSet( pm->mins, -15, -15, MINS_Z );
1261 			VectorSet( pm->maxs, 15, 15, 16 );
1262 		}
1263 		pm->ps->pm_flags |= PMF_DUCKED;
1264 		pm->ps->viewheight = CROUCH_VIEWHEIGHT;
1265 		return;
1266 	}
1267 	pm->ps->pm_flags &= ~PMF_INVULEXPAND;
1268 
1269 	pm->mins[0] = -15;
1270 	pm->mins[1] = -15;
1271 
1272 	pm->maxs[0] = 15;
1273 	pm->maxs[1] = 15;
1274 
1275 	pm->mins[2] = MINS_Z;
1276 
1277 	if (pm->ps->pm_type == PM_DEAD)
1278 	{
1279 		pm->maxs[2] = -8;
1280 		pm->ps->viewheight = DEAD_VIEWHEIGHT;
1281 		return;
1282 	}
1283 
1284 	if (pm->cmd.upmove < 0)
1285 	{	// duck
1286 		pm->ps->pm_flags |= PMF_DUCKED;
1287 	}
1288 	else
1289 	{	// stand up if possible
1290 		if (pm->ps->pm_flags & PMF_DUCKED)
1291 		{
1292 			// try to stand up
1293 			pm->maxs[2] = 32;
1294 			pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
1295 			if (!trace.allsolid)
1296 				pm->ps->pm_flags &= ~PMF_DUCKED;
1297 		}
1298 	}
1299 
1300 	if (pm->ps->pm_flags & PMF_DUCKED)
1301 	{
1302 		pm->maxs[2] = 16;
1303 		pm->ps->viewheight = CROUCH_VIEWHEIGHT;
1304 	}
1305 	else
1306 	{
1307 		pm->maxs[2] = 32;
1308 		pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
1309 	}
1310 }
1311 
1312 
1313 
1314 //===================================================================
1315 
1316 
1317 /*
1318 ===============
1319 PM_Footsteps
1320 ===============
1321 */
PM_Footsteps(void)1322 static void PM_Footsteps( void ) {
1323 	float		bobmove;
1324 	int			old;
1325 	qboolean	footstep;
1326 
1327 	//
1328 	// calculate speed and cycle to be used for
1329 	// all cyclic walking effects
1330 	//
1331 	pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
1332 		+  pm->ps->velocity[1] * pm->ps->velocity[1] );
1333 
1334 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
1335 
1336 		if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
1337 			PM_ContinueLegsAnim( LEGS_IDLECR );
1338 		}
1339 		// airborne leaves position in cycle intact, but doesn't advance
1340 		if ( pm->waterlevel > 1 ) {
1341 			PM_ContinueLegsAnim( LEGS_SWIM );
1342 		}
1343 		return;
1344 	}
1345 
1346 	// if not trying to move
1347 	if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
1348 		if (  pm->xyspeed < 5 ) {
1349 			pm->ps->bobCycle = 0;	// start at beginning of cycle again
1350 			if ( pm->ps->pm_flags & PMF_DUCKED ) {
1351 				PM_ContinueLegsAnim( LEGS_IDLECR );
1352 			} else {
1353 				PM_ContinueLegsAnim( LEGS_IDLE );
1354 			}
1355 		}
1356 		return;
1357 	}
1358 
1359 
1360 	footstep = qfalse;
1361 
1362 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
1363 		bobmove = 0.5;	// ducked characters bob much faster
1364 		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1365 			PM_ContinueLegsAnim( LEGS_BACKCR );
1366 		}
1367 		else {
1368 			PM_ContinueLegsAnim( LEGS_WALKCR );
1369 		}
1370 		// ducked characters never play footsteps
1371 	/*
1372 	} else 	if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1373 		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
1374 			bobmove = 0.4;	// faster speeds bob faster
1375 			footstep = qtrue;
1376 		} else {
1377 			bobmove = 0.3;
1378 		}
1379 		PM_ContinueLegsAnim( LEGS_BACK );
1380 	*/
1381 	} else {
1382 		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
1383 			bobmove = 0.4f;	// faster speeds bob faster
1384 			if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1385 				PM_ContinueLegsAnim( LEGS_BACK );
1386 			}
1387 			else {
1388 				PM_ContinueLegsAnim( LEGS_RUN );
1389 			}
1390 			footstep = qtrue;
1391 		} else {
1392 			bobmove = 0.3f;	// walking bobs slow
1393 			if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
1394 				PM_ContinueLegsAnim( LEGS_BACKWALK );
1395 			}
1396 			else {
1397 				PM_ContinueLegsAnim( LEGS_WALK );
1398 			}
1399 		}
1400 	}
1401 
1402 	// check for footstep / splash sounds
1403 	old = pm->ps->bobCycle;
1404 	pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
1405 
1406 	// if we just crossed a cycle boundary, play an apropriate footstep event
1407 	if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {
1408 		if ( pm->waterlevel == 0 ) {
1409 			// on ground will only play sounds if running
1410 			if ( footstep && !pm->noFootsteps ) {
1411 				PM_AddEvent( PM_FootstepForSurface() );
1412 			}
1413 		} else if ( pm->waterlevel == 1 ) {
1414 			// splashing
1415 			PM_AddEvent( EV_FOOTSPLASH );
1416 		} else if ( pm->waterlevel == 2 ) {
1417 			// wading / swimming at surface
1418 			PM_AddEvent( EV_SWIM );
1419 		} else if ( pm->waterlevel == 3 ) {
1420 			// no sound when completely underwater
1421 
1422 		}
1423 	}
1424 }
1425 
1426 /*
1427 ==============
1428 PM_WaterEvents
1429 
1430 Generate sound events for entering and leaving water
1431 ==============
1432 */
PM_WaterEvents(void)1433 static void PM_WaterEvents( void ) {		// FIXME?
1434 	//
1435 	// if just entered a water volume, play a sound
1436 	//
1437 	if (!pml.previous_waterlevel && pm->waterlevel) {
1438 		PM_AddEvent( EV_WATER_TOUCH );
1439 	}
1440 
1441 	//
1442 	// if just completely exited a water volume, play a sound
1443 	//
1444 	if (pml.previous_waterlevel && !pm->waterlevel) {
1445 		PM_AddEvent( EV_WATER_LEAVE );
1446 	}
1447 
1448 	//
1449 	// check for head just going under water
1450 	//
1451 	if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
1452 		PM_AddEvent( EV_WATER_UNDER );
1453 	}
1454 
1455 	//
1456 	// check for head just coming out of water
1457 	//
1458 	if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
1459 		PM_AddEvent( EV_WATER_CLEAR );
1460 	}
1461 }
1462 
1463 
1464 /*
1465 ===============
1466 PM_BeginWeaponChange
1467 ===============
1468 */
PM_BeginWeaponChange(int weapon)1469 static void PM_BeginWeaponChange( int weapon ) {
1470 	if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
1471 		return;
1472 	}
1473 
1474 	if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
1475 		return;
1476 	}
1477 
1478 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
1479 		return;
1480 	}
1481 
1482 	PM_AddEvent( EV_CHANGE_WEAPON );
1483 	pm->ps->weaponstate = WEAPON_DROPPING;
1484 	pm->ps->weaponTime += 200;
1485 	PM_StartTorsoAnim( TORSO_DROP );
1486 }
1487 
1488 
1489 /*
1490 ===============
1491 PM_FinishWeaponChange
1492 ===============
1493 */
PM_FinishWeaponChange(void)1494 static void PM_FinishWeaponChange( void ) {
1495 	int		weapon;
1496 
1497 	weapon = pm->cmd.weapon;
1498 	if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
1499 		weapon = WP_NONE;
1500 	}
1501 
1502 	if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
1503 		weapon = WP_NONE;
1504 	}
1505 
1506 	pm->ps->weapon = weapon;
1507 	pm->ps->weaponstate = WEAPON_RAISING;
1508 	pm->ps->weaponTime += 250;
1509 	PM_StartTorsoAnim( TORSO_RAISE );
1510 }
1511 
1512 
1513 /*
1514 ==============
1515 PM_TorsoAnimation
1516 
1517 ==============
1518 */
PM_TorsoAnimation(void)1519 static void PM_TorsoAnimation( void ) {
1520 	if ( pm->ps->weaponstate == WEAPON_READY ) {
1521 		if ( pm->ps->weapon == WP_GAUNTLET ) {
1522 			PM_ContinueTorsoAnim( TORSO_STAND2 );
1523 		} else {
1524 			PM_ContinueTorsoAnim( TORSO_STAND );
1525 		}
1526 		return;
1527 	}
1528 }
1529 
1530 
1531 /*
1532 ==============
1533 PM_Weapon
1534 
1535 Generates weapon events and modifes the weapon counter
1536 ==============
1537 */
PM_Weapon(void)1538 static void PM_Weapon( void ) {
1539 	int		addTime;
1540 
1541 	// don't allow attack until all buttons are up
1542 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
1543 		return;
1544 	}
1545 
1546 	// ignore if spectator
1547 	if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
1548 		return;
1549 	}
1550 
1551 	// check for dead player
1552 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
1553 		pm->ps->weapon = WP_NONE;
1554 		return;
1555 	}
1556 
1557 	// check for item using
1558 	if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
1559 		if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
1560 			if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
1561 				&& pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
1562 				// don't use medkit if at max health
1563 			} else {
1564 				pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
1565 				PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
1566 				pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
1567 			}
1568 			return;
1569 		}
1570 	} else {
1571 		pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
1572 	}
1573 
1574 
1575 	// make weapon function
1576 	if ( pm->ps->weaponTime > 0 ) {
1577 		pm->ps->weaponTime -= pml.msec;
1578 	}
1579 
1580 	// check for weapon change
1581 	// can't change if weapon is firing, but can change
1582 	// again if lowering or raising
1583 	if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
1584 		if ( pm->ps->weapon != pm->cmd.weapon ) {
1585 			PM_BeginWeaponChange( pm->cmd.weapon );
1586 		}
1587 	}
1588 
1589 	if ( pm->ps->weaponTime > 0 ) {
1590 		return;
1591 	}
1592 
1593 	// change weapon if time
1594 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
1595 		PM_FinishWeaponChange();
1596 		return;
1597 	}
1598 
1599 	if ( pm->ps->weaponstate == WEAPON_RAISING ) {
1600 		pm->ps->weaponstate = WEAPON_READY;
1601 		if ( pm->ps->weapon == WP_GAUNTLET ) {
1602 			PM_StartTorsoAnim( TORSO_STAND2 );
1603 		} else {
1604 			PM_StartTorsoAnim( TORSO_STAND );
1605 		}
1606 		return;
1607 	}
1608 
1609 	// check for fire
1610 	if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) {
1611 		pm->ps->weaponTime = 0;
1612 		pm->ps->weaponstate = WEAPON_READY;
1613 		return;
1614 	}
1615 
1616 	// start the animation even if out of ammo
1617 	if ( pm->ps->weapon == WP_GAUNTLET ) {
1618 		// the guantlet only "fires" when it actually hits something
1619 		if ( !pm->gauntletHit ) {
1620 			pm->ps->weaponTime = 0;
1621 			pm->ps->weaponstate = WEAPON_READY;
1622 			return;
1623 		}
1624 		PM_StartTorsoAnim( TORSO_ATTACK2 );
1625 	} else {
1626 		PM_StartTorsoAnim( TORSO_ATTACK );
1627 	}
1628 
1629 	pm->ps->weaponstate = WEAPON_FIRING;
1630 
1631 	// check for out of ammo
1632 	if ( ! pm->ps->ammo[ pm->ps->weapon ] ) {
1633 		PM_AddEvent( EV_NOAMMO );
1634 		pm->ps->weaponTime += 500;
1635 		return;
1636 	}
1637 
1638 	// take an ammo away if not infinite
1639 	if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) {
1640 		pm->ps->ammo[ pm->ps->weapon ]--;
1641 	}
1642 
1643 	// fire weapon
1644 	PM_AddEvent( EV_FIRE_WEAPON );
1645 
1646 	switch( pm->ps->weapon ) {
1647 	default:
1648 	case WP_GAUNTLET:
1649 		addTime = 400;
1650 		break;
1651 	case WP_LIGHTNING:
1652 		addTime = 50;
1653 		break;
1654 	case WP_SHOTGUN:
1655 		addTime = 1000;
1656 		break;
1657 	case WP_MACHINEGUN:
1658 		addTime = 100;
1659 		break;
1660 	case WP_GRENADE_LAUNCHER:
1661 		addTime = 800;
1662 		break;
1663 	case WP_ROCKET_LAUNCHER:
1664 		addTime = 800;
1665 		break;
1666 	case WP_PLASMAGUN:
1667 		addTime = 100;
1668 		break;
1669 	case WP_RAILGUN:
1670 		addTime = 1500;
1671 		break;
1672 	case WP_BFG:
1673 		addTime = 200;
1674 		break;
1675 	case WP_GRAPPLING_HOOK:
1676 		addTime = 400;
1677 		break;
1678 #ifdef MISSIONPACK
1679 	case WP_NAILGUN:
1680 		addTime = 1000;
1681 		break;
1682 	case WP_PROX_LAUNCHER:
1683 		addTime = 800;
1684 		break;
1685 	case WP_CHAINGUN:
1686 		addTime = 30;
1687 		break;
1688 #endif
1689 	}
1690 
1691 #ifdef MISSIONPACK
1692 	if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
1693 		addTime /= 1.5;
1694 	}
1695 	else
1696 	if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
1697 		addTime /= 1.3;
1698   }
1699   else
1700 #endif
1701 	if ( pm->ps->powerups[PW_HASTE] ) {
1702 		addTime /= 1.3;
1703 	}
1704 
1705 	pm->ps->weaponTime += addTime;
1706 }
1707 
1708 /*
1709 ================
1710 PM_Animate
1711 ================
1712 */
1713 
PM_Animate(void)1714 static void PM_Animate( void ) {
1715 	if ( pm->cmd.buttons & BUTTON_GESTURE ) {
1716 		if ( pm->ps->torsoTimer == 0 ) {
1717 			PM_StartTorsoAnim( TORSO_GESTURE );
1718 			pm->ps->torsoTimer = TIMER_GESTURE;
1719 			PM_AddEvent( EV_TAUNT );
1720 		}
1721 #ifdef MISSIONPACK
1722 	} else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
1723 		if ( pm->ps->torsoTimer == 0 ) {
1724 			PM_StartTorsoAnim( TORSO_GETFLAG );
1725 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
1726 		}
1727 	} else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
1728 		if ( pm->ps->torsoTimer == 0 ) {
1729 			PM_StartTorsoAnim( TORSO_GUARDBASE );
1730 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
1731 		}
1732 	} else if ( pm->cmd.buttons & BUTTON_PATROL ) {
1733 		if ( pm->ps->torsoTimer == 0 ) {
1734 			PM_StartTorsoAnim( TORSO_PATROL );
1735 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
1736 		}
1737 	} else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
1738 		if ( pm->ps->torsoTimer == 0 ) {
1739 			PM_StartTorsoAnim( TORSO_FOLLOWME );
1740 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
1741 		}
1742 	} else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
1743 		if ( pm->ps->torsoTimer == 0 ) {
1744 			PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
1745 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
1746 		}
1747 	} else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
1748 		if ( pm->ps->torsoTimer == 0 ) {
1749 			PM_StartTorsoAnim( TORSO_NEGATIVE );
1750 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
1751 		}
1752 #endif
1753 	}
1754 }
1755 
1756 
1757 /*
1758 ================
1759 PM_DropTimers
1760 ================
1761 */
PM_DropTimers(void)1762 static void PM_DropTimers( void ) {
1763 	// drop misc timing counter
1764 	if ( pm->ps->pm_time ) {
1765 		if ( pml.msec >= pm->ps->pm_time ) {
1766 			pm->ps->pm_flags &= ~PMF_ALL_TIMES;
1767 			pm->ps->pm_time = 0;
1768 		} else {
1769 			pm->ps->pm_time -= pml.msec;
1770 		}
1771 	}
1772 
1773 	// drop animation counter
1774 	if ( pm->ps->legsTimer > 0 ) {
1775 		pm->ps->legsTimer -= pml.msec;
1776 		if ( pm->ps->legsTimer < 0 ) {
1777 			pm->ps->legsTimer = 0;
1778 		}
1779 	}
1780 
1781 	if ( pm->ps->torsoTimer > 0 ) {
1782 		pm->ps->torsoTimer -= pml.msec;
1783 		if ( pm->ps->torsoTimer < 0 ) {
1784 			pm->ps->torsoTimer = 0;
1785 		}
1786 	}
1787 }
1788 
1789 /*
1790 ================
1791 PM_UpdateViewAngles
1792 
1793 This can be used as another entry point when only the viewangles
1794 are being updated isntead of a full move
1795 ================
1796 */
PM_UpdateViewAngles(playerState_t * ps,const usercmd_t * cmd)1797 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
1798 	short		temp;
1799 	int		i;
1800 
1801 	if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
1802 		return;		// no view changes at all
1803 	}
1804 
1805 	if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
1806 		return;		// no view changes at all
1807 	}
1808 
1809 	// circularly clamp the angles with deltas
1810 	for (i=0 ; i<3 ; i++) {
1811 		temp = cmd->angles[i] + ps->delta_angles[i];
1812 		if ( i == PITCH ) {
1813 			// don't let the player look up or down more than 90 degrees
1814 			if ( temp > 16000 ) {
1815 				ps->delta_angles[i] = 16000 - cmd->angles[i];
1816 				temp = 16000;
1817 			} else if ( temp < -16000 ) {
1818 				ps->delta_angles[i] = -16000 - cmd->angles[i];
1819 				temp = -16000;
1820 			}
1821 		}
1822 		ps->viewangles[i] = SHORT2ANGLE(temp);
1823 	}
1824 
1825 }
1826 
1827 
1828 /*
1829 ================
1830 PmoveSingle
1831 
1832 ================
1833 */
1834 void trap_SnapVector( float *v );
1835 
PmoveSingle(pmove_t * pmove)1836 void PmoveSingle (pmove_t *pmove) {
1837 	pm = pmove;
1838 
1839 	// this counter lets us debug movement problems with a journal
1840 	// by setting a conditional breakpoint fot the previous frame
1841 	c_pmove++;
1842 
1843 	// clear results
1844 	pm->numtouch = 0;
1845 	pm->watertype = 0;
1846 	pm->waterlevel = 0;
1847 
1848 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
1849 		pm->tracemask &= ~CONTENTS_BODY;	// corpses can fly through bodies
1850 	}
1851 
1852 	// make sure walking button is clear if they are running, to avoid
1853 	// proxy no-footsteps cheats
1854 	if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
1855 		pm->cmd.buttons &= ~BUTTON_WALKING;
1856 	}
1857 
1858 	// set the talk balloon flag
1859 	if ( pm->cmd.buttons & BUTTON_TALK ) {
1860 		pm->ps->eFlags |= EF_TALK;
1861 	} else {
1862 		pm->ps->eFlags &= ~EF_TALK;
1863 	}
1864 
1865 	// set the firing flag for continuous beam weapons
1866 	if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
1867 		&& ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
1868 		pm->ps->eFlags |= EF_FIRING;
1869 	} else {
1870 		pm->ps->eFlags &= ~EF_FIRING;
1871 	}
1872 
1873 	// clear the respawned flag if attack and use are cleared
1874 	if ( pm->ps->stats[STAT_HEALTH] > 0 &&
1875 		!( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
1876 		pm->ps->pm_flags &= ~PMF_RESPAWNED;
1877 	}
1878 
1879 	// if talk button is down, dissallow all other input
1880 	// this is to prevent any possible intercept proxy from
1881 	// adding fake talk balloons
1882 	if ( pmove->cmd.buttons & BUTTON_TALK ) {
1883 		// keep the talk button set tho for when the cmd.serverTime > 66 msec
1884 		// and the same cmd is used multiple times in Pmove
1885 		pmove->cmd.buttons = BUTTON_TALK;
1886 		pmove->cmd.forwardmove = 0;
1887 		pmove->cmd.rightmove = 0;
1888 		pmove->cmd.upmove = 0;
1889 	}
1890 
1891 	// clear all pmove local vars
1892 	memset (&pml, 0, sizeof(pml));
1893 
1894 	// determine the time
1895 	pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
1896 	if ( pml.msec < 1 ) {
1897 		pml.msec = 1;
1898 	} else if ( pml.msec > 200 ) {
1899 		pml.msec = 200;
1900 	}
1901 	pm->ps->commandTime = pmove->cmd.serverTime;
1902 
1903 	// save old org in case we get stuck
1904 	VectorCopy (pm->ps->origin, pml.previous_origin);
1905 
1906 	// save old velocity for crashlanding
1907 	VectorCopy (pm->ps->velocity, pml.previous_velocity);
1908 
1909 	pml.frametime = pml.msec * 0.001;
1910 
1911 	// update the viewangles
1912 	PM_UpdateViewAngles( pm->ps, &pm->cmd );
1913 
1914 	AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
1915 
1916 	if ( pm->cmd.upmove < 10 ) {
1917 		// not holding jump
1918 		pm->ps->pm_flags &= ~PMF_JUMP_HELD;
1919 	}
1920 
1921 	// decide if backpedaling animations should be used
1922 	if ( pm->cmd.forwardmove < 0 ) {
1923 		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
1924 	} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
1925 		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
1926 	}
1927 
1928 	if ( pm->ps->pm_type >= PM_DEAD ) {
1929 		pm->cmd.forwardmove = 0;
1930 		pm->cmd.rightmove = 0;
1931 		pm->cmd.upmove = 0;
1932 	}
1933 
1934 	if ( pm->ps->pm_type == PM_SPECTATOR ) {
1935 		PM_CheckDuck ();
1936 		PM_FlyMove ();
1937 		PM_DropTimers ();
1938 		return;
1939 	}
1940 
1941 	if ( pm->ps->pm_type == PM_NOCLIP ) {
1942 		PM_NoclipMove ();
1943 		PM_DropTimers ();
1944 		return;
1945 	}
1946 
1947 	if (pm->ps->pm_type == PM_FREEZE) {
1948 		return;		// no movement at all
1949 	}
1950 
1951 	if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) {
1952 		return;		// no movement at all
1953 	}
1954 
1955 	// set watertype, and waterlevel
1956 	PM_SetWaterLevel();
1957 	pml.previous_waterlevel = pmove->waterlevel;
1958 
1959 	// set mins, maxs, and viewheight
1960 	PM_CheckDuck ();
1961 
1962 	// set groundentity
1963 	PM_GroundTrace();
1964 
1965 	if ( pm->ps->pm_type == PM_DEAD ) {
1966 		PM_DeadMove ();
1967 	}
1968 
1969 	PM_DropTimers();
1970 
1971 #ifdef MISSIONPACK
1972 	if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
1973 		PM_InvulnerabilityMove();
1974 	} else
1975 #endif
1976 	if ( pm->ps->powerups[PW_FLIGHT] ) {
1977 		// flight powerup doesn't allow jump and has different friction
1978 		PM_FlyMove();
1979 	} else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) {
1980 		PM_GrappleMove();
1981 		// We can wiggle a bit
1982 		PM_AirMove();
1983 	} else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
1984 		PM_WaterJumpMove();
1985 	} else if ( pm->waterlevel > 1 ) {
1986 		// swimming
1987 		PM_WaterMove();
1988 	} else if ( pml.walking ) {
1989 		// walking on ground
1990 		PM_WalkMove();
1991 	} else {
1992 		// airborne
1993 		PM_AirMove();
1994 	}
1995 
1996 	PM_Animate();
1997 
1998 	// set groundentity, watertype, and waterlevel
1999 	PM_GroundTrace();
2000 	PM_SetWaterLevel();
2001 
2002 	// weapons
2003 	PM_Weapon();
2004 
2005 	// torso animation
2006 	PM_TorsoAnimation();
2007 
2008 	// footstep events / legs animations
2009 	PM_Footsteps();
2010 
2011 	// entering / leaving water splashes
2012 	PM_WaterEvents();
2013 
2014 	// snap some parts of playerstate to save network bandwidth
2015 	trap_SnapVector( pm->ps->velocity );
2016 }
2017 
2018 
2019 /*
2020 ================
2021 Pmove
2022 
2023 Can be called by either the server or the client
2024 ================
2025 */
Pmove(pmove_t * pmove)2026 void Pmove (pmove_t *pmove) {
2027 	int			finalTime;
2028 
2029 	finalTime = pmove->cmd.serverTime;
2030 
2031 	if ( finalTime < pmove->ps->commandTime ) {
2032 		return;	// should not happen
2033 	}
2034 
2035 	if ( finalTime > pmove->ps->commandTime + 1000 ) {
2036 		pmove->ps->commandTime = finalTime - 1000;
2037 	}
2038 
2039 	pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
2040 
2041 	// chop the move up if it is too long, to prevent framerate
2042 	// dependent behavior
2043 	while ( pmove->ps->commandTime != finalTime ) {
2044 		int		msec;
2045 
2046 		msec = finalTime - pmove->ps->commandTime;
2047 
2048 		if ( pmove->pmove_fixed ) {
2049 			if ( msec > pmove->pmove_msec ) {
2050 				msec = pmove->pmove_msec;
2051 			}
2052 		}
2053 		else {
2054 			if ( msec > 66 ) {
2055 				msec = 66;
2056 			}
2057 		}
2058 		pmove->cmd.serverTime = pmove->ps->commandTime + msec;
2059 		PmoveSingle( pmove );
2060 
2061 		if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
2062 			pmove->cmd.upmove = 20;
2063 		}
2064 	}
2065 
2066 	//PM_CheckStuck();
2067 
2068 }
2069 
2070