• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 Copyright (C) 1996-1997 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 #include "quakedef.h"
22 
23 
24 movevars_t		movevars;
25 
26 playermove_t	pmove;
27 
28 int			onground;
29 int			waterlevel;
30 int			watertype;
31 
32 float		frametime;
33 
34 vec3_t		forward, right, up;
35 
36 vec3_t	player_mins = {-16, -16, -24};
37 vec3_t	player_maxs = {16, 16, 32};
38 
39 // #define	PM_GRAVITY			800
40 // #define	PM_STOPSPEED		100
41 // #define	PM_MAXSPEED			320
42 // #define	PM_SPECTATORMAXSPEED	500
43 // #define	PM_ACCELERATE		10
44 // #define	PM_AIRACCELERATE	0.7
45 // #define	PM_WATERACCELERATE	10
46 // #define	PM_FRICTION			6
47 // #define	PM_WATERFRICTION	1
48 
49 void PM_InitBoxHull (void);
50 
Pmove_Init(void)51 void Pmove_Init (void)
52 {
53 	PM_InitBoxHull ();
54 }
55 
56 #define	STEPSIZE	18
57 
58 
59 #define	BUTTON_JUMP	2
60 
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 */
70 #define	STOP_EPSILON	0.1
71 
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)72 int PM_ClipVelocity (vec3_t in, vec3_t normal, vec3_t out, float overbounce)
73 {
74 	float	backoff;
75 	float	change;
76 	int		i, blocked;
77 
78 	blocked = 0;
79 	if (normal[2] > 0)
80 		blocked |= 1;		// floor
81 	if (!normal[2])
82 		blocked |= 2;		// step
83 
84 	backoff = DotProduct (in, normal) * overbounce;
85 
86 	for (i=0 ; i<3 ; i++)
87 	{
88 		change = normal[i]*backoff;
89 		out[i] = in[i] - change;
90 		if (out[i] > -STOP_EPSILON && out[i] < STOP_EPSILON)
91 			out[i] = 0;
92 	}
93 
94 	return blocked;
95 }
96 
97 
98 /*
99 ============
100 PM_FlyMove
101 
102 The basic solid body movement clip that slides along multiple planes
103 ============
104 */
105 #define	MAX_CLIP_PLANES	5
106 
PM_FlyMove(void)107 int PM_FlyMove (void)
108 {
109 	int			bumpcount, numbumps;
110 	vec3_t		dir;
111 	float		d;
112 	int			numplanes;
113 	vec3_t		planes[MAX_CLIP_PLANES];
114 	vec3_t		primal_velocity, original_velocity;
115 	int			i, j;
116 	pmtrace_t		trace;
117 	vec3_t		end;
118 	float		time_left;
119 	int			blocked;
120 
121 	numbumps = 4;
122 
123 	blocked = 0;
124 	VectorCopy (pmove.velocity, original_velocity);
125 	VectorCopy (pmove.velocity, primal_velocity);
126 	numplanes = 0;
127 
128 	time_left = frametime;
129 
130 	for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++)
131 	{
132 		for (i=0 ; i<3 ; i++)
133 			end[i] = pmove.origin[i] + time_left * pmove.velocity[i];
134 
135 		trace = PM_PlayerMove (pmove.origin, end);
136 
137 		if (trace.startsolid || trace.allsolid)
138 		{	// entity is trapped in another solid
139 			VectorCopy (vec3_origin, pmove.velocity);
140 			return 3;
141 		}
142 
143 		if (trace.fraction > 0)
144 		{	// actually covered some distance
145 			VectorCopy (trace.endpos, pmove.origin);
146 			numplanes = 0;
147 		}
148 
149 		if (trace.fraction == 1)
150 			 break;		// moved the entire distance
151 
152 		// save entity for contact
153 		pmove.touchindex[pmove.numtouch] = trace.ent;
154 		pmove.numtouch++;
155 
156 		if (trace.plane.normal[2] > 0.7)
157 		{
158 			blocked |= 1;		// floor
159 		}
160 		if (!trace.plane.normal[2])
161 		{
162 			blocked |= 2;		// step
163 		}
164 
165 		time_left -= time_left * trace.fraction;
166 
167 	// cliped to another plane
168 		if (numplanes >= MAX_CLIP_PLANES)
169 		{	// this shouldn't really happen
170 			VectorCopy (vec3_origin, pmove.velocity);
171 			break;
172 		}
173 
174 		VectorCopy (trace.plane.normal, planes[numplanes]);
175 		numplanes++;
176 
177 //
178 // modify original_velocity so it parallels all of the clip planes
179 //
180 		for (i=0 ; i<numplanes ; i++)
181 		{
182 			PM_ClipVelocity (original_velocity, planes[i], pmove.velocity, 1);
183 			for (j=0 ; j<numplanes ; j++)
184 				if (j != i)
185 				{
186 					if (DotProduct (pmove.velocity, planes[j]) < 0)
187 						break;	// not ok
188 				}
189 			if (j == numplanes)
190 				break;
191 		}
192 
193 		if (i != numplanes)
194 		{	// go along this plane
195 		}
196 		else
197 		{	// go along the crease
198 			if (numplanes != 2)
199 			{
200 //				Con_Printf ("clip velocity, numplanes == %i\n",numplanes);
201 				VectorCopy (vec3_origin, pmove.velocity);
202 				break;
203 			}
204 			CrossProduct (planes[0], planes[1], dir);
205 			d = DotProduct (dir, pmove.velocity);
206 			VectorScale (dir, d, pmove.velocity);
207 		}
208 
209 //
210 // if original velocity is against the original velocity, stop dead
211 // to avoid tiny occilations in sloping corners
212 //
213 		if (DotProduct (pmove.velocity, primal_velocity) <= 0)
214 		{
215 			VectorCopy (vec3_origin, pmove.velocity);
216 			break;
217 		}
218 	}
219 
220 	if (pmove.waterjumptime)
221 	{
222 		VectorCopy (primal_velocity, pmove.velocity);
223 	}
224 	return blocked;
225 }
226 
227 /*
228 =============
229 PM_GroundMove
230 
231 Player is on ground, with no upwards velocity
232 =============
233 */
PM_GroundMove(void)234 void PM_GroundMove (void)
235 {
236 	vec3_t	start, dest;
237 	pmtrace_t	trace;
238 	vec3_t	original, originalvel, down, up, downvel;
239 	float	downdist, updist;
240 
241 	pmove.velocity[2] = 0;
242 	if (!pmove.velocity[0] && !pmove.velocity[1] && !pmove.velocity[2])
243 		return;
244 
245 	// first try just moving to the destination
246 	dest[0] = pmove.origin[0] + pmove.velocity[0]*frametime;
247 	dest[1] = pmove.origin[1] + pmove.velocity[1]*frametime;
248 	dest[2] = pmove.origin[2];
249 
250 	// first try moving directly to the next spot
251 	VectorCopy (dest, start);
252 	trace = PM_PlayerMove (pmove.origin, dest);
253 	if (trace.fraction == 1)
254 	{
255 		VectorCopy (trace.endpos, pmove.origin);
256 		return;
257 	}
258 
259 	// try sliding forward both on ground and up 16 pixels
260 	// take the move that goes farthest
261 	VectorCopy (pmove.origin, original);
262 	VectorCopy (pmove.velocity, originalvel);
263 
264 	// slide move
265 	PM_FlyMove ();
266 
267 	VectorCopy (pmove.origin, down);
268 	VectorCopy (pmove.velocity, downvel);
269 
270 	VectorCopy (original, pmove.origin);
271 	VectorCopy (originalvel, pmove.velocity);
272 
273 // move up a stair height
274 	VectorCopy (pmove.origin, dest);
275 	dest[2] += STEPSIZE;
276 	trace = PM_PlayerMove (pmove.origin, dest);
277 	if (!trace.startsolid && !trace.allsolid)
278 	{
279 		VectorCopy (trace.endpos, pmove.origin);
280 	}
281 
282 // slide move
283 	PM_FlyMove ();
284 
285 // press down the stepheight
286 	VectorCopy (pmove.origin, dest);
287 	dest[2] -= STEPSIZE;
288 	trace = PM_PlayerMove (pmove.origin, dest);
289 	if ( trace.plane.normal[2] < 0.7)
290 		goto usedown;
291 	if (!trace.startsolid && !trace.allsolid)
292 	{
293 		VectorCopy (trace.endpos, pmove.origin);
294 	}
295 	VectorCopy (pmove.origin, up);
296 
297 	// decide which one went farther
298 	downdist = (down[0] - original[0])*(down[0] - original[0])
299 		+ (down[1] - original[1])*(down[1] - original[1]);
300 	updist = (up[0] - original[0])*(up[0] - original[0])
301 		+ (up[1] - original[1])*(up[1] - original[1]);
302 
303 	if (downdist > updist)
304 	{
305 usedown:
306 		VectorCopy (down, pmove.origin);
307 		VectorCopy (downvel, pmove.velocity);
308 	} else // copy z value from slide move
309 		pmove.velocity[2] = downvel[2];
310 
311 // if at a dead stop, retry the move with nudges to get around lips
312 
313 }
314 
315 
316 
317 /*
318 ==================
319 PM_Friction
320 
321 Handles both ground friction and water friction
322 ==================
323 */
PM_Friction(void)324 void PM_Friction (void)
325 {
326 	float	*vel;
327 	float	speed, newspeed, control;
328 	float	friction;
329 	float	drop;
330 	vec3_t	start, stop;
331 	pmtrace_t		trace;
332 
333 	if (pmove.waterjumptime)
334 		return;
335 
336 	vel = pmove.velocity;
337 
338 	speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1] + vel[2]*vel[2]);
339 	if (speed < 1)
340 	{
341 		vel[0] = 0;
342 		vel[1] = 0;
343 		return;
344 	}
345 
346 	friction = movevars.friction;
347 
348 // if the leading edge is over a dropoff, increase friction
349 	if (onground != -1) {
350 		start[0] = stop[0] = pmove.origin[0] + vel[0]/speed*16;
351 		start[1] = stop[1] = pmove.origin[1] + vel[1]/speed*16;
352 		start[2] = pmove.origin[2] + player_mins[2];
353 		stop[2] = start[2] - 34;
354 
355 		trace = PM_PlayerMove (start, stop);
356 
357 		if (trace.fraction == 1) {
358 			friction *= 2;
359 		}
360 	}
361 
362 	drop = 0;
363 
364 	if (waterlevel >= 2) // apply water friction
365 		drop += speed*movevars.waterfriction*waterlevel*frametime;
366 	else if (onground != -1) // apply ground friction
367 	{
368 		control = speed < movevars.stopspeed ? movevars.stopspeed : speed;
369 		drop += control*friction*frametime;
370 	}
371 
372 
373 // scale the velocity
374 	newspeed = speed - drop;
375 	if (newspeed < 0)
376 		newspeed = 0;
377 	newspeed /= speed;
378 
379 	vel[0] = vel[0] * newspeed;
380 	vel[1] = vel[1] * newspeed;
381 	vel[2] = vel[2] * newspeed;
382 }
383 
384 
385 /*
386 ==============
387 PM_Accelerate
388 ==============
389 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)390 void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel)
391 {
392 	int			i;
393 	float		addspeed, accelspeed, currentspeed;
394 
395 	if (pmove.dead)
396 		return;
397 	if (pmove.waterjumptime)
398 		return;
399 
400 	currentspeed = DotProduct (pmove.velocity, wishdir);
401 	addspeed = wishspeed - currentspeed;
402 	if (addspeed <= 0)
403 		return;
404 	accelspeed = accel*frametime*wishspeed;
405 	if (accelspeed > addspeed)
406 		accelspeed = addspeed;
407 
408 	for (i=0 ; i<3 ; i++)
409 		pmove.velocity[i] += accelspeed*wishdir[i];
410 }
411 
PM_AirAccelerate(vec3_t wishdir,float wishspeed,float accel)412 void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel)
413 {
414 	int			i;
415 	float		addspeed, accelspeed, currentspeed, wishspd = wishspeed;
416 
417 	if (pmove.dead)
418 		return;
419 	if (pmove.waterjumptime)
420 		return;
421 
422 	if (wishspd > 30)
423 		wishspd = 30;
424 	currentspeed = DotProduct (pmove.velocity, wishdir);
425 	addspeed = wishspd - currentspeed;
426 	if (addspeed <= 0)
427 		return;
428 	accelspeed = accel * wishspeed * frametime;
429 	if (accelspeed > addspeed)
430 		accelspeed = addspeed;
431 
432 	for (i=0 ; i<3 ; i++)
433 		pmove.velocity[i] += accelspeed*wishdir[i];
434 }
435 
436 
437 
438 /*
439 ===================
440 PM_WaterMove
441 
442 ===================
443 */
PM_WaterMove(void)444 void PM_WaterMove (void)
445 {
446 	int		i;
447 	vec3_t	wishvel;
448 	float	wishspeed;
449 	vec3_t	wishdir;
450 	vec3_t	start, dest;
451 	pmtrace_t	trace;
452 
453 //
454 // user intentions
455 //
456 	for (i=0 ; i<3 ; i++)
457 		wishvel[i] = forward[i]*pmove.cmd.forwardmove + right[i]*pmove.cmd.sidemove;
458 
459 	if (!pmove.cmd.forwardmove && !pmove.cmd.sidemove && !pmove.cmd.upmove)
460 		wishvel[2] -= 60;		// drift towards bottom
461 	else
462 		wishvel[2] += pmove.cmd.upmove;
463 
464 	VectorCopy (wishvel, wishdir);
465 	wishspeed = VectorNormalize(wishdir);
466 
467 	if (wishspeed > movevars.maxspeed)
468 	{
469 		VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel);
470 		wishspeed = movevars.maxspeed;
471 	}
472 	wishspeed *= 0.7;
473 
474 //
475 // water acceleration
476 //
477 //	if (pmove.waterjumptime)
478 //		Con_Printf ("wm->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]);
479 	PM_Accelerate (wishdir, wishspeed, movevars.wateraccelerate);
480 
481 // assume it is a stair or a slope, so press down from stepheight above
482 	VectorMA (pmove.origin, frametime, pmove.velocity, dest);
483 	VectorCopy (dest, start);
484 	start[2] += STEPSIZE + 1;
485 	trace = PM_PlayerMove (start, dest);
486 	if (!trace.startsolid && !trace.allsolid)	// FIXME: check steep slope?
487 	{	// walked up the step
488 		VectorCopy (trace.endpos, pmove.origin);
489 		return;
490 	}
491 
492 	PM_FlyMove ();
493 //	if (pmove.waterjumptime)
494 //		Con_Printf ("<-wm%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]);
495 }
496 
497 
498 /*
499 ===================
500 PM_AirMove
501 
502 ===================
503 */
PM_AirMove(void)504 void PM_AirMove (void)
505 {
506 	int			i;
507 	vec3_t		wishvel;
508 	float		fmove, smove;
509 	vec3_t		wishdir;
510 	float		wishspeed;
511 
512 	fmove = pmove.cmd.forwardmove;
513 	smove = pmove.cmd.sidemove;
514 
515 	forward[2] = 0;
516 	right[2] = 0;
517 	VectorNormalize (forward);
518 	VectorNormalize (right);
519 
520 	for (i=0 ; i<2 ; i++)
521 		wishvel[i] = forward[i]*fmove + right[i]*smove;
522 	wishvel[2] = 0;
523 
524 	VectorCopy (wishvel, wishdir);
525 	wishspeed = VectorNormalize(wishdir);
526 
527 //
528 // clamp to server defined max speed
529 //
530 	if (wishspeed > movevars.maxspeed)
531 	{
532 		VectorScale (wishvel, movevars.maxspeed/wishspeed, wishvel);
533 		wishspeed = movevars.maxspeed;
534 	}
535 
536 //	if (pmove.waterjumptime)
537 //		Con_Printf ("am->%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]);
538 
539 	if ( onground != -1)
540 	{
541 		pmove.velocity[2] = 0;
542 		PM_Accelerate (wishdir, wishspeed, movevars.accelerate);
543 		pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime;
544 		PM_GroundMove ();
545 	}
546 	else
547 	{	// not on ground, so little effect on velocity
548 		PM_AirAccelerate (wishdir, wishspeed, movevars.accelerate);
549 
550 		// add gravity
551 		pmove.velocity[2] -= movevars.entgravity * movevars.gravity * frametime;
552 
553 		PM_FlyMove ();
554 
555 	}
556 
557 //Con_Printf("airmove:vec: %4.2f %4.2f %4.2f\n",
558 //			pmove.velocity[0],
559 //			pmove.velocity[1],
560 //			pmove.velocity[2]);
561 //
562 
563 //	if (pmove.waterjumptime)
564 //		Con_Printf ("<-am%f, %f, %f\n", pmove.velocity[0], pmove.velocity[1], pmove.velocity[2]);
565 }
566 
567 
568 
569 /*
570 =============
571 PM_CatagorizePosition
572 =============
573 */
PM_CatagorizePosition(void)574 void PM_CatagorizePosition (void)
575 {
576 	vec3_t		point;
577 	int			cont;
578 	pmtrace_t		tr;
579 
580 // if the player hull point one unit down is solid, the player
581 // is on ground
582 
583 // see if standing on something solid
584 	point[0] = pmove.origin[0];
585 	point[1] = pmove.origin[1];
586 	point[2] = pmove.origin[2] - 1;
587 	if (pmove.velocity[2] > 180)
588 	{
589 		onground = -1;
590 	}
591 	else
592 	{
593 		tr = PM_PlayerMove (pmove.origin, point);
594 		if ( tr.plane.normal[2] < 0.7)
595 			onground = -1;	// too steep
596 		else
597 			onground = tr.ent;
598 		if (onground != -1)
599 		{
600 			pmove.waterjumptime = 0;
601 			if (!tr.startsolid && !tr.allsolid)
602 				VectorCopy (tr.endpos, pmove.origin);
603 		}
604 
605 		// standing on an entity other than the world
606 		if (tr.ent > 0)
607 		{
608 			pmove.touchindex[pmove.numtouch] = tr.ent;
609 			pmove.numtouch++;
610 		}
611 	}
612 
613 //
614 // get waterlevel
615 //
616 	waterlevel = 0;
617 	watertype = CONTENTS_EMPTY;
618 
619 	point[2] = pmove.origin[2] + player_mins[2] + 1;
620 	cont = PM_PointContents (point);
621 
622 	if (cont <= CONTENTS_WATER)
623 	{
624 		watertype = cont;
625 		waterlevel = 1;
626 		point[2] = pmove.origin[2] + (player_mins[2] + player_maxs[2])*0.5;
627 		cont = PM_PointContents (point);
628 		if (cont <= CONTENTS_WATER)
629 		{
630 			waterlevel = 2;
631 			point[2] = pmove.origin[2] + 22;
632 			cont = PM_PointContents (point);
633 			if (cont <= CONTENTS_WATER)
634 				waterlevel = 3;
635 		}
636 	}
637 }
638 
639 
640 /*
641 =============
642 JumpButton
643 =============
644 */
JumpButton(void)645 void JumpButton (void)
646 {
647 	if (pmove.dead)
648 	{
649 		pmove.oldbuttons |= BUTTON_JUMP;	// don't jump again until released
650 		return;
651 	}
652 
653 	if (pmove.waterjumptime)
654 	{
655 		pmove.waterjumptime -= frametime;
656 		if (pmove.waterjumptime < 0)
657 			pmove.waterjumptime = 0;
658 		return;
659 	}
660 
661 	if (waterlevel >= 2)
662 	{	// swimming, not jumping
663 		onground = -1;
664 
665 		if (watertype == CONTENTS_WATER)
666 			pmove.velocity[2] = 100;
667 		else if (watertype == CONTENTS_SLIME)
668 			pmove.velocity[2] = 80;
669 		else
670 			pmove.velocity[2] = 50;
671 		return;
672 	}
673 
674 	if (onground == -1)
675 		return;		// in air, so no effect
676 
677 	if ( pmove.oldbuttons & BUTTON_JUMP )
678 		return;		// don't pogo stick
679 
680 	onground = -1;
681 	pmove.velocity[2] += 270;
682 
683 	pmove.oldbuttons |= BUTTON_JUMP;	// don't jump again until released
684 }
685 
686 /*
687 =============
688 CheckWaterJump
689 =============
690 */
CheckWaterJump(void)691 void CheckWaterJump (void)
692 {
693 	vec3_t	spot;
694 	int		cont;
695 	vec3_t	flatforward;
696 
697 	if (pmove.waterjumptime)
698 		return;
699 
700 	// ZOID, don't hop out if we just jumped in
701 	if (pmove.velocity[2] < -180)
702 		return; // only hop out if we are moving up
703 
704 	// see if near an edge
705 	flatforward[0] = forward[0];
706 	flatforward[1] = forward[1];
707 	flatforward[2] = 0;
708 	VectorNormalize (flatforward);
709 
710 	VectorMA (pmove.origin, 24, flatforward, spot);
711 	spot[2] += 8;
712 	cont = PM_PointContents (spot);
713 	if (cont != CONTENTS_SOLID)
714 		return;
715 	spot[2] += 24;
716 	cont = PM_PointContents (spot);
717 	if (cont != CONTENTS_EMPTY)
718 		return;
719 	// jump out of water
720 	VectorScale (flatforward, 50, pmove.velocity);
721 	pmove.velocity[2] = 310;
722 	pmove.waterjumptime = 2;	// safety net
723 	pmove.oldbuttons |= BUTTON_JUMP;	// don't jump again until released
724 }
725 
726 /*
727 =================
728 NudgePosition
729 
730 If pmove.origin is in a solid position,
731 try nudging slightly on all axis to
732 allow for the cut precision of the net coordinates
733 =================
734 */
NudgePosition(void)735 void NudgePosition (void)
736 {
737 	vec3_t	base;
738 	int		x, y, z;
739 	int		i;
740 	static int		sign[3] = {0, -1, 1};
741 
742 	VectorCopy (pmove.origin, base);
743 
744 	for (i=0 ; i<3 ; i++)
745 		pmove.origin[i] = ((int)(pmove.origin[i]*8)) * 0.125;
746 //	pmove.origin[2] += 0.124;
747 
748 //	if (pmove.dead)
749 //		return;		// might be a squished point, so don'y bother
750 //	if (PM_TestPlayerPosition (pmove.origin) )
751 //		return;
752 
753 	for (z=0 ; z<=2 ; z++)
754 	{
755 		for (x=0 ; x<=2 ; x++)
756 		{
757 			for (y=0 ; y<=2 ; y++)
758 			{
759 				pmove.origin[0] = base[0] + (sign[x] * 1.0/8);
760 				pmove.origin[1] = base[1] + (sign[y] * 1.0/8);
761 				pmove.origin[2] = base[2] + (sign[z] * 1.0/8);
762 				if (PM_TestPlayerPosition (pmove.origin))
763 					return;
764 			}
765 		}
766 	}
767 	VectorCopy (base, pmove.origin);
768 //	Con_DPrintf ("NudgePosition: stuck\n");
769 }
770 
771 /*
772 ===============
773 SpectatorMove
774 ===============
775 */
SpectatorMove(void)776 void SpectatorMove (void)
777 {
778 	float	speed, drop, friction, control, newspeed, accel;
779 	float	currentspeed, addspeed, accelspeed;
780 	int			i;
781 	vec3_t		wishvel;
782 	float		fmove, smove;
783 	vec3_t		wishdir;
784 	float		wishspeed;
785 #ifndef SERVERONLY
786 	extern float	server_version;	// version of server we connected to
787 #endif
788 
789 	// friction
790 
791 	speed = Length (pmove.velocity);
792 	if (speed < 1)
793 	{
794 		VectorCopy (vec3_origin, pmove.velocity)
795 	}
796 	else
797 	{
798 		drop = 0;
799 
800 		friction = movevars.friction*1.5;	// extra friction
801 		control = speed < movevars.stopspeed ? movevars.stopspeed : speed;
802 		drop += control*friction*frametime;
803 
804 		// scale the velocity
805 		newspeed = speed - drop;
806 		if (newspeed < 0)
807 			newspeed = 0;
808 		newspeed /= speed;
809 
810 		VectorScale (pmove.velocity, newspeed, pmove.velocity);
811 	}
812 
813 	// accelerate
814 	fmove = pmove.cmd.forwardmove;
815 	smove = pmove.cmd.sidemove;
816 
817 	VectorNormalize (forward);
818 	VectorNormalize (right);
819 
820 	for (i=0 ; i<3 ; i++)
821 		wishvel[i] = forward[i]*fmove + right[i]*smove;
822 	wishvel[2] += pmove.cmd.upmove;
823 
824 	VectorCopy (wishvel, wishdir);
825 	wishspeed = VectorNormalize(wishdir);
826 
827 	//
828 	// clamp to server defined max speed
829 	//
830 	if (wishspeed > movevars.spectatormaxspeed)
831 	{
832 		VectorScale (wishvel, movevars.spectatormaxspeed/wishspeed, wishvel);
833 		wishspeed = movevars.spectatormaxspeed;
834 	}
835 
836 	currentspeed = DotProduct(pmove.velocity, wishdir);
837 	addspeed = wishspeed - currentspeed;
838 	if (addspeed <= 0)
839 		return;
840 	accelspeed = movevars.accelerate*frametime*wishspeed;
841 	if (accelspeed > addspeed)
842 		accelspeed = addspeed;
843 
844 	for (i=0 ; i<3 ; i++)
845 		pmove.velocity[i] += accelspeed*wishdir[i];
846 
847 
848 	// move
849 	VectorMA (pmove.origin, frametime, pmove.velocity, pmove.origin);
850 }
851 
852 /*
853 =============
854 PlayerMove
855 
856 Returns with origin, angles, and velocity modified in place.
857 
858 Numtouch and touchindex[] will be set if any of the physents
859 were contacted during the move.
860 =============
861 */
PlayerMove(void)862 void PlayerMove (void)
863 {
864 	frametime = pmove.cmd.msec * 0.001;
865 	pmove.numtouch = 0;
866 
867 	AngleVectors (pmove.angles, forward, right, up);
868 
869 	if (pmove.spectator)
870 	{
871 		SpectatorMove ();
872 		return;
873 	}
874 
875 	NudgePosition ();
876 
877 	// take angles directly from command
878 	VectorCopy (pmove.cmd.angles, pmove.angles);
879 
880 	// set onground, watertype, and waterlevel
881 	PM_CatagorizePosition ();
882 
883 	if (waterlevel == 2)
884 		CheckWaterJump ();
885 
886 	if (pmove.velocity[2] < 0)
887 		pmove.waterjumptime = 0;
888 
889 	if (pmove.cmd.buttons & BUTTON_JUMP)
890 		JumpButton ();
891 	else
892 		pmove.oldbuttons &= ~BUTTON_JUMP;
893 
894 	PM_Friction ();
895 
896 	if (waterlevel >= 2)
897 		PM_WaterMove ();
898 	else
899 		PM_AirMove ();
900 
901 	// set onground, watertype, and waterlevel for final spot
902 	PM_CatagorizePosition ();
903 }
904 
905