1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20
21#include "quakedef.h"
22
23
24movevars_t		movevars;
25
26playermove_t	pmove;
27
28int			onground;
29int			waterlevel;
30int			watertype;
31
32float		frametime;
33
34vec3_t		forward, right, up;
35
36vec3_t	player_mins = {-16, -16, -24};
37vec3_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
49void PM_InitBoxHull (void);
50
51void Pmove_Init (void)
52{
53	PM_InitBoxHull ();
54}
55
56#define	STEPSIZE	18
57
58
59#define	BUTTON_JUMP	2
60
61
62/*
63==================
64PM_ClipVelocity
65
66Slide off of the impacting object
67returns the blocked flags (1 = floor, 2 = step / wall)
68==================
69*/
70#define	STOP_EPSILON	0.1
71
72int 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============
100PM_FlyMove
101
102The basic solid body movement clip that slides along multiple planes
103============
104*/
105#define	MAX_CLIP_PLANES	5
106
107int 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=============
229PM_GroundMove
230
231Player is on ground, with no upwards velocity
232=============
233*/
234void 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	{
305usedown:
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==================
319PM_Friction
320
321Handles both ground friction and water friction
322==================
323*/
324void 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==============
387PM_Accelerate
388==============
389*/
390void 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
412void 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===================
440PM_WaterMove
441
442===================
443*/
444void 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===================
500PM_AirMove
501
502===================
503*/
504void 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=============
571PM_CatagorizePosition
572=============
573*/
574void 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=============
642JumpButton
643=============
644*/
645void 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=============
688CheckWaterJump
689=============
690*/
691void 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=================
728NudgePosition
729
730If pmove.origin is in a solid position,
731try nudging slightly on all axis to
732allow for the cut precision of the net coordinates
733=================
734*/
735void 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===============
773SpectatorMove
774===============
775*/
776void 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=============
854PlayerMove
855
856Returns with origin, angles, and velocity modified in place.
857
858Numtouch and touchindex[] will be set if any of the physents
859were contacted during the move.
860=============
861*/
862void 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