1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 // bg_pmove.c -- both games player movement code
25 // takes a playerstate and a usercmd as input and returns a modifed playerstate
26 
27 #include "qcommon/q_shared.h"
28 #include "bg_public.h"
29 #include "bg_local.h"
30 #include "ghoul2/G2.h"
31 
32 #ifdef _GAME
33 	#include "g_local.h"
34 #elif _CGAME
35 	#include "cgame/cg_local.h"
36 #elif UI_BUILD
37 	#include "ui/ui_local.h"
38 #endif
39 
40 #define MAX_WEAPON_CHARGE_TIME 5000
41 
42 #ifdef _GAME
43 	extern void G_CheapWeaponFire(int entNum, int ev);
44 	extern qboolean TryGrapple(gentity_t *ent); //g_cmds.c
45 #endif // _GAME
46 
47 extern qboolean BG_FullBodyTauntAnim( int anim );
48 extern float PM_WalkableGroundDistance(void);
49 extern qboolean PM_GroundSlideOkay( float zNormal );
50 extern saberInfo_t *BG_MySaber( int clientNum, int saberNum );
51 
52 pmove_t		*pm;
53 pml_t		pml;
54 
55 bgEntity_t *pm_entSelf = NULL;
56 bgEntity_t *pm_entVeh = NULL;
57 
58 qboolean gPMDoSlowFall = qfalse;
59 
60 qboolean pm_cancelOutZoom = qfalse;
61 
62 // movement parameters
63 float	pm_stopspeed = 100.0f;
64 float	pm_duckScale = 0.50f;
65 float	pm_swimScale = 0.50f;
66 float	pm_wadeScale = 0.70f;
67 
68 float	pm_vehicleaccelerate = 36.0f;
69 float	pm_accelerate = 10.0f;
70 float	pm_airaccelerate = 1.0f;
71 float	pm_wateraccelerate = 4.0f;
72 float	pm_flyaccelerate = 8.0f;
73 
74 float	pm_friction = 6.0f;
75 float	pm_waterfriction = 1.0f;
76 float	pm_flightfriction = 3.0f;
77 float	pm_spectatorfriction = 5.0f;
78 
79 int		c_pmove = 0;
80 
81 float forceSpeedLevels[4] =
82 {
83 	1, //rank 0?
84 	1.25,
85 	1.5,
86 	1.75
87 };
88 
89 int forcePowerNeeded[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] =
90 {
91 	{ //nothing should be usable at rank 0..
92 		999,//FP_HEAL,//instant
93 		999,//FP_LEVITATION,//hold/duration
94 		999,//FP_SPEED,//duration
95 		999,//FP_PUSH,//hold/duration
96 		999,//FP_PULL,//hold/duration
97 		999,//FP_TELEPATHY,//instant
98 		999,//FP_GRIP,//hold/duration
99 		999,//FP_LIGHTNING,//hold/duration
100 		999,//FP_RAGE,//duration
101 		999,//FP_PROTECT,//duration
102 		999,//FP_ABSORB,//duration
103 		999,//FP_TEAM_HEAL,//instant
104 		999,//FP_TEAM_FORCE,//instant
105 		999,//FP_DRAIN,//hold/duration
106 		999,//FP_SEE,//duration
107 		999,//FP_SABER_OFFENSE,
108 		999,//FP_SABER_DEFENSE,
109 		999//FP_SABERTHROW,
110 		//NUM_FORCE_POWERS
111 	},
112 	{
113 		65,//FP_HEAL,//instant //was 25, but that was way too little
114 		10,//FP_LEVITATION,//hold/duration
115 		50,//FP_SPEED,//duration
116 		20,//FP_PUSH,//hold/duration
117 		20,//FP_PULL,//hold/duration
118 		20,//FP_TELEPATHY,//instant
119 		30,//FP_GRIP,//hold/duration
120 		1,//FP_LIGHTNING,//hold/duration
121 		50,//FP_RAGE,//duration
122 		50,//FP_PROTECT,//duration
123 		50,//FP_ABSORB,//duration
124 		50,//FP_TEAM_HEAL,//instant
125 		50,//FP_TEAM_FORCE,//instant
126 		20,//FP_DRAIN,//hold/duration
127 		20,//FP_SEE,//duration
128 		0,//FP_SABER_OFFENSE,
129 		2,//FP_SABER_DEFENSE,
130 		20//FP_SABERTHROW,
131 		//NUM_FORCE_POWERS
132 	},
133 	{
134 		60,//FP_HEAL,//instant
135 		10,//FP_LEVITATION,//hold/duration
136 		50,//FP_SPEED,//duration
137 		20,//FP_PUSH,//hold/duration
138 		20,//FP_PULL,//hold/duration
139 		20,//FP_TELEPATHY,//instant
140 		30,//FP_GRIP,//hold/duration
141 		1,//FP_LIGHTNING,//hold/duration
142 		50,//FP_RAGE,//duration
143 		25,//FP_PROTECT,//duration
144 		25,//FP_ABSORB,//duration
145 		33,//FP_TEAM_HEAL,//instant
146 		33,//FP_TEAM_FORCE,//instant
147 		20,//FP_DRAIN,//hold/duration
148 		20,//FP_SEE,//duration
149 		0,//FP_SABER_OFFENSE,
150 		1,//FP_SABER_DEFENSE,
151 		20//FP_SABERTHROW,
152 		//NUM_FORCE_POWERS
153 	},
154 	{
155 		50,//FP_HEAL,//instant //You get 5 points of health.. for 50 force points!
156 		10,//FP_LEVITATION,//hold/duration
157 		50,//FP_SPEED,//duration
158 		20,//FP_PUSH,//hold/duration
159 		20,//FP_PULL,//hold/duration
160 		20,//FP_TELEPATHY,//instant
161 		60,//FP_GRIP,//hold/duration
162 		1,//FP_LIGHTNING,//hold/duration
163 		50,//FP_RAGE,//duration
164 		10,//FP_PROTECT,//duration
165 		10,//FP_ABSORB,//duration
166 		25,//FP_TEAM_HEAL,//instant
167 		25,//FP_TEAM_FORCE,//instant
168 		20,//FP_DRAIN,//hold/duration
169 		20,//FP_SEE,//duration
170 		0,//FP_SABER_OFFENSE,
171 		0,//FP_SABER_DEFENSE,
172 		20//FP_SABERTHROW,
173 		//NUM_FORCE_POWERS
174 	}
175 };
176 
177 float forceJumpHeight[NUM_FORCE_POWER_LEVELS] =
178 {
179 	32,//normal jump (+stepheight+crouchdiff = 66)
180 	96,//(+stepheight+crouchdiff = 130)
181 	192,//(+stepheight+crouchdiff = 226)
182 	384//(+stepheight+crouchdiff = 418)
183 };
184 
185 float forceJumpStrength[NUM_FORCE_POWER_LEVELS] =
186 {
187 	JUMP_VELOCITY,//normal jump
188 	420,
189 	590,
190 	840
191 };
192 
193 //rww - Get a pointer to the bgEntity by the index
PM_BGEntForNum(int num)194 bgEntity_t *PM_BGEntForNum( int num )
195 {
196 	bgEntity_t *ent;
197 
198 	if (!pm)
199 	{
200 		assert(!"You cannot call PM_BGEntForNum outside of pm functions!");
201 		return NULL;
202 	}
203 
204 	if (!pm->baseEnt)
205 	{
206 		assert(!"Base entity address not set");
207 		return NULL;
208 	}
209 
210 	if (!pm->entSize)
211 	{
212 		assert(!"sizeof(ent) is 0, impossible (not set?)");
213 		return NULL;
214 	}
215 
216 	assert(num >= 0 && num < MAX_GENTITIES);
217 
218     ent = (bgEntity_t *)((byte *)pm->baseEnt + pm->entSize*(num));
219 
220 	return ent;
221 }
222 
BG_SabersOff(playerState_t * ps)223 qboolean BG_SabersOff( playerState_t *ps )
224 {
225 	if ( !ps->saberHolstered )
226 	{
227 		return qfalse;
228 	}
229 	if ( ps->fd.saberAnimLevelBase == SS_DUAL
230 		|| ps->fd.saberAnimLevelBase == SS_STAFF )
231 	{
232 		if ( ps->saberHolstered < 2 )
233 		{
234 			return qfalse;
235 		}
236 	}
237 	return qtrue;
238 }
239 
BG_KnockDownable(playerState_t * ps)240 qboolean BG_KnockDownable(playerState_t *ps)
241 {
242 	if (!ps)
243 	{ //just for safety
244 		return qfalse;
245 	}
246 
247 	if (ps->m_iVehicleNum)
248 	{ //riding a vehicle, don't knock me down
249 		return qfalse;
250 	}
251 
252 	if (ps->emplacedIndex)
253 	{ //using emplaced gun or eweb, can't be knocked down
254 		return qfalse;
255 	}
256 
257 	//ok, I guess?
258 	return qtrue;
259 }
260 
261 //hacky assumption check, assume any client non-humanoid is a rocket trooper
PM_IsRocketTrooper(void)262 static QINLINE qboolean PM_IsRocketTrooper(void)
263 {
264 	/*
265 	if (pm->ps->clientNum < MAX_CLIENTS &&
266 		pm->gametype == GT_SIEGE &&
267 		pm->nonHumanoid)
268 	{
269 		return qtrue;
270 	}
271 	*/
272 
273 	return qfalse;
274 }
275 
PM_GetSaberStance(void)276 int PM_GetSaberStance(void)
277 {
278 	int anim = BOTH_STAND2;
279 	saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
280 	saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
281 
282 	if (!pm->ps->saberEntityNum)
283 	{ //lost it
284 		return BOTH_STAND1;
285 	}
286 
287 	if ( BG_SabersOff( pm->ps ) )
288 	{
289 		return BOTH_STAND1;
290 	}
291 
292 	if ( saber1
293 		&& saber1->readyAnim != -1 )
294 	{
295 		return saber1->readyAnim;
296 	}
297 
298 	if ( saber2
299 		&& saber2->readyAnim != -1 )
300 	{
301 		return saber2->readyAnim;
302 	}
303 
304 	if ( saber1
305 		&& saber2
306 		&& !pm->ps->saberHolstered )
307 	{//dual sabers, both on
308 		return BOTH_SABERDUAL_STANCE;
309 	}
310 
311 	switch ( pm->ps->fd.saberAnimLevel )
312 	{
313 	case SS_DUAL:
314 		anim = BOTH_SABERDUAL_STANCE;
315 		break;
316 	case SS_STAFF:
317 		anim = BOTH_SABERSTAFF_STANCE;
318 		break;
319 	case SS_FAST:
320 	case SS_TAVION:
321 		anim = BOTH_SABERFAST_STANCE;
322 		break;
323 	case SS_STRONG:
324 		anim = BOTH_SABERSLOW_STANCE;
325 		break;
326 	case SS_NONE:
327 	case SS_MEDIUM:
328 	case SS_DESANN:
329 	default:
330 		anim = BOTH_STAND2;
331 		break;
332 	}
333 	return anim;
334 }
335 
PM_DoSlowFall(void)336 qboolean PM_DoSlowFall(void)
337 {
338 	if ( ( (pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && pm->ps->legsTimer > 500 )
339 	{
340 		return qtrue;
341 	}
342 
343 	return qfalse;
344 }
345 
346 //begin vehicle functions crudely ported from sp -rww
347 /*
348 ====================================================================
349 void pitch_roll_for_slope (edict_t *forwhom, vec3_t *slope, vec3_t storeAngles )
350 
351 MG
352 
353 This will adjust the pitch and roll of a monster to match
354 a given slope - if a non-'0 0 0' slope is passed, it will
355 use that value, otherwise it will use the ground underneath
356 the monster.  If it doesn't find a surface, it does nothinh\g
357 and returns.
358 ====================================================================
359 */
360 
PM_pitch_roll_for_slope(bgEntity_t * forwhom,vec3_t pass_slope,vec3_t storeAngles)361 void PM_pitch_roll_for_slope( bgEntity_t *forwhom, vec3_t pass_slope, vec3_t storeAngles )
362 {
363 	vec3_t	slope;
364 	vec3_t	nvf, ovf, ovr, startspot, endspot, new_angles = { 0, 0, 0 };
365 	float	pitch, mod, dot;
366 
367 	//if we don't have a slope, get one
368 	if( !pass_slope || VectorCompare( vec3_origin, pass_slope ) )
369 	{
370 		trace_t trace;
371 
372 		VectorCopy( pm->ps->origin, startspot );
373 		startspot[2] += pm->mins[2] + 4;
374 		VectorCopy( startspot, endspot );
375 		endspot[2] -= 300;
376 		pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, endspot, forwhom->s.number, MASK_SOLID );
377 //		if(trace_fraction>0.05&&forwhom.movetype==MOVETYPE_STEP)
378 //			forwhom.flags(-)FL_ONGROUND;
379 
380 		if ( trace.fraction >= 1.0 )
381 			return;
382 
383 		if ( VectorCompare( vec3_origin, trace.plane.normal ) )
384 			return;
385 
386 		VectorCopy( trace.plane.normal, slope );
387 	}
388 	else
389 	{
390 		VectorCopy( pass_slope, slope );
391 	}
392 
393 	if ( forwhom->s.NPC_class == CLASS_VEHICLE )
394 	{//special code for vehicles
395 		Vehicle_t *pVeh = forwhom->m_pVehicle;
396 		vec3_t tempAngles;
397 
398 		tempAngles[PITCH] = tempAngles[ROLL] = 0;
399 		tempAngles[YAW] = pVeh->m_vOrientation[YAW];
400 		AngleVectors( tempAngles, ovf, ovr, NULL );
401 	}
402 	else
403 	{
404 		AngleVectors( pm->ps->viewangles, ovf, ovr, NULL );
405 	}
406 
407 	vectoangles( slope, new_angles );
408 	pitch = new_angles[PITCH] + 90;
409 	new_angles[ROLL] = new_angles[PITCH] = 0;
410 
411 	AngleVectors( new_angles, nvf, NULL, NULL );
412 
413 	mod = DotProduct( nvf, ovr );
414 
415 	if ( mod<0 )
416 		mod = -1;
417 	else
418 		mod = 1;
419 
420 	dot = DotProduct( nvf, ovf );
421 
422 	if ( storeAngles )
423 	{
424 		storeAngles[PITCH] = dot * pitch;
425 		storeAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
426 	}
427 	else //if ( forwhom->client )
428 	{
429 		float oldmins2;
430 
431 		pm->ps->viewangles[PITCH] = dot * pitch;
432 		pm->ps->viewangles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
433 		oldmins2 = pm->mins[2];
434 		pm->mins[2] = -24 + 12 * fabs(pm->ps->viewangles[PITCH])/180.0f;
435 		//FIXME: if it gets bigger, move up
436 		if ( oldmins2 > pm->mins[2] )
437 		{//our mins is now lower, need to move up
438 			//FIXME: trace?
439 			pm->ps->origin[2] += (oldmins2 - pm->mins[2]);
440 			//forwhom->currentOrigin[2] = forwhom->client->ps.origin[2];
441 			//trap->linkentity( forwhom );
442 		}
443 	}
444 	/*
445 	else
446 	{
447 		forwhom->currentAngles[PITCH] = dot * pitch;
448 		forwhom->currentAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
449 	}
450 	*/
451 }
452 
453 #define		FLY_NONE	0
454 #define		FLY_NORMAL	1
455 #define		FLY_VEHICLE	2
456 #define		FLY_HOVER	3
457 static int pm_flying = FLY_NONE;
458 
PM_SetSpecialMoveValues(void)459 void PM_SetSpecialMoveValues (void)
460 {
461 	bgEntity_t *pEnt;
462 
463 	if (pm->ps->clientNum < MAX_CLIENTS)
464 	{ //we know that real players aren't vehs
465 		pm_flying = FLY_NONE;
466 		return;
467 	}
468 
469 	//default until we decide otherwise
470 	pm_flying = FLY_NONE;
471 
472 	pEnt = pm_entSelf;
473 
474 	if ( pEnt )
475 	{
476 		if ( (pm->ps->eFlags2&EF2_FLYING) )// pm->gent->client->moveType == MT_FLYSWIM )
477 		{
478 			pm_flying = FLY_NORMAL;
479 		}
480 		else if ( pEnt->s.NPC_class == CLASS_VEHICLE )
481 		{
482 			if ( pEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
483 			{
484 				pm_flying = FLY_VEHICLE;
485 			}
486 			else if ( pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
487 			{
488 				pm_flying = FLY_HOVER;
489 			}
490 		}
491 	}
492 }
493 
PM_SetVehicleAngles(vec3_t normal)494 static void PM_SetVehicleAngles( vec3_t normal )
495 {
496 	bgEntity_t *pEnt = pm_entSelf;
497 	Vehicle_t *pVeh;
498 	vec3_t	vAngles;
499 	float vehicleBankingSpeed;
500 	float pitchBias;
501 	int i;
502 
503 	if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
504 	{
505 		return;
506 	}
507 
508 	pVeh = pEnt->m_pVehicle;
509 
510 	//float	curVehicleBankingSpeed;
511 	vehicleBankingSpeed = (pVeh->m_pVehicleInfo->bankingSpeed*32.0f)*pml.frametime;//0.25f
512 
513 	if ( vehicleBankingSpeed <= 0
514 		|| ( pVeh->m_pVehicleInfo->pitchLimit == 0 && pVeh->m_pVehicleInfo->rollLimit == 0 ) )
515 	{//don't bother, this vehicle doesn't bank
516 		return;
517 	}
518 	//FIXME: do 3 traces to define a plane and use that... smoothes it out some, too...
519 	//pitch_roll_for_slope( pm->gent, normal, vAngles );
520 	//FIXME: maybe have some pitch control in water and/or air?
521 
522 	if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
523 	{
524 		pitchBias = 0.0f;
525 	}
526 	else
527 	{
528 		//FIXME: gravity does not matter in SPACE!!!
529 		//center of gravity affects pitch in air/water (FIXME: what about roll?)
530 		pitchBias = 90.0f*pVeh->m_pVehicleInfo->centerOfGravity[0];//if centerOfGravity is all the way back (-1.0f), vehicle pitches up 90 degrees when in air
531 	}
532 
533 	VectorClear( vAngles );
534 	if ( pm->waterlevel > 0 )
535 	{//in water
536 		//view pitch has some influence when in water
537 		//FIXME: take center of gravity into account?
538 		vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5);
539 	}
540 	else if ( normal )
541 	{//have a valid surface below me
542 		PM_pitch_roll_for_slope( pEnt, normal, vAngles );
543 		if ( (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
544 		{//on water
545 			//view pitch has some influence when on a fluid surface
546 			//FIXME: take center of gravity into account
547 			vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.5f + (pitchBias*0.5f);
548 		}
549 	}
550 	else
551 	{//in air, let pitch match view...?
552 		//FIXME: take center of gravity into account
553 		vAngles[PITCH] = pm->ps->viewangles[PITCH]*0.5f + pitchBias;
554 		//don't bank so fast when in the air
555 		vehicleBankingSpeed *= (0.125f*pml.frametime);
556 	}
557 	//NOTE: if angles are flat and we're moving through air (not on ground),
558 	//		then pitch/bank?
559 	if ( pVeh->m_pVehicleInfo->rollLimit > 0 )
560 	{
561 		//roll when banking
562 		vec3_t	velocity;
563 		float	speed;
564 		VectorCopy( pm->ps->velocity, velocity );
565 		velocity[2] = 0.0f;
566 		speed = VectorNormalize( velocity );
567 		if ( speed > 32.0f || speed < -32.0f )
568 		{
569 			vec3_t	rt, tempVAngles;
570 			float	side;
571 			float	dp;
572 
573 			// Magic number fun!  Speed is used for banking, so modulate the speed by a sine wave
574 			//FIXME: this banks too early
575 			speed *= sin( (150 + pml.frametime) * 0.003 );
576 
577 			// Clamp to prevent harsh rolling
578 			if ( speed > 60 )
579 				speed = 60;
580 
581 			VectorCopy( pVeh->m_vOrientation, tempVAngles );
582 			tempVAngles[ROLL] = 0;
583 			AngleVectors( tempVAngles, NULL, rt, NULL );
584 			dp = DotProduct( velocity, rt );
585 			side = speed * dp;
586 			vAngles[ROLL] -= side;
587 		}
588 	}
589 
590 	//cap
591 	if ( pVeh->m_pVehicleInfo->pitchLimit != -1 )
592 	{
593 		if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
594 		{
595 			vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
596 		}
597 		else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit )
598 		{
599 			vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
600 		}
601 	}
602 
603 	if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
604 	{
605 		vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
606 	}
607 	else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit )
608 	{
609 		vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
610 	}
611 
612 	//do it
613 	for ( i = 0; i < 3; i++ )
614 	{
615 		if ( i == YAW )
616 		{//yawing done elsewhere
617 			continue;
618 		}
619 		//bank faster the higher the difference is
620 		/*
621 		else if ( i == PITCH )
622 		{
623 			curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f);
624 		}
625 		else if ( i == ROLL )
626 		{
627 			curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f);
628 		}
629 
630 		if ( curVehicleBankingSpeed )
631 		*/
632 		{
633 			if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed )
634 			{
635 				pVeh->m_vOrientation[i] -= vehicleBankingSpeed;
636 			}
637 			else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed )
638 			{
639 				pVeh->m_vOrientation[i] += vehicleBankingSpeed;
640 			}
641 			else
642 			{
643 				pVeh->m_vOrientation[i] = vAngles[i];
644 			}
645 		}
646 	}
647 }
648 
BG_VehicleTurnRateForSpeed(Vehicle_t * pVeh,float speed,float * mPitchOverride,float * mYawOverride)649 void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride )
650 {
651 	if ( pVeh && pVeh->m_pVehicleInfo )
652 	{
653 		float speedFrac = 1.0f;
654 		if ( pVeh->m_pVehicleInfo->speedDependantTurning )
655 		{
656 			if ( pVeh->m_LandTrace.fraction >= 1.0f
657 				|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
658 			{
659 				speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f));
660 				if ( speedFrac < 0.25f )
661 				{
662 					speedFrac = 0.25f;
663 				}
664 				else if ( speedFrac > 1.0f )
665 				{
666 					speedFrac = 1.0f;
667 				}
668 			}
669 		}
670 		if ( pVeh->m_pVehicleInfo->mousePitch )
671 		{
672 			*mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac;
673 		}
674 		if ( pVeh->m_pVehicleInfo->mouseYaw )
675 		{
676 			*mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac;
677 		}
678 	}
679 }
680 
681 
682 // Following couple things don't belong in the DLL namespace!
683 #ifdef _GAME
684 	#if !defined(MACOS_X) && !defined(__GCC__) && !defined(__GNUC__)
685 		typedef struct gentity_s gentity_t;
686 	#endif
687 	gentity_t *G_PlayEffectID( const int fxID, vec3_t org, vec3_t ang );
688 #endif
689 
690 
691 static void PM_GroundTraceMissed( void );
PM_HoverTrace(void)692 void PM_HoverTrace( void )
693 {
694 	Vehicle_t *pVeh;
695 	float hoverHeight;
696 	vec3_t		point, vAng, fxAxis[3];
697 	trace_t		*trace;
698 	float relativeWaterLevel;
699 
700 	bgEntity_t *pEnt = pm_entSelf;
701 	if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
702 	{
703 		return;
704 	}
705 
706 	pVeh = pEnt->m_pVehicle;
707 	hoverHeight = pVeh->m_pVehicleInfo->hoverHeight;
708 	trace = &pml.groundTrace;
709 
710 	pml.groundPlane = qfalse;
711 
712 	//relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2]));
713 	relativeWaterLevel = pm->waterlevel; //I.. guess this works
714 	if ( pm->waterlevel && relativeWaterLevel >= 0 )
715 	{//in water
716 		if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f )
717 		{//sink like a rock
718 		}
719 		else
720 		{//rise up
721 			float floatHeight = (pVeh->m_pVehicleInfo->bouyancy * ((pm->maxs[2]-pm->mins[2])*0.5f)) - (hoverHeight*0.5f);//1.0f should make you float half-in, half-out of water
722 			if ( relativeWaterLevel > floatHeight )
723 			{//too low, should rise up
724 				pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier;
725 			}
726 		}
727 		//if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] )
728 		if (pm->waterlevel <= 1)
729 		{//part of us is sticking out of water
730 			if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
731 			{//moving at a decent speed
732 				if ( Q_irand( pml.frametime, 100 ) >= 50 )
733 				{//splash
734 					vec3_t wakeOrg;
735 
736 					vAng[PITCH] = vAng[ROLL] = 0;
737 					vAng[YAW] = pVeh->m_vOrientation[YAW];
738 					AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
739 					VectorCopy( pm->ps->origin, wakeOrg );
740 					//wakeOrg[2] = pm->ps->waterheight;
741 					if (pm->waterlevel >= 2)
742 					{
743 						wakeOrg[2] = pm->ps->origin[2]+16;
744 					}
745 					else
746 					{
747 						wakeOrg[2] = pm->ps->origin[2];
748 					}
749 					#ifdef _GAME //yeah, this is kind of crappy and makes no use of prediction whatsoever
750 						if ( pVeh->m_pVehicleInfo->iWakeFX )
751 						{
752 							//G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis[0] );
753 							//tempent use bad!
754 							G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pVeh->m_pVehicleInfo->iWakeFX);
755 						}
756 					#endif
757 				}
758 			}
759 		}
760 	}
761 	else
762 	{
763 		int traceContents;
764 		float minNormal = pVeh->m_pVehicleInfo->maxSlope;
765 
766 		point[0] = pm->ps->origin[0];
767 		point[1] = pm->ps->origin[1];
768 		point[2] = pm->ps->origin[2] - hoverHeight;
769 
770 		//FIXME: check for water, too?  If over water, go slower and make wave effect
771 		//		If *in* water, go really slow and use bouyancy stat to determine how far below surface to float
772 
773 		//NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground.
774 		//		if it's 1.0f, you sink halfway into water.  If it's 0, you sink...
775 		traceContents = pm->tracemask;
776 		if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f )
777 		{//sit on water
778 			traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
779 		}
780 		pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents );
781 		if (trace->plane.normal[0] > 0.5f || trace->plane.normal[0] < -0.5f ||
782 			trace->plane.normal[1] > 0.5f || trace->plane.normal[1] < -0.5f)
783 		{ //steep slanted hill, don't go up it.
784 			float d = fabs(trace->plane.normal[0]);
785 			float e = fabs(trace->plane.normal[1]);
786 			if (e > d)
787 			{
788 				d = e;
789 			}
790 			pm->ps->velocity[2] = -300.0f*d;
791 		}
792 		else if ( trace->plane.normal[2] >= minNormal )
793 		{//not a steep slope, so push us up
794 			if ( trace->fraction < 1.0f )
795 			{//push up off ground
796 				float hoverForce = pVeh->m_pVehicleInfo->hoverStrength;
797 				if ( trace->fraction > 0.5f )
798 				{
799 					pm->ps->velocity[2] += (1.0f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier;
800 				}
801 				else
802 				{
803 					pm->ps->velocity[2] += (0.5f-(trace->fraction*trace->fraction))*hoverForce*2.0f*pVeh->m_fTimeModifier;
804 				}
805 				if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
806 				{//hovering on water, make a spash if moving
807 					if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
808 					{//moving at a decent speed
809 						if ( Q_irand( pml.frametime, 100 ) >= 50 )
810 						{//splash
811 							vAng[PITCH] = vAng[ROLL] = 0;
812 							vAng[YAW] = pVeh->m_vOrientation[YAW];
813 							AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
814 #ifdef _GAME
815 							if ( pVeh->m_pVehicleInfo->iWakeFX )
816 							{
817 								G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis[0] );
818 							}
819 #endif
820 						}
821 					}
822 				}
823 				pml.groundPlane = qtrue;
824 			}
825 		}
826 	}
827 	if ( pml.groundPlane )
828 	{
829 		PM_SetVehicleAngles( pml.groundTrace.plane.normal );
830 		// We're on the ground.
831 		pVeh->m_ulFlags &= ~VEH_FLYING;
832 
833 		pVeh->m_vAngularVelocity = 0.0f;
834 	}
835 	else
836 	{
837 		PM_SetVehicleAngles( NULL );
838 		// We're flying in the air.
839 		pVeh->m_ulFlags |= VEH_FLYING;
840 		//groundTrace
841 
842 		if (pVeh->m_vAngularVelocity==0.0f)
843 		{
844 			pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
845 			if (pVeh->m_vAngularVelocity<-15.0f)
846 			{
847 				pVeh->m_vAngularVelocity = -15.0f;
848 			}
849 			if (pVeh->m_vAngularVelocity> 15.0f)
850 			{
851 				pVeh->m_vAngularVelocity =  15.0f;
852 			}
853 		}
854 		//pVeh->m_vAngularVelocity *= 0.95f;		// Angular Velocity Decays Over Time
855 		if (pVeh->m_vAngularVelocity > 0.0f)
856 		{
857 			pVeh->m_vAngularVelocity -= pml.frametime;
858 			if (pVeh->m_vAngularVelocity < 0.0f)
859 			{
860 				pVeh->m_vAngularVelocity = 0.0f;
861 			}
862 		}
863 		else if (pVeh->m_vAngularVelocity < 0.0f)
864 		{
865 			pVeh->m_vAngularVelocity += pml.frametime;
866 			if (pVeh->m_vAngularVelocity > 0.0f)
867 			{
868 				pVeh->m_vAngularVelocity = 0.0f;
869 			}
870 		}
871 	}
872 	PM_GroundTraceMissed();
873 }
874 //end vehicle functions crudely ported from sp -rww
875 
876 /*
877 ===============
878 PM_AddEvent
879 
880 ===============
881 */
PM_AddEvent(int newEvent)882 void PM_AddEvent( int newEvent ) {
883 	BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
884 }
885 
PM_AddEventWithParm(int newEvent,int parm)886 void PM_AddEventWithParm( int newEvent, int parm )
887 {
888 	BG_AddPredictableEventToPlayerstate( newEvent, parm, pm->ps );
889 }
890 
891 /*
892 ===============
893 PM_AddTouchEnt
894 ===============
895 */
PM_AddTouchEnt(int entityNum)896 void PM_AddTouchEnt( int entityNum ) {
897 	int		i;
898 
899 	if ( entityNum == ENTITYNUM_WORLD ) {
900 		return;
901 	}
902 	if ( pm->numtouch >= MAXTOUCH ) {
903 		return;
904 	}
905 
906 	// see if it is already added
907 	for ( i = 0 ; i < pm->numtouch ; i++ ) {
908 		if ( pm->touchents[ i ] == entityNum ) {
909 			return;
910 		}
911 	}
912 
913 	// add it
914 	pm->touchents[pm->numtouch++] = entityNum;
915 }
916 
917 
918 /*
919 ==================
920 PM_ClipVelocity
921 
922 Slide off of the impacting surface
923 ==================
924 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)925 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
926 	float	backoff;
927 	float	change;
928 	float	oldInZ;
929 	int		i;
930 
931 	if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
932 	{//no sliding!
933 		VectorCopy( in, out );
934 		return;
935 	}
936 	oldInZ = in[2];
937 
938 	backoff = DotProduct (in, normal);
939 
940 	if ( backoff < 0 ) {
941 		backoff *= overbounce;
942 	} else {
943 		backoff /= overbounce;
944 	}
945 
946 	for ( i=0 ; i<3 ; i++ ) {
947 		change = normal[i]*backoff;
948 		out[i] = in[i] - change;
949 	}
950 	if ( pm->stepSlideFix )
951 	{
952 		if ( pm->ps->clientNum < MAX_CLIENTS//normal player
953 			&& pm->ps->groundEntityNum != ENTITYNUM_NONE//on the ground
954 			&& normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope
955 		{//if walking on the ground, don't slide up slopes that are too steep to walk on
956 			out[2] = oldInZ;
957 		}
958 	}
959 }
960 
961 
962 /*
963 ==================
964 PM_Friction
965 
966 Handles both ground friction and water friction
967 ==================
968 */
PM_Friction(void)969 static void PM_Friction( void ) {
970 	vec3_t	vec;
971 	float	*vel;
972 	float	speed, newspeed, control;
973 	float	drop;
974 	bgEntity_t *pEnt = NULL;
975 
976 	vel = pm->ps->velocity;
977 
978 	VectorCopy( vel, vec );
979 	if ( pml.walking ) {
980 		vec[2] = 0;	// ignore slope movement
981 	}
982 
983 	speed = VectorLength(vec);
984 	if (speed < 1) {
985 		vel[0] = 0;
986 		vel[1] = 0;		// allow sinking underwater
987 		if (pm->ps->pm_type == PM_SPECTATOR)
988 		{
989 			vel[2] = 0;
990 		}
991 		// FIXME: still have z friction underwater?
992 		return;
993 	}
994 
995 	drop = 0;
996 
997 	if (pm->ps->clientNum >= MAX_CLIENTS)
998 	{
999 		pEnt = pm_entSelf;
1000 	}
1001 
1002 	// apply ground friction, even if on ladder
1003 	if (pm_flying != FLY_VEHICLE &&
1004 		pEnt &&
1005 		pEnt->s.NPC_class == CLASS_VEHICLE &&
1006 		pEnt->m_pVehicle &&
1007 		pEnt->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL &&
1008 		pEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
1009 		pEnt->m_pVehicle->m_pVehicleInfo->friction )
1010 	{
1011 		float friction = pEnt->m_pVehicle->m_pVehicleInfo->friction;
1012 		if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) /*&& !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)*/ )
1013 		{
1014 			control = speed < pm_stopspeed ? pm_stopspeed : speed;
1015 			drop += control*friction*pml.frametime;
1016 			/*
1017 			if ( Flying == FLY_HOVER )
1018 			{
1019 				if ( pm->cmd.rightmove )
1020 				{//if turning, increase friction
1021 					control *= 2.0f;
1022 				}
1023 				if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
1024 				{//on the ground
1025 					drop += control*friction*pml.frametime;
1026 				}
1027 				else if ( pml.groundPlane )
1028 				{//on a slope
1029 					drop += control*friction*2.0f*pml.frametime;
1030 				}
1031 				else
1032 				{//in air
1033 					drop += control*2.0f*friction*pml.frametime;
1034 				}
1035 			}
1036 			*/
1037 		}
1038 	}
1039 	else if ( pm_flying != FLY_NORMAL && pm_flying != FLY_VEHICLE )
1040 	{
1041 		// apply ground friction
1042 		if ( pm->waterlevel <= 1 ) {
1043 			if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
1044 				// if getting knocked back, no friction
1045 				if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
1046 					control = speed < pm_stopspeed ? pm_stopspeed : speed;
1047 					drop += control*pm_friction*pml.frametime;
1048 				}
1049 			}
1050 		}
1051 	}
1052 
1053 	if ( pm_flying == FLY_VEHICLE )
1054 	{
1055 		if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) )
1056 		{
1057 			control = speed;// < pm_stopspeed ? pm_stopspeed : speed;
1058 			drop += control*pm_friction*pml.frametime;
1059 		}
1060 	}
1061 
1062 	// apply water friction even if just wading
1063 	if ( pm->waterlevel ) {
1064 		drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
1065 	}
1066 	// If on a client then there is no friction
1067 	else if ( pm->ps->groundEntityNum < MAX_CLIENTS )
1068 	{
1069 		drop = 0;
1070 	}
1071 
1072 	if ( pm->ps->pm_type == PM_SPECTATOR || pm->ps->pm_type == PM_FLOAT )
1073 	{
1074 		if (pm->ps->pm_type == PM_FLOAT)
1075 		{ //almost no friction while floating
1076 			drop += speed*0.1*pml.frametime;
1077 		}
1078 		else
1079 		{
1080 			drop += speed*pm_spectatorfriction*pml.frametime;
1081 		}
1082 	}
1083 
1084 	// scale the velocity
1085 	newspeed = speed - drop;
1086 	if (newspeed < 0) {
1087 		newspeed = 0;
1088 	}
1089 	newspeed /= speed;
1090 
1091 	VectorScale( vel, newspeed, vel );
1092 }
1093 
1094 
1095 /*
1096 ==============
1097 PM_Accelerate
1098 
1099 Handles user intended acceleration
1100 ==============
1101 */
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)1102 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
1103 {
1104 	if (pm->gametype != GT_SIEGE
1105 		|| pm->ps->m_iVehicleNum
1106 		|| pm->ps->clientNum >= MAX_CLIENTS
1107 		|| pm->ps->pm_type != PM_NORMAL)
1108 	{ //standard method, allows "bunnyhopping" and whatnot
1109 		int			i;
1110 		float		addspeed, accelspeed, currentspeed;
1111 
1112 		currentspeed = DotProduct (pm->ps->velocity, wishdir);
1113 		addspeed = wishspeed - currentspeed;
1114 		if (addspeed <= 0 && pm->ps->clientNum < MAX_CLIENTS) {
1115 			return;
1116 		}
1117 
1118 		if (addspeed < 0)
1119 		{
1120 			accelspeed = (-accel)*pml.frametime*wishspeed;
1121 			if (accelspeed < addspeed) {
1122 				accelspeed = addspeed;
1123 			}
1124 		}
1125 		else
1126 		{
1127 			accelspeed = accel*pml.frametime*wishspeed;
1128 			if (accelspeed > addspeed) {
1129 				accelspeed = addspeed;
1130 			}
1131 		}
1132 
1133 		for (i=0 ; i<3 ; i++) {
1134 			pm->ps->velocity[i] += accelspeed*wishdir[i];
1135 		}
1136 	}
1137 	else
1138 	{ //use the proper way for siege
1139 		vec3_t		wishVelocity;
1140 		vec3_t		pushDir;
1141 		float		pushLen;
1142 		float		canPush;
1143 
1144 		VectorScale( wishdir, wishspeed, wishVelocity );
1145 		VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
1146 		pushLen = VectorNormalize( pushDir );
1147 
1148 		canPush = accel*pml.frametime*wishspeed;
1149 		if (canPush > pushLen) {
1150 			canPush = pushLen;
1151 		}
1152 
1153 		VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
1154 	}
1155 }
1156 
1157 
1158 
1159 /*
1160 ============
1161 PM_CmdScale
1162 
1163 Returns the scale factor to apply to cmd movements
1164 This allows the clients to use axial -127 to 127 values for all directions
1165 without getting a sqrt(2) distortion in speed.
1166 ============
1167 */
PM_CmdScale(usercmd_t * cmd)1168 static float PM_CmdScale( usercmd_t *cmd ) {
1169 	int		max;
1170 	float	total;
1171 	float	scale;
1172 	int		umove = 0; //cmd->upmove;
1173 			//don't factor upmove into scaling speed
1174 
1175 	max = abs( cmd->forwardmove );
1176 	if ( abs( cmd->rightmove ) > max ) {
1177 		max = abs( cmd->rightmove );
1178 	}
1179 	if ( abs( umove ) > max ) {
1180 		max = abs( umove );
1181 	}
1182 	if ( !max ) {
1183 		return 0;
1184 	}
1185 
1186 	total = sqrt( (float)(cmd->forwardmove * cmd->forwardmove
1187 		+ cmd->rightmove * cmd->rightmove + umove * umove) );
1188 	scale = (float)pm->ps->speed * max / ( 127.0 * total );
1189 
1190 	return scale;
1191 }
1192 
1193 
1194 /*
1195 ================
1196 PM_SetMovementDir
1197 
1198 Determine the rotation of the legs reletive
1199 to the facing dir
1200 ================
1201 */
PM_SetMovementDir(void)1202 static void PM_SetMovementDir( void ) {
1203 	if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
1204 		if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
1205 			pm->ps->movementDir = 0;
1206 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
1207 			pm->ps->movementDir = 1;
1208 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
1209 			pm->ps->movementDir = 2;
1210 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
1211 			pm->ps->movementDir = 3;
1212 		} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
1213 			pm->ps->movementDir = 4;
1214 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
1215 			pm->ps->movementDir = 5;
1216 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
1217 			pm->ps->movementDir = 6;
1218 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
1219 			pm->ps->movementDir = 7;
1220 		}
1221 	} else {
1222 		// if they aren't actively going directly sideways,
1223 		// change the animation to the diagonal so they
1224 		// don't stop too crooked
1225 		if ( pm->ps->movementDir == 2 ) {
1226 			pm->ps->movementDir = 1;
1227 		} else if ( pm->ps->movementDir == 6 ) {
1228 			pm->ps->movementDir = 7;
1229 		}
1230 	}
1231 }
1232 
1233 #define METROID_JUMP 1
1234 
PM_ForceJumpingUp(void)1235 qboolean PM_ForceJumpingUp(void)
1236 {
1237 	if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) && pm->ps->fd.forceJumpCharge )
1238 	{//already jumped and let go
1239 		return qfalse;
1240 	}
1241 
1242 	if ( BG_InSpecialJump( pm->ps->legsAnim ) )
1243 	{
1244 		return qfalse;
1245 	}
1246 
1247 	if (BG_SaberInSpecial(pm->ps->saberMove))
1248 	{
1249 		return qfalse;
1250 	}
1251 
1252 	if (BG_SaberInSpecialAttack(pm->ps->legsAnim))
1253 	{
1254 		return qfalse;
1255 	}
1256 
1257 	if (BG_HasYsalamiri(pm->gametype, pm->ps))
1258 	{
1259 		return qfalse;
1260 	}
1261 
1262 	if (!BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
1263 	{
1264 		return qfalse;
1265 	}
1266 
1267 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && //in air
1268 		(pm->ps->pm_flags & PMF_JUMP_HELD) && //jumped
1269 		pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && //force-jump capable
1270 		pm->ps->velocity[2] > 0 )//going up
1271 	{
1272 		return qtrue;
1273 	}
1274 	return qfalse;
1275 }
1276 
PM_JumpForDir(void)1277 static void PM_JumpForDir( void )
1278 {
1279 	int anim = BOTH_JUMP1;
1280 	if ( pm->cmd.forwardmove > 0 )
1281 	{
1282 		anim = BOTH_JUMP1;
1283 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1284 	}
1285 	else if ( pm->cmd.forwardmove < 0 )
1286 	{
1287 		anim = BOTH_JUMPBACK1;
1288 		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1289 	}
1290 	else if ( pm->cmd.rightmove > 0 )
1291 	{
1292 		anim = BOTH_JUMPRIGHT1;
1293 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1294 	}
1295 	else if ( pm->cmd.rightmove < 0 )
1296 	{
1297 		anim = BOTH_JUMPLEFT1;
1298 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1299 	}
1300 	else
1301 	{
1302 		anim = BOTH_JUMP1;
1303 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1304 	}
1305 	if(!BG_InDeathAnim(pm->ps->legsAnim))
1306 	{
1307 		PM_SetAnim(SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE);
1308 	}
1309 }
1310 
PM_SetPMViewAngle(playerState_t * ps,vec3_t angle,usercmd_t * ucmd)1311 void PM_SetPMViewAngle(playerState_t *ps, vec3_t angle, usercmd_t *ucmd)
1312 {
1313 	int			i;
1314 
1315 	for (i=0 ; i<3 ; i++)
1316 	{ // set the delta angle
1317 		int		cmdAngle;
1318 
1319 		cmdAngle = ANGLE2SHORT(angle[i]);
1320 		ps->delta_angles[i] = cmdAngle - ucmd->angles[i];
1321 	}
1322 	VectorCopy (angle, ps->viewangles);
1323 }
1324 
PM_AdjustAngleForWallRun(playerState_t * ps,usercmd_t * ucmd,qboolean doMove)1325 qboolean PM_AdjustAngleForWallRun( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
1326 {
1327 	if (( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && ps->legsTimer > 500 )
1328 	{//wall-running and not at end of anim
1329 		//stick to wall, if there is one
1330 		vec3_t	fwd, rt, traceTo, mins, maxs, fwdAngles;
1331 		trace_t	trace;
1332 		float	dist, yawAdjust;
1333 
1334 		VectorSet(mins, -15, -15, 0);
1335 		VectorSet(maxs, 15, 15, 24);
1336 		VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
1337 
1338 		AngleVectors( fwdAngles, fwd, rt, NULL );
1339 		if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
1340 		{
1341 			dist = 128;
1342 			yawAdjust = -90;
1343 		}
1344 		else
1345 		{
1346 			dist = -128;
1347 			yawAdjust = 90;
1348 		}
1349 		VectorMA( ps->origin, dist, rt, traceTo );
1350 
1351 		pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1352 
1353 		if ( trace.fraction < 1.0f
1354 			&& (trace.plane.normal[2] >= 0.0f && trace.plane.normal[2] <= 0.4f) )//&& ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
1355 		{
1356 			trace_t	trace2;
1357 			vec3_t traceTo2;
1358 			vec3_t	wallRunFwd, wallRunAngles;
1359 
1360 			VectorClear( wallRunAngles );
1361 			wallRunAngles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
1362 			AngleVectors( wallRunAngles, wallRunFwd, NULL, NULL );
1363 
1364 			VectorMA( pm->ps->origin, 32, wallRunFwd, traceTo2 );
1365 			pm->trace( &trace2, pm->ps->origin, mins, maxs, traceTo2, pm->ps->clientNum, MASK_PLAYERSOLID );
1366 			if ( trace2.fraction < 1.0f && DotProduct( trace2.plane.normal, wallRunFwd ) <= -0.999f )
1367 			{//wall we can't run on in front of us
1368 				trace.fraction = 1.0f;//just a way to get it to kick us off the wall below
1369 			}
1370 		}
1371 
1372 		if ( trace.fraction < 1.0f
1373 			&& (trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2] <= 0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
1374 		{//still a wall there
1375 			if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
1376 			{
1377 				ucmd->rightmove = 127;
1378 			}
1379 			else
1380 			{
1381 				ucmd->rightmove = -127;
1382 			}
1383 			if ( ucmd->upmove < 0 )
1384 			{
1385 				ucmd->upmove = 0;
1386 			}
1387 			//make me face perpendicular to the wall
1388 			ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
1389 
1390 			PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
1391 
1392 			ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
1393 			if ( doMove )
1394 			{
1395 				//push me forward
1396 				float	zVel = ps->velocity[2];
1397 				if ( ps->legsTimer > 500 )
1398 				{//not at end of anim yet
1399 					float speed = 175;
1400 					if ( ucmd->forwardmove < 0 )
1401 					{//slower
1402 						speed = 100;
1403 					}
1404 					else if ( ucmd->forwardmove > 0 )
1405 					{
1406 						speed = 250;//running speed
1407 					}
1408 					VectorScale( fwd, speed, ps->velocity );
1409 				}
1410 				ps->velocity[2] = zVel;//preserve z velocity
1411 				//pull me toward the wall, too
1412 				VectorMA( ps->velocity, dist, rt, ps->velocity );
1413 			}
1414 			ucmd->forwardmove = 0;
1415 			return qtrue;
1416 		}
1417 		else if ( doMove )
1418 		{//stop it
1419 			if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
1420 			{
1421 				PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_RIGHT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1422 			}
1423 			else if ( (ps->legsAnim) == BOTH_WALL_RUN_LEFT )
1424 			{
1425 				PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_LEFT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1426 			}
1427 		}
1428 	}
1429 
1430 	return qfalse;
1431 }
1432 
PM_AdjustAnglesForWallRunUpFlipAlt(usercmd_t * ucmd)1433 qboolean PM_AdjustAnglesForWallRunUpFlipAlt( usercmd_t *ucmd )
1434 {
1435 //	ucmd->angles[PITCH] = ANGLE2SHORT( pm->ps->viewangles[PITCH] ) - pm->ps->delta_angles[PITCH];
1436 //	ucmd->angles[YAW] = ANGLE2SHORT( pm->ps->viewangles[YAW] ) - pm->ps->delta_angles[YAW];
1437 	PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
1438 	return qtrue;
1439 }
1440 
PM_AdjustAngleForWallRunUp(playerState_t * ps,usercmd_t * ucmd,qboolean doMove)1441 qboolean PM_AdjustAngleForWallRunUp( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
1442 {
1443 	if ( ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
1444 	{//wall-running up
1445 		//stick to wall, if there is one
1446 		vec3_t	fwd, traceTo, mins, maxs, fwdAngles;
1447 		trace_t	trace;
1448 		float	dist = 128;
1449 
1450 		VectorSet(mins, -15,-15,0);
1451 		VectorSet(maxs, 15,15,24);
1452 		VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
1453 
1454 		AngleVectors( fwdAngles, fwd, NULL, NULL );
1455 		VectorMA( ps->origin, dist, fwd, traceTo );
1456 		pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1457 		if ( trace.fraction > 0.5f )
1458 		{//hmm, some room, see if there's a floor right here
1459 			trace_t	trace2;
1460 			vec3_t	top, bottom;
1461 
1462 			VectorCopy( trace.endpos, top );
1463 			top[2] += (pm->mins[2]*-1) + 4.0f;
1464 			VectorCopy( top, bottom );
1465 			bottom[2] -= 64.0f;
1466 			pm->trace( &trace2, top, pm->mins, pm->maxs, bottom, ps->clientNum, MASK_PLAYERSOLID );
1467 			if ( !trace2.allsolid
1468 				&& !trace2.startsolid
1469 				&& trace2.fraction < 1.0f
1470 				&& trace2.plane.normal[2] > 0.7f )//slope we can stand on
1471 			{//cool, do the alt-flip and land on whetever it is we just scaled up
1472 				VectorScale( fwd, 100, pm->ps->velocity );
1473 				pm->ps->velocity[2] += 400;
1474 				PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_ALT, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1475 				pm->ps->pm_flags |= PMF_JUMP_HELD;
1476 				//ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1477 				//ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
1478 				//G_AddEvent( ent, EV_JUMP, 0 );
1479 				PM_AddEvent(EV_JUMP);
1480 				ucmd->upmove = 0;
1481 				return qfalse;
1482 			}
1483 		}
1484 
1485 		if ( //ucmd->upmove <= 0 &&
1486 			ps->legsTimer > 0 &&
1487 			ucmd->forwardmove > 0 &&
1488 			trace.fraction < 1.0f &&
1489 			(trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
1490 		{//still a vertical wall there
1491 			//make sure there's not a ceiling above us!
1492 			trace_t	trace2;
1493 			VectorCopy( ps->origin, traceTo );
1494 			traceTo[2] += 64;
1495 			pm->trace( &trace2, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1496 			if ( trace2.fraction < 1.0f )
1497 			{//will hit a ceiling, so force jump-off right now
1498 				//NOTE: hits any entity or clip brush in the way, too, not just architecture!
1499 			}
1500 			else
1501 			{//all clear, keep going
1502 				//FIXME: don't pull around 90 turns
1503 				//FIXME: simulate stepping up steps here, somehow?
1504 				ucmd->forwardmove = 127;
1505 				if ( ucmd->upmove < 0 )
1506 				{
1507 					ucmd->upmove = 0;
1508 				}
1509 				//make me face the wall
1510 				ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+180;
1511 				PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
1512 				/*
1513 				if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
1514 				{//don't clamp angles when looking through a viewEntity
1515 					SetClientViewAngle( ent, ent->client->ps.viewangles );
1516 				}
1517 				*/
1518 				ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
1519 				//if ( ent->s.number || !player_locked )
1520 				if (1) //aslkfhsakf
1521 				{
1522 					if ( doMove )
1523 					{
1524 						//pull me toward the wall
1525 						VectorScale( trace.plane.normal, -dist*trace.fraction, ps->velocity );
1526 						//push me up
1527 						if ( ps->legsTimer > 200 )
1528 						{//not at end of anim yet
1529 							float speed = 300;
1530 							/*
1531 							if ( ucmd->forwardmove < 0 )
1532 							{//slower
1533 								speed = 100;
1534 							}
1535 							else if ( ucmd->forwardmove > 0 )
1536 							{
1537 								speed = 250;//running speed
1538 							}
1539 							*/
1540 							ps->velocity[2] = speed;//preserve z velocity
1541 						}
1542 					}
1543 				}
1544 				ucmd->forwardmove = 0;
1545 				return qtrue;
1546 			}
1547 		}
1548 		//failed!
1549 		if ( doMove )
1550 		{//stop it
1551 			VectorScale( fwd, -300.0f, ps->velocity );
1552 			ps->velocity[2] += 200;
1553 			//NPC_SetAnim( ent, SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1554 			//why?!?#?#@!%$R@$KR#F:Hdl;asfm
1555 			PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
1556 			ps->pm_flags |= PMF_JUMP_HELD;
1557 			//ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1558 
1559 			//FIXME do I need this in mp?
1560 			//ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
1561 			PM_AddEvent(EV_JUMP);
1562 			ucmd->upmove = 0;
1563 			//return qtrue;
1564 		}
1565 	}
1566 	return qfalse;
1567 }
1568 
1569 #define	JUMP_OFF_WALL_SPEED	200.0f
1570 //nice...
BG_ForceWallJumpStrength(void)1571 static float BG_ForceWallJumpStrength( void )
1572 {
1573 	return (forceJumpStrength[FORCE_LEVEL_3]/2.5f);
1574 }
1575 
PM_AdjustAngleForWallJump(playerState_t * ps,usercmd_t * ucmd,qboolean doMove)1576 qboolean PM_AdjustAngleForWallJump( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
1577 {
1578 	if ( ( ( BG_InReboundJump( ps->legsAnim ) || BG_InReboundHold( ps->legsAnim ) )
1579 			&& ( BG_InReboundJump( ps->torsoAnim ) || BG_InReboundHold( ps->torsoAnim ) ) )
1580 		|| (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
1581 	{//hugging wall, getting ready to jump off
1582 		//stick to wall, if there is one
1583 		vec3_t	checkDir, traceTo, mins, maxs, fwdAngles;
1584 		trace_t	trace;
1585 		float	dist = 128.0f, yawAdjust;
1586 
1587 		VectorSet(mins, pm->mins[0],pm->mins[1],0);
1588 		VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
1589 		VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
1590 
1591 		switch ( ps->legsAnim )
1592 		{
1593 		case BOTH_FORCEWALLREBOUND_RIGHT:
1594 		case BOTH_FORCEWALLHOLD_RIGHT:
1595 			AngleVectors( fwdAngles, NULL, checkDir, NULL );
1596 			yawAdjust = -90;
1597 			break;
1598 		case BOTH_FORCEWALLREBOUND_LEFT:
1599 		case BOTH_FORCEWALLHOLD_LEFT:
1600 			AngleVectors( fwdAngles, NULL, checkDir, NULL );
1601 			VectorScale( checkDir, -1, checkDir );
1602 			yawAdjust = 90;
1603 			break;
1604 		case BOTH_FORCEWALLREBOUND_FORWARD:
1605 		case BOTH_FORCEWALLHOLD_FORWARD:
1606 			AngleVectors( fwdAngles, checkDir, NULL, NULL );
1607 			yawAdjust = 180;
1608 			break;
1609 		case BOTH_FORCEWALLREBOUND_BACK:
1610 		case BOTH_FORCEWALLHOLD_BACK:
1611 			AngleVectors( fwdAngles, checkDir, NULL, NULL );
1612 			VectorScale( checkDir, -1, checkDir );
1613 			yawAdjust = 0;
1614 			break;
1615 		default:
1616 			//WTF???
1617 			pm->ps->pm_flags &= ~PMF_STUCK_TO_WALL;
1618 			return qfalse;
1619 			break;
1620 		}
1621 		if ( pm->debugMelee )
1622 		{//uber-skillz
1623 			if ( ucmd->upmove > 0 )
1624 			{//hold on until you let go manually
1625 				if ( BG_InReboundHold( ps->legsAnim ) )
1626 				{//keep holding
1627 					if ( ps->legsTimer < 150 )
1628 					{
1629 						ps->legsTimer = 150;
1630 					}
1631 				}
1632 				else
1633 				{//if got to hold part of anim, play hold anim
1634 					if ( ps->legsTimer <= 300 )
1635 					{
1636 						ps->saberHolstered = 2;
1637 						PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1638 						ps->legsTimer = ps->torsoTimer = 150;
1639 					}
1640 				}
1641 			}
1642 		}
1643 		VectorMA( ps->origin, dist, checkDir, traceTo );
1644 		pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
1645 		if ( //ucmd->upmove <= 0 &&
1646 			ps->legsTimer > 100 &&
1647 			trace.fraction < 1.0f &&
1648 			fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/ )
1649 		{//still a vertical wall there
1650 			//FIXME: don't pull around 90 turns
1651 			/*
1652 			if ( ent->s.number || !player_locked )
1653 			{
1654 				ucmd->forwardmove = 127;
1655 			}
1656 			*/
1657 			if ( ucmd->upmove < 0 )
1658 			{
1659 				ucmd->upmove = 0;
1660 			}
1661 			//align me to the wall
1662 			ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
1663 			PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
1664 			/*
1665 			if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
1666 			{//don't clamp angles when looking through a viewEntity
1667 				SetClientViewAngle( ent, ent->client->ps.viewangles );
1668 			}
1669 			*/
1670 			ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
1671 			//if ( ent->s.number || !player_locked )
1672 			if (1)
1673 			{
1674 				if ( doMove )
1675 				{
1676 					//pull me toward the wall
1677 					VectorScale( trace.plane.normal, -128.0f, ps->velocity );
1678 				}
1679 			}
1680 			ucmd->upmove = 0;
1681 			ps->pm_flags |= PMF_STUCK_TO_WALL;
1682 			return qtrue;
1683 		}
1684 		else if ( doMove
1685 			&& (ps->pm_flags&PMF_STUCK_TO_WALL))
1686 		{//jump off
1687 			//push off of it!
1688 			ps->pm_flags &= ~PMF_STUCK_TO_WALL;
1689 			ps->velocity[0] = ps->velocity[1] = 0;
1690 			VectorScale( checkDir, -JUMP_OFF_WALL_SPEED, ps->velocity );
1691 			ps->velocity[2] = BG_ForceWallJumpStrength();
1692 			ps->pm_flags |= PMF_JUMP_HELD;//PMF_JUMPING|PMF_JUMP_HELD;
1693 			//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1694 			ps->fd.forceJumpSound = 1; //this is a stupid thing, i should fix it.
1695 			//ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
1696 			if (ps->origin[2] < ps->fd.forceJumpZStart)
1697 			{
1698 				ps->fd.forceJumpZStart = ps->origin[2];
1699 			}
1700 			//FIXME do I need this?
1701 
1702 			BG_ForcePowerDrain( ps, FP_LEVITATION, 10 );
1703 			//no control for half a second
1704 			ps->pm_flags |= PMF_TIME_KNOCKBACK;
1705 			ps->pm_time = 500;
1706 			ucmd->forwardmove = 0;
1707 			ucmd->rightmove = 0;
1708 			ucmd->upmove = 127;
1709 
1710 			if ( BG_InReboundHold( ps->legsAnim ) )
1711 			{//if was in hold pose, release now
1712 				PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1713 			}
1714 			else
1715 			{
1716 				//PM_JumpForDir();
1717 				PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
1718 			}
1719 
1720 			//return qtrue;
1721 		}
1722 	}
1723 	ps->pm_flags &= ~PMF_STUCK_TO_WALL;
1724 	return qfalse;
1725 }
1726 
1727 //Set the height for when a force jump was started. If it's 0, nuge it up (slight hack to prevent holding jump over slopes)
PM_SetForceJumpZStart(float value)1728 void PM_SetForceJumpZStart(float value)
1729 {
1730 	pm->ps->fd.forceJumpZStart = value;
1731 	if (!pm->ps->fd.forceJumpZStart)
1732 	{
1733 		pm->ps->fd.forceJumpZStart -= 0.1f;
1734 	}
1735 }
1736 
1737 float forceJumpHeightMax[NUM_FORCE_POWER_LEVELS] =
1738 {
1739 	66,//normal jump (32+stepheight(18)+crouchdiff(24) = 74)
1740 	130,//(96+stepheight(18)+crouchdiff(24) = 138)
1741 	226,//(192+stepheight(18)+crouchdiff(24) = 234)
1742 	418//(384+stepheight(18)+crouchdiff(24) = 426)
1743 };
1744 
PM_GrabWallForJump(int anim)1745 void PM_GrabWallForJump( int anim )
1746 {//NOTE!!! assumes an appropriate anim is being passed in!!!
1747 	PM_SetAnim( SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1748 	PM_AddEvent( EV_JUMP );//make sound for grab
1749 	pm->ps->pm_flags |= PMF_STUCK_TO_WALL;
1750 }
1751 
1752 /*
1753 =============
1754 PM_CheckJump
1755 =============
1756 */
PM_CheckJump(void)1757 static qboolean PM_CheckJump( void )
1758 {
1759 	qboolean allowFlips = qtrue;
1760 
1761 	if (pm->ps->clientNum >= MAX_CLIENTS)
1762 	{
1763 		bgEntity_t *pEnt = pm_entSelf;
1764 
1765 		if (pEnt->s.eType == ET_NPC &&
1766 			pEnt->s.NPC_class == CLASS_VEHICLE)
1767 		{ //no!
1768 			return qfalse;
1769 		}
1770 	}
1771 
1772 	if (pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
1773 		pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
1774 		pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
1775 	{
1776 		return qfalse;
1777 	}
1778 
1779 	if (pm->ps->pm_type == PM_JETPACK)
1780 	{ //there's no actual jumping while we jetpack
1781 		return qfalse;
1782 	}
1783 
1784 	//Don't allow jump until all buttons are up
1785 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
1786 		return qfalse;
1787 	}
1788 
1789 	if ( PM_InKnockDown( pm->ps ) || BG_InRoll( pm->ps, pm->ps->legsAnim ) )
1790 	{//in knockdown
1791 		return qfalse;
1792 	}
1793 
1794 	if ( pm->ps->weapon == WP_SABER )
1795 	{
1796 		saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
1797 		saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
1798 		if ( saber1
1799 			&& (saber1->saberFlags&SFL_NO_FLIPS) )
1800 		{
1801 			allowFlips = qfalse;
1802 		}
1803 		if ( saber2
1804 			&& (saber2->saberFlags&SFL_NO_FLIPS) )
1805 		{
1806 			allowFlips = qfalse;
1807 		}
1808 	}
1809 
1810 	if (pm->ps->groundEntityNum != ENTITYNUM_NONE || pm->ps->origin[2] < pm->ps->fd.forceJumpZStart)
1811 	{
1812 		pm->ps->fd.forcePowersActive &= ~(1<<FP_LEVITATION);
1813 	}
1814 
1815 	if (pm->ps->fd.forcePowersActive & (1 << FP_LEVITATION))
1816 	{ //Force jump is already active.. continue draining power appropriately until we land.
1817 		if (pm->ps->fd.forcePowerDebounce[FP_LEVITATION] < pm->cmd.serverTime)
1818 		{
1819 			if ( pm->gametype == GT_DUEL
1820 				|| pm->gametype == GT_POWERDUEL )
1821 			{//jump takes less power
1822 				BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 1 );
1823 			}
1824 			else
1825 			{
1826 				BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
1827 			}
1828 			if (pm->ps->fd.forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_2)
1829 			{
1830 				pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 300;
1831 			}
1832 			else
1833 			{
1834 				pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 200;
1835 			}
1836 		}
1837 	}
1838 
1839 	if (pm->ps->forceJumpFlip)
1840 	{ //Forced jump anim
1841 		int anim = BOTH_FORCEINAIR1;
1842 		int	parts = SETANIM_BOTH;
1843 		if ( allowFlips )
1844 		{
1845 			if ( pm->cmd.forwardmove > 0 )
1846 			{
1847 				anim = BOTH_FLIP_F;
1848 			}
1849 			else if ( pm->cmd.forwardmove < 0 )
1850 			{
1851 				anim = BOTH_FLIP_B;
1852 			}
1853 			else if ( pm->cmd.rightmove > 0 )
1854 			{
1855 				anim = BOTH_FLIP_R;
1856 			}
1857 			else if ( pm->cmd.rightmove < 0 )
1858 			{
1859 				anim = BOTH_FLIP_L;
1860 			}
1861 		}
1862 		else
1863 		{
1864 			if ( pm->cmd.forwardmove > 0 )
1865 			{
1866 				anim = BOTH_FORCEINAIR1;
1867 			}
1868 			else if ( pm->cmd.forwardmove < 0 )
1869 			{
1870 				anim = BOTH_FORCEINAIRBACK1;
1871 			}
1872 			else if ( pm->cmd.rightmove > 0 )
1873 			{
1874 				anim = BOTH_FORCEINAIRRIGHT1;
1875 			}
1876 			else if ( pm->cmd.rightmove < 0 )
1877 			{
1878 				anim = BOTH_FORCEINAIRLEFT1;
1879 			}
1880 		}
1881 		if ( pm->ps->weaponTime )
1882 		{//FIXME: really only care if we're in a saber attack anim...
1883 			parts = SETANIM_LEGS;
1884 		}
1885 
1886 		PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1887 		pm->ps->forceJumpFlip = qfalse;
1888 		return qtrue;
1889 	}
1890 #if METROID_JUMP
1891 	if ( pm->waterlevel < 2 )
1892 	{
1893 		if ( pm->ps->gravity > 0 )
1894 		{//can't do this in zero-G
1895 			if ( PM_ForceJumpingUp() )
1896 			{//holding jump in air
1897 				float curHeight = pm->ps->origin[2] - pm->ps->fd.forceJumpZStart;
1898 				//check for max force jump level and cap off & cut z vel
1899 				if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height
1900 						(pm->ps->fd.forcePower&&pm->cmd.upmove>=10) ) &&////still have force power available and still trying to jump up
1901 					curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] &&
1902 					pm->ps->fd.forceJumpZStart)//still below maximum jump height
1903 				{//can still go up
1904 					if ( curHeight > forceJumpHeight[0] )
1905 					{//passed normal jump height  *2?
1906 						if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) )//haven't started forcejump yet
1907 						{
1908 							//start force jump
1909 							pm->ps->fd.forcePowersActive |= (1<<FP_LEVITATION);
1910 							pm->ps->fd.forceJumpSound = 1;
1911 							//play flip
1912 							if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir
1913 								(pm->ps->legsAnim) != BOTH_FLIP_F &&//not already flipping
1914 								(pm->ps->legsAnim) != BOTH_FLIP_B &&
1915 								(pm->ps->legsAnim) != BOTH_FLIP_R &&
1916 								(pm->ps->legsAnim) != BOTH_FLIP_L
1917 								&& allowFlips )
1918 							{
1919 								int anim = BOTH_FORCEINAIR1;
1920 								int	parts = SETANIM_BOTH;
1921 
1922 								if ( pm->cmd.forwardmove > 0 )
1923 								{
1924 									anim = BOTH_FLIP_F;
1925 								}
1926 								else if ( pm->cmd.forwardmove < 0 )
1927 								{
1928 									anim = BOTH_FLIP_B;
1929 								}
1930 								else if ( pm->cmd.rightmove > 0 )
1931 								{
1932 									anim = BOTH_FLIP_R;
1933 								}
1934 								else if ( pm->cmd.rightmove < 0 )
1935 								{
1936 									anim = BOTH_FLIP_L;
1937 								}
1938 								if ( pm->ps->weaponTime )
1939 								{
1940 									parts = SETANIM_LEGS;
1941 								}
1942 
1943 								PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1944 							}
1945 							else if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1946 							{
1947 								vec3_t facingFwd, facingRight, facingAngles;
1948 								int	anim = -1;
1949 								float dotR, dotF;
1950 
1951 								VectorSet(facingAngles, 0, pm->ps->viewangles[YAW], 0);
1952 
1953 								AngleVectors( facingAngles, facingFwd, facingRight, NULL );
1954 								dotR = DotProduct( facingRight, pm->ps->velocity );
1955 								dotF = DotProduct( facingFwd, pm->ps->velocity );
1956 
1957 								if ( fabs(dotR) > fabs(dotF) * 1.5 )
1958 								{
1959 									if ( dotR > 150 )
1960 									{
1961 										anim = BOTH_FORCEJUMPRIGHT1;
1962 									}
1963 									else if ( dotR < -150 )
1964 									{
1965 										anim = BOTH_FORCEJUMPLEFT1;
1966 									}
1967 								}
1968 								else
1969 								{
1970 									if ( dotF > 150 )
1971 									{
1972 										anim = BOTH_FORCEJUMP1;
1973 									}
1974 									else if ( dotF < -150 )
1975 									{
1976 										anim = BOTH_FORCEJUMPBACK1;
1977 									}
1978 								}
1979 								if ( anim != -1 )
1980 								{
1981 									int parts = SETANIM_BOTH;
1982 									if ( pm->ps->weaponTime )
1983 									{//FIXME: really only care if we're in a saber attack anim...
1984 										parts = SETANIM_LEGS;
1985 									}
1986 
1987 									PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1988 								}
1989 							}
1990 						}
1991 						else
1992 						{ //jump is already active (the anim has started)
1993 							if ( pm->ps->legsTimer < 1 )
1994 							{//not in the middle of a legsAnim
1995 								int anim = (pm->ps->legsAnim);
1996 								int newAnim = -1;
1997 								switch ( anim )
1998 								{
1999 								case BOTH_FORCEJUMP1:
2000 									newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
2001 									break;
2002 								case BOTH_FORCEJUMPBACK1:
2003 									newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
2004 									break;
2005 								case BOTH_FORCEJUMPLEFT1:
2006 									newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
2007 									break;
2008 								case BOTH_FORCEJUMPRIGHT1:
2009 									newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
2010 									break;
2011 								}
2012 								if ( newAnim != -1 )
2013 								{
2014 									int parts = SETANIM_BOTH;
2015 									if ( pm->ps->weaponTime )
2016 									{
2017 										parts = SETANIM_LEGS;
2018 									}
2019 
2020 									PM_SetAnim( parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2021 								}
2022 							}
2023 						}
2024 					}
2025 
2026 					//need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel
2027 					pm->ps->velocity[2] = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->fd.forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY;
2028 					pm->ps->velocity[2] /= 10;
2029 					pm->ps->velocity[2] += JUMP_VELOCITY;
2030 					pm->ps->pm_flags |= PMF_JUMP_HELD;
2031 				}
2032 				else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] )
2033 				{//still have some headroom, don't totally stop it
2034 					if ( pm->ps->velocity[2] > JUMP_VELOCITY )
2035 					{
2036 						pm->ps->velocity[2] = JUMP_VELOCITY;
2037 					}
2038 				}
2039 				else
2040 				{
2041 					//pm->ps->velocity[2] = 0;
2042 					//rww - changed for the sake of balance in multiplayer
2043 
2044 					if ( pm->ps->velocity[2] > JUMP_VELOCITY )
2045 					{
2046 						pm->ps->velocity[2] = JUMP_VELOCITY;
2047 					}
2048 				}
2049 				pm->cmd.upmove = 0;
2050 				return qfalse;
2051 			}
2052 		}
2053 	}
2054 
2055 #endif
2056 
2057 	//Not jumping
2058 	if ( pm->cmd.upmove < 10 && pm->ps->groundEntityNum != ENTITYNUM_NONE) {
2059 		return qfalse;
2060 	}
2061 
2062 	// must wait for jump to be released
2063 	if ( pm->ps->pm_flags & PMF_JUMP_HELD )
2064 	{
2065 		// clear upmove so cmdscale doesn't lower running speed
2066 		pm->cmd.upmove = 0;
2067 		return qfalse;
2068 	}
2069 
2070 	if ( pm->ps->gravity <= 0 )
2071 	{//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of
2072 		vec3_t	forward, back;
2073 		trace_t	trace;
2074 
2075 		AngleVectors( pm->ps->viewangles, forward, NULL, NULL );
2076 		VectorMA( pm->ps->origin, -8, forward, back );
2077 		pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask );
2078 
2079 		if ( trace.fraction <= 1.0f )
2080 		{
2081 			VectorMA( pm->ps->velocity, JUMP_VELOCITY*2, forward, pm->ps->velocity );
2082 			PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
2083 		}//else no surf close enough to push off of
2084 		pm->cmd.upmove = 0;
2085 	}
2086 	else if ( pm->cmd.upmove > 0 && pm->waterlevel < 2 &&
2087 		pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 &&
2088 		!(pm->ps->pm_flags&PMF_JUMP_HELD) &&
2089 		(pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE) &&
2090 		!PM_IsRocketTrooper() &&
2091 		!BG_HasYsalamiri(pm->gametype, pm->ps) &&
2092 		BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION) )
2093 	{
2094 		qboolean allowWallRuns = qtrue;
2095 		qboolean allowWallFlips = qtrue;
2096 	//	qboolean allowFlips = qtrue;
2097 		qboolean allowWallGrabs = qtrue;
2098 		if ( pm->ps->weapon == WP_SABER )
2099 		{
2100 			saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
2101 			saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
2102 			if ( saber1
2103 				&& (saber1->saberFlags&SFL_NO_WALL_RUNS) )
2104 			{
2105 				allowWallRuns = qfalse;
2106 			}
2107 			if ( saber2
2108 				&& (saber2->saberFlags&SFL_NO_WALL_RUNS) )
2109 			{
2110 				allowWallRuns = qfalse;
2111 			}
2112 			if ( saber1
2113 				&& (saber1->saberFlags&SFL_NO_WALL_FLIPS) )
2114 			{
2115 				allowWallFlips = qfalse;
2116 			}
2117 			if ( saber2
2118 				&& (saber2->saberFlags&SFL_NO_WALL_FLIPS) )
2119 			{
2120 				allowWallFlips = qfalse;
2121 			}
2122 			if ( saber1
2123 				&& (saber1->saberFlags&SFL_NO_FLIPS) )
2124 			{
2125 				allowFlips = qfalse;
2126 			}
2127 			if ( saber2
2128 				&& (saber2->saberFlags&SFL_NO_FLIPS) )
2129 			{
2130 				allowFlips = qfalse;
2131 			}
2132 			if ( saber1
2133 				&& (saber1->saberFlags&SFL_NO_WALL_GRAB) )
2134 			{
2135 				allowWallGrabs = qfalse;
2136 			}
2137 			if ( saber2
2138 				&& (saber2->saberFlags&SFL_NO_WALL_GRAB) )
2139 			{
2140 				allowWallGrabs = qfalse;
2141 			}
2142 		}
2143 
2144 		if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
2145 		{//on the ground
2146 			//check for left-wall and right-wall special jumps
2147 			int anim = -1;
2148 			float	vertPush = 0;
2149 			if ( pm->cmd.rightmove > 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
2150 			{//strafing right
2151 				if ( pm->cmd.forwardmove > 0 )
2152 				{//wall-run
2153 					if ( allowWallRuns )
2154 					{
2155 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
2156 						anim = BOTH_WALL_RUN_RIGHT;
2157 					}
2158 				}
2159 				else if ( pm->cmd.forwardmove == 0 )
2160 				{//wall-flip
2161 					if ( allowWallFlips )
2162 					{
2163 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
2164 						anim = BOTH_WALL_FLIP_RIGHT;
2165 					}
2166 				}
2167 			}
2168 			else if ( pm->cmd.rightmove < 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
2169 			{//strafing left
2170 				if ( pm->cmd.forwardmove > 0 )
2171 				{//wall-run
2172 					if ( allowWallRuns )
2173 					{
2174 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
2175 						anim = BOTH_WALL_RUN_LEFT;
2176 					}
2177 				}
2178 				else if ( pm->cmd.forwardmove == 0 )
2179 				{//wall-flip
2180 					if ( allowWallFlips )
2181 					{
2182 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
2183 						anim = BOTH_WALL_FLIP_LEFT;
2184 					}
2185 				}
2186 			}
2187 			else if ( pm->cmd.forwardmove < 0 && !(pm->cmd.buttons&BUTTON_ATTACK) )
2188 			{//backflip
2189 				if ( allowFlips )
2190 				{
2191 					vertPush = JUMP_VELOCITY;
2192 					anim = BOTH_FLIP_BACK1;//BG_PickAnim( BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 );
2193 				}
2194 			}
2195 
2196 			vertPush += 128; //give them an extra shove
2197 
2198 			if ( anim != -1 )
2199 			{
2200 				vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
2201 				vec3_t	idealNormal={0}, wallNormal={0};
2202 				trace_t	trace;
2203 				qboolean doTrace = qfalse;
2204 				int contents = MASK_SOLID;//MASK_PLAYERSOLID;
2205 
2206 				VectorSet(mins, pm->mins[0],pm->mins[1],0);
2207 				VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
2208 				VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
2209 
2210 				memset(&trace, 0, sizeof(trace)); //to shut the compiler up
2211 
2212 				AngleVectors( fwdAngles, fwd, right, NULL );
2213 
2214 				//trace-check for a wall, if necc.
2215 				switch ( anim )
2216 				{
2217 				case BOTH_WALL_FLIP_LEFT:
2218 					//NOTE: purposely falls through to next case!
2219 				case BOTH_WALL_RUN_LEFT:
2220 					doTrace = qtrue;
2221 					VectorMA( pm->ps->origin, -16, right, traceto );
2222 					break;
2223 
2224 				case BOTH_WALL_FLIP_RIGHT:
2225 					//NOTE: purposely falls through to next case!
2226 				case BOTH_WALL_RUN_RIGHT:
2227 					doTrace = qtrue;
2228 					VectorMA( pm->ps->origin, 16, right, traceto );
2229 					break;
2230 
2231 				case BOTH_WALL_FLIP_BACK1:
2232 					doTrace = qtrue;
2233 					VectorMA( pm->ps->origin, 16, fwd, traceto );
2234 					break;
2235 				}
2236 
2237 				if ( doTrace )
2238 				{
2239 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );
2240 					VectorCopy( trace.plane.normal, wallNormal );
2241 					VectorNormalize( wallNormal );
2242 					VectorSubtract( pm->ps->origin, traceto, idealNormal );
2243 					VectorNormalize( idealNormal );
2244 				}
2245 
2246 				if ( !doTrace || (trace.fraction < 1.0f && (trace.entityNum < MAX_CLIENTS || DotProduct(wallNormal,idealNormal) > 0.7)) )
2247 				{//there is a wall there.. or hit a client
2248 					if ( (anim != BOTH_WALL_RUN_LEFT
2249 							&& anim != BOTH_WALL_RUN_RIGHT
2250 							&& anim != BOTH_FORCEWALLRUNFLIP_START)
2251 						|| (wallNormal[2] >= 0.0f&&wallNormal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
2252 					{//wall-runs can only run on perfectly flat walls, sorry.
2253 						int parts;
2254 						//move me to side
2255 						if ( anim == BOTH_WALL_FLIP_LEFT )
2256 						{
2257 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2258 							VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
2259 						}
2260 						else if ( anim == BOTH_WALL_FLIP_RIGHT )
2261 						{
2262 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2263 							VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
2264 						}
2265 						else if ( anim == BOTH_FLIP_BACK1
2266 							|| anim == BOTH_FLIP_BACK2
2267 							|| anim == BOTH_FLIP_BACK3
2268 							|| anim == BOTH_WALL_FLIP_BACK1 )
2269 						{
2270 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2271 							VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2272 						}
2273 
2274 						/*
2275 						if ( doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT )
2276 						{
2277 							if (trace.entityNum < MAX_CLIENTS)
2278 							{
2279 								pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
2280 							}
2281 						}
2282 						*/
2283 
2284 						//up
2285 						if ( vertPush )
2286 						{
2287 							pm->ps->velocity[2] = vertPush;
2288 							pm->ps->fd.forcePowersActive |= (1 << FP_LEVITATION);
2289 						}
2290 						//animate me
2291 						parts = SETANIM_LEGS;
2292 						if ( anim == BOTH_BUTTERFLY_LEFT )
2293 						{
2294 							parts = SETANIM_BOTH;
2295 							pm->cmd.buttons&=~BUTTON_ATTACK;
2296 							pm->ps->saberMove = LS_NONE;
2297 						}
2298 						else if ( !pm->ps->weaponTime )
2299 						{
2300 							parts = SETANIM_BOTH;
2301 						}
2302 						PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2303 						if ( anim == BOTH_BUTTERFLY_LEFT )
2304 						{
2305 							pm->ps->weaponTime = pm->ps->torsoTimer;
2306 						}
2307 						PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2308 						pm->ps->pm_flags |= PMF_JUMP_HELD;
2309 						pm->cmd.upmove = 0;
2310 						pm->ps->fd.forceJumpSound = 1;
2311 					}
2312 				}
2313 			}
2314 		}
2315 		else
2316 		{//in the air
2317 			int legsAnim = pm->ps->legsAnim;
2318 
2319 			if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT )
2320 			{//running on a wall
2321 				vec3_t right, traceto, mins, maxs, fwdAngles;
2322 				trace_t	trace;
2323 				int		anim = -1;
2324 
2325 				VectorSet(mins, pm->mins[0], pm->mins[0], 0);
2326 				VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24);
2327 				VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
2328 
2329 				AngleVectors( fwdAngles, NULL, right, NULL );
2330 
2331 				if ( legsAnim == BOTH_WALL_RUN_LEFT )
2332 				{
2333 					if ( pm->ps->legsTimer > 400 )
2334 					{//not at the end of the anim
2335 						float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_LEFT );
2336 						if ( pm->ps->legsTimer < animLen - 400 )
2337 						{//not at start of anim
2338 							VectorMA( pm->ps->origin, -16, right, traceto );
2339 							anim = BOTH_WALL_RUN_LEFT_FLIP;
2340 						}
2341 					}
2342 				}
2343 				else if ( legsAnim == BOTH_WALL_RUN_RIGHT )
2344 				{
2345 					if ( pm->ps->legsTimer > 400 )
2346 					{//not at the end of the anim
2347 						float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_RIGHT );
2348 						if ( pm->ps->legsTimer < animLen - 400 )
2349 						{//not at start of anim
2350 							VectorMA( pm->ps->origin, 16, right, traceto );
2351 							anim = BOTH_WALL_RUN_RIGHT_FLIP;
2352 						}
2353 					}
2354 				}
2355 				if ( anim != -1 )
2356 				{
2357 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
2358 					if ( trace.fraction < 1.0f )
2359 					{//flip off wall
2360 						int parts = 0;
2361 
2362 						if ( anim == BOTH_WALL_RUN_LEFT_FLIP )
2363 						{
2364 							pm->ps->velocity[0] *= 0.5f;
2365 							pm->ps->velocity[1] *= 0.5f;
2366 							VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
2367 						}
2368 						else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP )
2369 						{
2370 							pm->ps->velocity[0] *= 0.5f;
2371 							pm->ps->velocity[1] *= 0.5f;
2372 							VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
2373 						}
2374 						parts = SETANIM_LEGS;
2375 						if ( !pm->ps->weaponTime )
2376 						{
2377 							parts = SETANIM_BOTH;
2378 						}
2379 						PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2380 						pm->cmd.upmove = 0;
2381 					}
2382 				}
2383 				if ( pm->cmd.upmove != 0 )
2384 				{//jump failed, so don't try to do normal jump code, just return
2385 					return qfalse;
2386 				}
2387 			}
2388 			//NEW JKA
2389 			else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
2390 			{
2391 				vec3_t fwd, traceto, mins, maxs, fwdAngles;
2392 				trace_t	trace;
2393 				int		anim = -1;
2394 				float animLen;
2395 
2396 				VectorSet(mins, pm->mins[0], pm->mins[0], 0.0f);
2397 				VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24.0f);
2398 				//hmm, did you mean [1] and [1]?
2399 				VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
2400 				AngleVectors( fwdAngles, fwd, NULL, NULL );
2401 
2402 				assert(pm_entSelf); //null pm_entSelf would be a Bad Thing<tm>
2403 				animLen = BG_AnimLength( pm_entSelf->localAnimIndex, BOTH_FORCEWALLRUNFLIP_START );
2404 				if ( pm->ps->legsTimer < animLen - 400 )
2405 				{//not at start of anim
2406 					VectorMA( pm->ps->origin, 16, fwd, traceto );
2407 					anim = BOTH_FORCEWALLRUNFLIP_END;
2408 				}
2409 				if ( anim != -1 )
2410 				{
2411 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
2412 					if ( trace.fraction < 1.0f )
2413 					{//flip off wall
2414 						int parts = SETANIM_LEGS;
2415 
2416 						pm->ps->velocity[0] *= 0.5f;
2417 						pm->ps->velocity[1] *= 0.5f;
2418 						VectorMA( pm->ps->velocity, -300, fwd, pm->ps->velocity );
2419 						pm->ps->velocity[2] += 200;
2420 						if ( !pm->ps->weaponTime )
2421 						{//not attacking, set anim on both
2422 							parts = SETANIM_BOTH;
2423 						}
2424 						PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2425 						//FIXME: do damage to traceEnt, like above?
2426 						//pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2427 						//ha ha, so silly with your silly jumpy fally flags.
2428 						pm->cmd.upmove = 0;
2429 						PM_AddEvent( EV_JUMP );
2430 					}
2431 				}
2432 				if ( pm->cmd.upmove != 0 )
2433 				{//jump failed, so don't try to do normal jump code, just return
2434 					return qfalse;
2435 				}
2436 			}
2437 			/*
2438 			else if ( pm->cmd.forwardmove > 0 //pushing forward
2439 				&& pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
2440 				&& pm->ps->velocity[2] > 200
2441 				&& PM_GroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
2442 				&& !BG_InSpecialJump(pm->ps->legsAnim))
2443 			{//run up wall, flip backwards
2444 				vec3_t fwd, traceto, mins, maxs, fwdAngles;
2445 				trace_t	trace;
2446 				vec3_t	idealNormal;
2447 
2448 				VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]);
2449 				VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->maxs[2]);
2450 				VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
2451 
2452 				AngleVectors( fwdAngles, fwd, NULL, NULL );
2453 				VectorMA( pm->ps->origin, 32, fwd, traceto );
2454 
2455 				pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, MASK_PLAYERSOLID );//FIXME: clip brushes too?
2456 				VectorSubtract( pm->ps->origin, traceto, idealNormal );
2457 				VectorNormalize( idealNormal );
2458 
2459 				if ( trace.fraction < 1.0f )
2460 				{//there is a wall there
2461 					int parts = SETANIM_LEGS;
2462 
2463 					pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2464 					VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2465 					pm->ps->velocity[2] += 128;
2466 
2467 					if ( !pm->ps->weaponTime )
2468 					{
2469 						parts = SETANIM_BOTH;
2470 					}
2471 					PM_SetAnim( parts, BOTH_WALL_FLIP_BACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2472 
2473 					pm->ps->legsTimer -= 600; //I force this anim to play to the end to prevent landing on your head and suddenly flipping over.
2474 											  //It is a bit too long at the end though, so I'll just shorten it.
2475 
2476 					PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2477 					pm->cmd.upmove = 0;
2478 					pm->ps->fd.forceJumpSound = 1;
2479 					BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
2480 
2481 					if (trace.entityNum < MAX_CLIENTS)
2482 					{
2483 						pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
2484 					}
2485 				}
2486 			}
2487 			*/
2488 			else if ( pm->cmd.forwardmove > 0 //pushing forward
2489 				&& pm->ps->fd.forceRageRecoveryTime < pm->cmd.serverTime	//not in a force Rage recovery period
2490 				&& pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
2491 				&& PM_WalkableGroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
2492 				&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
2493 			{//run up wall, flip backwards
2494 				if ( allowWallRuns )
2495 				{
2496 					//FIXME: have to be moving... make sure it's opposite the wall... or at least forward?
2497 					int wallWalkAnim = BOTH_WALL_FLIP_BACK1;
2498 					int parts = SETANIM_LEGS;
2499 					int contents = MASK_SOLID;//MASK_PLAYERSOLID;//CONTENTS_SOLID;
2500 					//qboolean kick = qtrue;
2501 					if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
2502 					{
2503 						wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START;
2504 						parts = SETANIM_BOTH;
2505 						//kick = qfalse;
2506 					}
2507 					else
2508 					{
2509 						if ( !pm->ps->weaponTime )
2510 						{
2511 							parts = SETANIM_BOTH;
2512 						}
2513 					}
2514 					//if ( PM_HasAnimation( pm->gent, wallWalkAnim ) )
2515 					if (1) //sure, we have it! Because I SAID SO.
2516 					{
2517 						vec3_t fwd, traceto, mins, maxs, fwdAngles;
2518 						trace_t	trace;
2519 						vec3_t	idealNormal;
2520 						bgEntity_t *traceEnt;
2521 
2522 						VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
2523 						VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
2524 						VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
2525 
2526 						AngleVectors( fwdAngles, fwd, NULL, NULL );
2527 						VectorMA( pm->ps->origin, 32, fwd, traceto );
2528 
2529 						pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too?
2530 						VectorSubtract( pm->ps->origin, traceto, idealNormal );
2531 						VectorNormalize( idealNormal );
2532 						traceEnt = PM_BGEntForNum(trace.entityNum);
2533 
2534 						if ( trace.fraction < 1.0f
2535 							&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2536 						{//there is a wall there
2537 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2538 							if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START )
2539 							{
2540 								pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f;
2541 							}
2542 							else
2543 							{
2544 								VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2545 								pm->ps->velocity[2] += 150.0f;
2546 							}
2547 							//animate me
2548 							PM_SetAnim( parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2549 	//						pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2550 							//again with the flags!
2551 							//G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2552 							//yucky!
2553 							PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2554 							pm->cmd.upmove = 0;
2555 							pm->ps->fd.forceJumpSound = 1;
2556 							BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
2557 
2558 							//kick if jumping off an ent
2559 							/*
2560 							if ( kick && traceEnt && (traceEnt->s.eType == ET_PLAYER || traceEnt->s.eType == ET_NPC) )
2561 							{ //kick that thang!
2562 								pm->ps->forceKickFlip = traceEnt->s.number+1;
2563 							}
2564 							*/
2565 							pm->cmd.rightmove = pm->cmd.forwardmove= 0;
2566 						}
2567 					}
2568 				}
2569 			}
2570 			else if ( (!BG_InSpecialJump( legsAnim )//not in a special jump anim
2571 						||BG_InReboundJump( legsAnim )//we're already in a rebound
2572 						||BG_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you)
2573 					//&& pm->ps->velocity[2] <= 0
2574 					&& pm->ps->velocity[2] > -1200 //not falling down very fast
2575 					&& !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press
2576 					&& (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction
2577 					//&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime	//not in a force Rage recovery period
2578 					&& pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better
2579 					//&& WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 10 )//have enough force power to do another one
2580 					&& BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION)
2581 					&& (pm->ps->origin[2]-pm->ps->fd.forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(BG_ForceWallJumpStrength()/2.0f)) //can fit at least one more wall jump in (yes, using "magic numbers"... for now)
2582 					//&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
2583 					)
2584 			{//see if we're pushing at a wall and jump off it if so
2585 				if ( allowWallGrabs )
2586 				{
2587 					//FIXME: make sure we have enough force power
2588 					//FIXME: check  to see if we can go any higher
2589 					//FIXME: limit to a certain number of these in a row?
2590 					//FIXME: maybe don't require a ucmd direction, just check all 4?
2591 					//FIXME: should stick to the wall for a second, then push off...
2592 					vec3_t checkDir, traceto, mins, maxs, fwdAngles;
2593 					trace_t	trace;
2594 					vec3_t	idealNormal;
2595 					int		anim = -1;
2596 
2597 					VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
2598 					VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
2599 					VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
2600 
2601 					if ( pm->cmd.rightmove )
2602 					{
2603 						if ( pm->cmd.rightmove > 0 )
2604 						{
2605 							anim = BOTH_FORCEWALLREBOUND_RIGHT;
2606 							AngleVectors( fwdAngles, NULL, checkDir, NULL );
2607 						}
2608 						else if ( pm->cmd.rightmove < 0 )
2609 						{
2610 							anim = BOTH_FORCEWALLREBOUND_LEFT;
2611 							AngleVectors( fwdAngles, NULL, checkDir, NULL );
2612 							VectorScale( checkDir, -1, checkDir );
2613 						}
2614 					}
2615 					else if ( pm->cmd.forwardmove > 0 )
2616 					{
2617 						anim = BOTH_FORCEWALLREBOUND_FORWARD;
2618 						AngleVectors( fwdAngles, checkDir, NULL, NULL );
2619 					}
2620 					else if ( pm->cmd.forwardmove < 0 )
2621 					{
2622 						anim = BOTH_FORCEWALLREBOUND_BACK;
2623 						AngleVectors( fwdAngles, checkDir, NULL, NULL );
2624 						VectorScale( checkDir, -1, checkDir );
2625 					}
2626 					if ( anim != -1 )
2627 					{//trace in the dir we're pushing in and see if there's a vertical wall there
2628 						bgEntity_t *traceEnt;
2629 
2630 						VectorMA( pm->ps->origin, 8, checkDir, traceto );
2631 						pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );//FIXME: clip brushes too?
2632 						VectorSubtract( pm->ps->origin, traceto, idealNormal );
2633 						VectorNormalize( idealNormal );
2634 						traceEnt = PM_BGEntForNum(trace.entityNum);
2635 						if ( trace.fraction < 1.0f
2636 							&&fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/
2637 							&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2638 						{//there is a wall there
2639 							float dot = DotProduct( pm->ps->velocity, trace.plane.normal );
2640 							if ( dot < 1.0f )
2641 							{//can't be heading *away* from the wall!
2642 								//grab it!
2643 								PM_GrabWallForJump( anim );
2644 							}
2645 						}
2646 					}
2647 				}
2648 			}
2649 			else
2650 			{
2651 				//FIXME: if in a butterfly, kick people away?
2652 			}
2653 			//END NEW JKA
2654 		}
2655 	}
2656 
2657 	/*
2658 	if ( pm->cmd.upmove > 0
2659 		&& (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
2660 		&& !PM_IsRocketTrooper()
2661 		&& (pm->ps->weaponTime > 0||pm->cmd.buttons&BUTTON_ATTACK) )
2662 	{//okay, we just jumped and we're in an attack
2663 		if ( !BG_InRoll( pm->ps, pm->ps->legsAnim )
2664 			&& !PM_InKnockDown( pm->ps )
2665 			&& !BG_InDeathAnim(pm->ps->legsAnim)
2666 			&& !BG_FlippingAnim( pm->ps->legsAnim )
2667 			&& !PM_SpinningAnim( pm->ps->legsAnim )
2668 			&& !BG_SaberInSpecialAttack( pm->ps->torsoAnim )
2669 			&& ( BG_SaberInAttack( pm->ps->saberMove ) ) )
2670 		{//not in an anim we shouldn't interrupt
2671 			//see if it's not too late to start a special jump-attack
2672 			float animLength = PM_AnimLength( 0, (animNumber_t)pm->ps->torsoAnim );
2673 			if ( animLength - pm->ps->torsoTimer < 500 )
2674 			{//just started the saberMove
2675 				//check for special-case jump attacks
2676 				if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_2 )
2677 				{//using medium attacks
2678 					if (PM_GroundDistance() < 32 &&
2679 						!BG_InSpecialJump(pm->ps->legsAnim))
2680 					{ //FLIP AND DOWNWARD ATTACK
2681 						//trace_t tr;
2682 
2683 						//if (PM_SomeoneInFront(&tr))
2684 						{
2685 							PM_SetSaberMove(PM_SaberFlipOverAttackMove());
2686 							pml.groundPlane = qfalse;
2687 							pml.walking = qfalse;
2688 							pm->ps->pm_flags |= PMF_JUMP_HELD;
2689 							pm->ps->groundEntityNum = ENTITYNUM_NONE;
2690 							VectorClear(pml.groundTrace.plane.normal);
2691 
2692 							pm->ps->weaponTime = pm->ps->torsoTimer;
2693 						}
2694 					}
2695 				}
2696 				else if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_3 )
2697 				{//using strong attacks
2698 					if ( pm->cmd.forwardmove > 0 && //going forward
2699 						(pm->cmd.buttons & BUTTON_ATTACK) && //must be holding attack still
2700 						PM_GroundDistance() < 32 &&
2701 						!BG_InSpecialJump(pm->ps->legsAnim))
2702 					{//strong attack: jump-hack
2703 						PM_SetSaberMove( PM_SaberJumpAttackMove() );
2704 						pml.groundPlane = qfalse;
2705 						pml.walking = qfalse;
2706 						pm->ps->pm_flags |= PMF_JUMP_HELD;
2707 						pm->ps->groundEntityNum = ENTITYNUM_NONE;
2708 						VectorClear(pml.groundTrace.plane.normal);
2709 
2710 						pm->ps->weaponTime = pm->ps->torsoTimer;
2711 					}
2712 				}
2713 			}
2714 		}
2715 	}
2716 	*/
2717 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
2718 	{
2719 		return qfalse;
2720 	}
2721 	if ( pm->cmd.upmove > 0 )
2722 	{//no special jumps
2723 		pm->ps->velocity[2] = JUMP_VELOCITY;
2724 		PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
2725 		pm->ps->pm_flags |= PMF_JUMP_HELD;
2726 	}
2727 
2728 	//Jumping
2729 	pml.groundPlane = qfalse;
2730 	pml.walking = qfalse;
2731 	pm->ps->pm_flags |= PMF_JUMP_HELD;
2732 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
2733 	PM_SetForceJumpZStart(pm->ps->origin[2]);
2734 
2735 	PM_AddEvent( EV_JUMP );
2736 
2737 	//Set the animations
2738 	if ( pm->ps->gravity > 0 && !BG_InSpecialJump( pm->ps->legsAnim ) )
2739 	{
2740 		PM_JumpForDir();
2741 	}
2742 
2743 	return qtrue;
2744 }
2745 /*
2746 =============
2747 PM_CheckWaterJump
2748 =============
2749 */
PM_CheckWaterJump(void)2750 static qboolean	PM_CheckWaterJump( void ) {
2751 	vec3_t	spot;
2752 	int		cont;
2753 	vec3_t	flatforward;
2754 
2755 	if (pm->ps->pm_time) {
2756 		return qfalse;
2757 	}
2758 
2759 	// check for water jump
2760 	if ( pm->waterlevel != 2 ) {
2761 		return qfalse;
2762 	}
2763 
2764 	flatforward[0] = pml.forward[0];
2765 	flatforward[1] = pml.forward[1];
2766 	flatforward[2] = 0;
2767 	VectorNormalize (flatforward);
2768 
2769 	VectorMA (pm->ps->origin, 30, flatforward, spot);
2770 	spot[2] += 4;
2771 	cont = pm->pointcontents (spot, pm->ps->clientNum );
2772 	if ( !(cont & CONTENTS_SOLID) ) {
2773 		return qfalse;
2774 	}
2775 
2776 	spot[2] += 16;
2777 	cont = pm->pointcontents (spot, pm->ps->clientNum );
2778 	if ( cont & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) ) {
2779 		return qfalse;
2780 	}
2781 
2782 	// jump out of water
2783 	VectorScale (pml.forward, 200, pm->ps->velocity);
2784 	pm->ps->velocity[2] = 350;
2785 
2786 	pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
2787 	pm->ps->pm_time = 2000;
2788 
2789 	return qtrue;
2790 }
2791 
2792 //============================================================================
2793 
2794 
2795 /*
2796 ===================
2797 PM_WaterJumpMove
2798 
2799 Flying out of the water
2800 ===================
2801 */
PM_WaterJumpMove(void)2802 static void PM_WaterJumpMove( void ) {
2803 	// waterjump has no control, but falls
2804 
2805 	PM_StepSlideMove( qtrue );
2806 
2807 	pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2808 	if (pm->ps->velocity[2] < 0) {
2809 		// cancel as soon as we are falling down again
2810 		pm->ps->pm_flags &= ~PMF_ALL_TIMES;
2811 		pm->ps->pm_time = 0;
2812 	}
2813 }
2814 
2815 /*
2816 ===================
2817 PM_WaterMove
2818 
2819 ===================
2820 */
PM_WaterMove(void)2821 static void PM_WaterMove( void ) {
2822 	int		i;
2823 	vec3_t	wishvel;
2824 	float	wishspeed;
2825 	vec3_t	wishdir;
2826 	float	scale;
2827 	float	vel;
2828 
2829 	if ( PM_CheckWaterJump() ) {
2830 		PM_WaterJumpMove();
2831 		return;
2832 	}
2833 #if 0
2834 	// jump = head for surface
2835 	if ( pm->cmd.upmove >= 10 ) {
2836 		if (pm->ps->velocity[2] > -300) {
2837 			if ( pm->watertype == CONTENTS_WATER ) {
2838 				pm->ps->velocity[2] = 100;
2839 			} else if (pm->watertype == CONTENTS_SLIME) {
2840 				pm->ps->velocity[2] = 80;
2841 			} else {
2842 				pm->ps->velocity[2] = 50;
2843 			}
2844 		}
2845 	}
2846 #endif
2847 	PM_Friction ();
2848 
2849 	scale = PM_CmdScale( &pm->cmd );
2850 	//
2851 	// user intentions
2852 	//
2853 	if ( !scale ) {
2854 		wishvel[0] = 0;
2855 		wishvel[1] = 0;
2856 		wishvel[2] = -60;		// sink towards bottom
2857 	} else {
2858 		for (i=0 ; i<3 ; i++)
2859 			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
2860 
2861 		wishvel[2] += scale * pm->cmd.upmove;
2862 	}
2863 
2864 	VectorCopy (wishvel, wishdir);
2865 	wishspeed = VectorNormalize(wishdir);
2866 
2867 	if ( wishspeed > pm->ps->speed * pm_swimScale ) {
2868 		wishspeed = pm->ps->speed * pm_swimScale;
2869 	}
2870 
2871 	PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
2872 
2873 	// make sure we can go up slopes easily under water
2874 	if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
2875 		vel = VectorLength(pm->ps->velocity);
2876 		// slide along the ground plane
2877 		PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
2878 			pm->ps->velocity, OVERCLIP );
2879 
2880 		VectorNormalize(pm->ps->velocity);
2881 		VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
2882 	}
2883 
2884 	PM_SlideMove( qfalse );
2885 }
2886 
2887 /*
2888 ===================
2889 PM_FlyVehicleMove
2890 
2891 ===================
2892 */
PM_FlyVehicleMove(void)2893 static void PM_FlyVehicleMove( void )
2894 {
2895 	int		i;
2896 	vec3_t	wishvel;
2897 	float	wishspeed;
2898 	vec3_t	wishdir;
2899 	float	scale;
2900 	float	zVel;
2901 	float	fmove = 0.0f, smove = 0.0f;
2902 
2903 	// We don't use these here because we pre-calculate the movedir in the vehicle update anyways, and if
2904 	// you leave this, you get strange motion during boarding (the player can move the vehicle).
2905 	//fmove = pm->cmd.forwardmove;
2906 	//smove = pm->cmd.rightmove;
2907 
2908 	// normal slowdown
2909 	if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE )
2910 	{//falling
2911 		zVel = pm->ps->velocity[2];
2912 		PM_Friction ();
2913 		pm->ps->velocity[2] = zVel;
2914 	}
2915 	else
2916 	{
2917 		PM_Friction ();
2918 		if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
2919 		{
2920 			pm->ps->velocity[2] = 0;	// ignore slope movement
2921 		}
2922 	}
2923 
2924 	scale = PM_CmdScale( &pm->cmd );
2925 
2926 	// Get The WishVel And WishSpeed
2927 	//-------------------------------
2928 	if ( pm->ps->clientNum >= MAX_CLIENTS )
2929 	{//NPC
2930 
2931 		// If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
2932 		//--------------------------------------------------------------------------------------------
2933 		if ((fmove!=0.0f || smove!=0.0f) &&	VectorCompare(pm->ps->moveDir, vec3_origin))
2934 		{
2935 			//trap->Printf("Generating MoveDir\n");
2936 			for ( i = 0 ; i < 3 ; i++ )
2937 			{
2938 				wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2939 			}
2940 
2941 			VectorCopy( wishvel, wishdir );
2942 			wishspeed = VectorNormalize(wishdir);
2943 			wishspeed *= scale;
2944 		}
2945 		// Otherwise, Use The Move Dir
2946 		//-----------------------------
2947 		else
2948 		{
2949 			wishspeed = pm->ps->speed;
2950 			VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
2951 			VectorCopy( pm->ps->moveDir, wishdir );
2952 		}
2953 	}
2954 	else
2955 	{
2956 		for ( i = 0 ; i < 3 ; i++ ) {
2957 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2958 		}
2959 		// when going up or down slopes the wish velocity should Not be zero
2960 	//	wishvel[2] = 0;
2961 
2962 		VectorCopy (wishvel, wishdir);
2963 		wishspeed = VectorNormalize(wishdir);
2964 		wishspeed *= scale;
2965 	}
2966 
2967 	// Handle negative speed.
2968 	if ( wishspeed < 0 )
2969 	{
2970 		wishspeed = wishspeed * -1.0f;
2971 		VectorScale( wishvel, -1.0f, wishvel );
2972 		VectorScale( wishdir, -1.0f, wishdir );
2973 	}
2974 
2975 	VectorCopy( wishvel, wishdir );
2976 	wishspeed = VectorNormalize( wishdir );
2977 
2978 	PM_Accelerate( wishdir, wishspeed, 100 );
2979 
2980 	PM_StepSlideMove( 1 );
2981 }
2982 
2983 /*
2984 ===================
2985 PM_FlyMove
2986 
2987 Only with the flight powerup
2988 ===================
2989 */
PM_FlyMove(void)2990 static void PM_FlyMove( void ) {
2991 	int		i;
2992 	vec3_t	wishvel;
2993 	float	wishspeed;
2994 	vec3_t	wishdir;
2995 	float	scale;
2996 
2997 	// normal slowdown
2998 	PM_Friction ();
2999 
3000 	scale = PM_CmdScale( &pm->cmd );
3001 
3002 	if ( pm->ps->pm_type == PM_SPECTATOR && pm->cmd.buttons & BUTTON_ALT_ATTACK) {
3003 		//turbo boost
3004 		scale *= 10;
3005 	}
3006 
3007 	//
3008 	// user intentions
3009 	//
3010 	if ( !scale ) {
3011 		wishvel[0] = 0;
3012 		wishvel[1] = 0;
3013 		wishvel[2] = pm->ps->speed * (pm->cmd.upmove/127.0f);
3014 	} else {
3015 		for (i=0 ; i<3 ; i++) {
3016 			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
3017 		}
3018 
3019 		wishvel[2] += scale * pm->cmd.upmove;
3020 	}
3021 
3022 	VectorCopy (wishvel, wishdir);
3023 	wishspeed = VectorNormalize(wishdir);
3024 
3025 	PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
3026 
3027 	PM_StepSlideMove( qfalse );
3028 }
3029 
3030 
3031 /*
3032 ===================
3033 PM_AirMove
3034 
3035 ===================
3036 */
PM_AirMove(void)3037 static void PM_AirMove( void ) {
3038 	int			i;
3039 	vec3_t		wishvel;
3040 	float		fmove, smove;
3041 	vec3_t		wishdir;
3042 	float		wishspeed;
3043 	float		scale;
3044 	float		accelerate;
3045 	usercmd_t	cmd;
3046 	Vehicle_t	*pVeh = NULL;
3047 
3048 	if (pm->ps->clientNum >= MAX_CLIENTS)
3049 	{
3050 		bgEntity_t	*pEnt = pm_entSelf;
3051 
3052 		if ( pEnt && pEnt->s.NPC_class == CLASS_VEHICLE )
3053 		{
3054 			pVeh = pEnt->m_pVehicle;
3055 		}
3056 	}
3057 
3058 	if (pm->ps->pm_type != PM_SPECTATOR)
3059 	{
3060 #if METROID_JUMP
3061 		PM_CheckJump();
3062 #else
3063 		if (pm->ps->fd.forceJumpZStart &&
3064 			pm->ps->forceJumpFlip)
3065 		{
3066 			PM_CheckJump();
3067 		}
3068 #endif
3069 	}
3070 	PM_Friction();
3071 
3072 	fmove = pm->cmd.forwardmove;
3073 	smove = pm->cmd.rightmove;
3074 
3075 	cmd = pm->cmd;
3076 	scale = PM_CmdScale( &cmd );
3077 
3078 	// set the movementDir so clients can rotate the legs for strafing
3079 	PM_SetMovementDir();
3080 
3081 	// project moves down to flat plane
3082 	pml.forward[2] = 0;
3083 	pml.right[2] = 0;
3084 	VectorNormalize (pml.forward);
3085 	VectorNormalize (pml.right);
3086 
3087 	if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 )
3088 	{//in a hovering vehicle, have air control
3089 		if ( 1 )
3090 		{
3091 			wishspeed = pm->ps->speed;
3092 			VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
3093 			VectorCopy( pm->ps->moveDir, wishdir );
3094 			scale = 1.0f;
3095 		}
3096 #if 0
3097 		else
3098 		{
3099 			float controlMod = 1.0f;
3100 			if ( pml.groundPlane )
3101 			{//on a slope of some kind, shouldn't have much control and should slide a lot
3102 				controlMod = pml.groundTrace.plane.normal[2];
3103 			}
3104 
3105 			vec3_t	vfwd, vrt;
3106 			vec3_t	vAngles;
3107 
3108 			VectorCopy( pVeh->m_vOrientation, vAngles );
3109 			vAngles[ROLL] = 0;//since we're a hovercraft, we really don't want to stafe up into the air if we're banking
3110 			AngleVectors( vAngles, vfwd, vrt, NULL );
3111 
3112 			float speed = pm->ps->speed;
3113 			float strafeSpeed = 0;
3114 
3115 			if ( fmove < 0 )
3116 			{//going backwards
3117 				if ( speed < 0 )
3118 				{//speed is negative, but since our command is reverse, make speed positive
3119 					speed = fabs( speed );
3120 					/*
3121 					if ( pml.groundPlane )
3122 					{//on a slope, still have some control
3123 						speed = fabs( speed );
3124 					}
3125 					else
3126 					{//can't reverse in air
3127 						speed = 0;
3128 					}
3129 					*/
3130 				}
3131 				else if ( speed > 0 )
3132 				{//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually)
3133 					speed = 0;
3134 				}
3135 			}
3136 
3137 			if ( pm->ps->clientNum < MAX_CLIENTS )
3138 			{//do normal adding to wishvel
3139 				VectorScale( vfwd, speed*controlMod*(fmove/127.0f), wishvel );
3140 				//just add strafing
3141 				if ( pVeh->m_pVehicleInfo->strafePerc )
3142 				{//we can strafe
3143 					if ( smove )
3144 					{//trying to strafe
3145 						float minSpeed = pVeh->m_pVehicleInfo->speedMax * 0.5f * pVeh->m_pVehicleInfo->strafePerc;
3146 						strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ))*pVeh->m_pVehicleInfo->strafePerc;
3147 						if ( strafeSpeed < minSpeed )
3148 						{
3149 							strafeSpeed = minSpeed;
3150 						}
3151 						strafeSpeed *= controlMod*((float)(smove))/127.0f;
3152 						if ( strafeSpeed < 0 )
3153 						{//pm_accelerate does not understand negative numbers
3154 							strafeSpeed *= -1;
3155 							VectorScale( vrt, -1, vrt );
3156 						}
3157 						//now just add it to actual velocity
3158 						PM_Accelerate( vrt, strafeSpeed, pVeh->m_pVehicleInfo->traction );
3159 					}
3160 				}
3161 			}
3162 			else
3163 			{
3164 				if ( pVeh->m_pVehicleInfo->strafePerc )
3165 				{//we can strafe
3166 					if ( pm->ps->clientNum )
3167 					{//alternate control scheme: can strafe
3168 						if ( smove )
3169 						{
3170 							/*
3171 							if ( fmove > 0 )
3172 							{//actively accelerating
3173 								strafeSpeed = pm->ps->speed;
3174 							}
3175 							else
3176 							{//not stepping on accelerator, only strafe based on magnitude of current forward velocity
3177 								strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ));
3178 							}
3179 							*/
3180 							strafeSpeed = ((float)(smove))/127.0f;
3181 						}
3182 					}
3183 				}
3184 				//strafing takes away from forward speed
3185 				VectorScale( vfwd, (fmove/127.0f)*(1.0f-pVeh->m_pVehicleInfo->strafePerc), wishvel );
3186 				if ( strafeSpeed )
3187 				{
3188 					VectorMA( wishvel, strafeSpeed*pVeh->m_pVehicleInfo->strafePerc, vrt, wishvel );
3189 				}
3190 				VectorNormalize( wishvel );
3191 				VectorScale( wishvel, speed*controlMod, wishvel );
3192 			}
3193 		}
3194 #endif
3195 	}
3196 	else if ( gPMDoSlowFall )
3197 	{//no air-control
3198 		VectorClear( wishvel );
3199 	}
3200 	else if (pm->ps->pm_type == PM_JETPACK)
3201 	{ //reduced air control while not jetting
3202 		for ( i = 0 ; i < 2 ; i++ )
3203 		{
3204 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3205 		}
3206 		wishvel[2] = 0;
3207 
3208 		if (pm->cmd.upmove <= 0)
3209 		{
3210             VectorScale(wishvel, 0.8f, wishvel);
3211 		}
3212 		else
3213 		{ //if we are jetting then we have more control than usual
3214             VectorScale(wishvel, 2.0f, wishvel);
3215 		}
3216 	}
3217 	else
3218 	{
3219 		for ( i = 0 ; i < 2 ; i++ )
3220 		{
3221 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3222 		}
3223 		wishvel[2] = 0;
3224 	}
3225 
3226 	VectorCopy (wishvel, wishdir);
3227 	wishspeed = VectorNormalize(wishdir);
3228 	wishspeed *= scale;
3229 
3230 	accelerate = pm_airaccelerate;
3231 	if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER )
3232 	{//speeders have more control in air
3233 		//in mid-air
3234 		accelerate = pVeh->m_pVehicleInfo->traction;
3235 		if ( pml.groundPlane )
3236 		{//on a slope of some kind, shouldn't have much control and should slide a lot
3237 			accelerate *= 0.5f;
3238 		}
3239 	}
3240 	// not on ground, so little effect on velocity
3241 	PM_Accelerate (wishdir, wishspeed, accelerate);
3242 
3243 	// we may have a ground plane that is very steep, even
3244 	// though we don't have a groundentity
3245 	// slide along the steep plane
3246 	if ( pml.groundPlane )
3247 	{
3248 		if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
3249 		{//don't slide when stuck to a wall
3250 			if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) )
3251 			{
3252 				PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
3253 					pm->ps->velocity, OVERCLIP );
3254 			}
3255 		}
3256 	}
3257 
3258 	if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
3259 	{//no grav when stuck to wall
3260 		PM_StepSlideMove( qfalse );
3261 	}
3262 	else
3263 	{
3264 		PM_StepSlideMove( qtrue );
3265 	}
3266 }
3267 
3268 /*
3269 ===================
3270 PM_WalkMove
3271 
3272 ===================
3273 */
PM_WalkMove(void)3274 static void PM_WalkMove( void ) {
3275 	int			i;
3276 	vec3_t		wishvel;
3277 	float		fmove, smove;
3278 	vec3_t		wishdir;
3279 	float		wishspeed = 0.0f;
3280 	float		scale;
3281 	usercmd_t	cmd;
3282 	float		accelerate;
3283 	float		vel;
3284 	qboolean	npcMovement = qfalse;
3285 
3286 	if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
3287 		// begin swimming
3288 		PM_WaterMove();
3289 		return;
3290 	}
3291 
3292 
3293 	if (pm->ps->pm_type != PM_SPECTATOR)
3294 	{
3295 		if ( PM_CheckJump () ) {
3296 			// jumped away
3297 			if ( pm->waterlevel > 1 ) {
3298 				PM_WaterMove();
3299 			} else {
3300 				PM_AirMove();
3301 			}
3302 			return;
3303 		}
3304 	}
3305 
3306 	PM_Friction ();
3307 
3308 	fmove = pm->cmd.forwardmove;
3309 	smove = pm->cmd.rightmove;
3310 
3311 	cmd = pm->cmd;
3312 	scale = PM_CmdScale( &cmd );
3313 
3314 	// set the movementDir so clients can rotate the legs for strafing
3315 	PM_SetMovementDir();
3316 
3317 	// project moves down to flat plane
3318 	pml.forward[2] = 0;
3319 	pml.right[2] = 0;
3320 
3321 	// project the forward and right directions onto the ground plane
3322 	PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
3323 	PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
3324 	//
3325 	VectorNormalize (pml.forward);
3326 	VectorNormalize (pml.right);
3327 
3328 	// Get The WishVel And WishSpeed
3329 	//-------------------------------
3330 	if ( pm->ps->clientNum >= MAX_CLIENTS && !VectorCompare( pm->ps->moveDir, vec3_origin ) )
3331 	{//NPC
3332 		bgEntity_t *pEnt = pm_entSelf;
3333 
3334 		if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
3335 		{
3336 			// If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
3337 			//--------------------------------------------------------------------------------------------
3338 			if ((fmove!=0.0f || smove!=0.0f) &&	VectorCompare(pm->ps->moveDir, vec3_origin))
3339 			{
3340 				//trap->Printf("Generating MoveDir\n");
3341 				for ( i = 0 ; i < 3 ; i++ )
3342 				{
3343 					wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3344 				}
3345 
3346 				VectorCopy( wishvel, wishdir );
3347 				wishspeed = VectorNormalize(wishdir);
3348 				wishspeed *= scale;
3349 			}
3350 			// Otherwise, Use The Move Dir
3351 			//-----------------------------
3352 			else
3353 			{
3354 				//wishspeed = pm->ps->speed;
3355 				VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
3356 				VectorCopy (wishvel, wishdir);
3357 				wishspeed = VectorNormalize(wishdir);
3358 			}
3359 
3360 			npcMovement = qtrue;
3361 		}
3362 	}
3363 
3364 	if (!npcMovement)
3365 	{
3366 		for ( i = 0 ; i < 3 ; i++ ) {
3367 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3368 		}
3369 		// when going up or down slopes the wish velocity should Not be zero
3370 
3371 		VectorCopy (wishvel, wishdir);
3372 		wishspeed = VectorNormalize(wishdir);
3373 		wishspeed *= scale;
3374 	}
3375 
3376 	// clamp the speed lower if ducking
3377 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
3378 		if ( wishspeed > pm->ps->speed * pm_duckScale ) {
3379 			wishspeed = pm->ps->speed * pm_duckScale;
3380 		}
3381 	}
3382 	else if ( (pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
3383 		!PM_InRollComplete(pm->ps, pm->ps->legsAnim))
3384 	{
3385 		if ( wishspeed > pm->ps->speed * pm_duckScale ) {
3386 			wishspeed = pm->ps->speed * pm_duckScale;
3387 		}
3388 	}
3389 
3390 	// clamp the speed lower if wading or walking on the bottom
3391 	if ( pm->waterlevel ) {
3392 		float	waterScale;
3393 
3394 		waterScale = pm->waterlevel / 3.0;
3395 		waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
3396 		if ( wishspeed > pm->ps->speed * waterScale ) {
3397 			wishspeed = pm->ps->speed * waterScale;
3398 		}
3399 	}
3400 
3401 	// when a player gets hit, they temporarily lose
3402 	// full control, which allows them to be moved a bit
3403 	if ( pm_flying == FLY_HOVER )
3404 	{
3405 		accelerate = pm_vehicleaccelerate;
3406 	}
3407 	else if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
3408 	{
3409 		accelerate = pm_airaccelerate;
3410 	}
3411 	else
3412 	{
3413 		accelerate = pm_accelerate;
3414 	}
3415 
3416 	PM_Accelerate (wishdir, wishspeed, accelerate);
3417 	/*
3418 	if (pm->ps->clientNum >= MAX_CLIENTS)
3419 	{
3420 #ifdef _GAME
3421 		Com_Printf("^1S: %f, %f\n", wishspeed, pm->ps->speed);
3422 #else
3423 		Com_Printf("^2C: %f, %f\n", wishspeed, pm->ps->speed);
3424 #endif
3425 	}
3426 	*/
3427 
3428 	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
3429 	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
3430 
3431 	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK )
3432 	{
3433 		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3434 	}
3435 
3436 	vel = VectorLength(pm->ps->velocity);
3437 
3438 	// slide along the ground plane
3439 	PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
3440 		pm->ps->velocity, OVERCLIP );
3441 
3442 	// don't decrease velocity when going up or down a slope
3443 	VectorNormalize(pm->ps->velocity);
3444 	VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
3445 
3446 	// don't do anything if standing still
3447 	if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
3448 		return;
3449 	}
3450 
3451 	PM_StepSlideMove( qfalse );
3452 
3453 	//Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
3454 }
3455 
3456 
3457 /*
3458 ==============
3459 PM_DeadMove
3460 ==============
3461 */
PM_DeadMove(void)3462 static void PM_DeadMove( void ) {
3463 	float	forward;
3464 
3465 	if ( !pml.walking ) {
3466 		return;
3467 	}
3468 
3469 	// extra friction
3470 
3471 	forward = VectorLength (pm->ps->velocity);
3472 	forward -= 20;
3473 	if ( forward <= 0 ) {
3474 		VectorClear (pm->ps->velocity);
3475 	} else {
3476 		VectorNormalize (pm->ps->velocity);
3477 		VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
3478 	}
3479 }
3480 
3481 
3482 /*
3483 ===============
3484 PM_NoclipMove
3485 ===============
3486 */
PM_NoclipMove(void)3487 static void PM_NoclipMove( void ) {
3488 	float	speed, drop, friction, control, newspeed;
3489 	int			i;
3490 	vec3_t		wishvel;
3491 	float		fmove, smove;
3492 	vec3_t		wishdir;
3493 	float		wishspeed;
3494 	float		scale;
3495 
3496 	pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
3497 
3498 	// friction
3499 
3500 	speed = VectorLength (pm->ps->velocity);
3501 	if (speed < 1)
3502 	{
3503 		VectorCopy (vec3_origin, pm->ps->velocity);
3504 	}
3505 	else
3506 	{
3507 		drop = 0;
3508 
3509 		friction = pm_friction*1.5;	// extra friction
3510 		control = speed < pm_stopspeed ? pm_stopspeed : speed;
3511 		drop += control*friction*pml.frametime;
3512 
3513 		// scale the velocity
3514 		newspeed = speed - drop;
3515 		if (newspeed < 0)
3516 			newspeed = 0;
3517 		newspeed /= speed;
3518 
3519 		VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
3520 	}
3521 
3522 	// accelerate
3523 	scale = PM_CmdScale( &pm->cmd );
3524 	if (pm->cmd.buttons & BUTTON_ATTACK) {	//turbo boost
3525 		scale *= 10;
3526 	}
3527 	if (pm->cmd.buttons & BUTTON_ALT_ATTACK) {	//turbo boost
3528 		scale *= 10;
3529 	}
3530 
3531 	fmove = pm->cmd.forwardmove;
3532 	smove = pm->cmd.rightmove;
3533 
3534 	for (i=0 ; i<3 ; i++)
3535 		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3536 	wishvel[2] += pm->cmd.upmove;
3537 
3538 	VectorCopy (wishvel, wishdir);
3539 	wishspeed = VectorNormalize(wishdir);
3540 	wishspeed *= scale;
3541 
3542 	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
3543 
3544 	// move
3545 	VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
3546 }
3547 
3548 //============================================================================
3549 
3550 /*
3551 ================
3552 PM_FootstepForSurface
3553 
3554 Returns an event number appropriate for the groundsurface
3555 ================
3556 */
PM_FootstepForSurface(void)3557 static int PM_FootstepForSurface( void )
3558 {
3559 	if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS )
3560 	{
3561 		return 0;
3562 	}
3563 	return ( pml.groundTrace.surfaceFlags & MATERIAL_MASK );
3564 }
3565 
3566 extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
PM_TryRoll(void)3567 static int PM_TryRoll( void )
3568 {
3569 	trace_t	trace;
3570 	int		anim = -1;
3571 	vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
3572 
3573 	if ( BG_SaberInAttack( pm->ps->saberMove ) || BG_SaberInSpecialAttack( pm->ps->torsoAnim )
3574 		|| BG_SpinningSaberAnim( pm->ps->legsAnim )
3575 		|| PM_SaberInStart( pm->ps->saberMove ) )
3576 	{//attacking or spinning (or, if player, starting an attack)
3577 		if ( PM_CanRollFromSoulCal( pm->ps ) )
3578 		{//hehe
3579 		}
3580 		else
3581 		{
3582 			return 0;
3583 		}
3584 	}
3585 
3586 	if ((pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE) ||
3587 		PM_IsRocketTrooper() ||
3588 		BG_HasYsalamiri(pm->gametype, pm->ps) ||
3589 		!BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
3590 	{ //Not using saber, or can't use jump
3591 		return 0;
3592 	}
3593 
3594 	if ( pm->ps->weapon == WP_SABER )
3595 	{
3596 		saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 );
3597 		if ( saber
3598 			&& (saber->saberFlags&SFL_NO_ROLLS) )
3599 		{
3600 			return 0;
3601 		}
3602 		saber = BG_MySaber( pm->ps->clientNum, 1 );
3603 		if ( saber
3604 			&& (saber->saberFlags&SFL_NO_ROLLS) )
3605 		{
3606 			return 0;
3607 		}
3608 	}
3609 
3610 	VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]+STEPSIZE);
3611 	VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->ps->crouchheight);
3612 
3613 	VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
3614 
3615 	AngleVectors( fwdAngles, fwd, right, NULL );
3616 
3617 	if ( pm->cmd.forwardmove )
3618 	{ //check forward/backward rolls
3619 		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
3620 		{
3621 			anim = BOTH_ROLL_B;
3622 			VectorMA( pm->ps->origin, -64, fwd, traceto );
3623 		}
3624 		else
3625 		{
3626 			anim = BOTH_ROLL_F;
3627 			VectorMA( pm->ps->origin, 64, fwd, traceto );
3628 		}
3629 	}
3630 	else if ( pm->cmd.rightmove > 0 )
3631 	{ //right
3632 		anim = BOTH_ROLL_R;
3633 		VectorMA( pm->ps->origin, 64, right, traceto );
3634 	}
3635 	else if ( pm->cmd.rightmove < 0 )
3636 	{ //left
3637 		anim = BOTH_ROLL_L;
3638 		VectorMA( pm->ps->origin, -64, right, traceto );
3639 	}
3640 
3641 	if ( anim != -1 )
3642 	{ //We want to roll. Perform a trace to see if we can, and if so, send us into one.
3643 		pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );
3644 		if ( trace.fraction >= 1.0f )
3645 		{
3646 			pm->ps->saberMove = LS_NONE;
3647 			return anim;
3648 		}
3649 	}
3650 	return 0;
3651 }
3652 
3653 #ifdef _GAME
PM_CrashLandEffect(void)3654 static void PM_CrashLandEffect( void )
3655 {
3656 	float delta;
3657 	if ( pm->waterlevel )
3658 	{
3659 		return;
3660 	}
3661 	delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );?
3662 	if ( delta >= 30 )
3663 	{
3664 		vec3_t bottom;
3665 		int	effectID = -1;
3666 		int material = (pml.groundTrace.surfaceFlags&MATERIAL_MASK);
3667 		VectorSet( bottom, pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1 );
3668 		switch ( material )
3669 		{
3670 		case MATERIAL_MUD:
3671 			effectID = EFFECT_LANDING_MUD;
3672 			break;
3673 		case MATERIAL_SAND:
3674 			effectID = EFFECT_LANDING_SAND;
3675 			break;
3676 		case MATERIAL_DIRT:
3677 			effectID = EFFECT_LANDING_DIRT;
3678 			break;
3679 		case MATERIAL_SNOW:
3680 			effectID = EFFECT_LANDING_SNOW;
3681 			break;
3682 		case MATERIAL_GRAVEL:
3683 			effectID = EFFECT_LANDING_GRAVEL;
3684 			break;
3685 		}
3686 
3687 		if ( effectID != -1 )
3688 		{
3689 			G_PlayEffect( effectID, bottom, pml.groundTrace.plane.normal );
3690 		}
3691 	}
3692 }
3693 #endif
3694 /*
3695 =================
3696 PM_CrashLand
3697 
3698 Check for hard landings that generate sound events
3699 =================
3700 */
PM_CrashLand(void)3701 static void PM_CrashLand( void ) {
3702 	float		delta;
3703 	float		dist;
3704 	float		vel, acc;
3705 	float		t;
3706 	float		a, b, c, den;
3707 	qboolean	didRoll = qfalse;
3708 
3709 	// calculate the exact velocity on landing
3710 	dist = pm->ps->origin[2] - pml.previous_origin[2];
3711 	vel = pml.previous_velocity[2];
3712 	acc = -pm->ps->gravity;
3713 
3714 	a = acc / 2;
3715 	b = vel;
3716 	c = -dist;
3717 
3718 	den =  b * b - 4 * a * c;
3719 	if ( den < 0 ) {
3720 		pm->ps->inAirAnim = qfalse;
3721 		return;
3722 	}
3723 	t = (-b - sqrt( den ) ) / ( 2 * a );
3724 
3725 	delta = vel + t * acc;
3726 	delta = delta*delta * 0.0001;
3727 
3728 #ifdef _GAME
3729 	PM_CrashLandEffect();
3730 #endif
3731 	// ducking while falling doubles damage
3732 	if ( pm->ps->pm_flags & PMF_DUCKED ) {
3733 		delta *= 2;
3734 	}
3735 
3736 	if (pm->ps->legsAnim == BOTH_A7_KICK_F_AIR ||
3737 		pm->ps->legsAnim == BOTH_A7_KICK_B_AIR ||
3738 		pm->ps->legsAnim == BOTH_A7_KICK_R_AIR ||
3739 		pm->ps->legsAnim == BOTH_A7_KICK_L_AIR)
3740 	{
3741 		int landAnim = -1;
3742 		switch ( pm->ps->legsAnim )
3743 		{
3744 		case BOTH_A7_KICK_F_AIR:
3745 			landAnim = BOTH_FORCELAND1;
3746 			break;
3747 		case BOTH_A7_KICK_B_AIR:
3748 			landAnim = BOTH_FORCELANDBACK1;
3749 			break;
3750 		case BOTH_A7_KICK_R_AIR:
3751 			landAnim = BOTH_FORCELANDRIGHT1;
3752 			break;
3753 		case BOTH_A7_KICK_L_AIR:
3754 			landAnim = BOTH_FORCELANDLEFT1;
3755 			break;
3756 		}
3757 		if ( landAnim != -1 )
3758 		{
3759 			if ( pm->ps->torsoAnim == pm->ps->legsAnim )
3760 			{
3761 				PM_SetAnim(SETANIM_BOTH, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3762 			}
3763 			else
3764 			{
3765 				PM_SetAnim(SETANIM_LEGS, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3766 			}
3767 		}
3768 	}
3769 	else if (pm->ps->legsAnim == BOTH_FORCEJUMPLEFT1 ||
3770 			pm->ps->legsAnim == BOTH_FORCEJUMPRIGHT1 ||
3771 			pm->ps->legsAnim == BOTH_FORCEJUMPBACK1 ||
3772 			pm->ps->legsAnim == BOTH_FORCEJUMP1)
3773 	{
3774 		int fjAnim;
3775 		switch (pm->ps->legsAnim)
3776 		{
3777 		case BOTH_FORCEJUMPLEFT1:
3778 			fjAnim = BOTH_LANDLEFT1;
3779 			break;
3780 		case BOTH_FORCEJUMPRIGHT1:
3781 			fjAnim = BOTH_LANDRIGHT1;
3782 			break;
3783 		case BOTH_FORCEJUMPBACK1:
3784 			fjAnim = BOTH_LANDBACK1;
3785 			break;
3786 		default:
3787 			fjAnim = BOTH_LAND1;
3788 			break;
3789 		}
3790 		PM_SetAnim(SETANIM_BOTH, fjAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3791 	}
3792 	// decide which landing animation to use
3793 	else if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim && !pm->ps->m_iVehicleNum)
3794 	{ //only play a land animation if we transitioned into an in-air animation while off the ground
3795 		if (!BG_SaberInSpecial(pm->ps->saberMove))
3796 		{
3797 			if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
3798 				PM_ForceLegsAnim( BOTH_LANDBACK1 );
3799 			} else {
3800 				PM_ForceLegsAnim( BOTH_LAND1 );
3801 			}
3802 		}
3803 	}
3804 
3805 	if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
3806 	{ //saber handles its own anims
3807 		//This will push us back into our weaponready stance from the land anim.
3808 		if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
3809 		{
3810 			PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
3811 		}
3812 		else
3813 		{
3814 			if (pm->ps->weapon == WP_EMPLACED_GUN)
3815 			{
3816 				PM_StartTorsoAnim( BOTH_GUNSIT1 );
3817 			}
3818 			else
3819 			{
3820 				PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
3821 			}
3822 		}
3823 	}
3824 
3825 	if (!BG_InSpecialJump(pm->ps->legsAnim) ||
3826 		pm->ps->legsTimer < 1 ||
3827 		(pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ||
3828 		(pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT)
3829 	{ //Only set the timer if we're in an anim that can be interrupted (this would not be, say, a flip)
3830 		if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim)
3831 		{
3832 			if (!BG_SaberInSpecial(pm->ps->saberMove) || pm->ps->weapon != WP_SABER)
3833 			{
3834 				if (pm->ps->legsAnim != BOTH_FORCELAND1			&&	pm->ps->legsAnim != BOTH_FORCELANDBACK1 &&
3835 					pm->ps->legsAnim != BOTH_FORCELANDRIGHT1	&&	pm->ps->legsAnim != BOTH_FORCELANDLEFT1)
3836 				{ //don't override if we have started a force land
3837 					pm->ps->legsTimer = TIMER_LAND;
3838 				}
3839 			}
3840 		}
3841 	}
3842 
3843 	pm->ps->inAirAnim = qfalse;
3844 
3845 	if (pm->ps->m_iVehicleNum)
3846 	{ //don't do fall stuff while on a vehicle
3847 		return;
3848 	}
3849 
3850 	// never take falling damage if completely underwater
3851 	if ( pm->waterlevel == 3 ) {
3852 		return;
3853 	}
3854 
3855 	// reduce falling damage if there is standing water
3856 	if ( pm->waterlevel == 2 ) {
3857 		delta *= 0.25;
3858 	}
3859 	if ( pm->waterlevel == 1 ) {
3860 		delta *= 0.5;
3861 	}
3862 
3863 	if ( delta < 1 ) {
3864 		return;
3865 	}
3866 
3867 	if ( pm->ps->pm_flags & PMF_DUCKED )
3868 	{
3869 		if( delta >= 2 && !PM_InOnGroundAnim( pm->ps->legsAnim ) && !PM_InKnockDown( pm->ps ) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
3870 			pm->ps->forceHandExtend == HANDEXTEND_NONE )
3871 		{//roll!
3872 			int anim = PM_TryRoll();
3873 
3874 			if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
3875 			{
3876 				anim = 0;
3877 				pm->ps->legsTimer = 0;
3878 				pm->ps->legsAnim = 0;
3879 				PM_SetAnim(SETANIM_BOTH,BOTH_LAND1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3880 				pm->ps->legsTimer = TIMER_LAND;
3881 			}
3882 
3883 			if ( anim )
3884 			{//absorb some impact
3885 				pm->ps->legsTimer = 0;
3886 				delta /= 3; // /= 2 just cancels out the above delta *= 2 when landing while crouched, the roll itself should absorb a little damage
3887 				pm->ps->legsAnim = 0;
3888 				if (pm->ps->torsoAnim == BOTH_A7_SOULCAL)
3889 				{ //get out of it on torso
3890 					pm->ps->torsoTimer = 0;
3891 				}
3892 				PM_SetAnim(SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
3893 				didRoll = qtrue;
3894 			}
3895 		}
3896 	}
3897 
3898 	// SURF_NODAMAGE is used for bounce pads where you don't ever
3899 	// want to take damage or play a crunch sound
3900 	if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )  {
3901 		if (delta > 7)
3902 		{
3903 			int delta_send = (int)delta;
3904 
3905 			if (delta_send > 600)
3906 			{ //will never need to know any value above this
3907 				delta_send = 600;
3908 			}
3909 
3910 			if (pm->ps->fd.forceJumpZStart)
3911 			{
3912 				if ((int)pm->ps->origin[2] >= (int)pm->ps->fd.forceJumpZStart)
3913 				{ //was force jumping, landed on higher or same level as when force jump was started
3914 					if (delta_send > 8)
3915 					{
3916 						delta_send = 8;
3917 					}
3918 				}
3919 				else
3920 				{
3921 					if (delta_send > 8)
3922 					{
3923 						int dif = ((int)pm->ps->fd.forceJumpZStart - (int)pm->ps->origin[2]);
3924 						int dmgLess = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - dif);
3925 
3926 						if (dmgLess < 0)
3927 						{
3928 							dmgLess = 0;
3929 						}
3930 
3931 						delta_send -= (dmgLess*0.3);
3932 
3933 						if (delta_send < 8)
3934 						{
3935 							delta_send = 8;
3936 						}
3937 
3938 						//Com_Printf("Damage sub: %i\n", (int)((dmgLess*0.1)));
3939 					}
3940 				}
3941 			}
3942 
3943 			if (didRoll)
3944 			{ //Add the appropriate event..
3945 				PM_AddEventWithParm( EV_ROLL, delta_send );
3946 			}
3947 			else
3948 			{
3949 				PM_AddEventWithParm( EV_FALL, delta_send );
3950 			}
3951 		}
3952 		else
3953 		{
3954 			if (didRoll)
3955 			{
3956 				PM_AddEventWithParm( EV_ROLL, 0 );
3957 			}
3958 			else
3959 			{
3960 				PM_AddEventWithParm( EV_FOOTSTEP, PM_FootstepForSurface() );
3961 			}
3962 		}
3963 	}
3964 
3965 	// make sure velocity resets so we don't bounce back up again in case we miss the clear elsewhere
3966 	pm->ps->velocity[2] = 0;
3967 
3968 	// start footstep cycle over
3969 	pm->ps->bobCycle = 0;
3970 }
3971 
3972 /*
3973 =============
3974 PM_CorrectAllSolid
3975 =============
3976 */
PM_CorrectAllSolid(trace_t * trace)3977 static int PM_CorrectAllSolid( trace_t *trace ) {
3978 	int			i, j, k;
3979 	vec3_t		point;
3980 
3981 	if ( pm->debugLevel ) {
3982 		Com_Printf("%i:allsolid\n", c_pmove);
3983 	}
3984 
3985 	// jitter around
3986 	for (i = -1; i <= 1; i++) {
3987 		for (j = -1; j <= 1; j++) {
3988 			for (k = -1; k <= 1; k++) {
3989 				VectorCopy(pm->ps->origin, point);
3990 				point[0] += (float) i;
3991 				point[1] += (float) j;
3992 				point[2] += (float) k;
3993 				pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
3994 				if ( !trace->allsolid ) {
3995 					point[0] = pm->ps->origin[0];
3996 					point[1] = pm->ps->origin[1];
3997 					point[2] = pm->ps->origin[2] - 0.25;
3998 
3999 					pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4000 					pml.groundTrace = *trace;
4001 					return qtrue;
4002 				}
4003 			}
4004 		}
4005 	}
4006 
4007 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
4008 	pml.groundPlane = qfalse;
4009 	pml.walking = qfalse;
4010 
4011 	return qfalse;
4012 }
4013 
4014 /*
4015 =============
4016 PM_GroundTraceMissed
4017 
4018 The ground trace didn't hit a surface, so we are in freefall
4019 =============
4020 */
PM_GroundTraceMissed(void)4021 static void PM_GroundTraceMissed( void ) {
4022 	trace_t		trace;
4023 	vec3_t		point;
4024 
4025 	//rww - don't want to do this when handextend_choke, because you can be standing on the ground
4026 	//while still holding your throat.
4027 	if ( pm->ps->pm_type == PM_FLOAT )
4028 	{
4029 		//we're assuming this is because you're being choked
4030 		int parts = SETANIM_LEGS;
4031 
4032 		//rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
4033 		//a proper anim even when on the ground.
4034 		PM_SetAnim(parts, BOTH_CHOKE3, SETANIM_FLAG_OVERRIDE);
4035 	}
4036 	else if ( pm->ps->pm_type == PM_JETPACK )
4037 	{//jetpacking
4038 		//rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
4039 		//a proper anim even when on the ground.
4040 		//PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE);
4041 	}
4042 	//If the anim is choke3, act like we just went into the air because we aren't in a float
4043 	else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE || (pm->ps->legsAnim) == BOTH_CHOKE3 )
4044 	{
4045 		// we just transitioned into freefall
4046 		if ( pm->debugLevel ) {
4047 			Com_Printf("%i:lift\n", c_pmove);
4048 		}
4049 
4050 		// if they aren't in a jumping animation and the ground is a ways away, force into it
4051 		// if we didn't do the trace, the player would be backflipping down staircases
4052 		VectorCopy( pm->ps->origin, point );
4053 		point[2] -= 64;
4054 
4055 		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4056 		if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT ) {
4057 			if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD))
4058 			{
4059 				//PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE);
4060 				PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,0);
4061 				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
4062 			}
4063 			else if ( pm->cmd.forwardmove >= 0 )
4064 			{
4065 				PM_SetAnim(SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE);
4066 				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
4067 			}
4068 			else
4069 			{
4070 				PM_SetAnim(SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE);
4071 				pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
4072 			}
4073 
4074 			pm->ps->inAirAnim = qtrue;
4075 		}
4076 	}
4077 	else if (!pm->ps->inAirAnim)
4078 	{
4079 		// if they aren't in a jumping animation and the ground is a ways away, force into it
4080 		// if we didn't do the trace, the player would be backflipping down staircases
4081 		VectorCopy( pm->ps->origin, point );
4082 		point[2] -= 64;
4083 
4084 		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4085 		if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT )
4086 		{
4087 			pm->ps->inAirAnim = qtrue;
4088 		}
4089 	}
4090 
4091 	if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
4092 	{ //Client won't catch an animation restart because it only checks frame against incoming frame, so if you roll when you land after rolling
4093 	  //off of something it won't replay the roll anim unless we switch it off in the air. This fixes that.
4094 		PM_SetAnim(SETANIM_BOTH,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4095 		pm->ps->inAirAnim = qtrue;
4096 	}
4097 
4098 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
4099 	pml.groundPlane = qfalse;
4100 	pml.walking = qfalse;
4101 }
4102 
4103 
4104 /*
4105 =============
4106 PM_GroundTrace
4107 =============
4108 */
PM_GroundTrace(void)4109 static void PM_GroundTrace( void ) {
4110 	vec3_t		point;
4111 	trace_t		trace;
4112 	float minNormal = (float)MIN_WALK_NORMAL;
4113 
4114 	if ( pm->ps->clientNum >= MAX_CLIENTS)
4115 	{
4116 		bgEntity_t *pEnt = pm_entSelf;
4117 
4118 		if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
4119 		{
4120 			minNormal = pEnt->m_pVehicle->m_pVehicleInfo->maxSlope;
4121 		}
4122 	}
4123 
4124 	point[0] = pm->ps->origin[0];
4125 	point[1] = pm->ps->origin[1];
4126 	point[2] = pm->ps->origin[2] - 0.25;
4127 
4128 	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4129 	pml.groundTrace = trace;
4130 
4131 	// do something corrective if the trace starts in a solid...
4132 	if ( trace.allsolid ) {
4133 		if ( !PM_CorrectAllSolid(&trace) )
4134 			return;
4135 	}
4136 
4137 	if (pm->ps->pm_type == PM_FLOAT || pm->ps->pm_type == PM_JETPACK)
4138 	{
4139 		PM_GroundTraceMissed();
4140 		pml.groundPlane = qfalse;
4141 		pml.walking = qfalse;
4142 		return;
4143 	}
4144 
4145 	// if the trace didn't hit anything, we are in free fall
4146 	if ( trace.fraction == 1.0 ) {
4147 		PM_GroundTraceMissed();
4148 		pml.groundPlane = qfalse;
4149 		pml.walking = qfalse;
4150 		return;
4151 	}
4152 
4153 	// check if getting thrown off the ground
4154 	if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
4155 		if ( pm->debugLevel ) {
4156 			Com_Printf("%i:kickoff\n", c_pmove);
4157 		}
4158 		// go into jump animation
4159 		if ( pm->cmd.forwardmove >= 0 ) {
4160 			PM_ForceLegsAnim( BOTH_JUMP1 );
4161 			pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
4162 		} else {
4163 			PM_ForceLegsAnim( BOTH_JUMPBACK1 );
4164 			pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
4165 		}
4166 
4167 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
4168 		pml.groundPlane = qfalse;
4169 		pml.walking = qfalse;
4170 		return;
4171 	}
4172 
4173 	// slopes that are too steep will not be considered onground
4174 	if ( trace.plane.normal[2] < minNormal ) {
4175 		if ( pm->debugLevel ) {
4176 			Com_Printf("%i:steep\n", c_pmove);
4177 		}
4178 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
4179 		pml.groundPlane = qtrue;
4180 		pml.walking = qfalse;
4181 		return;
4182 	}
4183 
4184 	pml.groundPlane = qtrue;
4185 	pml.walking = qtrue;
4186 
4187 	// hitting solid ground will end a waterjump
4188 	if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
4189 	{
4190 		pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
4191 		pm->ps->pm_time = 0;
4192 	}
4193 
4194 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
4195 		// just hit the ground
4196 		if ( pm->debugLevel ) {
4197 			Com_Printf("%i:Land\n", c_pmove);
4198 		}
4199 
4200 		PM_CrashLand();
4201 
4202 #ifdef _GAME
4203 		if (pm->ps->clientNum < MAX_CLIENTS &&
4204 			!pm->ps->m_iVehicleNum &&
4205 			trace.entityNum < ENTITYNUM_WORLD &&
4206 			trace.entityNum >= MAX_CLIENTS &&
4207 			!pm->ps->zoomMode &&
4208 			pm_entSelf)
4209 		{ //check if we landed on a vehicle
4210 			gentity_t *trEnt = &g_entities[trace.entityNum];
4211 			if (trEnt->inuse && trEnt->client && trEnt->s.eType == ET_NPC && trEnt->s.NPC_class == CLASS_VEHICLE &&
4212 				!trEnt->client->ps.m_iVehicleNum &&
4213 				trEnt->m_pVehicle &&
4214 				trEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
4215 				trEnt->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
4216 			{ //it's a vehicle alright, let's board it.. if it's not an atst or ship
4217 				if (!BG_SaberInSpecial(pm->ps->saberMove) &&
4218 					pm->ps->forceHandExtend == HANDEXTEND_NONE &&
4219 					pm->ps->weaponTime <= 0)
4220 				{
4221 					gentity_t *servEnt = (gentity_t *)pm_entSelf;
4222 					if (level.gametype < GT_TEAM ||
4223 						!trEnt->alliedTeam ||
4224 						(trEnt->alliedTeam == servEnt->client->sess.sessionTeam))
4225 					{ //not belonging to a team, or client is on same team
4226 						trEnt->m_pVehicle->m_pVehicleInfo->Board(trEnt->m_pVehicle, pm_entSelf);
4227 					}
4228 				}
4229 			}
4230 		}
4231 #endif
4232 
4233 		// don't do landing time if we were just going down a slope
4234 		if ( pml.previous_velocity[2] < -200 ) {
4235 			// don't allow another jump for a little while
4236 			pm->ps->pm_flags |= PMF_TIME_LAND;
4237 			pm->ps->pm_time = 250;
4238 		}
4239 	}
4240 
4241 	pm->ps->groundEntityNum = trace.entityNum;
4242 	pm->ps->lastOnGround = pm->cmd.serverTime;
4243 
4244 	PM_AddTouchEnt( trace.entityNum );
4245 }
4246 
4247 
4248 /*
4249 =============
4250 PM_SetWaterLevel
4251 =============
4252 */
PM_SetWaterLevel(void)4253 static void PM_SetWaterLevel( void ) {
4254 	vec3_t		point;
4255 	int			cont;
4256 	int			sample1;
4257 	int			sample2;
4258 
4259 	//
4260 	// get waterlevel, accounting for ducking
4261 	//
4262 	pm->waterlevel = 0;
4263 	pm->watertype = 0;
4264 
4265 	point[0] = pm->ps->origin[0];
4266 	point[1] = pm->ps->origin[1];
4267 	point[2] = pm->ps->origin[2] + MINS_Z + 1;
4268 	cont = pm->pointcontents( point, pm->ps->clientNum );
4269 
4270 	if ( cont & MASK_WATER ) {
4271 		sample2 = pm->ps->viewheight - MINS_Z;
4272 		sample1 = sample2 / 2;
4273 
4274 		pm->watertype = cont;
4275 		pm->waterlevel = 1;
4276 		point[2] = pm->ps->origin[2] + MINS_Z + sample1;
4277 		cont = pm->pointcontents (point, pm->ps->clientNum );
4278 		if ( cont & MASK_WATER ) {
4279 			pm->waterlevel = 2;
4280 			point[2] = pm->ps->origin[2] + MINS_Z + sample2;
4281 			cont = pm->pointcontents (point, pm->ps->clientNum );
4282 			if ( cont & MASK_WATER ){
4283 				pm->waterlevel = 3;
4284 			}
4285 		}
4286 	}
4287 
4288 }
4289 
PM_CheckDualForwardJumpDuck(void)4290 qboolean PM_CheckDualForwardJumpDuck( void )
4291 {
4292 	qboolean resized = qfalse;
4293 	if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
4294 	{
4295 		//dynamically reduce bounding box to let character sail over heads of enemies
4296 		if ( ( pm->ps->legsTimer >= 1450
4297 				&& PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 400 )
4298 			||(pm->ps->legsTimer >= 400
4299 				&& PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 1100 ) )
4300 		{//in a part of the anim that we're pretty much sideways in, raise up the mins
4301 			pm->mins[2] = 0;
4302 			pm->ps->pm_flags |= PMF_FIX_MINS;
4303 			resized = qtrue;
4304 		}
4305 	}
4306 	return resized;
4307 }
4308 
PM_CheckFixMins(void)4309 void PM_CheckFixMins( void )
4310 {
4311 	if ( (pm->ps->pm_flags&PMF_FIX_MINS) )// pm->mins[2] > DEFAULT_MINS_2 )
4312 	{//drop the mins back down
4313 		//do a trace to make sure it's okay
4314 		trace_t	trace;
4315 		vec3_t end, curMins, curMaxs;
4316 
4317 		VectorSet( end, pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2]+MINS_Z );
4318 		VectorSet( curMins, pm->mins[0], pm->mins[1], 0 );
4319 		VectorSet( curMaxs, pm->maxs[0], pm->maxs[1], pm->ps->standheight );
4320 
4321 		pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
4322 		if ( !trace.allsolid && !trace.startsolid )
4323 		{//should never start in solid
4324 			if ( trace.fraction >= 1.0f )
4325 			{//all clear
4326 				//drop the bottom of my bbox back down
4327 				pm->mins[2] = MINS_Z;
4328 				pm->ps->pm_flags &= ~PMF_FIX_MINS;
4329 			}
4330 			else
4331 			{//move me up so the bottom of my bbox will be where the trace ended, at least
4332 				//need to trace up, too
4333 				float updist = ((1.0f-trace.fraction) * -MINS_Z);
4334 				end[2] = pm->ps->origin[2]+updist;
4335 				pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
4336 				if ( !trace.allsolid && !trace.startsolid )
4337 				{//should never start in solid
4338 					if ( trace.fraction >= 1.0f )
4339 					{//all clear
4340 						//move me up
4341 						pm->ps->origin[2] += updist;
4342 						//drop the bottom of my bbox back down
4343 						pm->mins[2] = MINS_Z;
4344 						pm->ps->pm_flags &= ~PMF_FIX_MINS;
4345 					}
4346 					else
4347 					{//crap, no room to expand, so just crouch us
4348 						if ( pm->ps->legsAnim != BOTH_JUMPATTACK6
4349 							|| pm->ps->legsTimer <= 200 )
4350 						{//at the end of the anim, and we can't leave ourselves like this
4351 							//so drop the maxs, put the mins back and move us up
4352 							pm->maxs[2] += MINS_Z;
4353 							pm->ps->origin[2] -= MINS_Z;
4354 							pm->mins[2] = MINS_Z;
4355 							//this way we'll be in a crouch when we're done
4356 							if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
4357 							{
4358 								pm->ps->legsTimer = pm->ps->torsoTimer = 0;
4359 							}
4360 							pm->ps->pm_flags |= PMF_DUCKED;
4361 							//FIXME: do we need to set a crouch anim here?
4362 							pm->ps->pm_flags &= ~PMF_FIX_MINS;
4363 						}
4364 					}
4365 				}//crap, stuck
4366 			}
4367 		}//crap, stuck!
4368 	}
4369 }
4370 
PM_CanStand(void)4371 static qboolean PM_CanStand ( void )
4372 {
4373     qboolean canStand = qtrue;
4374     float x, y;
4375     trace_t trace;
4376 
4377     const vec3_t lineMins = { -5.0f, -5.0f, -2.5f };
4378     const vec3_t lineMaxs = { 5.0f, 5.0f, 0.0f };
4379 
4380     for ( x = pm->mins[0] + 5.0f; canStand && x <= (pm->maxs[0] - 5.0f); x += 10.0f )
4381     {
4382         for ( y = pm->mins[1] + 5.0f; y <= (pm->maxs[1] - 5.0f); y += 10.0f )
4383         {
4384 			vec3_t start, end;//
4385 			VectorSet( start, x, y, pm->maxs[2] );
4386 			VectorSet( end, x, y, pm->ps->standheight );
4387 
4388 			VectorAdd (start, pm->ps->origin, start);
4389 			VectorAdd (end, pm->ps->origin, end);
4390 
4391 			pm->trace (&trace, start, lineMins, lineMaxs, end, pm->ps->clientNum, pm->tracemask);
4392 			if ( trace.allsolid || trace.fraction < 1.0f )
4393 			{
4394 				canStand = qfalse;
4395 				break;
4396 			}
4397 		}
4398 	}
4399 
4400     return canStand;
4401 }
4402 
4403 /*
4404 ==============
4405 PM_CheckDuck
4406 
4407 Sets mins, maxs, and pm->ps->viewheight
4408 ==============
4409 */
PM_CheckDuck(void)4410 static void PM_CheckDuck (void)
4411 {
4412 //	trace_t	trace;
4413 
4414 	if ( pm->ps->m_iVehicleNum > 0 && pm->ps->m_iVehicleNum < ENTITYNUM_NONE )
4415 	{//riding a vehicle or are a vehicle
4416 		//no ducking or rolling when on a vehicle
4417 		//right?  not even on ones that you just ride on top of?
4418 		pm->ps->pm_flags &= ~PMF_DUCKED;
4419 		pm->ps->pm_flags &= ~PMF_ROLLING;
4420 		//NOTE: we don't clear the pm->cmd.upmove here because
4421 		//the vehicle code may need it later... but, for riders,
4422 		//it should have already been copied over to the vehicle, right?
4423 
4424 		if (pm->ps->clientNum >= MAX_CLIENTS)
4425 		{
4426 			return;
4427 		}
4428 		if (pm_entVeh && pm_entVeh->m_pVehicle &&
4429 			(pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
4430 			 pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL))
4431 		{
4432 			trace_t solidTr;
4433 
4434 			pm->mins[0] = -16;
4435 			pm->mins[1] = -16;
4436 			pm->mins[2] = MINS_Z;
4437 
4438 			pm->maxs[0] = 16;
4439 			pm->maxs[1] = 16;
4440 			pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
4441 			pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4442 
4443 			pm->trace (&solidTr, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->m_iVehicleNum, pm->tracemask);
4444 			if (solidTr.startsolid || solidTr.allsolid || solidTr.fraction != 1.0f)
4445 			{ //whoops, can't fit here. Down to 0!
4446 				VectorClear(pm->mins);
4447 				VectorClear(pm->maxs);
4448 #ifdef _GAME
4449 				{
4450 					gentity_t *me = &g_entities[pm->ps->clientNum];
4451 					if (me->inuse && me->client)
4452 					{ //yeah, this is a really terrible hack.
4453 						me->client->solidHack = level.time + 200;
4454 					}
4455 				}
4456 #endif
4457 			}
4458 		}
4459 	}
4460 	else
4461 	{
4462 		if (pm->ps->clientNum < MAX_CLIENTS)
4463 		{
4464 			pm->mins[0] = -15;
4465 			pm->mins[1] = -15;
4466 
4467 			pm->maxs[0] = 15;
4468 			pm->maxs[1] = 15;
4469 		}
4470 
4471 		if ( PM_CheckDualForwardJumpDuck() )
4472 		{//special anim resizing us
4473 		}
4474 		else
4475 		{
4476 			PM_CheckFixMins();
4477 
4478 			if ( !pm->mins[2] )
4479 			{
4480 				pm->mins[2] = MINS_Z;
4481 			}
4482 		}
4483 
4484 		if (pm->ps->pm_type == PM_DEAD && pm->ps->clientNum < MAX_CLIENTS)
4485 		{
4486 			pm->maxs[2] = -8;
4487 			pm->ps->viewheight = DEAD_VIEWHEIGHT;
4488 			return;
4489 		}
4490 
4491 		if (BG_InRoll(pm->ps, pm->ps->legsAnim) && !BG_KickingAnim(pm->ps->legsAnim))
4492 		{
4493 			pm->maxs[2] = pm->ps->crouchheight; //CROUCH_MAXS_2;
4494 			pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4495 			pm->ps->pm_flags &= ~PMF_DUCKED;
4496 			pm->ps->pm_flags |= PMF_ROLLING;
4497 			return;
4498 		}
4499 		else if (pm->ps->pm_flags & PMF_ROLLING)
4500 		{
4501 			if ( PM_CanStand() ) {
4502 				pm->maxs[2] = pm->ps->standheight;
4503 				pm->ps->pm_flags &= ~PMF_ROLLING;
4504 			}
4505 		}
4506 		else if (pm->cmd.upmove < 0 ||
4507 			pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
4508 			pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
4509 			pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
4510 		{	// duck
4511 			pm->ps->pm_flags |= PMF_DUCKED;
4512 		}
4513 		else
4514 		{	// stand up if possible
4515 			if (pm->ps->pm_flags & PMF_DUCKED)
4516 			{
4517 				if ( PM_CanStand() ) {
4518 					pm->maxs[2] = pm->ps->standheight;
4519 					pm->ps->pm_flags &= ~PMF_DUCKED;
4520 				}
4521 			}
4522 		}
4523 	}
4524 
4525 	if (pm->ps->pm_flags & PMF_DUCKED)
4526 	{
4527 		pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
4528 		pm->ps->viewheight = CROUCH_VIEWHEIGHT;
4529 	}
4530 	else if (pm->ps->pm_flags & PMF_ROLLING)
4531 	{
4532 		pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
4533 		pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4534 	}
4535 	else
4536 	{
4537 		pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
4538 		pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
4539 	}
4540 }
4541 
4542 
4543 
4544 //===================================================================
4545 
4546 
4547 
4548 /*
4549 ==============
4550 PM_Use
4551 
4552 Generates a use event
4553 ==============
4554 */
4555 #define USE_DELAY 2000
4556 
PM_Use(void)4557 void PM_Use( void )
4558 {
4559 	if ( pm->ps->useTime > 0 )
4560 		pm->ps->useTime -= 100;//pm->cmd.msec;
4561 
4562 	if ( pm->ps->useTime > 0 ) {
4563 		return;
4564 	}
4565 
4566 	if ( ! (pm->cmd.buttons & BUTTON_USE ) )
4567 	{
4568 		pm->useEvent = 0;
4569 		pm->ps->useTime = 0;
4570 		return;
4571 	}
4572 
4573 	pm->useEvent = EV_USE;
4574 	pm->ps->useTime = USE_DELAY;
4575 }
4576 
PM_WalkingAnim(int anim)4577 qboolean PM_WalkingAnim( int anim )
4578 {
4579 	switch ( anim )
4580 	{
4581 	case BOTH_WALK1:				//# Normal walk
4582 	case BOTH_WALK2:				//# Normal walk with saber
4583 	case BOTH_WALK_STAFF:			//# Normal walk with staff
4584 	case BOTH_WALK_DUAL:			//# Normal walk with staff
4585 	case BOTH_WALK5:				//# Tavion taunting Kyle (cin 22)
4586 	case BOTH_WALK6:				//# Slow walk for Luke (cin 12)
4587 	case BOTH_WALK7:				//# Fast walk
4588 	case BOTH_WALKBACK1:			//# Walk1 backwards
4589 	case BOTH_WALKBACK2:			//# Walk2 backwards
4590 	case BOTH_WALKBACK_STAFF:		//# Walk backwards with staff
4591 	case BOTH_WALKBACK_DUAL:		//# Walk backwards with dual
4592 		return qtrue;
4593 		break;
4594 	}
4595 	return qfalse;
4596 }
4597 
PM_RunningAnim(int anim)4598 qboolean PM_RunningAnim( int anim )
4599 {
4600 	switch ( (anim) )
4601 	{
4602 	case BOTH_RUN1:
4603 	case BOTH_RUN2:
4604 	case BOTH_RUN_STAFF:
4605 	case BOTH_RUN_DUAL:
4606 	case BOTH_RUNBACK1:
4607 	case BOTH_RUNBACK2:
4608 	case BOTH_RUNBACK_STAFF:
4609 	case BOTH_RUNBACK_DUAL:
4610 	case BOTH_RUN1START:			//# Start into full run1
4611 	case BOTH_RUN1STOP:			//# Stop from full run1
4612 	case BOTH_RUNSTRAFE_LEFT1:	//# Sidestep left: should loop
4613 	case BOTH_RUNSTRAFE_RIGHT1:	//# Sidestep right: should loop
4614 		return qtrue;
4615 		break;
4616 	}
4617 	return qfalse;
4618 }
4619 
PM_SwimmingAnim(int anim)4620 qboolean PM_SwimmingAnim( int anim )
4621 {
4622 	switch ( anim )
4623 	{
4624 	case BOTH_SWIM_IDLE1:		//# Swimming Idle 1
4625 	case BOTH_SWIMFORWARD:		//# Swim forward loop
4626 	case BOTH_SWIMBACKWARD:		//# Swim backward loop
4627 		return qtrue;
4628 		break;
4629 	}
4630 	return qfalse;
4631 }
4632 
PM_RollingAnim(int anim)4633 qboolean PM_RollingAnim( int anim )
4634 {
4635 	switch ( anim )
4636 	{
4637 	case BOTH_ROLL_F:			//# Roll forward
4638 	case BOTH_ROLL_B:			//# Roll backward
4639 	case BOTH_ROLL_L:			//# Roll left
4640 	case BOTH_ROLL_R:			//# Roll right
4641 		return qtrue;
4642 		break;
4643 	}
4644 	return qfalse;
4645 }
4646 
PM_AnglesForSlope(const float yaw,const vec3_t slope,vec3_t angles)4647 void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles )
4648 {
4649 	vec3_t	nvf, ovf, ovr, new_angles;
4650 	float	pitch, mod, dot;
4651 
4652 	VectorSet( angles, 0, yaw, 0 );
4653 	AngleVectors( angles, ovf, ovr, NULL );
4654 
4655 	vectoangles( slope, new_angles );
4656 	pitch = new_angles[PITCH] + 90;
4657 	new_angles[ROLL] = new_angles[PITCH] = 0;
4658 
4659 	AngleVectors( new_angles, nvf, NULL, NULL );
4660 
4661 	mod = DotProduct( nvf, ovr );
4662 
4663 	if ( mod < 0 )
4664 		mod = -1;
4665 	else
4666 		mod = 1;
4667 
4668 	dot = DotProduct( nvf, ovf );
4669 
4670 	angles[YAW] = 0;
4671 	angles[PITCH] = dot * pitch;
4672 	angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
4673 }
4674 
PM_FootSlopeTrace(float * pDiff,float * pInterval)4675 void PM_FootSlopeTrace( float *pDiff, float *pInterval )
4676 {
4677 	vec3_t	footLOrg, footROrg, footLBot, footRBot;
4678 	vec3_t footLPoint, footRPoint;
4679 	vec3_t footMins, footMaxs;
4680 	vec3_t footLSlope, footRSlope;
4681 
4682 	trace_t	trace;
4683 	float	diff, interval;
4684 
4685 	mdxaBone_t	boltMatrix;
4686 	vec3_t		G2Angles;
4687 
4688 	VectorSet(G2Angles, 0, pm->ps->viewangles[YAW], 0);
4689 
4690 	interval = 4;//?
4691 
4692 	trap->G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_LFoot, &boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime, NULL, pm->modelScale );
4693 	footLPoint[0] = boltMatrix.matrix[0][3];
4694 	footLPoint[1] = boltMatrix.matrix[1][3];
4695 	footLPoint[2] = boltMatrix.matrix[2][3];
4696 
4697 	trap->G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_RFoot, &boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime, NULL, pm->modelScale );
4698 	footRPoint[0] = boltMatrix.matrix[0][3];
4699 	footRPoint[1] = boltMatrix.matrix[1][3];
4700 	footRPoint[2] = boltMatrix.matrix[2][3];
4701 
4702 	//get these on the cgame and store it, save ourselves a ghoul2 construct skel call
4703 	VectorCopy( footLPoint, footLOrg );
4704 	VectorCopy( footRPoint, footROrg );
4705 
4706 	//step 2: adjust foot tag z height to bottom of bbox+1
4707 	footLOrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
4708 	footROrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
4709 	VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 );
4710 	VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 );
4711 
4712 	//step 3: trace down from each, find difference
4713 	VectorSet( footMins, -3, -3, 0 );
4714 	VectorSet( footMaxs, 3, 3, 1 );
4715 
4716 	pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask );
4717 	VectorCopy( trace.endpos, footLBot );
4718 	VectorCopy( trace.plane.normal, footLSlope );
4719 
4720 	pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask );
4721 	VectorCopy( trace.endpos, footRBot );
4722 	VectorCopy( trace.plane.normal, footRSlope );
4723 
4724 	diff = footLBot[2] - footRBot[2];
4725 
4726 	if ( pDiff != NULL )
4727 	{
4728 		*pDiff = diff;
4729 	}
4730 	if ( pInterval != NULL )
4731 	{
4732 		*pInterval = interval;
4733 	}
4734 }
4735 
BG_InSlopeAnim(int anim)4736 qboolean BG_InSlopeAnim( int anim )
4737 {
4738 	switch ( anim )
4739 	{
4740 	case LEGS_LEFTUP1:			//# On a slope with left foot 4 higher than right
4741 	case LEGS_LEFTUP2:			//# On a slope with left foot 8 higher than right
4742 	case LEGS_LEFTUP3:			//# On a slope with left foot 12 higher than right
4743 	case LEGS_LEFTUP4:			//# On a slope with left foot 16 higher than right
4744 	case LEGS_LEFTUP5:			//# On a slope with left foot 20 higher than right
4745 	case LEGS_RIGHTUP1:			//# On a slope with RIGHT foot 4 higher than left
4746 	case LEGS_RIGHTUP2:			//# On a slope with RIGHT foot 8 higher than left
4747 	case LEGS_RIGHTUP3:			//# On a slope with RIGHT foot 12 higher than left
4748 	case LEGS_RIGHTUP4:			//# On a slope with RIGHT foot 16 higher than left
4749 	case LEGS_RIGHTUP5:			//# On a slope with RIGHT foot 20 higher than left
4750 	case LEGS_S1_LUP1:
4751 	case LEGS_S1_LUP2:
4752 	case LEGS_S1_LUP3:
4753 	case LEGS_S1_LUP4:
4754 	case LEGS_S1_LUP5:
4755 	case LEGS_S1_RUP1:
4756 	case LEGS_S1_RUP2:
4757 	case LEGS_S1_RUP3:
4758 	case LEGS_S1_RUP4:
4759 	case LEGS_S1_RUP5:
4760 	case LEGS_S3_LUP1:
4761 	case LEGS_S3_LUP2:
4762 	case LEGS_S3_LUP3:
4763 	case LEGS_S3_LUP4:
4764 	case LEGS_S3_LUP5:
4765 	case LEGS_S3_RUP1:
4766 	case LEGS_S3_RUP2:
4767 	case LEGS_S3_RUP3:
4768 	case LEGS_S3_RUP4:
4769 	case LEGS_S3_RUP5:
4770 	case LEGS_S4_LUP1:
4771 	case LEGS_S4_LUP2:
4772 	case LEGS_S4_LUP3:
4773 	case LEGS_S4_LUP4:
4774 	case LEGS_S4_LUP5:
4775 	case LEGS_S4_RUP1:
4776 	case LEGS_S4_RUP2:
4777 	case LEGS_S4_RUP3:
4778 	case LEGS_S4_RUP4:
4779 	case LEGS_S4_RUP5:
4780 	case LEGS_S5_LUP1:
4781 	case LEGS_S5_LUP2:
4782 	case LEGS_S5_LUP3:
4783 	case LEGS_S5_LUP4:
4784 	case LEGS_S5_LUP5:
4785 	case LEGS_S5_RUP1:
4786 	case LEGS_S5_RUP2:
4787 	case LEGS_S5_RUP3:
4788 	case LEGS_S5_RUP4:
4789 	case LEGS_S5_RUP5:
4790 		return qtrue;
4791 		break;
4792 	}
4793 	return qfalse;
4794 }
4795 
4796 #define	SLOPE_RECALC_INT 100
4797 
PM_AdjustStandAnimForSlope(void)4798 qboolean PM_AdjustStandAnimForSlope( void )
4799 {
4800 	float	diff;
4801 	float	interval;
4802 	int		destAnim;
4803 	int		legsAnim;
4804 	#define SLOPERECALCVAR pm->ps->slopeRecalcTime //this is purely convenience
4805 
4806 	if (!pm->ghoul2)
4807 	{ //probably just changed models and not quite in sync yet
4808 		return qfalse;
4809 	}
4810 
4811 	if ( pm->g2Bolts_LFoot == -1 || pm->g2Bolts_RFoot == -1 )
4812 	{//need these bolts!
4813 		return qfalse;
4814 	}
4815 
4816 	//step 1: find the 2 foot tags
4817 	PM_FootSlopeTrace( &diff, &interval );
4818 
4819 	//step 4: based on difference, choose one of the left/right slope-match intervals
4820 	if ( diff >= interval*5 )
4821 	{
4822 		destAnim = LEGS_LEFTUP5;
4823 	}
4824 	else if ( diff >= interval*4 )
4825 	{
4826 		destAnim = LEGS_LEFTUP4;
4827 	}
4828 	else if ( diff >= interval*3 )
4829 	{
4830 		destAnim = LEGS_LEFTUP3;
4831 	}
4832 	else if ( diff >= interval*2 )
4833 	{
4834 		destAnim = LEGS_LEFTUP2;
4835 	}
4836 	else if ( diff >= interval )
4837 	{
4838 		destAnim = LEGS_LEFTUP1;
4839 	}
4840 	else if ( diff <= interval*-5 )
4841 	{
4842 		destAnim = LEGS_RIGHTUP5;
4843 	}
4844 	else if ( diff <= interval*-4 )
4845 	{
4846 		destAnim = LEGS_RIGHTUP4;
4847 	}
4848 	else if ( diff <= interval*-3 )
4849 	{
4850 		destAnim = LEGS_RIGHTUP3;
4851 	}
4852 	else if ( diff <= interval*-2 )
4853 	{
4854 		destAnim = LEGS_RIGHTUP2;
4855 	}
4856 	else if ( diff <= interval*-1 )
4857 	{
4858 		destAnim = LEGS_RIGHTUP1;
4859 	}
4860 	else
4861 	{
4862 		return qfalse;
4863 	}
4864 
4865 	legsAnim = pm->ps->legsAnim;
4866 	//adjust for current legs anim
4867 	switch ( legsAnim )
4868 	{
4869 	case BOTH_STAND1:
4870 
4871 	case LEGS_S1_LUP1:
4872 	case LEGS_S1_LUP2:
4873 	case LEGS_S1_LUP3:
4874 	case LEGS_S1_LUP4:
4875 	case LEGS_S1_LUP5:
4876 	case LEGS_S1_RUP1:
4877 	case LEGS_S1_RUP2:
4878 	case LEGS_S1_RUP3:
4879 	case LEGS_S1_RUP4:
4880 	case LEGS_S1_RUP5:
4881 		destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1);
4882 		break;
4883 	case BOTH_STAND2:
4884 	case BOTH_SABERFAST_STANCE:
4885 	case BOTH_SABERSLOW_STANCE:
4886 	case BOTH_CROUCH1IDLE:
4887 	case BOTH_CROUCH1:
4888 	case LEGS_LEFTUP1:			//# On a slope with left foot 4 higher than right
4889 	case LEGS_LEFTUP2:			//# On a slope with left foot 8 higher than right
4890 	case LEGS_LEFTUP3:			//# On a slope with left foot 12 higher than right
4891 	case LEGS_LEFTUP4:			//# On a slope with left foot 16 higher than right
4892 	case LEGS_LEFTUP5:			//# On a slope with left foot 20 higher than right
4893 	case LEGS_RIGHTUP1:			//# On a slope with RIGHT foot 4 higher than left
4894 	case LEGS_RIGHTUP2:			//# On a slope with RIGHT foot 8 higher than left
4895 	case LEGS_RIGHTUP3:			//# On a slope with RIGHT foot 12 higher than left
4896 	case LEGS_RIGHTUP4:			//# On a slope with RIGHT foot 16 higher than left
4897 	case LEGS_RIGHTUP5:			//# On a slope with RIGHT foot 20 higher than left
4898 		//fine
4899 		break;
4900 	case BOTH_STAND3:
4901 	case LEGS_S3_LUP1:
4902 	case LEGS_S3_LUP2:
4903 	case LEGS_S3_LUP3:
4904 	case LEGS_S3_LUP4:
4905 	case LEGS_S3_LUP5:
4906 	case LEGS_S3_RUP1:
4907 	case LEGS_S3_RUP2:
4908 	case LEGS_S3_RUP3:
4909 	case LEGS_S3_RUP4:
4910 	case LEGS_S3_RUP5:
4911 		destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1);
4912 		break;
4913 	case BOTH_STAND4:
4914 	case LEGS_S4_LUP1:
4915 	case LEGS_S4_LUP2:
4916 	case LEGS_S4_LUP3:
4917 	case LEGS_S4_LUP4:
4918 	case LEGS_S4_LUP5:
4919 	case LEGS_S4_RUP1:
4920 	case LEGS_S4_RUP2:
4921 	case LEGS_S4_RUP3:
4922 	case LEGS_S4_RUP4:
4923 	case LEGS_S4_RUP5:
4924 		destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1);
4925 		break;
4926 	case BOTH_STAND5:
4927 	case LEGS_S5_LUP1:
4928 	case LEGS_S5_LUP2:
4929 	case LEGS_S5_LUP3:
4930 	case LEGS_S5_LUP4:
4931 	case LEGS_S5_LUP5:
4932 	case LEGS_S5_RUP1:
4933 	case LEGS_S5_RUP2:
4934 	case LEGS_S5_RUP3:
4935 	case LEGS_S5_RUP4:
4936 	case LEGS_S5_RUP5:
4937 		destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1);
4938 		break;
4939 	case BOTH_STAND6:
4940 	default:
4941 		return qfalse;
4942 		break;
4943 	}
4944 
4945 	//step 5: based on the chosen interval and the current legsAnim, pick the correct anim
4946 	//step 6: increment/decrement to the dest anim, not instant
4947 	if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5)
4948 		|| (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5)
4949 		|| (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5)
4950 		|| (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5)
4951 		|| (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5) )
4952 	{//already in left-side up
4953 		if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4954 		{
4955 			legsAnim++;
4956 			SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4957 		}
4958 		else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4959 		{
4960 			legsAnim--;
4961 			SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4962 		}
4963 		else //if (SLOPERECALCVAR < pm->cmd.serverTime)
4964 		{
4965 			legsAnim = destAnim;
4966 		}
4967 
4968 		destAnim = legsAnim;
4969 	}
4970 	else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5)
4971 		|| (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5)
4972 		|| (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5)
4973 		|| (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5)
4974 		|| (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5) )
4975 	{//already in right-side up
4976 		if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4977 		{
4978 			legsAnim++;
4979 			SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4980 		}
4981 		else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
4982 		{
4983 			legsAnim--;
4984 			SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
4985 		}
4986 		else //if (SLOPERECALCVAR < pm->cmd.serverTime)
4987 		{
4988 			legsAnim = destAnim;
4989 		}
4990 
4991 		destAnim = legsAnim;
4992 	}
4993 	else
4994 	{//in a stand of some sort?
4995 		switch ( legsAnim )
4996 		{
4997 		case BOTH_STAND1:
4998 		case TORSO_WEAPONREADY1:
4999 		case TORSO_WEAPONREADY2:
5000 		case TORSO_WEAPONREADY3:
5001 		case TORSO_WEAPONREADY10:
5002 
5003 			if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 )
5004 			{//going into left side up
5005 				destAnim = LEGS_S1_LUP1;
5006 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5007 			}
5008 			else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 )
5009 			{//going into right side up
5010 				destAnim = LEGS_S1_RUP1;
5011 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5012 			}
5013 			else
5014 			{//will never get here
5015 				return qfalse;
5016 			}
5017 			break;
5018 		case BOTH_STAND2:
5019 		case BOTH_SABERFAST_STANCE:
5020 		case BOTH_SABERSLOW_STANCE:
5021 		case BOTH_CROUCH1IDLE:
5022 			if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
5023 			{//going into left side up
5024 				destAnim = LEGS_LEFTUP1;
5025 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5026 			}
5027 			else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
5028 			{//going into right side up
5029 				destAnim = LEGS_RIGHTUP1;
5030 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5031 			}
5032 			else
5033 			{//will never get here
5034 				return qfalse;
5035 			}
5036 			break;
5037 		case BOTH_STAND3:
5038 			if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 )
5039 			{//going into left side up
5040 				destAnim = LEGS_S3_LUP1;
5041 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5042 			}
5043 			else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 )
5044 			{//going into right side up
5045 				destAnim = LEGS_S3_RUP1;
5046 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5047 			}
5048 			else
5049 			{//will never get here
5050 				return qfalse;
5051 			}
5052 			break;
5053 		case BOTH_STAND4:
5054 			if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 )
5055 			{//going into left side up
5056 				destAnim = LEGS_S4_LUP1;
5057 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5058 			}
5059 			else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 )
5060 			{//going into right side up
5061 				destAnim = LEGS_S4_RUP1;
5062 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5063 			}
5064 			else
5065 			{//will never get here
5066 				return qfalse;
5067 			}
5068 			break;
5069 		case BOTH_STAND5:
5070 			if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 )
5071 			{//going into left side up
5072 				destAnim = LEGS_S5_LUP1;
5073 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5074 			}
5075 			else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 )
5076 			{//going into right side up
5077 				destAnim = LEGS_S5_RUP1;
5078 				SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
5079 			}
5080 			else
5081 			{//will never get here
5082 				return qfalse;
5083 			}
5084 			break;
5085 		case BOTH_STAND6:
5086 		default:
5087 			return qfalse;
5088 			break;
5089 		}
5090 	}
5091 	//step 7: set the anim
5092 	//PM_SetAnim( SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL );
5093 	PM_ContinueLegsAnim(destAnim);
5094 
5095 	return qtrue;
5096 }
5097 
5098 extern int WeaponReadyLegsAnim[WP_NUM_WEAPONS];
5099 
5100 //rww - slowly back out of slope leg anims, to prevent skipping between slope anims and general jittering
PM_LegsSlopeBackTransition(int desiredAnim)5101 int PM_LegsSlopeBackTransition(int desiredAnim)
5102 {
5103 	int anim = pm->ps->legsAnim;
5104 	int resultingAnim = desiredAnim;
5105 
5106 	switch ( anim )
5107 	{
5108 	case LEGS_LEFTUP2:			//# On a slope with left foot 8 higher than right
5109 	case LEGS_LEFTUP3:			//# On a slope with left foot 12 higher than right
5110 	case LEGS_LEFTUP4:			//# On a slope with left foot 16 higher than right
5111 	case LEGS_LEFTUP5:			//# On a slope with left foot 20 higher than right
5112 	case LEGS_RIGHTUP2:			//# On a slope with RIGHT foot 8 higher than left
5113 	case LEGS_RIGHTUP3:			//# On a slope with RIGHT foot 12 higher than left
5114 	case LEGS_RIGHTUP4:			//# On a slope with RIGHT foot 16 higher than left
5115 	case LEGS_RIGHTUP5:			//# On a slope with RIGHT foot 20 higher than left
5116 	case LEGS_S1_LUP2:
5117 	case LEGS_S1_LUP3:
5118 	case LEGS_S1_LUP4:
5119 	case LEGS_S1_LUP5:
5120 	case LEGS_S1_RUP2:
5121 	case LEGS_S1_RUP3:
5122 	case LEGS_S1_RUP4:
5123 	case LEGS_S1_RUP5:
5124 	case LEGS_S3_LUP2:
5125 	case LEGS_S3_LUP3:
5126 	case LEGS_S3_LUP4:
5127 	case LEGS_S3_LUP5:
5128 	case LEGS_S3_RUP2:
5129 	case LEGS_S3_RUP3:
5130 	case LEGS_S3_RUP4:
5131 	case LEGS_S3_RUP5:
5132 	case LEGS_S4_LUP2:
5133 	case LEGS_S4_LUP3:
5134 	case LEGS_S4_LUP4:
5135 	case LEGS_S4_LUP5:
5136 	case LEGS_S4_RUP2:
5137 	case LEGS_S4_RUP3:
5138 	case LEGS_S4_RUP4:
5139 	case LEGS_S4_RUP5:
5140 	case LEGS_S5_LUP2:
5141 	case LEGS_S5_LUP3:
5142 	case LEGS_S5_LUP4:
5143 	case LEGS_S5_LUP5:
5144 	case LEGS_S5_RUP2:
5145 	case LEGS_S5_RUP3:
5146 	case LEGS_S5_RUP4:
5147 	case LEGS_S5_RUP5:
5148 		if (pm->ps->slopeRecalcTime < pm->cmd.serverTime)
5149 		{
5150 			resultingAnim = anim-1;
5151 			pm->ps->slopeRecalcTime = pm->cmd.serverTime + 8;//SLOPE_RECALC_INT;
5152 		}
5153 		else
5154 		{
5155 			resultingAnim = anim;
5156 		}
5157 		VectorClear(pm->ps->velocity);
5158 		break;
5159 	}
5160 
5161 	return resultingAnim;
5162 }
5163 
5164 /*
5165 ===============
5166 PM_Footsteps
5167 ===============
5168 */
PM_Footsteps(void)5169 static void PM_Footsteps( void ) {
5170 	float		bobmove;
5171 	int			old;
5172 	int			setAnimFlags = 0;
5173 
5174 	if ( (PM_InSaberAnim( (pm->ps->legsAnim) ) && !BG_SpinningSaberAnim( (pm->ps->legsAnim) ))
5175 		|| (pm->ps->legsAnim) == BOTH_STAND1
5176 		|| (pm->ps->legsAnim) == BOTH_STAND1TO2
5177 		|| (pm->ps->legsAnim) == BOTH_STAND2TO1
5178 		|| (pm->ps->legsAnim) == BOTH_STAND2
5179 		|| (pm->ps->legsAnim) == BOTH_SABERFAST_STANCE
5180 		|| (pm->ps->legsAnim) == BOTH_SABERSLOW_STANCE
5181 		|| (pm->ps->legsAnim) == BOTH_BUTTON_HOLD
5182 		|| (pm->ps->legsAnim) == BOTH_BUTTON_RELEASE
5183 		|| PM_LandingAnim( (pm->ps->legsAnim) )
5184 		|| PM_PainAnim( (pm->ps->legsAnim) ))
5185 	{//legs are in a saber anim, and not spinning, be sure to override it
5186 		setAnimFlags |= SETANIM_FLAG_OVERRIDE;
5187 	}
5188 
5189 	//
5190 	// calculate speed and cycle to be used for
5191 	// all cyclic walking effects
5192 	//
5193 	pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
5194 		+  pm->ps->velocity[1] * pm->ps->velocity[1] );
5195 
5196 	if (pm->ps->saberMove == LS_SPINATTACK)
5197 	{
5198 		PM_ContinueLegsAnim( pm->ps->torsoAnim );
5199 	}
5200 	else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
5201 
5202 		// airborne leaves position in cycle intact, but doesn't advance
5203 		if ( pm->waterlevel > 1 )
5204 		{
5205 			if (pm->xyspeed > 60)
5206 			{
5207 				PM_ContinueLegsAnim( BOTH_SWIMFORWARD );
5208 			}
5209 			else
5210 			{
5211 				PM_ContinueLegsAnim( BOTH_SWIM_IDLE1 );
5212 			}
5213 		}
5214 		return;
5215 	}
5216 	// if not trying to move
5217 	else if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
5218 		if (  pm->xyspeed < 5 ) {
5219 			pm->ps->bobCycle = 0;	// start at beginning of cycle again
5220 			if ( pm->ps->clientNum >= MAX_CLIENTS &&
5221 				pm_entSelf &&
5222 				pm_entSelf->s.NPC_class == CLASS_RANCOR )
5223 			{
5224 				if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
5225 				{//holding someone
5226 					PM_ContinueLegsAnim( BOTH_STAND4 );
5227 					//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
5228 				}
5229 				else if ( (pm->ps->eFlags2&EF2_ALERTED) )
5230 				{//have an enemy or have had one since we spawned
5231 					PM_ContinueLegsAnim( BOTH_STAND2 );
5232 					//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
5233 				}
5234 				else
5235 				{//just stand there
5236 					PM_ContinueLegsAnim( BOTH_STAND1 );
5237 					//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
5238 				}
5239 			}
5240 			else if ( pm->ps->clientNum >= MAX_CLIENTS &&
5241 				pm_entSelf &&
5242 				pm_entSelf->s.NPC_class == CLASS_WAMPA )
5243 			{
5244 				if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
5245 				{//holding a victim
5246 					PM_ContinueLegsAnim( BOTH_STAND2 );
5247 					//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
5248 				}
5249 				else
5250 				{//not holding a victim
5251 					PM_ContinueLegsAnim( BOTH_STAND1 );
5252 					//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
5253 				}
5254 			}
5255 			else if ( (pm->ps->pm_flags & PMF_DUCKED) || (pm->ps->pm_flags & PMF_ROLLING) ) {
5256 				if ((pm->ps->legsAnim) != BOTH_CROUCH1IDLE)
5257 				{
5258 					PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1IDLE, setAnimFlags);
5259 				}
5260 				else
5261 				{
5262 					PM_ContinueLegsAnim( BOTH_CROUCH1IDLE );
5263 				}
5264 			} else {
5265 				if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
5266 				{
5267 					///????  continue legs anim on a torso anim...??!!!
5268 					//yeah.. the anim has a valid pose for the legs, it uses it (you can't move while using disruptor)
5269 					PM_ContinueLegsAnim( TORSO_WEAPONREADY4 );
5270 				}
5271 				else
5272 				{
5273 					if (pm->ps->weapon == WP_SABER && BG_SabersOff( pm->ps ) )
5274 					{
5275 						if (!PM_AdjustStandAnimForSlope())
5276 						{
5277 							//PM_ContinueLegsAnim( BOTH_STAND1 );
5278 							PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(BOTH_STAND1));
5279 						}
5280 					}
5281 					else
5282 					{
5283 						if (pm->ps->weapon != WP_SABER || !PM_AdjustStandAnimForSlope())
5284 						{
5285 							if (pm->ps->weapon == WP_SABER)
5286 							{
5287 								PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(PM_GetSaberStance()));
5288 							}
5289 							else
5290 							{
5291 								PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(WeaponReadyLegsAnim[pm->ps->weapon]));
5292 							}
5293 						}
5294 					}
5295 				}
5296 			}
5297 		}
5298 		return;
5299 	}
5300 
5301 	if (pm->ps->saberMove == LS_SPINATTACK)
5302 	{
5303 		bobmove = 0.2f;
5304 		PM_ContinueLegsAnim( pm->ps->torsoAnim );
5305 	}
5306 	else if ( pm->ps->pm_flags & PMF_DUCKED )
5307 	{
5308 		int rolled = 0;
5309 
5310 		bobmove = 0.5;	// ducked characters bob much faster
5311 
5312 		if ( ( (PM_RunningAnim( pm->ps->legsAnim )&&VectorLengthSquared(pm->ps->velocity)>=40000/*200*200*/) || PM_CanRollFromSoulCal( pm->ps ) ) &&
5313 			!BG_InRoll(pm->ps, pm->ps->legsAnim) )
5314 		{//roll!
5315 			rolled = PM_TryRoll();
5316 		}
5317 		if ( !rolled )
5318 		{ //if the roll failed or didn't attempt, do standard crouching anim stuff.
5319 			if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
5320 				if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
5321 				{
5322 					PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags);
5323 				}
5324 				else
5325 				{
5326 					PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
5327 				}
5328 			}
5329 			else {
5330 				if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
5331 				{
5332 					PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags);
5333 				}
5334 				else
5335 				{
5336 					PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
5337 				}
5338 			}
5339 		}
5340 		else
5341 		{ //otherwise send us into the roll
5342 			pm->ps->legsTimer = 0;
5343 			pm->ps->legsAnim = 0;
5344 			PM_SetAnim(SETANIM_BOTH,rolled,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
5345 			PM_AddEventWithParm( EV_ROLL, 0 );
5346 			pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
5347 			pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
5348 			pm->ps->pm_flags &= ~PMF_DUCKED;
5349 			pm->ps->pm_flags |= PMF_ROLLING;
5350 		}
5351 	}
5352 	else if ((pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
5353 		!PM_InRollComplete(pm->ps, pm->ps->legsAnim))
5354 	{
5355 		bobmove = 0.5;	// ducked characters bob much faster
5356 
5357 		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
5358 		{
5359 			if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
5360 			{
5361 				PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags);
5362 			}
5363 			else
5364 			{
5365 				PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
5366 			}
5367 		}
5368 		else
5369 		{
5370 			if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
5371 			{
5372 				PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags);
5373 			}
5374 			else
5375 			{
5376 				PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
5377 			}
5378 		}
5379 	}
5380 	else
5381 	{
5382 		int desiredAnim = -1;
5383 
5384 		if ((pm->ps->legsAnim == BOTH_FORCELAND1 ||
5385 			pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
5386 			pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
5387 			pm->ps->legsAnim == BOTH_FORCELANDLEFT1) &&
5388 			pm->ps->legsTimer > 0)
5389 		{ //let it finish first
5390 			bobmove = 0.2f;
5391 		}
5392 		else if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
5393 		{//running
5394 			bobmove = 0.4f;	// faster speeds bob faster
5395 			if ( pm->ps->clientNum >= MAX_CLIENTS &&
5396 				pm_entSelf &&
5397 				pm_entSelf->s.NPC_class == CLASS_WAMPA )
5398 			{
5399 				if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
5400 				{//full on run, on all fours
5401 					desiredAnim = BOTH_RUN1;
5402 				}
5403 				else
5404 				{//regular, upright run
5405 					desiredAnim = BOTH_RUN2;
5406 				}
5407 			}
5408 			else if ( pm->ps->clientNum >= MAX_CLIENTS &&
5409 				pm_entSelf &&
5410 				pm_entSelf->s.NPC_class == CLASS_RANCOR )
5411 			{//no run anims
5412 				if ( (pm->ps->pm_flags&PMF_BACKWARDS_RUN) )
5413 				{
5414 					desiredAnim = BOTH_WALKBACK1;
5415 				}
5416 				else
5417 				{
5418 					desiredAnim = BOTH_WALK1;
5419 				}
5420 			}
5421 #ifdef _GAME
5422 			else if ( pm->ps->clientNum >= MAX_CLIENTS &&
5423 				pm_entSelf &&
5424 				pm_entSelf->s.NPC_class == CLASS_JAWA)
5425 			{
5426 				// Jawa has a special run animation :D
5427 				desiredAnim = BOTH_RUN4;
5428 				bobmove = 0.2f;
5429 			}
5430 #endif
5431 			else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
5432 			{
5433 #ifndef BASE_COMPAT
5434 				if( pm->ps->weapon != WP_SABER )
5435 				{
5436 					desiredAnim = BOTH_RUNBACK1;
5437 				}
5438 				else
5439 				{
5440 #endif
5441 				switch (pm->ps->fd.saberAnimLevel)
5442 				{
5443 				case SS_STAFF:
5444 					if ( pm->ps->saberHolstered > 1 )
5445 					{//saber off
5446 						desiredAnim = BOTH_RUNBACK1;
5447 					}
5448 					else
5449 					{
5450 						//desiredAnim = BOTH_RUNBACK_STAFF;
5451 						//hmm.. stuff runback anim is pretty messed up for some reason.
5452 						desiredAnim = BOTH_RUNBACK2;
5453 					}
5454 					break;
5455 				case SS_DUAL:
5456 					if ( pm->ps->saberHolstered > 1 )
5457 					{//sabers off
5458 						desiredAnim = BOTH_RUNBACK1;
5459 					}
5460 					else
5461 					{
5462 						//desiredAnim = BOTH_RUNBACK_DUAL;
5463 						//and so is the dual
5464 						desiredAnim = BOTH_RUNBACK2;
5465 					}
5466 					break;
5467 				default:
5468 					if ( pm->ps->saberHolstered )
5469 					{//saber off
5470 						desiredAnim = BOTH_RUNBACK1;
5471 					}
5472 					else
5473 					{
5474 						desiredAnim = BOTH_RUNBACK2;
5475 					}
5476 					break;
5477 				}
5478 #ifndef BASE_COMPAT
5479 				}
5480 #endif
5481 			}
5482 			else
5483 			{
5484 #ifndef BASE_COMPAT					// FIXME: this doesn't break base compatibility at all, remove #ifndef
5485 				if ( pm->ps->weapon != WP_SABER )
5486 				{
5487 					desiredAnim = BOTH_RUN1;
5488 				}
5489 				else
5490 				{
5491 #endif
5492 				switch (pm->ps->fd.saberAnimLevel)
5493 				{
5494 				case SS_STAFF:
5495 					if ( pm->ps->saberHolstered > 1 )
5496 					{//blades off
5497 						desiredAnim = BOTH_RUN1;
5498 					}
5499 					else if ( pm->ps->saberHolstered == 1 )
5500 					{//1 blade on
5501 						desiredAnim = BOTH_RUN2;
5502 					}
5503 					else
5504 					{
5505 						if (pm->ps->fd.forcePowersActive & (1<<FP_SPEED))
5506 						{
5507 							desiredAnim = BOTH_RUN1;
5508 						}
5509 						else
5510 						{
5511 							desiredAnim = BOTH_RUN_STAFF;
5512 						}
5513 					}
5514 					break;
5515 				case SS_DUAL:
5516 					if ( pm->ps->saberHolstered > 1 )
5517 					{//blades off
5518 						desiredAnim = BOTH_RUN1;
5519 					}
5520 					else if ( pm->ps->saberHolstered == 1 )
5521 					{//1 saber on
5522 						desiredAnim = BOTH_RUN2;
5523 					}
5524 					else
5525 					{
5526 						desiredAnim = BOTH_RUN_DUAL;
5527 					}
5528 					break;
5529 				default:
5530 					if ( pm->ps->saberHolstered )
5531 					{//saber off
5532 						desiredAnim = BOTH_RUN1;
5533 					}
5534 					else
5535 					{
5536 						desiredAnim = BOTH_RUN2;
5537 					}
5538 					break;
5539 				}
5540 #ifndef BASE_COMPAT
5541 				}
5542 #endif
5543 			}
5544 		}
5545 		else
5546 		{
5547 			bobmove = 0.2f;	// walking bobs slow
5548 			if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
5549 			{
5550 #ifndef BASE_COMPAT // fixme, doesn't break base compat if enabled (I tested this to be sure)
5551 				if( pm->ps->weapon != WP_SABER )
5552 				{
5553 					desiredAnim = BOTH_WALKBACK1;
5554 				}
5555 				else
5556 				{
5557 #endif
5558 				switch (pm->ps->fd.saberAnimLevel)
5559 				{
5560 				case SS_STAFF:
5561 					if ( pm->ps->saberHolstered > 1 )
5562 					{
5563 						desiredAnim = BOTH_WALKBACK1;
5564 					}
5565 					else if ( pm->ps->saberHolstered )
5566 					{
5567 						desiredAnim = BOTH_WALKBACK2;
5568 					}
5569 					else
5570 					{
5571 						desiredAnim = BOTH_WALKBACK_STAFF;
5572 					}
5573 					break;
5574 				case SS_DUAL:
5575 					if ( pm->ps->saberHolstered > 1 )
5576 					{
5577 						desiredAnim = BOTH_WALKBACK1;
5578 					}
5579 					else if ( pm->ps->saberHolstered )
5580 					{
5581 						desiredAnim = BOTH_WALKBACK2;
5582 					}
5583 					else
5584 					{
5585 						desiredAnim = BOTH_WALKBACK_DUAL;
5586 					}
5587 					break;
5588 				default:
5589 					if ( pm->ps->saberHolstered )
5590 					{
5591 						desiredAnim = BOTH_WALKBACK1;
5592 					}
5593 					else
5594 					{
5595 						desiredAnim = BOTH_WALKBACK2;
5596 					}
5597 					break;
5598 				}
5599 #ifndef BASE_COMPAT
5600 				}
5601 #endif
5602 			}
5603 			else
5604 			{
5605 				if ( pm->ps->weapon == WP_MELEE )
5606 				{
5607 					desiredAnim = BOTH_WALK1;
5608 				}
5609 				else if ( BG_SabersOff( pm->ps ) )
5610 				{
5611 					desiredAnim = BOTH_WALK1;
5612 				}
5613 #ifndef BASE_COMPAT
5614 				else if ( pm->ps->weapon != WP_SABER )
5615 				{
5616 					desiredAnim = BOTH_WALK1;
5617 				}
5618 #endif
5619 				else
5620 				{
5621 					switch (pm->ps->fd.saberAnimLevel)
5622 					{
5623 					case SS_STAFF:
5624 						if ( pm->ps->saberHolstered > 1 )
5625 						{
5626 							desiredAnim = BOTH_WALK1;
5627 						}
5628 						else if ( pm->ps->saberHolstered )
5629 						{
5630 							desiredAnim = BOTH_WALK2;
5631 						}
5632 						else
5633 						{
5634 							desiredAnim = BOTH_WALK_STAFF;
5635 						}
5636 						break;
5637 					case SS_DUAL:
5638 						if ( pm->ps->saberHolstered > 1 )
5639 						{
5640 							desiredAnim = BOTH_WALK1;
5641 						}
5642 						else if ( pm->ps->saberHolstered )
5643 						{
5644 							desiredAnim = BOTH_WALK2;
5645 						}
5646 						else
5647 						{
5648 							desiredAnim = BOTH_WALK_DUAL;
5649 						}
5650 						break;
5651 					default:
5652 						if ( pm->ps->saberHolstered )
5653 						{
5654 							desiredAnim = BOTH_WALK1;
5655 						}
5656 						else
5657 						{
5658 							desiredAnim = BOTH_WALK2;
5659 						}
5660 						break;
5661 					}
5662 				}
5663 			}
5664 		}
5665 
5666 		if (desiredAnim != -1)
5667 		{
5668 			int ires = PM_LegsSlopeBackTransition(desiredAnim);
5669 
5670 			if ((pm->ps->legsAnim) != desiredAnim && ires == desiredAnim)
5671 			{
5672 				PM_SetAnim(SETANIM_LEGS, desiredAnim, setAnimFlags);
5673 			}
5674 			else
5675 			{
5676 				PM_ContinueLegsAnim(ires);
5677 			}
5678 		}
5679 	}
5680 
5681 	// check for footstep / splash sounds
5682 	old = pm->ps->bobCycle;
5683 	pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
5684 
5685 	// if we just crossed a cycle boundary, play an appropriate footstep event
5686 	if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
5687 	{
5688 		pm->ps->footstepTime = pm->cmd.serverTime + 300;
5689 		if ( pm->waterlevel == 1 ) {
5690 			// splashing
5691 			PM_AddEvent( EV_FOOTSPLASH );
5692 		} else if ( pm->waterlevel == 2 ) {
5693 			// wading / swimming at surface
5694 			PM_AddEvent( EV_SWIM );
5695 		} else if ( pm->waterlevel == 3 ) {
5696 			// no sound when completely underwater
5697 		}
5698 	}
5699 }
5700 
5701 /*
5702 ==============
5703 PM_WaterEvents
5704 
5705 Generate sound events for entering and leaving water
5706 ==============
5707 */
PM_WaterEvents(void)5708 static void PM_WaterEvents( void ) {		// FIXME?
5709 #ifdef _GAME
5710 	qboolean impact_splash = qfalse;
5711 #endif
5712 	//
5713 	// if just entered a water volume, play a sound
5714 	//
5715 	if (!pml.previous_waterlevel && pm->waterlevel) {
5716 #ifdef _GAME
5717 		if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
5718 		{
5719 			impact_splash = qtrue;
5720 		}
5721 #endif
5722 		PM_AddEvent( EV_WATER_TOUCH );
5723 	}
5724 
5725 	//
5726 	// if just completely exited a water volume, play a sound
5727 	//
5728 	if (pml.previous_waterlevel && !pm->waterlevel) {
5729 #ifdef _GAME
5730 		if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
5731 		{
5732 			impact_splash = qtrue;
5733 		}
5734 #endif
5735 		PM_AddEvent( EV_WATER_LEAVE );
5736 	}
5737 
5738 #ifdef _GAME
5739 	if ( impact_splash )
5740 	{
5741 		//play the splash effect
5742 		trace_t	tr;
5743 		vec3_t	start, end;
5744 
5745 
5746 		VectorCopy( pm->ps->origin, start );
5747 		VectorCopy( pm->ps->origin, end );
5748 
5749 		// FIXME: set start and end better
5750 		start[2] += 10;
5751 		end[2] -= 40;
5752 
5753 		pm->trace( &tr, start, vec3_origin, vec3_origin, end, pm->ps->clientNum, MASK_WATER );
5754 
5755 		if ( tr.fraction < 1.0f )
5756 		{
5757 			if ( (tr.contents&CONTENTS_LAVA) )
5758 			{
5759 				G_PlayEffect( EFFECT_LAVA_SPLASH, tr.endpos, tr.plane.normal );
5760 			}
5761 			else if ( (tr.contents&CONTENTS_SLIME) )
5762 			{
5763 				G_PlayEffect( EFFECT_ACID_SPLASH, tr.endpos, tr.plane.normal );
5764 			}
5765 			else //must be water
5766 			{
5767 				G_PlayEffect( EFFECT_WATER_SPLASH, tr.endpos, tr.plane.normal );
5768 			}
5769 		}
5770 	}
5771 #endif
5772 
5773 	//
5774 	// check for head just going under water
5775 	//
5776 	if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
5777 		PM_AddEvent( EV_WATER_UNDER );
5778 	}
5779 
5780 	//
5781 	// check for head just coming out of water
5782 	//
5783 	if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
5784 		PM_AddEvent( EV_WATER_CLEAR );
5785 	}
5786 }
5787 
BG_ClearRocketLock(playerState_t * ps)5788 void BG_ClearRocketLock( playerState_t *ps )
5789 {
5790 	if ( ps )
5791 	{
5792 		ps->rocketLockIndex = ENTITYNUM_NONE;
5793 		ps->rocketLastValidTime = 0;
5794 		ps->rocketLockTime = -1;
5795 		ps->rocketTargetTime = 0;
5796 	}
5797 }
5798 
5799 /*
5800 ===============
5801 PM_BeginWeaponChange
5802 ===============
5803 */
PM_BeginWeaponChange(int weapon)5804 void PM_BeginWeaponChange( int weapon ) {
5805 	if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
5806 		return;
5807 	}
5808 
5809 	if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
5810 		return;
5811 	}
5812 
5813 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
5814 		return;
5815 	}
5816 
5817 	// turn of any kind of zooming when weapon switching.
5818 	if (pm->ps->zoomMode)
5819 	{
5820 		pm->ps->zoomMode = 0;
5821 		pm->ps->zoomTime = pm->ps->commandTime;
5822 	}
5823 
5824 	PM_AddEventWithParm( EV_CHANGE_WEAPON, weapon );
5825 	pm->ps->weaponstate = WEAPON_DROPPING;
5826 	pm->ps->weaponTime += 200;
5827 	//PM_StartTorsoAnim( TORSO_DROPWEAP1 );
5828 	PM_SetAnim(SETANIM_TORSO, TORSO_DROPWEAP1, SETANIM_FLAG_OVERRIDE);
5829 
5830 	BG_ClearRocketLock( pm->ps );
5831 }
5832 
5833 
5834 /*
5835 ===============
5836 PM_FinishWeaponChange
5837 ===============
5838 */
PM_FinishWeaponChange(void)5839 void PM_FinishWeaponChange( void ) {
5840 	int		weapon;
5841 
5842 	weapon = pm->cmd.weapon;
5843 	if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
5844 		weapon = WP_NONE;
5845 	}
5846 
5847 	if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
5848 		weapon = WP_NONE;
5849 	}
5850 
5851 	if (weapon == WP_SABER)
5852 	{
5853 		PM_SetSaberMove(LS_DRAW);
5854 	}
5855 	else
5856 	{
5857 		//PM_StartTorsoAnim( TORSO_RAISEWEAP1);
5858 		PM_SetAnim(SETANIM_TORSO, TORSO_RAISEWEAP1, SETANIM_FLAG_OVERRIDE);
5859 	}
5860 	pm->ps->weapon = weapon;
5861 	pm->ps->weaponstate = WEAPON_RAISING;
5862 	pm->ps->weaponTime += 250;
5863 }
5864 
5865 #ifdef _GAME
5866 extern void WP_GetVehicleCamPos( gentity_t *ent, gentity_t *pilot, vec3_t camPos );
5867 #else
5868 extern void CG_GetVehicleCamPos( vec3_t camPos );
5869 #endif
5870 #define MAX_XHAIR_DIST_ACCURACY	20000.0f
BG_VehTraceFromCamPos(trace_t * camTrace,bgEntity_t * bgEnt,const vec3_t entOrg,const vec3_t shotStart,const vec3_t end,vec3_t newEnd,vec3_t shotDir,float bestDist)5871 int BG_VehTraceFromCamPos( trace_t *camTrace, bgEntity_t *bgEnt, const vec3_t entOrg, const vec3_t shotStart, const vec3_t end, vec3_t newEnd, vec3_t shotDir, float bestDist )
5872 {
5873 	//NOTE: this MUST stay up to date with the method used in CG_ScanForCrosshairEntity (where it checks the doExtraVehTraceFromViewPos bool)
5874 	vec3_t	viewDir2End, extraEnd, camPos;
5875 	float	minAutoAimDist;
5876 
5877 #ifdef _GAME
5878 	WP_GetVehicleCamPos( (gentity_t *)bgEnt, (gentity_t *)bgEnt->m_pVehicle->m_pPilot, camPos );
5879 #else
5880 	CG_GetVehicleCamPos( camPos );
5881 #endif
5882 
5883 	minAutoAimDist = Distance( entOrg, camPos ) + (bgEnt->m_pVehicle->m_pVehicleInfo->length/2.0f) + 200.0f;
5884 
5885 	VectorCopy( end, newEnd );
5886 	VectorSubtract( end, camPos, viewDir2End );
5887 	VectorNormalize( viewDir2End );
5888 	VectorMA( camPos, MAX_XHAIR_DIST_ACCURACY, viewDir2End, extraEnd );
5889 
5890 	pm->trace( camTrace, camPos, vec3_origin, vec3_origin, extraEnd, bgEnt->s.number, CONTENTS_SOLID|CONTENTS_BODY );
5891 
5892 	if ( !camTrace->allsolid
5893 		&& !camTrace->startsolid
5894 		&& camTrace->fraction < 1.0f
5895 		&& (camTrace->fraction*MAX_XHAIR_DIST_ACCURACY) > minAutoAimDist
5896 		&& ((camTrace->fraction*MAX_XHAIR_DIST_ACCURACY)-Distance( entOrg, camPos )) < bestDist )
5897 	{//this trace hit *something* that's closer than the thing the main trace hit, so use this result instead
5898 		VectorCopy( camTrace->endpos, newEnd );
5899 		VectorSubtract( newEnd, shotStart, shotDir );
5900 		VectorNormalize( shotDir );
5901 		return (camTrace->entityNum+1);
5902 	}
5903 	return 0;
5904 }
5905 
PM_RocketLock(float lockDist,qboolean vehicleLock)5906 void PM_RocketLock( float lockDist, qboolean vehicleLock )
5907 {
5908 	// Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
5909 	//	implement our alt-fire locking stuff
5910 	vec3_t		ang;
5911 	trace_t		tr;
5912 
5913 	vec3_t muzzleOffPoint, muzzlePoint, forward, right, up;
5914 
5915 	if ( vehicleLock )
5916 	{
5917 		AngleVectors( pm->ps->viewangles, forward, right, up );
5918 		VectorCopy( pm->ps->origin, muzzlePoint );
5919 		VectorMA( muzzlePoint, lockDist, forward, ang );
5920 	}
5921 	else
5922 	{
5923 		AngleVectors( pm->ps->viewangles, forward, right, up );
5924 
5925 		AngleVectors(pm->ps->viewangles, ang, NULL, NULL);
5926 
5927 		VectorCopy( pm->ps->origin, muzzlePoint );
5928 		VectorCopy(WP_MuzzlePoint[WP_ROCKET_LAUNCHER], muzzleOffPoint);
5929 
5930 		VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
5931 		VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
5932 		muzzlePoint[2] += pm->ps->viewheight + muzzleOffPoint[2];
5933 		ang[0] = muzzlePoint[0] + ang[0]*lockDist;
5934 		ang[1] = muzzlePoint[1] + ang[1]*lockDist;
5935 		ang[2] = muzzlePoint[2] + ang[2]*lockDist;
5936 	}
5937 
5938 
5939 	pm->trace(&tr, muzzlePoint, NULL, NULL, ang, pm->ps->clientNum, MASK_PLAYERSOLID);
5940 
5941 	if ( vehicleLock )
5942 	{//vehicles also do a trace from the camera point if the main one misses
5943 		if ( tr.fraction >= 1.0f )
5944 		{
5945 			trace_t camTrace;
5946 			vec3_t newEnd, shotDir;
5947 			if ( BG_VehTraceFromCamPos( &camTrace, PM_BGEntForNum(pm->ps->clientNum), pm->ps->origin, muzzlePoint, tr.endpos, newEnd, shotDir, (tr.fraction*lockDist) ) )
5948 			{
5949 				memcpy( &tr, &camTrace, sizeof(tr) );
5950 			}
5951 		}
5952 	}
5953 
5954 	if (tr.fraction != 1 && tr.entityNum < ENTITYNUM_NONE && tr.entityNum != pm->ps->clientNum)
5955 	{
5956 		bgEntity_t *bgEnt = PM_BGEntForNum(tr.entityNum);
5957 		if ( bgEnt && (bgEnt->s.powerups&PW_CLOAKED) )
5958 		{
5959 			pm->ps->rocketLockIndex = ENTITYNUM_NONE;
5960 			pm->ps->rocketLockTime = 0;
5961 		}
5962 		else if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC))
5963 		{
5964 			if (pm->ps->rocketLockIndex == ENTITYNUM_NONE)
5965 			{
5966 				pm->ps->rocketLockIndex = tr.entityNum;
5967 				pm->ps->rocketLockTime = pm->cmd.serverTime;
5968 			}
5969 			else if (pm->ps->rocketLockIndex != tr.entityNum && pm->ps->rocketTargetTime < pm->cmd.serverTime)
5970 			{
5971 				pm->ps->rocketLockIndex = tr.entityNum;
5972 				pm->ps->rocketLockTime = pm->cmd.serverTime;
5973 			}
5974 			else if (pm->ps->rocketLockIndex == tr.entityNum)
5975 			{
5976 				if (pm->ps->rocketLockTime == -1)
5977 				{
5978 					pm->ps->rocketLockTime = pm->ps->rocketLastValidTime;
5979 				}
5980 			}
5981 
5982 			if (pm->ps->rocketLockIndex == tr.entityNum)
5983 			{
5984 				pm->ps->rocketTargetTime = pm->cmd.serverTime + 500;
5985 			}
5986 		}
5987 		else if (!vehicleLock)
5988 		{
5989 			if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
5990 			{
5991 				pm->ps->rocketLockIndex = ENTITYNUM_NONE;
5992 				pm->ps->rocketLockTime = 0;
5993 			}
5994 		}
5995 	}
5996 	else if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
5997 	{
5998 		pm->ps->rocketLockIndex = ENTITYNUM_NONE;
5999 		pm->ps->rocketLockTime = 0;
6000 	}
6001 	else
6002 	{
6003 		if (pm->ps->rocketLockTime != -1)
6004 		{
6005 			pm->ps->rocketLastValidTime = pm->ps->rocketLockTime;
6006 		}
6007 		pm->ps->rocketLockTime = -1;
6008 	}
6009 }
6010 
6011 //---------------------------------------
PM_DoChargedWeapons(qboolean vehicleRocketLock,bgEntity_t * veh)6012 static qboolean PM_DoChargedWeapons( qboolean vehicleRocketLock, bgEntity_t *veh )
6013 //---------------------------------------
6014 {
6015 	qboolean	charging = qfalse,
6016 				altFire = qfalse;
6017 
6018 	if ( vehicleRocketLock )
6019 	{
6020 		if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
6021 		{//actually charging
6022 			if ( veh
6023 				&& veh->m_pVehicle )
6024 			{//just make sure we have this veh info
6025 				if ( ( (pm->cmd.buttons&BUTTON_ATTACK)
6026 						&&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
6027 						&&pm->ps->ammo[0]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
6028 						||
6029 					( (pm->cmd.buttons&BUTTON_ALT_ATTACK)
6030 						&&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming
6031 						&&pm->ps->ammo[1]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot ) )
6032 				{//pressing the appropriate fire button for the lock-on/charging weapon
6033 					PM_RocketLock(16384, qtrue);
6034 					charging = qtrue;
6035 				}
6036 				if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6037 				{
6038 					altFire = qtrue;
6039 				}
6040 			}
6041 		}
6042 		//else, let go and should fire now
6043 	}
6044 	else
6045 	{
6046 		// If you want your weapon to be a charging weapon, just set this bit up
6047 		switch( pm->ps->weapon )
6048 		{
6049 		//------------------
6050 		case WP_BRYAR_PISTOL:
6051 
6052 			// alt-fire charges the weapon
6053 			//if ( pm->gametype == GT_SIEGE )
6054 			if (1)
6055 			{
6056 				if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6057 				{
6058 					charging = qtrue;
6059 					altFire = qtrue;
6060 				}
6061 			}
6062 			break;
6063 
6064 		case WP_CONCUSSION:
6065 			if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6066 			{
6067 				altFire = qtrue;
6068 			}
6069 			break;
6070 
6071 		case WP_BRYAR_OLD:
6072 
6073 			// alt-fire charges the weapon
6074 			if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6075 			{
6076 				charging = qtrue;
6077 				altFire = qtrue;
6078 			}
6079 			break;
6080 
6081 		//------------------
6082 		case WP_BOWCASTER:
6083 
6084 			// primary fire charges the weapon
6085 			if ( pm->cmd.buttons & BUTTON_ATTACK )
6086 			{
6087 				charging = qtrue;
6088 			}
6089 			break;
6090 
6091 		//------------------
6092 		case WP_ROCKET_LAUNCHER:
6093 			if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK)
6094 				&& pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] >= weaponData[pm->ps->weapon].altEnergyPerShot )
6095 			{
6096 				PM_RocketLock(2048,qfalse);
6097 				charging = qtrue;
6098 				altFire = qtrue;
6099 			}
6100 			break;
6101 
6102 		//------------------
6103 		case WP_THERMAL:
6104 
6105 			if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6106 			{
6107 				altFire = qtrue; // override default of not being an alt-fire
6108 				charging = qtrue;
6109 			}
6110 			else if ( pm->cmd.buttons & BUTTON_ATTACK )
6111 			{
6112 				charging = qtrue;
6113 			}
6114 			break;
6115 
6116 		case WP_DEMP2:
6117 			if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6118 			{
6119 				altFire = qtrue; // override default of not being an alt-fire
6120 				charging = qtrue;
6121 			}
6122 			break;
6123 
6124 		case WP_DISRUPTOR:
6125 			if ((pm->cmd.buttons & BUTTON_ATTACK) &&
6126 				pm->ps->zoomMode == 1 &&
6127 				pm->ps->zoomLocked)
6128 			{
6129 				if (!pm->cmd.forwardmove &&
6130 					!pm->cmd.rightmove &&
6131 					pm->cmd.upmove <= 0)
6132 				{
6133 					charging = qtrue;
6134 					altFire = qtrue;
6135 				}
6136 				else
6137 				{
6138 					charging = qfalse;
6139 					altFire = qfalse;
6140 				}
6141 			}
6142 
6143 			if (pm->ps->zoomMode != 1 &&
6144 				pm->ps->weaponstate == WEAPON_CHARGING_ALT)
6145 			{
6146 				pm->ps->weaponstate = WEAPON_READY;
6147 				charging = qfalse;
6148 				altFire = qfalse;
6149 			}
6150 
6151 		} // end switch
6152 	}
6153 
6154 	// set up the appropriate weapon state based on the button that's down.
6155 	//	Note that we ALWAYS return if charging is set ( meaning the buttons are still down )
6156 	if ( charging )
6157 	{
6158 		if ( altFire )
6159 		{
6160 			if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT )
6161 			{
6162 				// charge isn't started, so do it now
6163 				pm->ps->weaponstate = WEAPON_CHARGING_ALT;
6164 				pm->ps->weaponChargeTime = pm->cmd.serverTime;
6165 				pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
6166 
6167 #ifdef _DEBUG
6168 			//	Com_Printf("Starting charge\n");
6169 #endif
6170 				assert(pm->ps->weapon > WP_NONE);
6171 				BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE_ALT, pm->ps->weapon, pm->ps);
6172 			}
6173 
6174 			if ( vehicleRocketLock )
6175 			{//check vehicle ammo
6176 				if ( veh && pm->ps->ammo[1] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot )
6177 				{
6178 					pm->ps->weaponstate = WEAPON_CHARGING_ALT;
6179 					goto rest;
6180 				}
6181 			}
6182 			else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].altChargeSub+weaponData[pm->ps->weapon].altEnergyPerShot))
6183 			{
6184 				pm->ps->weaponstate = WEAPON_CHARGING_ALT;
6185 
6186 				goto rest;
6187 			}
6188 			else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].altMaxCharge)
6189 			{
6190 				if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
6191 				{
6192 					pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].altChargeSub;
6193 					pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
6194 				}
6195 			}
6196 		}
6197 		else
6198 		{
6199 			if ( pm->ps->weaponstate != WEAPON_CHARGING )
6200 			{
6201 				// charge isn't started, so do it now
6202 				pm->ps->weaponstate = WEAPON_CHARGING;
6203 				pm->ps->weaponChargeTime = pm->cmd.serverTime;
6204 				pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
6205 
6206 #ifdef _DEBUG
6207 			//	Com_Printf("Starting charge\n");
6208 #endif
6209 				BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE, pm->ps->weapon, pm->ps);
6210 			}
6211 
6212 			if ( vehicleRocketLock )
6213 			{
6214 				if ( veh && pm->ps->ammo[0] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
6215 				{//check vehicle ammo
6216 					pm->ps->weaponstate = WEAPON_CHARGING;
6217 					goto rest;
6218 				}
6219 			}
6220 			else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].chargeSub+weaponData[pm->ps->weapon].energyPerShot))
6221 			{
6222 				pm->ps->weaponstate = WEAPON_CHARGING;
6223 
6224 				goto rest;
6225 			}
6226 			else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].maxCharge)
6227 			{
6228 				if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
6229 				{
6230 					pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].chargeSub;
6231 					pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
6232 				}
6233 			}
6234 		}
6235 
6236 		return qtrue; // short-circuit rest of weapon code
6237 	}
6238 rest:
6239 	// Only charging weapons should be able to set these states...so....
6240 	//	let's see which fire mode we need to set up now that the buttons are up
6241 	if ( pm->ps->weaponstate == WEAPON_CHARGING )
6242 	{
6243 		// weapon has a charge, so let us do an attack
6244 #ifdef _DEBUG
6245 	//	Com_Printf("Firing.  Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
6246 #endif
6247 
6248 		// dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
6249 		pm->cmd.buttons |= BUTTON_ATTACK;
6250 		pm->ps->eFlags |= EF_FIRING;
6251 	}
6252 	else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
6253 	{
6254 		// weapon has a charge, so let us do an alt-attack
6255 #ifdef _DEBUG
6256 	//	Com_Printf("Firing.  Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
6257 #endif
6258 
6259 		// dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
6260 		pm->cmd.buttons |= BUTTON_ALT_ATTACK;
6261 		pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING);
6262 	}
6263 
6264 	return qfalse; // continue with the rest of the weapon code
6265 }
6266 
6267 
6268 #define BOWCASTER_CHARGE_UNIT	200.0f	// bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
6269 #define BRYAR_CHARGE_UNIT		200.0f	// bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
6270 
PM_ItemUsable(playerState_t * ps,int forcedUse)6271 int PM_ItemUsable(playerState_t *ps, int forcedUse)
6272 {
6273 	vec3_t fwd, fwdorg, dest, pos;
6274 	vec3_t yawonly;
6275 	vec3_t mins, maxs;
6276 	vec3_t trtest;
6277 	trace_t tr;
6278 
6279 	if (ps->m_iVehicleNum)
6280 	{
6281 		return 0;
6282 	}
6283 
6284 	if (ps->pm_flags & PMF_USE_ITEM_HELD)
6285 	{ //force to let go first
6286 		return 0;
6287 	}
6288 
6289 	if (ps->duelInProgress)
6290 	{ //not allowed to use holdables while in a private duel.
6291 		return 0;
6292 	}
6293 
6294 	if (!forcedUse)
6295 	{
6296 		forcedUse = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
6297 	}
6298 
6299 	if (!BG_IsItemSelectable(ps, forcedUse))
6300 	{
6301 		return 0;
6302 	}
6303 
6304 	switch (forcedUse)
6305 	{
6306 	case HI_MEDPAC:
6307 	case HI_MEDPAC_BIG:
6308 		if (ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH])
6309 		{
6310 			return 0;
6311 		}
6312 		if (ps->stats[STAT_HEALTH] <= 0 ||
6313 			(ps->eFlags & EF_DEAD))
6314 		{
6315 			return 0;
6316 		}
6317 
6318 		return 1;
6319 	case HI_SEEKER:
6320 		if (ps->eFlags & EF_SEEKERDRONE)
6321 		{
6322 			PM_AddEventWithParm(EV_ITEMUSEFAIL, SEEKER_ALREADYDEPLOYED);
6323 			return 0;
6324 		}
6325 
6326 		return 1;
6327 	case HI_SENTRY_GUN:
6328 		if (ps->fd.sentryDeployed)
6329 		{
6330 			PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_ALREADYPLACED);
6331 			return 0;
6332 		}
6333 
6334 		yawonly[ROLL] = 0;
6335 		yawonly[PITCH] = 0;
6336 		yawonly[YAW] = ps->viewangles[YAW];
6337 
6338 		VectorSet( mins, -8, -8, 0 );
6339 		VectorSet( maxs, 8, 8, 24 );
6340 
6341 		AngleVectors(yawonly, fwd, NULL, NULL);
6342 
6343 		fwdorg[0] = ps->origin[0] + fwd[0]*64;
6344 		fwdorg[1] = ps->origin[1] + fwd[1]*64;
6345 		fwdorg[2] = ps->origin[2] + fwd[2]*64;
6346 
6347 		trtest[0] = fwdorg[0] + fwd[0]*16;
6348 		trtest[1] = fwdorg[1] + fwd[1]*16;
6349 		trtest[2] = fwdorg[2] + fwd[2]*16;
6350 
6351 		pm->trace(&tr, ps->origin, mins, maxs, trtest, ps->clientNum, MASK_PLAYERSOLID);
6352 
6353 		if ((tr.fraction != 1 && tr.entityNum != ps->clientNum) || tr.startsolid || tr.allsolid)
6354 		{
6355 			PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_NOROOM);
6356 			return 0;
6357 		}
6358 
6359 		return 1;
6360 	case HI_SHIELD:
6361 		mins[0] = -8;
6362 		mins[1] = -8;
6363 		mins[2] = 0;
6364 
6365 		maxs[0] = 8;
6366 		maxs[1] = 8;
6367 		maxs[2] = 8;
6368 
6369 		AngleVectors (ps->viewangles, fwd, NULL, NULL);
6370 		fwd[2] = 0;
6371 		VectorMA(ps->origin, 64, fwd, dest);
6372 		pm->trace(&tr, ps->origin, mins, maxs, dest, ps->clientNum, MASK_SHOT );
6373 		if (tr.fraction > 0.9 && !tr.startsolid && !tr.allsolid)
6374 		{
6375 			VectorCopy(tr.endpos, pos);
6376 			VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
6377 			pm->trace( &tr, pos, mins, maxs, dest, ps->clientNum, MASK_SOLID );
6378 			if ( !tr.startsolid && !tr.allsolid )
6379 			{
6380 				return 1;
6381 			}
6382 		}
6383 		PM_AddEventWithParm(EV_ITEMUSEFAIL, SHIELD_NOROOM);
6384 		return 0;
6385 	case HI_JETPACK: //check for stuff here?
6386 		return 1;
6387 	case HI_HEALTHDISP:
6388 		return 1;
6389 	case HI_AMMODISP:
6390 		return 1;
6391 	case HI_EWEB:
6392 		return 1;
6393 	case HI_CLOAK: //check for stuff here?
6394 		return 1;
6395 	default:
6396 		return 1;
6397 	}
6398 }
6399 
6400 //cheesy vehicle weapon hackery
PM_CanSetWeaponAnims(void)6401 qboolean PM_CanSetWeaponAnims(void)
6402 {
6403     if (pm->ps->m_iVehicleNum)
6404 	{
6405 		return qfalse;
6406 	}
6407 
6408 	return qtrue;
6409 }
6410 
6411 //perform player anim overrides while on vehicle.
6412 extern int PM_irand_timesync(int val1, int val2);
PM_VehicleWeaponAnimate(void)6413 void PM_VehicleWeaponAnimate(void)
6414 {
6415 	bgEntity_t *veh = pm_entVeh;
6416 	Vehicle_t *pVeh;
6417 	int iFlags = 0, Anim = -1;
6418 
6419 	if (!veh ||
6420 		!veh->m_pVehicle ||
6421 		!veh->m_pVehicle->m_pPilot ||
6422 		!veh->m_pVehicle->m_pPilot->playerState ||
6423 		pm->ps->clientNum != veh->m_pVehicle->m_pPilot->playerState->clientNum)
6424 	{ //make sure the vehicle exists, and its pilot is this player
6425 		return;
6426 	}
6427 
6428 	pVeh = veh->m_pVehicle;
6429 
6430 	if (pVeh->m_pVehicleInfo->type == VH_WALKER ||
6431 		pVeh->m_pVehicleInfo->type == VH_FIGHTER)
6432 	{ //slightly hacky I guess, but whatever.
6433 		return;
6434 	}
6435 backAgain:
6436 	// If they're firing, play the right fire animation.
6437 	if ( pm->cmd.buttons & ( BUTTON_ATTACK | BUTTON_ALT_ATTACK ) )
6438 	{
6439 		iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
6440 
6441 		switch ( pm->ps->weapon )
6442 		{
6443 			case WP_SABER:
6444 				if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
6445 				{ //don't do anything.. I guess.
6446 					pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
6447 					goto backAgain;
6448 				}
6449 				// If we're already in an attack animation, leave (let it continue).
6450 				if (pm->ps->torsoTimer <= 0)
6451 				{ //we'll be starting a new attack
6452 					PM_AddEvent(EV_SABER_ATTACK);
6453 				}
6454 
6455 				//just set it to something so we have a proper trail. This is a stupid
6456 				//hack (much like the rest of this function)
6457 				pm->ps->saberMove = LS_R_TL2BR;
6458 
6459 				if ( pm->ps->torsoTimer > 0 && (pm->ps->torsoAnim == BOTH_VS_ATR_S ||
6460 						pm->ps->torsoAnim == BOTH_VS_ATL_S) )
6461 				{
6462 					/*
6463 					//FIXME: no need to even call the PM_SetAnim at all in this case
6464 					Anim = (animNumber_t)pm->ps->torsoAnim;
6465 					iFlags = SETANIM_FLAG_NORMAL;
6466 					break;
6467 					*/
6468 					return;
6469 				}
6470 
6471 				// Start the attack.
6472 				if ( pm->cmd.rightmove > 0 )	//right side attack
6473 				{
6474 					Anim = BOTH_VS_ATR_S;
6475 				}
6476 				else if ( pm->cmd.rightmove < 0 )	//left-side attack
6477 				{
6478 					Anim = BOTH_VS_ATL_S;
6479 				}
6480 				else	//random
6481 				{
6482 					//FIXME: alternate back and forth or auto-aim?
6483 					//if ( !Q_irand( 0, 1 ) )
6484 					if (!PM_irand_timesync(0, 1))
6485 					{
6486 						Anim = BOTH_VS_ATR_S;
6487 					}
6488 					else
6489 					{
6490 						Anim = BOTH_VS_ATL_S;
6491 					}
6492 				}
6493 
6494 				if (pm->ps->torsoTimer <= 0)
6495 				{ //restart the anim if we are already in it (and finished)
6496 					iFlags |= SETANIM_FLAG_RESTART;
6497 				}
6498 				break;
6499 
6500 			case WP_BLASTER:
6501 				// Override the shoot anim.
6502 				if ( pm->ps->torsoAnim == BOTH_ATTACK3 )
6503 				{
6504 					if ( pm->cmd.rightmove > 0 )			//right side attack
6505 					{
6506 						Anim = BOTH_VS_ATR_G;
6507 					}
6508 					else if ( pm->cmd.rightmove < 0 )	//left side
6509 					{
6510 						Anim = BOTH_VS_ATL_G;
6511 					}
6512 					else	//frontal
6513 					{
6514 						Anim = BOTH_VS_ATF_G;
6515 					}
6516 				}
6517 				break;
6518 
6519 			default:
6520 				Anim = BOTH_VS_IDLE;
6521 				break;
6522 		}
6523 	}
6524 	else if (veh->playerState && veh->playerState->speed < 0 &&
6525 		pVeh->m_pVehicleInfo->type == VH_ANIMAL)
6526 	{ //tauntaun is going backwards
6527 		Anim = BOTH_VT_WALK_REV;
6528 	}
6529 	else if (veh->playerState && veh->playerState->speed < 0 &&
6530 		pVeh->m_pVehicleInfo->type == VH_SPEEDER)
6531 	{ //speeder is going backwards
6532 		Anim = BOTH_VS_REV;
6533 	}
6534 	// They're not firing so play the Idle for the weapon.
6535 	else
6536 	{
6537 		iFlags = SETANIM_FLAG_NORMAL;
6538 
6539 		switch ( pm->ps->weapon )
6540 		{
6541 			case WP_SABER:
6542 				if ( BG_SabersOff( pm->ps ) )
6543 				{ //saber holstered, normal idle
6544 					Anim = BOTH_VS_IDLE;
6545 				}
6546 				// In the Air.
6547 				//else if ( pVeh->m_ulFlags & VEH_FLYING )
6548 				else if (0)
6549 				{
6550 					Anim = BOTH_VS_AIR_G;
6551 					iFlags = SETANIM_FLAG_OVERRIDE;
6552 				}
6553 				// Crashing.
6554 				//else if ( pVeh->m_ulFlags & VEH_CRASHING )
6555 				else if (0)
6556 				{
6557 					pVeh->m_ulFlags &= ~VEH_CRASHING;	// Remove the flag, we are doing the animation.
6558 					Anim = BOTH_VS_LAND_SR;
6559 					iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
6560 				}
6561 				else
6562 				{
6563 					Anim = BOTH_VS_IDLE_SR;
6564 				}
6565 				break;
6566 
6567 			case WP_BLASTER:
6568 				// In the Air.
6569 				//if ( pVeh->m_ulFlags & VEH_FLYING )
6570 				if (0)
6571 				{
6572 					Anim = BOTH_VS_AIR_G;
6573 					iFlags = SETANIM_FLAG_OVERRIDE;
6574 				}
6575 				// Crashing.
6576 				//else if ( pVeh->m_ulFlags & VEH_CRASHING )
6577 				else if (0)
6578 				{
6579 					pVeh->m_ulFlags &= ~VEH_CRASHING;	// Remove the flag, we are doing the animation.
6580 					Anim = BOTH_VS_LAND_G;
6581 					iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
6582 				}
6583 				else
6584 				{
6585 					Anim = BOTH_VS_IDLE_G;
6586 				}
6587 				break;
6588 
6589 			default:
6590 				Anim = BOTH_VS_IDLE;
6591 				break;
6592 		}
6593 	}
6594 
6595 	if (Anim != -1)
6596 	{ //override it
6597 		if (pVeh->m_pVehicleInfo->type == VH_ANIMAL)
6598 		{ //agh.. remap anims for the tauntaun
6599 			switch (Anim)
6600 			{
6601 			case BOTH_VS_IDLE:
6602 				if (veh->playerState && veh->playerState->speed > 0)
6603 				{
6604 					if (veh->playerState->speed > pVeh->m_pVehicleInfo->speedMax)
6605 					{ //turbo
6606 						Anim = BOTH_VT_TURBO;
6607 					}
6608 					else
6609 					{
6610 						Anim = BOTH_VT_RUN_FWD;
6611 					}
6612 				}
6613 				else
6614 				{
6615 					Anim = BOTH_VT_IDLE;
6616 				}
6617 				break;
6618 			case BOTH_VS_ATR_S:
6619 				Anim = BOTH_VT_ATR_S;
6620 				break;
6621 			case BOTH_VS_ATL_S:
6622 				Anim = BOTH_VT_ATL_S;
6623 				break;
6624 			case BOTH_VS_ATR_G:
6625                 Anim = BOTH_VT_ATR_G;
6626 				break;
6627 			case BOTH_VS_ATL_G:
6628 				Anim = BOTH_VT_ATL_G;
6629 				break;
6630 			case BOTH_VS_ATF_G:
6631 				Anim = BOTH_VT_ATF_G;
6632 				break;
6633 			case BOTH_VS_IDLE_SL:
6634 				Anim = BOTH_VT_IDLE_S;
6635 				break;
6636 			case BOTH_VS_IDLE_SR:
6637 				Anim = BOTH_VT_IDLE_S;
6638 				break;
6639 			case BOTH_VS_IDLE_G:
6640 				Anim = BOTH_VT_IDLE_G;
6641 				break;
6642 
6643 			//should not happen for tauntaun:
6644 			case BOTH_VS_AIR_G:
6645 			case BOTH_VS_LAND_SL:
6646 			case BOTH_VS_LAND_SR:
6647 			case BOTH_VS_LAND_G:
6648 				return;
6649 			default:
6650 				break;
6651 			}
6652 		}
6653 
6654 		PM_SetAnim(SETANIM_BOTH, Anim, iFlags);
6655 	}
6656 }
6657 
6658 /*
6659 ==============
6660 PM_Weapon
6661 
6662 Generates weapon events and modifes the weapon counter
6663 ==============
6664 */
6665 extern int PM_KickMoveForConditions(void);
PM_Weapon(void)6666 static void PM_Weapon( void )
6667 {
6668 	int		addTime;
6669 	int amount;
6670 	int		killAfterItem = 0;
6671 	bgEntity_t *veh = NULL;
6672 	qboolean vehicleRocketLock = qfalse;
6673 
6674 #ifdef _GAME
6675 	if (pm->ps->clientNum >= MAX_CLIENTS &&
6676 		pm->ps->weapon == WP_NONE &&
6677 		pm->cmd.weapon == WP_NONE &&
6678 		pm_entSelf)
6679 	{ //npc with no weapon
6680 		gentity_t *gent = (gentity_t *)pm_entSelf;
6681 		if (gent->inuse && gent->client &&
6682 			!gent->localAnimIndex)
6683 		{ //humanoid
6684 			pm->ps->torsoAnim = pm->ps->legsAnim;
6685 			pm->ps->torsoTimer = pm->ps->legsTimer;
6686 			return;
6687 		}
6688 	}
6689 #endif
6690 
6691 	if (!pm->ps->emplacedIndex &&
6692 		pm->ps->weapon == WP_EMPLACED_GUN)
6693 	{ //oh no!
6694 		int i = 0;
6695 		int weap = -1;
6696 
6697 		while (i < WP_NUM_WEAPONS)
6698 		{
6699 			if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) && i != WP_NONE)
6700 			{ //this one's good
6701 				weap = i;
6702 				break;
6703 			}
6704 			i++;
6705 		}
6706 
6707 		if (weap != -1)
6708 		{
6709 			pm->cmd.weapon = weap;
6710 			pm->ps->weapon = weap;
6711 			return;
6712 		}
6713 	}
6714 
6715 	if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
6716 		&&pm->ps->m_iVehicleNum)
6717 	{ //riding a vehicle
6718 		if ( (veh = pm_entVeh) &&
6719 			(veh->m_pVehicle && (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) ) )
6720 		{//riding a walker/fighter
6721 			//keep saber off, do no weapon stuff at all!
6722 			pm->ps->saberHolstered = 2;
6723 #ifdef _GAME
6724 			pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
6725 #else
6726 			if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
6727 				||  g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
6728 			{//our vehicle uses a rocket launcher, so do the normal checks
6729 				vehicleRocketLock = qtrue;
6730 				pm->cmd.buttons &= ~BUTTON_ATTACK;
6731 			}
6732 			else
6733 			{
6734 				pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
6735 			}
6736 #endif
6737 		}
6738 	}
6739 
6740 	if (pm->ps->weapon != WP_DISRUPTOR //not using disruptor
6741 		&& pm->ps->weapon != WP_ROCKET_LAUNCHER//not using rocket launcher
6742 		&& pm->ps->weapon != WP_THERMAL//not using thermals
6743 		&& !pm->ps->m_iVehicleNum )//not a vehicle or in a vehicle
6744 	{ //check for exceeding max charge time if not using disruptor or rocket launcher or thermals
6745 		if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
6746 		{
6747 			int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
6748 
6749 			if (timeDif > MAX_WEAPON_CHARGE_TIME)
6750 			{
6751 				pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
6752 			}
6753 		}
6754 
6755 		if ( pm->ps->weaponstate == WEAPON_CHARGING )
6756 		{
6757 			int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
6758 
6759 			if (timeDif > MAX_WEAPON_CHARGE_TIME)
6760 			{
6761 				pm->cmd.buttons &= ~BUTTON_ATTACK;
6762 			}
6763 		}
6764 	}
6765 
6766 	if (pm->ps->forceHandExtend == HANDEXTEND_WEAPONREADY &&
6767 		PM_CanSetWeaponAnims())
6768 	{ //reset into weapon stance
6769 		if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
6770 		{ //saber handles its own anims
6771 			if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
6772 			{
6773 				//PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
6774 				PM_StartTorsoAnim( TORSO_RAISEWEAP1);
6775 			}
6776 			else
6777 			{
6778 				if (pm->ps->weapon == WP_EMPLACED_GUN)
6779 				{
6780 					PM_StartTorsoAnim( BOTH_GUNSIT1 );
6781 				}
6782 				else
6783 				{
6784 					//PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
6785 					PM_StartTorsoAnim( TORSO_RAISEWEAP1);
6786 				}
6787 			}
6788 		}
6789 
6790 		//we now go into a weapon raise anim after every force hand extend.
6791 		//this is so that my holster-view-weapon-when-hand-extend stuff works.
6792 		pm->ps->weaponstate = WEAPON_RAISING;
6793 		pm->ps->weaponTime += 250;
6794 
6795 		pm->ps->forceHandExtend = HANDEXTEND_NONE;
6796 	}
6797 	else if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
6798 	{ //nothing else should be allowed to happen during this time, including weapon fire
6799 		int desiredAnim = 0;
6800 		qboolean seperateOnTorso = qfalse;
6801 		qboolean playFullBody = qfalse;
6802 		int desiredOnTorso = 0;
6803 
6804 		switch(pm->ps->forceHandExtend)
6805 		{
6806 		case HANDEXTEND_FORCEPUSH:
6807 			desiredAnim = BOTH_FORCEPUSH;
6808 			break;
6809 		case HANDEXTEND_FORCEPULL:
6810 			desiredAnim = BOTH_FORCEPULL;
6811 			break;
6812 		case HANDEXTEND_FORCE_HOLD:
6813 			if ( (pm->ps->fd.forcePowersActive&(1<<FP_GRIP)) )
6814 			{//gripping
6815 				desiredAnim = BOTH_FORCEGRIP_HOLD;
6816 			}
6817 			else if ( (pm->ps->fd.forcePowersActive&(1<<FP_LIGHTNING)) )
6818 			{//lightning
6819 				if ( pm->ps->weapon == WP_MELEE
6820 					&& pm->ps->activeForcePass > FORCE_LEVEL_2 )
6821 				{//2-handed lightning
6822 					desiredAnim = BOTH_FORCE_2HANDEDLIGHTNING_HOLD;
6823 				}
6824 				else
6825 				{
6826 					desiredAnim = BOTH_FORCELIGHTNING_HOLD;
6827 				}
6828 			}
6829 			else if ( (pm->ps->fd.forcePowersActive&(1<<FP_DRAIN)) )
6830 			{//draining
6831 				desiredAnim = BOTH_FORCEGRIP_HOLD;
6832 			}
6833 			else
6834 			{//???
6835 				desiredAnim = BOTH_FORCEGRIP_HOLD;
6836 			}
6837 			break;
6838 		case HANDEXTEND_SABERPULL:
6839 			desiredAnim = BOTH_SABERPULL;
6840 			break;
6841 		case HANDEXTEND_CHOKE:
6842 			desiredAnim = BOTH_CHOKE3; //left-handed choke
6843 			break;
6844 		case HANDEXTEND_DODGE:
6845 			desiredAnim = pm->ps->forceDodgeAnim;
6846 			break;
6847 		case HANDEXTEND_KNOCKDOWN:
6848 			if (pm->ps->forceDodgeAnim)
6849 			{
6850 				if (pm->ps->forceDodgeAnim > 4)
6851 				{ //this means that we want to play a sepereate anim on the torso
6852 					int originalDAnim = pm->ps->forceDodgeAnim-8; //-8 is the original legs anim
6853 					if (originalDAnim == 2)
6854 					{
6855 						desiredAnim = BOTH_FORCE_GETUP_B1;
6856 					}
6857 					else if (originalDAnim == 3)
6858 					{
6859 						desiredAnim = BOTH_FORCE_GETUP_B3;
6860 					}
6861 					else
6862 					{
6863 						desiredAnim = BOTH_GETUP1;
6864 					}
6865 
6866 					//now specify the torso anim
6867 					seperateOnTorso = qtrue;
6868 					desiredOnTorso = BOTH_FORCEPUSH;
6869 				}
6870 				else if (pm->ps->forceDodgeAnim == 2)
6871 				{
6872 					desiredAnim = BOTH_FORCE_GETUP_B1;
6873 				}
6874 				else if (pm->ps->forceDodgeAnim == 3)
6875 				{
6876 					desiredAnim = BOTH_FORCE_GETUP_B3;
6877 				}
6878 				else
6879 				{
6880 					desiredAnim = BOTH_GETUP1;
6881 				}
6882 			}
6883 			else
6884 			{
6885 				desiredAnim = BOTH_KNOCKDOWN1;
6886 			}
6887 			break;
6888 		case HANDEXTEND_DUELCHALLENGE:
6889 			desiredAnim = BOTH_ENGAGETAUNT;
6890 			break;
6891 		case HANDEXTEND_TAUNT:
6892 			desiredAnim = pm->ps->forceDodgeAnim;
6893 			if ( desiredAnim != BOTH_ENGAGETAUNT
6894 				&& VectorCompare( pm->ps->velocity, vec3_origin )
6895 				&& pm->ps->groundEntityNum != ENTITYNUM_NONE )
6896 			{
6897 				playFullBody = qtrue;
6898 			}
6899 			break;
6900 		case HANDEXTEND_PRETHROW:
6901 			desiredAnim = BOTH_A3_TL_BR;
6902 			playFullBody = qtrue;
6903 			break;
6904 		case HANDEXTEND_POSTTHROW:
6905 			desiredAnim = BOTH_D3_TL___;
6906 			playFullBody = qtrue;
6907 			break;
6908 		case HANDEXTEND_PRETHROWN:
6909 			desiredAnim = BOTH_KNEES1;
6910 			playFullBody = qtrue;
6911 			break;
6912 		case HANDEXTEND_POSTTHROWN:
6913 			if (pm->ps->forceDodgeAnim)
6914 			{
6915 				desiredAnim = BOTH_FORCE_GETUP_F2;
6916 			}
6917 			else
6918 			{
6919 				desiredAnim = BOTH_KNOCKDOWN5;
6920 			}
6921 			playFullBody = qtrue;
6922 			break;
6923 		case HANDEXTEND_DRAGGING:
6924 			desiredAnim = BOTH_B1_BL___;
6925 			break;
6926 		case HANDEXTEND_JEDITAUNT:
6927 			desiredAnim = BOTH_GESTURE1;
6928 			//playFullBody = qtrue;
6929 			break;
6930 			//Hmm... maybe use these, too?
6931 			//BOTH_FORCEHEAL_QUICK //quick heal (SP level 2 & 3)
6932 			//BOTH_MINDTRICK1 // wave (maybe for mind trick 2 & 3 - whole area, and for force seeing)
6933 			//BOTH_MINDTRICK2 // tap (maybe for mind trick 1 - one person)
6934 			//BOTH_FORCEGRIP_START //start grip
6935 			//BOTH_FORCEGRIP_HOLD //hold grip
6936 			//BOTH_FORCEGRIP_RELEASE //release grip
6937 			//BOTH_FORCELIGHTNING //quick lightning burst (level 1)
6938 			//BOTH_FORCELIGHTNING_START //start lightning
6939 			//BOTH_FORCELIGHTNING_HOLD //hold lightning
6940 			//BOTH_FORCELIGHTNING_RELEASE //release lightning
6941 		default:
6942 			desiredAnim = BOTH_FORCEPUSH;
6943 			break;
6944 		}
6945 
6946 		if (!seperateOnTorso)
6947 		{ //of seperateOnTorso, handle it after setting the legs
6948 			PM_SetAnim(SETANIM_TORSO, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6949 			pm->ps->torsoTimer = 1;
6950 		}
6951 
6952 		if (playFullBody)
6953 		{ //sorry if all these exceptions are getting confusing. This one just means play on both legs and torso.
6954 			PM_SetAnim(SETANIM_BOTH, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6955 			pm->ps->legsTimer = pm->ps->torsoTimer = 1;
6956 		}
6957 		else if (pm->ps->forceHandExtend == HANDEXTEND_DODGE || pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
6958 			(pm->ps->forceHandExtend == HANDEXTEND_CHOKE && pm->ps->groundEntityNum == ENTITYNUM_NONE) )
6959 		{ //special case, play dodge anim on whole body, choke anim too if off ground
6960 			if (seperateOnTorso)
6961 			{
6962 				PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6963 				pm->ps->legsTimer = 1;
6964 
6965 				PM_SetAnim(SETANIM_TORSO, desiredOnTorso, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6966 				pm->ps->torsoTimer = 1;
6967 			}
6968 			else
6969 			{
6970 				PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
6971 				pm->ps->legsTimer = 1;
6972 			}
6973 		}
6974 
6975 		return;
6976 	}
6977 
6978 	if (BG_InSpecialJump(pm->ps->legsAnim) ||
6979 		BG_InRoll(pm->ps, pm->ps->legsAnim) ||
6980 		PM_InRollComplete(pm->ps, pm->ps->legsAnim))
6981 	{
6982 		/*
6983 		if (pm->cmd.weapon != WP_MELEE &&
6984 			pm->ps->weapon != WP_MELEE &&
6985 			(pm->ps->stats[STAT_WEAPONS] & (1<<WP_SABER)))
6986 		{ //it's alright also if we are melee
6987 			pm->cmd.weapon = WP_SABER;
6988 			pm->ps->weapon = WP_SABER;
6989 		}
6990 		*/
6991 		if (pm->ps->weaponTime < pm->ps->legsTimer)
6992 		{
6993 			pm->ps->weaponTime = pm->ps->legsTimer;
6994 		}
6995 	}
6996 
6997 	if (pm->ps->duelInProgress)
6998 	{
6999 		pm->cmd.weapon = WP_SABER;
7000 		pm->ps->weapon = WP_SABER;
7001 
7002 		if (pm->ps->duelTime >= pm->cmd.serverTime)
7003 		{
7004 			pm->cmd.upmove = 0;
7005 			pm->cmd.forwardmove = 0;
7006 			pm->cmd.rightmove = 0;
7007 		}
7008 	}
7009 
7010 	if (pm->ps->weapon == WP_SABER && pm->ps->saberMove != LS_READY && pm->ps->saberMove != LS_NONE)
7011 	{
7012 		pm->cmd.weapon = WP_SABER; //don't allow switching out mid-attack
7013 	}
7014 
7015 	if (pm->ps->weapon == WP_SABER)
7016 	{
7017 		//rww - we still need the item stuff, so we won't return immediately
7018 		PM_WeaponLightsaber();
7019 		killAfterItem = 1;
7020 	}
7021 	else if (pm->ps->weapon != WP_EMPLACED_GUN)
7022 	{
7023 		pm->ps->saberHolstered = 0;
7024 	}
7025 
7026 	if (PM_CanSetWeaponAnims())
7027 	{
7028 		if (pm->ps->weapon == WP_THERMAL ||
7029 			pm->ps->weapon == WP_TRIP_MINE ||
7030 			pm->ps->weapon == WP_DET_PACK)
7031 		{
7032 			if (pm->ps->weapon == WP_THERMAL)
7033 			{
7034 				if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
7035 					(pm->ps->weaponTime-200) <= 0)
7036 				{
7037 					PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7038 				}
7039 			}
7040 			else
7041 			{
7042 				if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
7043 					(pm->ps->weaponTime-700) <= 0)
7044 				{
7045 					PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7046 				}
7047 			}
7048 		}
7049 	}
7050 
7051 	// don't allow attack until all buttons are up
7052 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
7053 		return;
7054 	}
7055 
7056 	// ignore if spectator
7057 	if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
7058 			return;
7059 	}
7060 
7061 	// check for dead player
7062 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
7063 		pm->ps->weapon = WP_NONE;
7064 		return;
7065 	}
7066 
7067 	// check for item using
7068 	if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
7069 		// fix: rocket lock bug, one of many...
7070 		BG_ClearRocketLock( pm->ps );
7071 
7072 		if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
7073 
7074 			if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
7075 				&& pm->ps->m_iVehicleNum)
7076 			{//riding a vehicle, can't use holdable items, this button operates as the weapon link/unlink toggle
7077 				return;
7078 			}
7079 
7080 			if (!pm->ps->stats[STAT_HOLDABLE_ITEM])
7081 			{
7082 				return;
7083 			}
7084 
7085 			if (!PM_ItemUsable(pm->ps, 0))
7086 			{
7087 				pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
7088 				return;
7089 			}
7090 			else
7091 			{
7092 				if (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag))
7093 				{
7094 					if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
7095 						bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
7096 						bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
7097 						bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
7098 						bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
7099 						bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
7100 					{ //never use up the binoculars or jetpack or dispensers or cloak or ...
7101 						pm->ps->stats[STAT_HOLDABLE_ITEMS] -= (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag);
7102 					}
7103 				}
7104 				else
7105 				{
7106 					return; //this should not happen...
7107 				}
7108 
7109 				pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
7110 				PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
7111 
7112 				if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
7113 					bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
7114 					bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
7115 					bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
7116 					bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
7117 					bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
7118 				{
7119 					pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
7120 					BG_CycleInven(pm->ps, 1);
7121 				}
7122 			}
7123 			return;
7124 		}
7125 	} else {
7126 		pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
7127 	}
7128 
7129 	/*
7130 	if (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
7131 	{ //we can't toggle zoom while using saber (for obvious reasons) so make sure it's always off
7132 		pm->ps->zoomMode = 0;
7133 		pm->ps->zoomFov = 0;
7134 		pm->ps->zoomLocked = qfalse;
7135 		pm->ps->zoomLockTime = 0;
7136 	}
7137 	*/
7138 
7139 	if (killAfterItem)
7140 	{
7141 		return;
7142 	}
7143 
7144 	// make weapon function
7145 	if ( pm->ps->weaponTime > 0 ) {
7146 		pm->ps->weaponTime -= pml.msec;
7147 	}
7148 
7149 	if (pm->ps->isJediMaster && pm->ps->emplacedIndex)
7150 	{
7151 		pm->ps->emplacedIndex = 0;
7152 		pm->ps->saberHolstered = 0;
7153 	}
7154 
7155 	if (pm->ps->duelInProgress && pm->ps->emplacedIndex)
7156 	{
7157 		pm->ps->emplacedIndex = 0;
7158 		pm->ps->saberHolstered = 0;
7159 	}
7160 
7161 	if (pm->ps->weapon == WP_EMPLACED_GUN && pm->ps->emplacedIndex)
7162 	{
7163 		pm->cmd.weapon = WP_EMPLACED_GUN; //No switch for you!
7164 		PM_StartTorsoAnim( BOTH_GUNSIT1 );
7165 	}
7166 
7167 	if (pm->ps->isJediMaster || pm->ps->duelInProgress || pm->ps->trueJedi)
7168 	{
7169 		pm->cmd.weapon = WP_SABER;
7170 		pm->ps->weapon = WP_SABER;
7171 
7172 		if (pm->ps->isJediMaster || pm->ps->trueJedi)
7173 		{
7174 			pm->ps->stats[STAT_WEAPONS] = (1 << WP_SABER);
7175 		}
7176 	}
7177 
7178 	amount = weaponData[pm->ps->weapon].energyPerShot;
7179 
7180 	// take an ammo away if not infinite
7181 	if ( pm->ps->weapon != WP_NONE &&
7182 		pm->ps->weapon == pm->cmd.weapon &&
7183 		(pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) )
7184 	{
7185 		if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
7186 		{
7187 			// enough energy to fire this weapon?
7188 			if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].energyPerShot &&
7189 				pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].altEnergyPerShot)
7190 			{ //the weapon is out of ammo essentially because it cannot fire primary or secondary, so do the switch
7191 			  //regardless of if the player is attacking or not
7192 				PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
7193 
7194 				if (pm->ps->weaponTime < 500)
7195 				{
7196 					pm->ps->weaponTime += 500;
7197 				}
7198 				return;
7199 			}
7200 
7201 			if (pm->ps->weapon == WP_DET_PACK && !pm->ps->hasDetPackPlanted && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < 1)
7202 			{
7203 				PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
7204 
7205 				if (pm->ps->weaponTime < 500)
7206 				{
7207 					pm->ps->weaponTime += 500;
7208 				}
7209 				return;
7210 			}
7211 		}
7212 	}
7213 
7214 	// check for weapon change
7215 	// can't change if weapon is firing, but can change
7216 	// again if lowering or raising
7217 	if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
7218 		if ( pm->ps->weapon != pm->cmd.weapon ) {
7219 			PM_BeginWeaponChange( pm->cmd.weapon );
7220 		}
7221 	}
7222 
7223 	if ( pm->ps->weaponTime > 0 ) {
7224 		return;
7225 	}
7226 
7227 	if (pm->ps->weapon == WP_DISRUPTOR &&
7228 		pm->ps->zoomMode == 1)
7229 	{
7230 		if (pm_cancelOutZoom)
7231 		{
7232 			pm->ps->zoomMode = 0;
7233 			pm->ps->zoomFov = 0;
7234 			pm->ps->zoomLocked = qfalse;
7235 			pm->ps->zoomLockTime = 0;
7236 			PM_AddEvent( EV_DISRUPTOR_ZOOMSOUND );
7237 			return;
7238 		}
7239 
7240 		if (pm->cmd.forwardmove ||
7241 			pm->cmd.rightmove ||
7242 			pm->cmd.upmove > 0)
7243 		{
7244 			return;
7245 		}
7246 	}
7247 
7248 	// change weapon if time
7249 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
7250 		PM_FinishWeaponChange();
7251 		return;
7252 	}
7253 
7254 	if ( pm->ps->weaponstate == WEAPON_RAISING ) {
7255 		pm->ps->weaponstate = WEAPON_READY;
7256 		if (PM_CanSetWeaponAnims())
7257 		{
7258 			if ( pm->ps->weapon == WP_SABER )
7259 			{
7260 				PM_StartTorsoAnim( PM_GetSaberStance() );
7261 			}
7262 			else if (pm->ps->weapon == WP_MELEE || PM_IsRocketTrooper())
7263 			{
7264 				PM_StartTorsoAnim( pm->ps->legsAnim );
7265 			}
7266 			else
7267 			{
7268 				if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
7269 				{
7270 					PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
7271 				}
7272 				else
7273 				{
7274 					if (pm->ps->weapon == WP_EMPLACED_GUN)
7275 					{
7276 						PM_StartTorsoAnim( BOTH_GUNSIT1 );
7277 					}
7278 					else
7279 					{
7280 						PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7281 					}
7282 				}
7283 			}
7284 		}
7285 		return;
7286 	}
7287 
7288 	if (PM_CanSetWeaponAnims() &&
7289 		!PM_IsRocketTrooper() &&
7290 		pm->ps->weaponstate == WEAPON_READY && pm->ps->weaponTime <= 0 &&
7291 		(pm->ps->weapon >= WP_BRYAR_PISTOL || pm->ps->weapon == WP_STUN_BATON) &&
7292 		pm->ps->torsoTimer <= 0 &&
7293 		(pm->ps->torsoAnim) != WeaponReadyAnim[pm->ps->weapon] &&
7294 		pm->ps->torsoAnim != TORSO_WEAPONIDLE3 &&
7295 		pm->ps->weapon != WP_EMPLACED_GUN)
7296 	{
7297 		PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7298 	}
7299 	else if (PM_CanSetWeaponAnims() &&
7300 		pm->ps->weapon == WP_MELEE)
7301 	{
7302 		if (pm->ps->weaponTime <= 0 &&
7303 			pm->ps->forceHandExtend == HANDEXTEND_NONE)
7304 		{
7305 			int desTAnim = pm->ps->legsAnim;
7306 
7307 			if (desTAnim == BOTH_STAND1 ||
7308 				desTAnim == BOTH_STAND2)
7309 			{ //remap the standard standing anims for melee stance
7310 				desTAnim = BOTH_STAND6;
7311 			}
7312 
7313 			if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
7314 			{ //don't do this while holding attack
7315 				if (pm->ps->torsoAnim != desTAnim)
7316 				{
7317 					PM_StartTorsoAnim( desTAnim );
7318 				}
7319 			}
7320 		}
7321 	}
7322 	else if (PM_CanSetWeaponAnims() && PM_IsRocketTrooper())
7323 	{
7324 		int desTAnim = pm->ps->legsAnim;
7325 
7326 		if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
7327 		{ //don't do this while holding attack
7328 			if (pm->ps->torsoAnim != desTAnim)
7329 			{
7330 				PM_StartTorsoAnim( desTAnim );
7331 			}
7332 		}
7333 	}
7334 
7335 	if (((pm->ps->torsoAnim) == TORSO_WEAPONREADY4 ||
7336 		(pm->ps->torsoAnim) == BOTH_ATTACK4) &&
7337 		(pm->ps->weapon != WP_DISRUPTOR || pm->ps->zoomMode != 1))
7338 	{
7339 		if (pm->ps->weapon == WP_EMPLACED_GUN)
7340 		{
7341 			PM_StartTorsoAnim( BOTH_GUNSIT1 );
7342 		}
7343 		else if (PM_CanSetWeaponAnims())
7344 		{
7345 			PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
7346 		}
7347 	}
7348 	else if (((pm->ps->torsoAnim) != TORSO_WEAPONREADY4 &&
7349 		(pm->ps->torsoAnim) != BOTH_ATTACK4) &&
7350 		PM_CanSetWeaponAnims() &&
7351 		(pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1))
7352 	{
7353 		PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
7354 	}
7355 
7356 	if (pm->ps->clientNum >= MAX_CLIENTS &&
7357 		pm_entSelf &&
7358 		pm_entSelf->s.NPC_class == CLASS_VEHICLE)
7359 	{//we are a vehicle
7360 		veh = pm_entSelf;
7361 	}
7362 	if ( veh
7363 		&& veh->m_pVehicle )
7364 	{
7365 		if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
7366 			|| g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
7367 		{//don't clear the rocket locking ever?
7368 			vehicleRocketLock = qtrue;
7369 		}
7370 	}
7371 
7372 	if ( !vehicleRocketLock )
7373 	{
7374 		if (pm->ps->weapon != WP_ROCKET_LAUNCHER)
7375 		{
7376 			if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
7377 				&&pm->ps->m_iVehicleNum)
7378 			{//riding a vehicle, the vehicle will tell me my rocketlock stuff...
7379 			}
7380 			else
7381 			{
7382 				pm->ps->rocketLockIndex = ENTITYNUM_NONE;
7383 				pm->ps->rocketLockTime = 0;
7384 				pm->ps->rocketTargetTime = 0;
7385 			}
7386 		}
7387 	}
7388 
7389 	if ( PM_DoChargedWeapons(vehicleRocketLock, veh))
7390 	{
7391 		// In some cases the charged weapon code may want us to short circuit the rest of the firing code
7392 		return;
7393 	}
7394 
7395 	// check for fire
7396 	if ( ! (pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
7397 	{
7398 		pm->ps->weaponTime = 0;
7399 		pm->ps->weaponstate = WEAPON_READY;
7400 		return;
7401 	}
7402 
7403 	if (pm->ps->weapon == WP_EMPLACED_GUN)
7404 	{
7405 		addTime = weaponData[pm->ps->weapon].fireTime;
7406 		pm->ps->weaponTime += addTime;
7407 		if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
7408 		{
7409 			PM_AddEvent( EV_ALT_FIRE );
7410 		}
7411 		else
7412 		{
7413 			PM_AddEvent( EV_FIRE_WEAPON );
7414 		}
7415 		return;
7416 	}
7417 	else if (pm->ps->m_iVehicleNum
7418 		&& pm_entSelf->s.NPC_class==CLASS_VEHICLE)
7419 	{ //a vehicle NPC that has a pilot
7420 		pm->ps->weaponstate = WEAPON_FIRING;
7421 		pm->ps->weaponTime += 100;
7422 #ifdef _GAME //hack, only do it game-side. vehicle weapons don't really need predicting I suppose.
7423 		if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
7424 		{
7425 			G_CheapWeaponFire(pm->ps->clientNum, EV_ALT_FIRE);
7426 		}
7427 		else
7428 		{
7429 			G_CheapWeaponFire(pm->ps->clientNum, EV_FIRE_WEAPON);
7430 		}
7431 #endif
7432 		/*
7433 		addTime = weaponData[WP_EMPLACED_GUN].fireTime;
7434 		pm->ps->weaponTime += addTime;
7435 		if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
7436 		{
7437 			PM_AddEvent( EV_ALT_FIRE );
7438 		}
7439 		else
7440 		{
7441 			PM_AddEvent( EV_FIRE_WEAPON );
7442 		}
7443 		*/
7444 		return;
7445 	}
7446 
7447 	if (pm->ps->weapon == WP_DISRUPTOR &&
7448 		(pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
7449 		!pm->ps->zoomLocked)
7450 	{
7451 		return;
7452 	}
7453 
7454 	if (pm->ps->weapon == WP_DISRUPTOR &&
7455 		(pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
7456 		pm->ps->zoomMode == 2)
7457 	{ //can't use disruptor secondary while zoomed binoculars
7458 		return;
7459 	}
7460 
7461 	if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
7462 	{
7463 		PM_StartTorsoAnim( BOTH_ATTACK4 );
7464 	}
7465 	else if (pm->ps->weapon == WP_MELEE)
7466 	{ //special anims for standard melee attacks
7467 		//Alternate between punches and use the anim length as weapon time.
7468 		if (!pm->ps->m_iVehicleNum)
7469 		{ //if riding a vehicle don't do this stuff at all
7470 			if (pm->debugMelee &&
7471 				(pm->cmd.buttons & BUTTON_ATTACK) &&
7472 				(pm->cmd.buttons & BUTTON_ALT_ATTACK))
7473 			{ //ok, grapple time
7474 #if 0 //eh, I want to try turning the saber off, but can't do that reliably for prediction..
7475 				qboolean icandoit = qtrue;
7476 				if (pm->ps->weaponTime > 0)
7477 				{ //weapon busy
7478 					icandoit = qfalse;
7479 				}
7480 				if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
7481 				{ //force power or knockdown or something
7482 					icandoit = qfalse;
7483 				}
7484 				if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE)
7485 				{
7486 					icandoit = qfalse;
7487 				}
7488 
7489 				if (icandoit)
7490 				{
7491 					//G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7492 					PM_SetAnim(SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7493 					if (pm->ps->torsoAnim == BOTH_KYLE_GRAB)
7494 					{ //providing the anim set succeeded..
7495 						pm->ps->torsoTimer += 500; //make the hand stick out a little longer than it normally would
7496 						if (pm->ps->legsAnim == pm->ps->torsoAnim)
7497 						{
7498 							pm->ps->legsTimer = pm->ps->torsoTimer;
7499 						}
7500 						pm->ps->weaponTime = pm->ps->torsoTimer;
7501 						return;
7502 					}
7503 				}
7504 #else
7505 	#ifdef _GAME
7506 				if (pm_entSelf)
7507 				{
7508 					if (TryGrapple((gentity_t *)pm_entSelf))
7509 					{
7510 						return;
7511 					}
7512 				}
7513 	#else
7514 				return;
7515 	#endif
7516 #endif
7517 			}
7518 			else if (pm->debugMelee &&
7519 				(pm->cmd.buttons & BUTTON_ALT_ATTACK))
7520 			{ //kicks
7521 				if (!BG_KickingAnim(pm->ps->torsoAnim) &&
7522 					!BG_KickingAnim(pm->ps->legsAnim))
7523 				{
7524 					int kickMove = PM_KickMoveForConditions();
7525 					if (kickMove == LS_HILT_BASH)
7526 					{ //yeah.. no hilt to bash with!
7527 						kickMove = LS_KICK_F;
7528 					}
7529 
7530 					if (kickMove != -1)
7531 					{
7532 						if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
7533 						{//if in air, convert kick to an in-air kick
7534 							float gDist = PM_GroundDistance();
7535 							//let's only allow air kicks if a certain distance from the ground
7536 							//it's silly to be able to do them right as you land.
7537 							//also looks wrong to transition from a non-complete flip anim...
7538 							if ((!BG_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsTimer <= 0) &&
7539 								gDist > 64.0f && //strict minimum
7540 								gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
7541 								)
7542 							{
7543 								switch ( kickMove )
7544 								{
7545 								case LS_KICK_F:
7546 									kickMove = LS_KICK_F_AIR;
7547 									break;
7548 								case LS_KICK_B:
7549 									kickMove = LS_KICK_B_AIR;
7550 									break;
7551 								case LS_KICK_R:
7552 									kickMove = LS_KICK_R_AIR;
7553 									break;
7554 								case LS_KICK_L:
7555 									kickMove = LS_KICK_L_AIR;
7556 									break;
7557 								default: //oh well, can't do any other kick move while in-air
7558 									kickMove = -1;
7559 									break;
7560 								}
7561 							}
7562 							else
7563 							{ //off ground, but too close to ground
7564 								kickMove = -1;
7565 							}
7566 						}
7567 					}
7568 
7569 					if (kickMove != -1)
7570 					{
7571 						int kickAnim = saberMoveData[kickMove].animToUse;
7572 
7573 						if (kickAnim != -1)
7574 						{
7575 							PM_SetAnim(SETANIM_BOTH, kickAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7576 							if (pm->ps->legsAnim == kickAnim)
7577 							{
7578 								pm->ps->weaponTime = pm->ps->legsTimer;
7579 								return;
7580 							}
7581 						}
7582 					}
7583 				}
7584 
7585 				//if got here then no move to do so put torso into leg idle or whatever
7586 				if (pm->ps->torsoAnim != pm->ps->legsAnim)
7587 				{
7588 					PM_SetAnim(SETANIM_BOTH, pm->ps->legsAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
7589 				}
7590 				pm->ps->weaponTime = 0;
7591 				return;
7592 			}
7593 			else
7594 			{ //just punch
7595 				int desTAnim = BOTH_MELEE1;
7596 				if (pm->ps->torsoAnim == BOTH_MELEE1)
7597 				{
7598 					desTAnim = BOTH_MELEE2;
7599 				}
7600 				PM_StartTorsoAnim( desTAnim );
7601 
7602 				if (pm->ps->torsoAnim == desTAnim)
7603 				{
7604 					pm->ps->weaponTime = pm->ps->torsoTimer;
7605 				}
7606 			}
7607 		}
7608 	}
7609 	else
7610 	{
7611 		PM_StartTorsoAnim( WeaponAttackAnim[pm->ps->weapon] );
7612 	}
7613 
7614 	if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
7615 	{
7616 		amount = weaponData[pm->ps->weapon].altEnergyPerShot;
7617 	}
7618 	else
7619 	{
7620 		amount = weaponData[pm->ps->weapon].energyPerShot;
7621 	}
7622 
7623 	pm->ps->weaponstate = WEAPON_FIRING;
7624 
7625 	// take an ammo away if not infinite
7626 	if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
7627 	{
7628 		// enough energy to fire this weapon?
7629 		if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
7630 		{
7631 			pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
7632 		}
7633 		else	// Not enough energy
7634 		{
7635 			// Switch weapons
7636 			if (pm->ps->weapon != WP_DET_PACK || !pm->ps->hasDetPackPlanted)
7637 			{
7638 				PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
7639 				if (pm->ps->weaponTime < 500)
7640 				{
7641 					pm->ps->weaponTime += 500;
7642 				}
7643 			}
7644 			return;
7645 		}
7646 	}
7647 
7648 	if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) 	{
7649 		//if ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->gametype != GT_SIEGE )
7650 		if (0)
7651 		{ //kind of a hack for now
7652 			PM_AddEvent( EV_FIRE_WEAPON );
7653 			addTime = weaponData[pm->ps->weapon].fireTime;
7654 		}
7655 		else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode != 1)
7656 		{
7657 			PM_AddEvent( EV_FIRE_WEAPON );
7658 			addTime = weaponData[pm->ps->weapon].fireTime;
7659 		}
7660 		else
7661 		{
7662 			if (pm->ps->weapon != WP_MELEE ||
7663 				!pm->ps->m_iVehicleNum)
7664 			{ //do not fire melee events at all when on vehicle
7665 				PM_AddEvent( EV_ALT_FIRE );
7666 			}
7667 			addTime = weaponData[pm->ps->weapon].altFireTime;
7668 		}
7669 	}
7670 	else {
7671 		if (pm->ps->weapon != WP_MELEE ||
7672 			!pm->ps->m_iVehicleNum)
7673 		{ //do not fire melee events at all when on vehicle
7674 			PM_AddEvent( EV_FIRE_WEAPON );
7675 		}
7676 		addTime = weaponData[pm->ps->weapon].fireTime;
7677 		if ( pm->gametype == GT_SIEGE && pm->ps->weapon == WP_DET_PACK )
7678 		{	// were far too spammy before?  So says Rick.
7679 			addTime *= 2;
7680 		}
7681 	}
7682 
7683 	/*
7684 	if ( pm->ps->powerups[PW_HASTE] ) {
7685 		addTime /= 1.3;
7686 	}
7687 	*/
7688 
7689 	if (pm->ps->fd.forcePowersActive & (1 << FP_RAGE))
7690 	{
7691 		addTime *= 0.75;
7692 	}
7693 	else if (pm->ps->fd.forceRageRecoveryTime > pm->cmd.serverTime)
7694 	{
7695 		addTime *= 1.5;
7696 	}
7697 
7698 	pm->ps->weaponTime += addTime;
7699 }
7700 
7701 /*
7702 ================
7703 PM_Animate
7704 ================
7705 */
7706 
PM_Animate(void)7707 static void PM_Animate( void ) {
7708 	if ( pm->cmd.buttons & BUTTON_GESTURE ) {
7709 		if (pm->ps->m_iVehicleNum)
7710 		{ //eh, fine, clear it
7711 			if (pm->ps->forceHandExtendTime < pm->cmd.serverTime)
7712 			{
7713 				pm->ps->forceHandExtend = HANDEXTEND_NONE;
7714 			}
7715 		}
7716 
7717 		if ( pm->ps->torsoTimer < 1 && pm->ps->forceHandExtend == HANDEXTEND_NONE &&
7718 			pm->ps->legsTimer < 1 && pm->ps->weaponTime < 1 && pm->ps->saberLockTime < pm->cmd.serverTime) {
7719 
7720 			pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
7721 
7722 			//FIXME: random taunt anims?
7723 			pm->ps->forceDodgeAnim = BOTH_ENGAGETAUNT;
7724 
7725 			pm->ps->forceHandExtendTime = pm->cmd.serverTime + 1000;
7726 
7727 			//pm->ps->weaponTime = 100;
7728 
7729 			PM_AddEvent( EV_TAUNT );
7730 		}
7731 #if 0
7732 // Here's an interesting bit.  The bots in TA used buttons to do additional gestures.
7733 // I ripped them out because I didn't want too many buttons given the fact that I was already adding some for JK2.
7734 // We can always add some back in if we want though.
7735 	} else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
7736 		if ( pm->ps->torsoTimer == 0 ) {
7737 			PM_StartTorsoAnim( TORSO_GETFLAG );
7738 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
7739 		}
7740 	} else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
7741 		if ( pm->ps->torsoTimer == 0 ) {
7742 			PM_StartTorsoAnim( TORSO_GUARDBASE );
7743 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
7744 		}
7745 	} else if ( pm->cmd.buttons & BUTTON_PATROL ) {
7746 		if ( pm->ps->torsoTimer == 0 ) {
7747 			PM_StartTorsoAnim( TORSO_PATROL );
7748 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
7749 		}
7750 	} else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
7751 		if ( pm->ps->torsoTimer == 0 ) {
7752 			PM_StartTorsoAnim( TORSO_FOLLOWME );
7753 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
7754 		}
7755 	} else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
7756 		if ( pm->ps->torsoTimer == 0 ) {
7757 			PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
7758 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
7759 		}
7760 	} else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
7761 		if ( pm->ps->torsoTimer == 0 ) {
7762 			PM_StartTorsoAnim( TORSO_NEGATIVE );
7763 			pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
7764 		}
7765 #endif //
7766 	}
7767 }
7768 
7769 
7770 /*
7771 ================
7772 PM_DropTimers
7773 ================
7774 */
PM_DropTimers(void)7775 static void PM_DropTimers( void ) {
7776 	// drop misc timing counter
7777 	if ( pm->ps->pm_time ) {
7778 		if ( pml.msec >= pm->ps->pm_time ) {
7779 			pm->ps->pm_flags &= ~PMF_ALL_TIMES;
7780 			pm->ps->pm_time = 0;
7781 		} else {
7782 			pm->ps->pm_time -= pml.msec;
7783 		}
7784 	}
7785 
7786 	// drop animation counter
7787 	if ( pm->ps->legsTimer > 0 ) {
7788 		pm->ps->legsTimer -= pml.msec;
7789 		if ( pm->ps->legsTimer < 0 ) {
7790 			pm->ps->legsTimer = 0;
7791 		}
7792 	}
7793 
7794 	if ( pm->ps->torsoTimer > 0 ) {
7795 		pm->ps->torsoTimer -= pml.msec;
7796 		if ( pm->ps->torsoTimer < 0 ) {
7797 			pm->ps->torsoTimer = 0;
7798 		}
7799 	}
7800 }
7801 
7802 // Following function is stateless (at the moment). And hoisting it out
7803 // of the namespace here is easier than fixing all the places it's used,
7804 // which includes files that are also compiled in SP. We do need to make
7805 // sure we only get one copy in the linker, though.
7806 
7807 extern	vmCvar_t	bg_fighterAltControl;
BG_UnrestrainedPitchRoll(playerState_t * ps,Vehicle_t * pVeh)7808 qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh )
7809 {
7810 	if ( bg_fighterAltControl.integer
7811 		&& ps->clientNum < MAX_CLIENTS //real client
7812 		&& ps->m_iVehicleNum//in a vehicle
7813 		&& pVeh //valid vehicle data pointer
7814 		&& pVeh->m_pVehicleInfo//valid vehicle info
7815 		&& pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
7816 		//FIXME: specify per vehicle instead of assuming true for all fighters
7817 		//FIXME: map/server setting?
7818 	{//can roll and pitch without limitation!
7819 		return qtrue;
7820 	}
7821 	return qfalse;
7822 }
7823 
7824 
7825 /*
7826 ================
7827 PM_UpdateViewAngles
7828 
7829 This can be used as another entry point when only the viewangles
7830 are being updated isntead of a full move
7831 ================
7832 */
PM_UpdateViewAngles(playerState_t * ps,const usercmd_t * cmd)7833 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
7834 	short		temp;
7835 	int		i;
7836 
7837 	if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
7838 		return;		// no view changes at all
7839 	}
7840 
7841 	if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
7842 		return;		// no view changes at all
7843 	}
7844 
7845 	// circularly clamp the angles with deltas
7846 	for (i=0 ; i<3 ; i++) {
7847 		temp = cmd->angles[i] + ps->delta_angles[i];
7848 #ifdef VEH_CONTROL_SCHEME_4
7849 		if ( pm_entVeh
7850 			&& pm_entVeh->m_pVehicle
7851 			&& pm_entVeh->m_pVehicle->m_pVehicleInfo
7852 			&& pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER
7853 			&& (cmd->serverTime-pm_entVeh->playerState->hyperSpaceTime) >= HYPERSPACE_TIME )
7854 		{//in a vehicle and not hyperspacing
7855 			if ( i == PITCH )
7856 			{
7857 				int pitchClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[PITCH]+10.0f));
7858 				// don't let the player look up or down more than 22.5 degrees
7859 				if ( temp > pitchClamp )
7860 				{
7861 					ps->delta_angles[i] = pitchClamp - cmd->angles[i];
7862 					temp = pitchClamp;
7863 				}
7864 				else if ( temp < -pitchClamp )
7865 				{
7866 					ps->delta_angles[i] = -pitchClamp - cmd->angles[i];
7867 					temp = -pitchClamp;
7868 				}
7869 			}
7870 			if ( i == YAW )
7871 			{
7872 				int yawClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[YAW]+10.0f));
7873 				// don't let the player look left or right more than 22.5 degrees
7874 				if ( temp > yawClamp )
7875 				{
7876 					ps->delta_angles[i] = yawClamp - cmd->angles[i];
7877 					temp = yawClamp;
7878 				}
7879 				else if ( temp < -yawClamp )
7880 				{
7881 					ps->delta_angles[i] = -yawClamp - cmd->angles[i];
7882 					temp = -yawClamp;
7883 				}
7884 			}
7885 		}
7886 #else //VEH_CONTROL_SCHEME_4
7887 		if ( pm_entVeh && BG_UnrestrainedPitchRoll( ps, pm_entVeh->m_pVehicle ) )
7888 		{//in a fighter
7889 			/*
7890 			if ( i == ROLL )
7891 			{//get roll from vehicle
7892 				ps->viewangles[ROLL] = pm_entVeh->playerState->viewangles[ROLL];//->m_pVehicle->m_vOrientation[ROLL];
7893 				continue;
7894 
7895 			}
7896 			*/
7897 		}
7898 #endif // VEH_CONTROL_SCHEME_4
7899 		else
7900 		{
7901 			if ( i == PITCH ) {
7902 				// don't let the player look up or down more than 90 degrees
7903 				if ( temp > 16000 ) {
7904 					ps->delta_angles[i] = 16000 - cmd->angles[i];
7905 					temp = 16000;
7906 				} else if ( temp < -16000 ) {
7907 					ps->delta_angles[i] = -16000 - cmd->angles[i];
7908 					temp = -16000;
7909 				}
7910 			}
7911 		}
7912 		ps->viewangles[i] = SHORT2ANGLE(temp);
7913 	}
7914 }
7915 
7916 /*
7917 void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
7918 	short		temp;
7919 	int		i;
7920 	float	rootPitch = 0, pitchMin=-90, pitchMax=90, yawMin=0, yawMax=0, lockedYawValue = 0;	//just to shut up warnings
7921 	qboolean	lockedYaw = qfalse, clamped = qfalse;
7922 	bgEntity_t *vehEnt = NULL;
7923 
7924 	if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
7925 		return;		// no view changes at all
7926 	}
7927 
7928 	if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
7929 		return;		// no view changes at all
7930 	}
7931 
7932 	// If we're a vehicle, or we're riding a vehicle...?
7933 	if ( ps->m_iVehicleNum )
7934 	{
7935 		if ( ps->clientNum < MAX_CLIENTS )
7936 		{ //player riding vehicle
7937 			vehEnt = PM_BGEntForNum(ps->m_iVehicleNum);
7938 		}
7939 		else
7940 		{ //vehicle with player pilot
7941 			vehEnt = PM_BGEntForNum(ps->clientNum);
7942 		}
7943 		if ( vehEnt )
7944 		{//there is a vehicle
7945 			Vehicle_t *pVeh = vehEnt->m_pVehicle;
7946 			if ( pVeh && pVeh->m_pVehicleInfo )
7947 			{
7948 				// There is a vehicle...
7949 				if ( pVeh->m_pVehicleInfo->type != VH_ANIMAL )
7950 				{//animals just turn normally, no clamping
7951 					if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
7952 					{
7953 						rootPitch = pVeh->m_vOrientation[PITCH];//gent->owner->client->ps.vehicleAngles[PITCH];//???  what if goes over 90 when add the min/max?
7954 						if ( pVeh->m_pVehicleInfo->pitchLimit == -1 )
7955 						{
7956 							pitchMax = 180;
7957 						}
7958 						else
7959 						{
7960 							pitchMax = pVeh->m_pVehicleInfo->pitchLimit;
7961 						}
7962 						pitchMin = -pitchMax;
7963 					}
7964 					else
7965 					{
7966 						lockedYawValue = 0;//gent->owner->client->ps.vehicleAngles[YAW];
7967 						lockedYaw = qtrue;
7968 						yawMax = pVeh->m_pVehicleInfo->lookYaw;
7969 						yawMin = -yawMax;
7970 						rootPitch = 0;//gent->owner->client->ps.vehicleAngles[PITCH];//???  what if goes over 90 when add the min/max?
7971 						pitchMax = pVeh->m_pVehicleInfo->lookPitch;
7972 						pitchMin = -pitchMax;
7973 					}
7974 				}
7975 			}
7976 		}
7977 	}
7978 	if ( 1 )
7979 	{
7980 		const short pitchClampMin = ANGLE2SHORT(rootPitch+pitchMin);
7981 		const short pitchClampMax = ANGLE2SHORT(rootPitch+pitchMax);
7982 		const short yawClampMin = ANGLE2SHORT(lockedYawValue+yawMin);
7983 		const short yawClampMax = ANGLE2SHORT(lockedYawValue+yawMax);
7984 		for (i=0 ; i<3 ; i++)
7985 		{
7986 			temp = cmd->angles[i] + ps->delta_angles[i];
7987 			if ( i == PITCH )
7988 			{
7989 				//FIXME get this limit from the NPCs stats?
7990 				// don't let the player look up or down more than 90 degrees
7991 				if ( temp > pitchClampMax )
7992 				{
7993 					ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff;	//& clamp to short
7994 					temp = pitchClampMax;
7995 					clamped = qtrue;
7996 				}
7997 				else if ( temp < pitchClampMin )
7998 				{
7999 					ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff;	//& clamp to short
8000 					temp = pitchClampMin;
8001 					clamped = qtrue;
8002 				}
8003 			}
8004 			if ( i == YAW && lockedYaw )
8005 			{
8006 				//FIXME get this limit from the NPCs stats?
8007 				// don't let the player look up or down more than 90 degrees
8008 				if ( temp > yawClampMax )
8009 				{
8010 					ps->delta_angles[i] = (yawClampMax - cmd->angles[i]) & 0xffff;	//& clamp to short
8011 					temp = yawClampMax;
8012 					clamped = qtrue;
8013 				}
8014 				else if ( temp < yawClampMin )
8015 				{
8016 					ps->delta_angles[i] = (yawClampMin - cmd->angles[i]) & 0xffff;	//& clamp to short
8017 					temp = yawClampMin;
8018 					clamped = qtrue;
8019 				}
8020 				ps->viewangles[i] = SHORT2ANGLE(temp);
8021 			}
8022 			else
8023 			{
8024 				ps->viewangles[i] = SHORT2ANGLE(temp);
8025 			}
8026 		}
8027 	}
8028 	else
8029 	{
8030 		// circularly clamp the angles with deltas
8031 		for (i=0 ; i<3 ; i++) {
8032 			temp = cmd->angles[i] + ps->delta_angles[i];
8033 			if ( i == PITCH ) {
8034 				// don't let the player look up or down more than 90 degrees
8035 				if ( temp > 16000 ) {
8036 					ps->delta_angles[i] = 16000 - cmd->angles[i];
8037 					temp = 16000;
8038 				} else if ( temp < -16000 ) {
8039 					ps->delta_angles[i] = -16000 - cmd->angles[i];
8040 					temp = -16000;
8041 				}
8042 			}
8043 			ps->viewangles[i] = SHORT2ANGLE(temp);
8044 		}
8045 	}
8046 
8047 }
8048 */
8049 
8050 //-------------------------------------------
PM_AdjustAttackStates(pmove_t * pmove)8051 void PM_AdjustAttackStates( pmove_t *pmove )
8052 //-------------------------------------------
8053 {
8054 	int amount;
8055 
8056 	if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
8057 		&&pmove->ps->m_iVehicleNum)
8058 	{ //riding a vehicle
8059 		bgEntity_t *veh = pm_entVeh;
8060 		if ( veh &&
8061 			(veh->m_pVehicle && (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)) )
8062 		{//riding a walker/fighter
8063 			//not firing, ever
8064 			pmove->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
8065 			return;
8066 		}
8067 	}
8068 	// get ammo usage
8069 	if ( pmove->cmd.buttons & BUTTON_ALT_ATTACK )
8070 	{
8071 		amount = pmove->ps->ammo[weaponData[ pmove->ps->weapon ].ammoIndex] - weaponData[pmove->ps->weapon].altEnergyPerShot;
8072 	}
8073 	else
8074 	{
8075 		amount = pmove->ps->ammo[weaponData[ pmove->ps->weapon ].ammoIndex] - weaponData[pmove->ps->weapon].energyPerShot;
8076 	}
8077 
8078 	// disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player?
8079 	if ( pmove->ps->weapon == WP_DISRUPTOR && pmove->ps->weaponstate == WEAPON_READY )
8080 	{
8081 		if ( !(pmove->ps->eFlags & EF_ALT_FIRING) && (pmove->cmd.buttons & BUTTON_ALT_ATTACK) /*&&
8082 			pmove->cmd.upmove <= 0 && !pmove->cmd.forwardmove && !pmove->cmd.rightmove*/)
8083 		{
8084 			// We just pressed the alt-fire key
8085 			if ( !pmove->ps->zoomMode && pmove->ps->pm_type != PM_DEAD )
8086 			{
8087 				// not already zooming, so do it now
8088 				pmove->ps->zoomMode = 1;
8089 				pmove->ps->zoomLocked = qfalse;
8090 				pmove->ps->zoomFov = 80.0f;//cg_fov.value;
8091 				pmove->ps->zoomLockTime = pmove->cmd.serverTime + 50;
8092 				PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8093 			}
8094 			else if (pmove->ps->zoomMode == 1 && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8095 			{ //check for == 1 so we can't turn binoculars off with disruptor alt fire
8096 				// already zooming, so must be wanting to turn it off
8097 				pmove->ps->zoomMode = 0;
8098 				pmove->ps->zoomTime = pmove->ps->commandTime;
8099 				pmove->ps->zoomLocked = qfalse;
8100 				PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8101 				pmove->ps->weaponTime = 1000;
8102 			}
8103 		}
8104 		else if ( !(pmove->cmd.buttons & BUTTON_ALT_ATTACK ) && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8105 		{
8106 			// Not pressing zoom any more
8107 			if ( pmove->ps->zoomMode )
8108 			{
8109 				if (pmove->ps->zoomMode == 1 && !pmove->ps->zoomLocked)
8110 				{ //approximate what level the client should be zoomed at based on how long zoom was held
8111 					pmove->ps->zoomFov = ((pmove->cmd.serverTime+50) - pmove->ps->zoomLockTime) * 0.035f;
8112 					if (pmove->ps->zoomFov > 50)
8113 					{
8114 						pmove->ps->zoomFov = 50;
8115 					}
8116 					if (pmove->ps->zoomFov < 1)
8117 					{
8118 						pmove->ps->zoomFov = 1;
8119 					}
8120 				}
8121 				// were zooming in, so now lock the zoom
8122 				pmove->ps->zoomLocked = qtrue;
8123 			}
8124 		}
8125 		//This seemed like a good idea, but apparently it confuses people. So disabled for now.
8126 		/*
8127 		else if (!(pmove->ps->eFlags & EF_ALT_FIRING) && (pmove->cmd.buttons & BUTTON_ALT_ATTACK) &&
8128 			(pmove->cmd.upmove > 0 || pmove->cmd.forwardmove || pmove->cmd.rightmove))
8129 		{ //if you try to zoom while moving, just convert it into a primary attack
8130 			pmove->cmd.buttons &= ~BUTTON_ALT_ATTACK;
8131 			pmove->cmd.buttons |= BUTTON_ATTACK;
8132 		}
8133 		*/
8134 
8135 		/*
8136 		if (pmove->cmd.upmove > 0 || pmove->cmd.forwardmove || pmove->cmd.rightmove)
8137 		{
8138 			if (pmove->ps->zoomMode == 1 && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8139 			{ //check for == 1 so we can't turn binoculars off with disruptor alt fire
8140 				pmove->ps->zoomMode = 0;
8141 				pmove->ps->zoomTime = pmove->ps->commandTime;
8142 				pmove->ps->zoomLocked = qfalse;
8143 				PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8144 			}
8145 		}
8146 		*/
8147 
8148 		if ( pmove->cmd.buttons & BUTTON_ATTACK )
8149 		{
8150 			// If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll
8151 			//	just use whatever ammo was selected from above
8152 			if ( pmove->ps->zoomMode )
8153 			{
8154 				amount = pmove->ps->ammo[weaponData[ pmove->ps->weapon ].ammoIndex] -
8155 							weaponData[pmove->ps->weapon].altEnergyPerShot;
8156 			}
8157 		}
8158 		else
8159 		{
8160 			// alt-fire button pressing doesn't use any ammo
8161 			amount = 0;
8162 		}
8163 	}
8164 	/*
8165 	else if (pmove->ps->weapon == WP_DISRUPTOR) //still perform certain checks, even if the weapon is not ready
8166 	{
8167 		if (pmove->cmd.upmove > 0 || pmove->cmd.forwardmove || pmove->cmd.rightmove)
8168 		{
8169 			if (pmove->ps->zoomMode == 1 && pmove->ps->zoomLockTime < pmove->cmd.serverTime)
8170 			{ //check for == 1 so we can't turn binoculars off with disruptor alt fire
8171 				pmove->ps->zoomMode = 0;
8172 				pmove->ps->zoomTime = pmove->ps->commandTime;
8173 				pmove->ps->zoomLocked = qfalse;
8174 				PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
8175 			}
8176 		}
8177 	}
8178 	*/
8179 
8180 	// set the firing flag for continuous beam weapons, saber will fire even if out of ammo
8181 	if ( !(pmove->ps->pm_flags & PMF_RESPAWNED) &&
8182 			pmove->ps->pm_type != PM_INTERMISSION &&
8183 			pmove->ps->pm_type != PM_NOCLIP &&
8184 			( pmove->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) &&
8185 			( amount >= 0 || pmove->ps->weapon == WP_SABER ))
8186 	{
8187 		if ( pmove->cmd.buttons & BUTTON_ALT_ATTACK )
8188 		{
8189 			pmove->ps->eFlags |= EF_ALT_FIRING;
8190 		}
8191 		else
8192 		{
8193 			pmove->ps->eFlags &= ~EF_ALT_FIRING;
8194 		}
8195 
8196 		// This flag should always get set, even when alt-firing
8197 		pmove->ps->eFlags |= EF_FIRING;
8198 	}
8199 	else
8200 	{
8201 		// Clear 'em out
8202 		pmove->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
8203 	}
8204 
8205 	// disruptor should convert a main fire to an alt-fire if the gun is currently zoomed
8206 	if ( pmove->ps->weapon == WP_DISRUPTOR)
8207 	{
8208 		if ( pmove->cmd.buttons & BUTTON_ATTACK && pmove->ps->zoomMode == 1 && pmove->ps->zoomLocked)
8209 		{
8210 			// converting the main fire to an alt-fire
8211 			pmove->cmd.buttons |= BUTTON_ALT_ATTACK;
8212 			pmove->ps->eFlags |= EF_ALT_FIRING;
8213 		}
8214 		else if ( pmove->cmd.buttons & BUTTON_ALT_ATTACK && pmove->ps->zoomMode == 1 && pmove->ps->zoomLocked)
8215 		{
8216 			pmove->cmd.buttons &= ~BUTTON_ALT_ATTACK;
8217 			pmove->ps->eFlags &= ~EF_ALT_FIRING;
8218 		}
8219 	}
8220 }
8221 
BG_CmdForRoll(playerState_t * ps,int anim,usercmd_t * pCmd)8222 void BG_CmdForRoll( playerState_t *ps, int anim, usercmd_t *pCmd )
8223 {
8224 	switch ( (anim) )
8225 	{
8226 	case BOTH_ROLL_F:
8227 		pCmd->forwardmove = 127;
8228 		pCmd->rightmove = 0;
8229 		break;
8230 	case BOTH_ROLL_B:
8231 		pCmd->forwardmove = -127;
8232 		pCmd->rightmove = 0;
8233 		break;
8234 	case BOTH_ROLL_R:
8235 		pCmd->forwardmove = 0;
8236 		pCmd->rightmove = 127;
8237 		break;
8238 	case BOTH_ROLL_L:
8239 		pCmd->forwardmove = 0;
8240 		pCmd->rightmove = -127;
8241 		break;
8242 	case BOTH_GETUP_BROLL_R:
8243 		pCmd->forwardmove = 0;
8244 		pCmd->rightmove = 48;
8245 		//NOTE: speed is 400
8246 		break;
8247 
8248 	case BOTH_GETUP_FROLL_R:
8249 		if ( ps->legsTimer <= 250 )
8250 		{//end of anim
8251 			pCmd->forwardmove = pCmd->rightmove = 0;
8252 		}
8253 		else
8254 		{
8255 			pCmd->forwardmove = 0;
8256 			pCmd->rightmove = 48;
8257 			//NOTE: speed is 400
8258 		}
8259 		break;
8260 
8261 	case BOTH_GETUP_BROLL_L:
8262 		pCmd->forwardmove = 0;
8263 		pCmd->rightmove = -48;
8264 		//NOTE: speed is 400
8265 		break;
8266 
8267 	case BOTH_GETUP_FROLL_L:
8268 		if ( ps->legsTimer <= 250 )
8269 		{//end of anim
8270 			pCmd->forwardmove = pCmd->rightmove = 0;
8271 		}
8272 		else
8273 		{
8274 			pCmd->forwardmove = 0;
8275 			pCmd->rightmove = -48;
8276 			//NOTE: speed is 400
8277 		}
8278 		break;
8279 
8280 	case BOTH_GETUP_BROLL_B:
8281 		if ( ps->torsoTimer <= 250 )
8282 		{//end of anim
8283 			pCmd->forwardmove = pCmd->rightmove = 0;
8284 		}
8285 		else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 350 )
8286 		{//beginning of anim
8287 			pCmd->forwardmove = pCmd->rightmove = 0;
8288 		}
8289 		else
8290 		{
8291 			//FIXME: ramp down over length of anim
8292 			pCmd->forwardmove = -64;
8293 			pCmd->rightmove = 0;
8294 			//NOTE: speed is 400
8295 		}
8296 		break;
8297 
8298 	case BOTH_GETUP_FROLL_B:
8299 		if ( ps->torsoTimer <= 100 )
8300 		{//end of anim
8301 			pCmd->forwardmove = pCmd->rightmove = 0;
8302 		}
8303 		else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 200 )
8304 		{//beginning of anim
8305 			pCmd->forwardmove = pCmd->rightmove = 0;
8306 		}
8307 		else
8308 		{
8309 			//FIXME: ramp down over length of anim
8310 			pCmd->forwardmove = -64;
8311 			pCmd->rightmove = 0;
8312 			//NOTE: speed is 400
8313 		}
8314 		break;
8315 
8316 	case BOTH_GETUP_BROLL_F:
8317 		if ( ps->torsoTimer <= 550 )
8318 		{//end of anim
8319 			pCmd->forwardmove = pCmd->rightmove = 0;
8320 		}
8321 		else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 150 )
8322 		{//beginning of anim
8323 			pCmd->forwardmove = pCmd->rightmove = 0;
8324 		}
8325 		else
8326 		{
8327 			pCmd->forwardmove = 64;
8328 			pCmd->rightmove = 0;
8329 			//NOTE: speed is 400
8330 		}
8331 		break;
8332 
8333 	case BOTH_GETUP_FROLL_F:
8334 		if ( ps->torsoTimer <= 100 )
8335 		{//end of anim
8336 			pCmd->forwardmove = pCmd->rightmove = 0;
8337 		}
8338 		else
8339 		{
8340 			//FIXME: ramp down over length of anim
8341 			pCmd->forwardmove = 64;
8342 			pCmd->rightmove = 0;
8343 			//NOTE: speed is 400
8344 		}
8345 		break;
8346 	}
8347 	pCmd->upmove = 0;
8348 }
8349 
8350 qboolean PM_SaberInTransition( int move );
8351 
BG_AdjustClientSpeed(playerState_t * ps,usercmd_t * cmd,int svTime)8352 void BG_AdjustClientSpeed(playerState_t *ps, usercmd_t *cmd, int svTime)
8353 {
8354 	saberInfo_t	*saber;
8355 
8356 	if (ps->clientNum >= MAX_CLIENTS)
8357 	{
8358 		bgEntity_t *bgEnt = pm_entSelf;
8359 
8360 		if (bgEnt && bgEnt->s.NPC_class == CLASS_VEHICLE)
8361 		{ //vehicles manage their own speed
8362 			return;
8363 		}
8364 	}
8365 
8366 	//For prediction, always reset speed back to the last known server base speed
8367 	//If we didn't do this, under lag we'd eventually dwindle speed down to 0 even though
8368 	//that would not be the correct predicted value.
8369 	ps->speed = ps->basespeed;
8370 
8371 	if (ps->forceHandExtend == HANDEXTEND_DODGE)
8372 	{
8373 		ps->speed = 0;
8374 	}
8375 
8376 	if (ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
8377 		ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
8378 		ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
8379 	{
8380 		ps->speed = 0;
8381 	}
8382 
8383 
8384 	if ( cmd->forwardmove < 0 && !(cmd->buttons&BUTTON_WALKING) && pm->ps->groundEntityNum != ENTITYNUM_NONE )
8385 	{//running backwards is slower than running forwards (like SP)
8386 		ps->speed *= 0.75f;
8387 	}
8388 
8389 	if (ps->fd.forcePowersActive & (1 << FP_GRIP))
8390 	{
8391 		ps->speed *= 0.4f;
8392 	}
8393 
8394 	if (ps->fd.forcePowersActive & (1 << FP_SPEED))
8395 	{
8396 		ps->speed *= 1.7f;
8397 	}
8398 	else if (ps->fd.forcePowersActive & (1 << FP_RAGE))
8399 	{
8400 		ps->speed *= 1.3f;
8401 	}
8402 	else if (ps->fd.forceRageRecoveryTime > svTime)
8403 	{
8404 		ps->speed *= 0.75f;
8405 	}
8406 
8407 	if (pm->ps->weapon == WP_DISRUPTOR &&
8408 		pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
8409 	{
8410 		ps->speed *= 0.5f;
8411 	}
8412 
8413 	if ( ps->fd.forceGripCripple && pm->ps->persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
8414 		if ( ps->fd.forcePowersActive & (1 << FP_RAGE) )
8415 			ps->speed *= 0.9f;
8416 		else if ( ps->fd.forcePowersActive & (1 << FP_SPEED) )
8417 			ps->speed *= 0.8f;
8418 		else
8419 			ps->speed *= 0.2f;
8420 	}
8421 
8422 	if ( BG_SaberInAttack( ps->saberMove ) && cmd->forwardmove < 0 )
8423 	{//if running backwards while attacking, don't run as fast.
8424 		switch( ps->fd.saberAnimLevel )
8425 		{
8426 		case FORCE_LEVEL_1:
8427 			ps->speed *= 0.75f;
8428 			break;
8429 		case FORCE_LEVEL_2:
8430 		case SS_DUAL:
8431 		case SS_STAFF:
8432 			ps->speed *= 0.60f;
8433 			break;
8434 		case FORCE_LEVEL_3:
8435 			ps->speed *= 0.45f;
8436 			break;
8437 		default:
8438 			break;
8439 		}
8440 	}
8441 	else if ( BG_SpinningSaberAnim( ps->legsAnim ) )
8442 	{
8443 		if (ps->fd.saberAnimLevel == FORCE_LEVEL_3)
8444 		{
8445 			ps->speed *= 0.3f;
8446 		}
8447 		else
8448 		{
8449 			ps->speed *= 0.5f;
8450 		}
8451 	}
8452 	else if ( ps->weapon == WP_SABER && BG_SaberInAttack( ps->saberMove ) )
8453 	{//if attacking with saber while running, drop your speed
8454 		switch( ps->fd.saberAnimLevel )
8455 		{
8456 		case FORCE_LEVEL_2:
8457 		case SS_DUAL:
8458 		case SS_STAFF:
8459 			ps->speed *= 0.85f;
8460 			break;
8461 		case FORCE_LEVEL_3:
8462 			ps->speed *= 0.55f;
8463 			break;
8464 		default:
8465 			break;
8466 		}
8467 	}
8468 	else if (ps->weapon == WP_SABER && ps->fd.saberAnimLevel == FORCE_LEVEL_3 &&
8469 		PM_SaberInTransition(ps->saberMove))
8470 	{ //Now, we want to even slow down in transitions for level 3 (since it has chains and stuff now)
8471 		if (cmd->forwardmove < 0)
8472 		{
8473 			ps->speed *= 0.4f;
8474 		}
8475 		else
8476 		{
8477 			ps->speed *= 0.6f;
8478 		}
8479 	}
8480 
8481 	if ( BG_InRoll( ps, ps->legsAnim ) && ps->speed > 50 )
8482 	{ //can't roll unless you're able to move normally
8483 		if ((ps->legsAnim) == BOTH_ROLL_B)
8484 		{ //backwards roll is pretty fast, should also be slower
8485 			if (ps->legsTimer > 800)
8486 			{
8487 				ps->speed = ps->legsTimer/2.5;
8488 			}
8489 			else
8490 			{
8491 				ps->speed = ps->legsTimer/6.0;//450;
8492 			}
8493 		}
8494 		else
8495 		{
8496 			if (ps->legsTimer > 800)
8497 			{
8498 				ps->speed = ps->legsTimer/1.5;//450;
8499 			}
8500 			else
8501 			{
8502 				ps->speed = ps->legsTimer/5.0;//450;
8503 			}
8504 		}
8505 		if (ps->speed > 600)
8506 		{
8507 			ps->speed = 600;
8508 		}
8509 		//Automatically slow down as the roll ends.
8510 	}
8511 
8512 	saber = BG_MySaber( ps->clientNum, 0 );
8513 	if ( saber
8514 		&& saber->moveSpeedScale != 1.0f )
8515 	{
8516 		ps->speed *= saber->moveSpeedScale;
8517 	}
8518 	saber = BG_MySaber( ps->clientNum, 1 );
8519 	if ( saber
8520 		&& saber->moveSpeedScale != 1.0f )
8521 	{
8522 		ps->speed *= saber->moveSpeedScale;
8523 	}
8524 }
8525 
BG_InRollAnim(entityState_t * cent)8526 qboolean BG_InRollAnim( entityState_t *cent )
8527 {
8528 	switch ( (cent->legsAnim) )
8529 	{
8530 	case BOTH_ROLL_F:
8531 	case BOTH_ROLL_B:
8532 	case BOTH_ROLL_R:
8533 	case BOTH_ROLL_L:
8534 		return qtrue;
8535 	}
8536 	return qfalse;
8537 }
8538 
BG_InKnockDown(int anim)8539 qboolean BG_InKnockDown( int anim )
8540 {
8541 	switch ( (anim) )
8542 	{
8543 	case BOTH_KNOCKDOWN1:
8544 	case BOTH_KNOCKDOWN2:
8545 	case BOTH_KNOCKDOWN3:
8546 	case BOTH_KNOCKDOWN4:
8547 	case BOTH_KNOCKDOWN5:
8548 		return qtrue;
8549 		break;
8550 	case BOTH_GETUP1:
8551 	case BOTH_GETUP2:
8552 	case BOTH_GETUP3:
8553 	case BOTH_GETUP4:
8554 	case BOTH_GETUP5:
8555 	case BOTH_FORCE_GETUP_F1:
8556 	case BOTH_FORCE_GETUP_F2:
8557 	case BOTH_FORCE_GETUP_B1:
8558 	case BOTH_FORCE_GETUP_B2:
8559 	case BOTH_FORCE_GETUP_B3:
8560 	case BOTH_FORCE_GETUP_B4:
8561 	case BOTH_FORCE_GETUP_B5:
8562 	case BOTH_GETUP_BROLL_B:
8563 	case BOTH_GETUP_BROLL_F:
8564 	case BOTH_GETUP_BROLL_L:
8565 	case BOTH_GETUP_BROLL_R:
8566 	case BOTH_GETUP_FROLL_B:
8567 	case BOTH_GETUP_FROLL_F:
8568 	case BOTH_GETUP_FROLL_L:
8569 	case BOTH_GETUP_FROLL_R:
8570 		return qtrue;
8571 		break;
8572 	}
8573 	return qfalse;
8574 }
8575 
BG_InRollES(entityState_t * ps,int anim)8576 qboolean BG_InRollES( entityState_t *ps, int anim )
8577 {
8578 	switch ( (anim) )
8579 	{
8580 	case BOTH_ROLL_F:
8581 	case BOTH_ROLL_B:
8582 	case BOTH_ROLL_R:
8583 	case BOTH_ROLL_L:
8584 		return qtrue;
8585 		break;
8586 	}
8587 	return qfalse;
8588 }
8589 
BG_IK_MoveArm(void * ghoul2,int lHandBolt,int time,entityState_t * ent,int basePose,vec3_t desiredPos,qboolean * ikInProgress,vec3_t origin,vec3_t angles,vec3_t scale,int blendTime,qboolean forceHalt)8590 void BG_IK_MoveArm(void *ghoul2, int lHandBolt, int time, entityState_t *ent, int basePose, vec3_t desiredPos, qboolean *ikInProgress,
8591 					 vec3_t origin, vec3_t angles, vec3_t scale, int blendTime, qboolean forceHalt)
8592 {
8593 	mdxaBone_t lHandMatrix;
8594 	vec3_t lHand;
8595 	vec3_t torg;
8596 	float distToDest;
8597 
8598 	if (!ghoul2)
8599 	{
8600 		return;
8601 	}
8602 
8603 	assert(bgHumanoidAnimations[basePose].firstFrame > 0);
8604 
8605 	if (!*ikInProgress && !forceHalt)
8606 	{
8607 		int baseposeAnim = basePose;
8608 		sharedSetBoneIKStateParams_t ikP;
8609 
8610 		//restrict the shoulder joint
8611 		//VectorSet(ikP.pcjMins,-50.0f,-80.0f,-15.0f);
8612 		//VectorSet(ikP.pcjMaxs,15.0f,40.0f,15.0f);
8613 
8614 		//for now, leaving it unrestricted, but restricting elbow joint.
8615 		//This lets us break the arm however we want in order to fling people
8616 		//in throws, and doesn't look bad.
8617 		VectorSet(ikP.pcjMins,0,0,0);
8618 		VectorSet(ikP.pcjMaxs,0,0,0);
8619 
8620 		//give the info on our entity.
8621 		ikP.blendTime = blendTime;
8622 		VectorCopy(origin, ikP.origin);
8623 		VectorCopy(angles, ikP.angles);
8624 		ikP.angles[PITCH] = 0;
8625 		ikP.pcjOverrides = 0;
8626 		ikP.radius = 10.0f;
8627 		VectorCopy(scale, ikP.scale);
8628 
8629 		//base pose frames for the limb
8630 		ikP.startFrame = bgHumanoidAnimations[baseposeAnim].firstFrame + bgHumanoidAnimations[baseposeAnim].numFrames;
8631 		ikP.endFrame = bgHumanoidAnimations[baseposeAnim].firstFrame + bgHumanoidAnimations[baseposeAnim].numFrames;
8632 
8633 		ikP.forceAnimOnBone = qfalse; //let it use existing anim if it's the same as this one.
8634 
8635 		//we want to call with a null bone name first. This will init all of the
8636 		//ik system stuff on the g2 instance, because we need ragdoll effectors
8637 		//in order for our pcj's to know how to angle properly.
8638 		if (!trap->G2API_SetBoneIKState(ghoul2, time, NULL, IKS_DYNAMIC, &ikP))
8639 		{
8640 			assert(!"Failed to init IK system for g2 instance!");
8641 		}
8642 
8643 		//Now, create our IK bone state.
8644 		if (trap->G2API_SetBoneIKState(ghoul2, time, "lhumerus", IKS_DYNAMIC, &ikP))
8645 		{
8646 			//restrict the elbow joint
8647 			VectorSet(ikP.pcjMins,-90.0f,-20.0f,-20.0f);
8648 			VectorSet(ikP.pcjMaxs,30.0f,20.0f,-20.0f);
8649 
8650 			if (trap->G2API_SetBoneIKState(ghoul2, time, "lradius", IKS_DYNAMIC, &ikP))
8651 			{ //everything went alright.
8652 				*ikInProgress = qtrue;
8653 			}
8654 		}
8655 	}
8656 
8657 	if (*ikInProgress && !forceHalt)
8658 	{ //actively update our ik state.
8659 		sharedIKMoveParams_t ikM;
8660 		sharedRagDollUpdateParams_t tuParms;
8661 		vec3_t tAngles;
8662 
8663 		//set the argument struct up
8664 		VectorCopy(desiredPos, ikM.desiredOrigin); //we want the bone to move here.. if possible
8665 
8666 		VectorCopy(angles, tAngles);
8667 		tAngles[PITCH] = tAngles[ROLL] = 0;
8668 
8669 		trap->G2API_GetBoltMatrix(ghoul2, 0, lHandBolt, &lHandMatrix, tAngles, origin, time, 0, scale);
8670 
8671 		//Get the point position from the matrix.
8672 		lHand[0] = lHandMatrix.matrix[0][3];
8673 		lHand[1] = lHandMatrix.matrix[1][3];
8674 		lHand[2] = lHandMatrix.matrix[2][3];
8675 
8676 		VectorSubtract(lHand, desiredPos, torg);
8677 		distToDest = VectorLength(torg);
8678 
8679 		//closer we are, more we want to keep updated.
8680 		//if we're far away we don't want to be too fast or we'll start twitching all over.
8681 		if (distToDest < 2)
8682 		{ //however if we're this close we want very precise movement
8683 			ikM.movementSpeed = 0.4f;
8684 		}
8685 		else if (distToDest < 16)
8686 		{
8687 			ikM.movementSpeed = 0.9f;//8.0f;
8688 		}
8689 		else if (distToDest < 32)
8690 		{
8691 			ikM.movementSpeed = 0.8f;//4.0f;
8692 		}
8693 		else if (distToDest < 64)
8694 		{
8695 			ikM.movementSpeed = 0.7f;//2.0f;
8696 		}
8697 		else
8698 		{
8699 			ikM.movementSpeed = 0.6f;
8700 		}
8701 		VectorCopy(origin, ikM.origin); //our position in the world.
8702 
8703 		ikM.boneName[0] = 0;
8704 		if (trap->G2API_IKMove(ghoul2, time, &ikM))
8705 		{
8706 			//now do the standard model animate stuff with ragdoll update params.
8707 			VectorCopy(angles, tuParms.angles);
8708 			tuParms.angles[PITCH] = 0;
8709 
8710 			VectorCopy(origin, tuParms.position);
8711 			VectorCopy(scale, tuParms.scale);
8712 
8713 			tuParms.me = ent->number;
8714 			VectorClear(tuParms.velocity);
8715 
8716 			trap->G2API_AnimateG2Models(ghoul2, time, &tuParms);
8717 		}
8718 		else
8719 		{
8720 			*ikInProgress = qfalse;
8721 		}
8722 	}
8723 	else if (*ikInProgress)
8724 	{ //kill it
8725 		float cFrame, animSpeed;
8726 		int sFrame, eFrame, flags;
8727 
8728 		trap->G2API_SetBoneIKState(ghoul2, time, "lhumerus", IKS_NONE, NULL);
8729 		trap->G2API_SetBoneIKState(ghoul2, time, "lradius", IKS_NONE, NULL);
8730 
8731 		//then reset the angles/anims on these PCJs
8732 		trap->G2API_SetBoneAngles(ghoul2, 0, "lhumerus", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, time);
8733 		trap->G2API_SetBoneAngles(ghoul2, 0, "lradius", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, time);
8734 
8735 		//Get the anim/frames that the pelvis is on exactly, and match the left arm back up with them again.
8736 		trap->G2API_GetBoneAnim(ghoul2, "pelvis", (const int)time, &cFrame, &sFrame, &eFrame, &flags, &animSpeed, 0, 0);
8737 		trap->G2API_SetBoneAnim(ghoul2, 0, "lhumerus", sFrame, eFrame, flags, animSpeed, time, sFrame, 300);
8738 		trap->G2API_SetBoneAnim(ghoul2, 0, "lradius", sFrame, eFrame, flags, animSpeed, time, sFrame, 300);
8739 
8740 		//And finally, get rid of all the ik state effector data by calling with null bone name (similar to how we init it).
8741 		trap->G2API_SetBoneIKState(ghoul2, time, NULL, IKS_NONE, NULL);
8742 
8743 		*ikInProgress = qfalse;
8744 	}
8745 }
8746 
8747 //Adjust the head/neck desired angles
BG_UpdateLookAngles(int lookingDebounceTime,vec3_t lastHeadAngles,int time,vec3_t lookAngles,float lookSpeed,float minPitch,float maxPitch,float minYaw,float maxYaw,float minRoll,float maxRoll)8748 void BG_UpdateLookAngles( int lookingDebounceTime, vec3_t lastHeadAngles, int time, vec3_t lookAngles, float lookSpeed, float minPitch, float maxPitch, float minYaw, float maxYaw, float minRoll, float maxRoll )
8749 {
8750 	static const float fFrameInter = 0.1f;
8751 	static vec3_t oldLookAngles;
8752 	static vec3_t lookAnglesDiff;
8753 	static int ang;
8754 
8755 	if ( lookingDebounceTime > time )
8756 	{
8757 		//clamp so don't get "Exorcist" effect
8758 		if ( lookAngles[PITCH] > maxPitch )
8759 		{
8760 			lookAngles[PITCH] = maxPitch;
8761 		}
8762 		else if ( lookAngles[PITCH] < minPitch )
8763 		{
8764 			lookAngles[PITCH] = minPitch;
8765 		}
8766 		if ( lookAngles[YAW] > maxYaw )
8767 		{
8768 			lookAngles[YAW] = maxYaw;
8769 		}
8770 		else if ( lookAngles[YAW] < minYaw )
8771 		{
8772 			lookAngles[YAW] = minYaw;
8773 		}
8774 		if ( lookAngles[ROLL] > maxRoll )
8775 		{
8776 			lookAngles[ROLL] = maxRoll;
8777 		}
8778 		else if ( lookAngles[ROLL] < minRoll )
8779 		{
8780 			lookAngles[ROLL] = minRoll;
8781 		}
8782 
8783 		//slowly lerp to this new value
8784 		//Remember last headAngles
8785 		VectorCopy( lastHeadAngles, oldLookAngles );
8786 		VectorSubtract( lookAngles, oldLookAngles, lookAnglesDiff );
8787 
8788 		for ( ang = 0; ang < 3; ang++ )
8789 		{
8790 			lookAnglesDiff[ang] = AngleNormalize180( lookAnglesDiff[ang] );
8791 		}
8792 
8793 		if( VectorLengthSquared( lookAnglesDiff ) )
8794 		{
8795 			lookAngles[PITCH] = AngleNormalize180( oldLookAngles[PITCH]+(lookAnglesDiff[PITCH]*fFrameInter*lookSpeed) );
8796 			lookAngles[YAW] = AngleNormalize180( oldLookAngles[YAW]+(lookAnglesDiff[YAW]*fFrameInter*lookSpeed) );
8797 			lookAngles[ROLL] = AngleNormalize180( oldLookAngles[ROLL]+(lookAnglesDiff[ROLL]*fFrameInter*lookSpeed) );
8798 		}
8799 	}
8800 	//Remember current lookAngles next time
8801 	VectorCopy( lookAngles, lastHeadAngles );
8802 }
8803 
8804 //for setting visual look (headturn) angles
BG_G2ClientNeckAngles(void * ghoul2,int time,const vec3_t lookAngles,vec3_t headAngles,vec3_t neckAngles,vec3_t thoracicAngles,vec3_t headClampMinAngles,vec3_t headClampMaxAngles)8805 static void BG_G2ClientNeckAngles( void *ghoul2, int time, const vec3_t lookAngles, vec3_t headAngles, vec3_t neckAngles, vec3_t thoracicAngles, vec3_t headClampMinAngles, vec3_t headClampMaxAngles )
8806 {
8807 	vec3_t	lA;
8808 	VectorCopy( lookAngles, lA );
8809 	//clamp the headangles (which should now be relative to the cervical (neck) angles
8810 	if ( lA[PITCH] < headClampMinAngles[PITCH] )
8811 	{
8812 		lA[PITCH] = headClampMinAngles[PITCH];
8813 	}
8814 	else if ( lA[PITCH] > headClampMaxAngles[PITCH] )
8815 	{
8816 		lA[PITCH] = headClampMaxAngles[PITCH];
8817 	}
8818 
8819 	if ( lA[YAW] < headClampMinAngles[YAW] )
8820 	{
8821 		lA[YAW] = headClampMinAngles[YAW];
8822 	}
8823 	else if ( lA[YAW] > headClampMaxAngles[YAW] )
8824 	{
8825 		lA[YAW] = headClampMaxAngles[YAW];
8826 	}
8827 
8828 	if ( lA[ROLL] < headClampMinAngles[ROLL] )
8829 	{
8830 		lA[ROLL] = headClampMinAngles[ROLL];
8831 	}
8832 	else if ( lA[ROLL] > headClampMaxAngles[ROLL] )
8833 	{
8834 		lA[ROLL] = headClampMaxAngles[ROLL];
8835 	}
8836 
8837 	//split it up between the neck and cranium
8838 	if ( thoracicAngles[PITCH] )
8839 	{//already been set above, blend them
8840 		thoracicAngles[PITCH] = (thoracicAngles[PITCH] + (lA[PITCH] * 0.4)) * 0.5f;
8841 	}
8842 	else
8843 	{
8844 		thoracicAngles[PITCH] = lA[PITCH] * 0.4;
8845 	}
8846 	if ( thoracicAngles[YAW] )
8847 	{//already been set above, blend them
8848 		thoracicAngles[YAW] = (thoracicAngles[YAW] + (lA[YAW] * 0.1)) * 0.5f;
8849 	}
8850 	else
8851 	{
8852 		thoracicAngles[YAW] = lA[YAW] * 0.1;
8853 	}
8854 	if ( thoracicAngles[ROLL] )
8855 	{//already been set above, blend them
8856 		thoracicAngles[ROLL] = (thoracicAngles[ROLL] + (lA[ROLL] * 0.1)) * 0.5f;
8857 	}
8858 	else
8859 	{
8860 		thoracicAngles[ROLL] = lA[ROLL] * 0.1;
8861 	}
8862 
8863 	neckAngles[PITCH] = lA[PITCH] * 0.2f;
8864 	neckAngles[YAW] = lA[YAW] * 0.3f;
8865 	neckAngles[ROLL] = lA[ROLL] * 0.3f;
8866 
8867 	headAngles[PITCH] = lA[PITCH] * 0.4;
8868 	headAngles[YAW] = lA[YAW] * 0.6;
8869 	headAngles[ROLL] = lA[ROLL] * 0.6;
8870 
8871 	/* //non-applicable SP code
8872 	if ( G_RidingVehicle( cent->gent ) )// && type == VH_SPEEDER ?
8873 	{//aim torso forward too
8874 		headAngles[YAW] = neckAngles[YAW] = thoracicAngles[YAW] = 0;
8875 	}
8876 	*/
8877 
8878 	trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", headAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
8879 	trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
8880 	trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
8881 }
8882 
8883 //rww - Finally decided to convert all this stuff to BG form.
BG_G2ClientSpineAngles(void * ghoul2,int motionBolt,vec3_t cent_lerpOrigin,vec3_t cent_lerpAngles,entityState_t * cent,int time,vec3_t viewAngles,int ciLegs,int ciTorso,const vec3_t angles,vec3_t thoracicAngles,vec3_t ulAngles,vec3_t llAngles,vec3_t modelScale,float * tPitchAngle,float * tYawAngle,int * corrTime)8884 static void BG_G2ClientSpineAngles( void *ghoul2, int motionBolt, vec3_t cent_lerpOrigin, vec3_t cent_lerpAngles, entityState_t *cent,
8885 							int time, vec3_t viewAngles, int ciLegs, int ciTorso, const vec3_t angles, vec3_t thoracicAngles,
8886 							vec3_t ulAngles, vec3_t llAngles, vec3_t modelScale, float *tPitchAngle, float *tYawAngle, int *corrTime )
8887 {
8888 	qboolean doCorr = qfalse;
8889 
8890 	//*tPitchAngle = viewAngles[PITCH];
8891 	viewAngles[YAW] = AngleDelta( cent_lerpAngles[YAW], angles[YAW] );
8892 	//*tYawAngle = viewAngles[YAW];
8893 
8894 #if 1
8895 	if ( !BG_FlippingAnim( cent->legsAnim ) &&
8896 		!BG_SpinningSaberAnim( cent->legsAnim ) &&
8897 		!BG_SpinningSaberAnim( cent->torsoAnim ) &&
8898 		!BG_InSpecialJump( cent->legsAnim ) &&
8899 		!BG_InSpecialJump( cent->torsoAnim ) &&
8900 		!BG_InDeathAnim(cent->legsAnim) &&
8901 		!BG_InDeathAnim(cent->torsoAnim) &&
8902 		!BG_InRollES(cent, cent->legsAnim) &&
8903 		!BG_InRollAnim(cent) &&
8904 		!BG_SaberInSpecial(cent->saberMove) &&
8905 		!BG_SaberInSpecialAttack(cent->torsoAnim) &&
8906 		!BG_SaberInSpecialAttack(cent->legsAnim) &&
8907 
8908 		!BG_InKnockDown(cent->torsoAnim) &&
8909 		!BG_InKnockDown(cent->legsAnim) &&
8910 		!BG_InKnockDown(ciTorso) &&
8911 		!BG_InKnockDown(ciLegs) &&
8912 
8913 		!BG_FlippingAnim( ciLegs ) &&
8914 		!BG_SpinningSaberAnim( ciLegs ) &&
8915 		!BG_SpinningSaberAnim( ciTorso ) &&
8916 		!BG_InSpecialJump( ciLegs ) &&
8917 		!BG_InSpecialJump( ciTorso ) &&
8918 		!BG_InDeathAnim(ciLegs) &&
8919 		!BG_InDeathAnim(ciTorso) &&
8920 		!BG_SaberInSpecialAttack(ciTorso) &&
8921 		!BG_SaberInSpecialAttack(ciLegs) &&
8922 
8923 		!(cent->eFlags & EF_DEAD) &&
8924 		(cent->legsAnim) != (cent->torsoAnim) &&
8925 		(ciLegs) != (ciTorso) &&
8926 		!cent->m_iVehicleNum)
8927 	{
8928 		doCorr = qtrue;
8929 	}
8930 #else
8931 	if ( ((!BG_FlippingAnim( cent->legsAnim )
8932 		&& !BG_SpinningSaberAnim( cent->legsAnim )
8933 		&& !BG_SpinningSaberAnim( cent->torsoAnim )
8934 		&& (cent->legsAnim) != (cent->torsoAnim)) //NOTE: presumes your legs & torso are on the same frame, though they *should* be because PM_SetAnimFinal tries to keep them in synch
8935 		||
8936 		(!BG_FlippingAnim( ciLegs )
8937 		&& !BG_SpinningSaberAnim( ciLegs )
8938 		&& !BG_SpinningSaberAnim( ciTorso )
8939 		&& (ciLegs) != (ciTorso)))
8940 		||
8941 		ciLegs != cent->legsAnim
8942 		||
8943 		ciTorso != cent->torsoAnim)
8944 	{
8945 		doCorr = qtrue;
8946 		*corrTime = time + 1000; //continue correcting for a second after to smooth things out. SP doesn't need this for whatever reason but I can't find a way around it.
8947 	}
8948 	else if (*corrTime >= time)
8949 	{
8950 		if (!BG_FlippingAnim( cent->legsAnim )
8951 			&& !BG_SpinningSaberAnim( cent->legsAnim )
8952 			&& !BG_SpinningSaberAnim( cent->torsoAnim )
8953 			&& !BG_FlippingAnim( ciLegs )
8954 			&& !BG_SpinningSaberAnim( ciLegs )
8955 			&& !BG_SpinningSaberAnim( ciTorso ))
8956 		{
8957 			doCorr = qtrue;
8958 		}
8959 	}
8960 #endif
8961 
8962 	if (doCorr)
8963 	{//FIXME: no need to do this if legs and torso on are same frame
8964 		//adjust for motion offset
8965 		mdxaBone_t	boltMatrix;
8966 		vec3_t		motionFwd, motionAngles;
8967 		vec3_t		motionRt, tempAng;
8968 		int			ang;
8969 
8970 		trap->G2API_GetBoltMatrix_NoRecNoRot( ghoul2, 0, motionBolt, &boltMatrix, vec3_origin, cent_lerpOrigin, time, 0, modelScale);
8971 		//BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, motionFwd );
8972 		motionFwd[0] = -boltMatrix.matrix[0][1];
8973 		motionFwd[1] = -boltMatrix.matrix[1][1];
8974 		motionFwd[2] = -boltMatrix.matrix[2][1];
8975 
8976 		vectoangles( motionFwd, motionAngles );
8977 
8978 		//BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, motionRt );
8979 		motionRt[0] = -boltMatrix.matrix[0][0];
8980 		motionRt[1] = -boltMatrix.matrix[1][0];
8981 		motionRt[2] = -boltMatrix.matrix[2][0];
8982 
8983 		vectoangles( motionRt, tempAng );
8984 		motionAngles[ROLL] = -tempAng[PITCH];
8985 
8986 		for ( ang = 0; ang < 3; ang++ )
8987 		{
8988 			viewAngles[ang] = AngleNormalize180( viewAngles[ang] - AngleNormalize180( motionAngles[ang] ) );
8989 		}
8990 	}
8991 
8992 	//distribute the angles differently up the spine
8993 	//NOTE: each of these distributions must add up to 1.0f
8994 	thoracicAngles[PITCH] = viewAngles[PITCH]*0.20f;
8995 	llAngles[PITCH] = viewAngles[PITCH]*0.40f;
8996 	ulAngles[PITCH] = viewAngles[PITCH]*0.40f;
8997 
8998 	thoracicAngles[YAW] = viewAngles[YAW]*0.20f;
8999 	ulAngles[YAW] = viewAngles[YAW]*0.35f;
9000 	llAngles[YAW] = viewAngles[YAW]*0.45f;
9001 
9002 	thoracicAngles[ROLL] = viewAngles[ROLL]*0.20f;
9003 	ulAngles[ROLL] = viewAngles[ROLL]*0.35f;
9004 	llAngles[ROLL] = viewAngles[ROLL]*0.45f;
9005 }
9006 
9007 /*
9008 ==================
9009 CG_SwingAngles
9010 ==================
9011 */
BG_SwingAngles(float destination,float swingTolerance,float clampTolerance,float speed,float * angle,qboolean * swinging,int frametime)9012 static float BG_SwingAngles( float destination, float swingTolerance, float clampTolerance,
9013 					float speed, float *angle, qboolean *swinging, int frametime ) {
9014 	float	swing;
9015 	float	move;
9016 	float	scale;
9017 
9018 	if ( !*swinging ) {
9019 		// see if a swing should be started
9020 		swing = AngleSubtract( *angle, destination );
9021 		if ( swing > swingTolerance || swing < -swingTolerance ) {
9022 			*swinging = qtrue;
9023 		}
9024 	}
9025 
9026 	if ( !*swinging ) {
9027 		return 0;
9028 	}
9029 
9030 	// modify the speed depending on the delta
9031 	// so it doesn't seem so linear
9032 	swing = AngleSubtract( destination, *angle );
9033 	scale = fabs( swing );
9034 	if ( scale < swingTolerance * 0.5 ) {
9035 		scale = 0.5;
9036 	} else if ( scale < swingTolerance ) {
9037 		scale = 1.0;
9038 	} else {
9039 		scale = 2.0;
9040 	}
9041 
9042 	// swing towards the destination angle
9043 	if ( swing >= 0 ) {
9044 		move = frametime * scale * speed;
9045 		if ( move >= swing ) {
9046 			move = swing;
9047 			*swinging = qfalse;
9048 		}
9049 		*angle = AngleMod( *angle + move );
9050 	} else if ( swing < 0 ) {
9051 		move = frametime * scale * -speed;
9052 		if ( move <= swing ) {
9053 			move = swing;
9054 			*swinging = qfalse;
9055 		}
9056 		*angle = AngleMod( *angle + move );
9057 	}
9058 
9059 	// clamp to no more than tolerance
9060 	swing = AngleSubtract( destination, *angle );
9061 	if ( swing > clampTolerance ) {
9062 		*angle = AngleMod( destination - (clampTolerance - 1) );
9063 	} else if ( swing < -clampTolerance ) {
9064 		*angle = AngleMod( destination + (clampTolerance - 1) );
9065 	}
9066 
9067 	return swing;
9068 }
9069 
9070 //#define BONE_BASED_LEG_ANGLES
9071 
9072 //I apologize for this function
BG_InRoll2(entityState_t * es)9073 qboolean BG_InRoll2( entityState_t *es )
9074 {
9075 	switch ( (es->legsAnim) )
9076 	{
9077 	case BOTH_GETUP_BROLL_B:
9078 	case BOTH_GETUP_BROLL_F:
9079 	case BOTH_GETUP_BROLL_L:
9080 	case BOTH_GETUP_BROLL_R:
9081 	case BOTH_GETUP_FROLL_B:
9082 	case BOTH_GETUP_FROLL_F:
9083 	case BOTH_GETUP_FROLL_L:
9084 	case BOTH_GETUP_FROLL_R:
9085 	case BOTH_ROLL_F:
9086 	case BOTH_ROLL_B:
9087 	case BOTH_ROLL_R:
9088 	case BOTH_ROLL_L:
9089 		return qtrue;
9090 		break;
9091 	}
9092 	return qfalse;
9093 }
9094 
9095 
9096 extern qboolean BG_SaberLockBreakAnim( int anim ); //bg_panimate.c
BG_G2PlayerAngles(void * ghoul2,int motionBolt,entityState_t * cent,int time,vec3_t cent_lerpOrigin,vec3_t cent_lerpAngles,matrix3_t legs,vec3_t legsAngles,qboolean * tYawing,qboolean * tPitching,qboolean * lYawing,float * tYawAngle,float * tPitchAngle,float * lYawAngle,int frametime,vec3_t turAngles,vec3_t modelScale,int ciLegs,int ciTorso,int * corrTime,vec3_t lookAngles,vec3_t lastHeadAngles,int lookTime,entityState_t * emplaced,int * crazySmoothFactor)9097 void BG_G2PlayerAngles(void *ghoul2, int motionBolt, entityState_t *cent, int time, vec3_t cent_lerpOrigin,
9098 					   vec3_t cent_lerpAngles, matrix3_t legs, vec3_t legsAngles, qboolean *tYawing,
9099 					   qboolean *tPitching, qboolean *lYawing, float *tYawAngle, float *tPitchAngle,
9100 					   float *lYawAngle, int frametime, vec3_t turAngles, vec3_t modelScale, int ciLegs,
9101 					   int ciTorso, int *corrTime, vec3_t lookAngles, vec3_t lastHeadAngles, int lookTime,
9102 					   entityState_t *emplaced, int *crazySmoothFactor)
9103 {
9104 	int					adddir = 0;
9105 	static int			dir;
9106 	static int			i;
9107 //	static int			movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
9108 	float				degrees_negative = 0;
9109 	float				degrees_positive = 0;
9110 	static float		dif;
9111 	static float		dest;
9112 	static float		speed; //, speed_dif, speed_desired;
9113 	static const float	lookSpeed = 1.5f;
9114 #ifdef BONE_BASED_LEG_ANGLES
9115 	static float		legBoneYaw;
9116 #endif
9117 	static vec3_t		eyeAngles;
9118 	static vec3_t		neckAngles;
9119 	static vec3_t		velocity;
9120 	static vec3_t		torsoAngles, headAngles;
9121 	static vec3_t		velPos, velAng;
9122 	static vec3_t		ulAngles, llAngles, viewAngles, angles, thoracicAngles = {0,0,0};
9123 	static vec3_t		headClampMinAngles = {-25,-55,-10}, headClampMaxAngles = {50,50,10};
9124 
9125 	if ( cent->m_iVehicleNum || cent->forceFrame || BG_SaberLockBreakAnim(cent->legsAnim) || BG_SaberLockBreakAnim(cent->torsoAnim) )
9126 	{ //a vehicle or riding a vehicle - in either case we don't need to be in here
9127 		vec3_t forcedAngles;
9128 
9129 		VectorClear(forcedAngles);
9130 		forcedAngles[YAW] = cent_lerpAngles[YAW];
9131 		forcedAngles[ROLL] = cent_lerpAngles[ROLL];
9132 		AnglesToAxis( forcedAngles, legs );
9133 		VectorCopy(forcedAngles, legsAngles);
9134 		VectorCopy(legsAngles, turAngles);
9135 
9136 		if (cent->number < MAX_CLIENTS)
9137 		{
9138 			trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9139 			trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9140 			trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9141 			trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9142 			trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9143 		}
9144 		return;
9145 	}
9146 
9147 	if ((time+2000) < *corrTime)
9148 	{
9149 		*corrTime = 0;
9150 	}
9151 
9152 	VectorCopy( cent_lerpAngles, headAngles );
9153 	headAngles[YAW] = AngleMod( headAngles[YAW] );
9154 	VectorClear( legsAngles );
9155 	VectorClear( torsoAngles );
9156 	// --------- yaw -------------
9157 
9158 	// allow yaw to drift a bit
9159 	if ((( cent->legsAnim ) != BOTH_STAND1) ||
9160 			( cent->torsoAnim ) != WeaponReadyAnim[cent->weapon]  )
9161 	{
9162 		// if not standing still, always point all in the same direction
9163 		//cent->pe.torso.yawing = qtrue;	// always center
9164 		*tYawing = qtrue;
9165 		//cent->pe.torso.pitching = qtrue;	// always center
9166 		*tPitching = qtrue;
9167 		//cent->pe.legs.yawing = qtrue;	// always center
9168 		*lYawing = qtrue;
9169 	}
9170 
9171 	// adjust legs for movement dir
9172 	if ( cent->eFlags & EF_DEAD ) {
9173 		// don't let dead bodies twitch
9174 		dir = 0;
9175 	} else {
9176 		dir = cent->angles2[YAW];
9177 		if ( dir < 0 || dir > 7 ) {
9178 			Com_Error( ERR_DROP, "Bad player movement angle (%i)", dir );
9179 		}
9180 	}
9181 
9182 	torsoAngles[YAW] = headAngles[YAW];
9183 
9184 	//for now, turn torso instantly and let the legs swing to follow
9185 	*tYawAngle = torsoAngles[YAW];
9186 
9187 	// --------- pitch -------------
9188 
9189 	VectorCopy( cent->pos.trDelta, velocity );
9190 
9191 	if (BG_InRoll2(cent))
9192 	{ //don't affect angles based on vel then
9193 		VectorClear(velocity);
9194 	}
9195 	else if (cent->weapon == WP_SABER &&
9196 		BG_SaberInSpecial(cent->saberMove))
9197 	{
9198 		VectorClear(velocity);
9199 	}
9200 
9201 	speed = VectorNormalize( velocity );
9202 
9203 	if (!speed)
9204 	{
9205 		torsoAngles[YAW] = headAngles[YAW];
9206 	}
9207 
9208 	// only show a fraction of the pitch angle in the torso
9209 	if ( headAngles[PITCH] > 180 ) {
9210 		dest = (-360 + headAngles[PITCH]) * 0.75;
9211 	} else {
9212 		dest = headAngles[PITCH] * 0.75;
9213 	}
9214 
9215 	if (cent->m_iVehicleNum)
9216 	{ //swing instantly on vehicles
9217 		*tPitchAngle = dest;
9218 	}
9219 	else
9220 	{
9221 		BG_SwingAngles( dest, 15, 30, 0.1f, tPitchAngle, tPitching, frametime );
9222 	}
9223 	torsoAngles[PITCH] = *tPitchAngle;
9224 
9225 	// --------- roll -------------
9226 
9227 	if ( speed ) {
9228 		matrix3_t	axis;
9229 		float	side;
9230 
9231 		speed *= 0.05f;
9232 
9233 		AnglesToAxis( legsAngles, axis );
9234 		side = speed * DotProduct( velocity, axis[1] );
9235 		legsAngles[ROLL] -= side;
9236 
9237 		side = speed * DotProduct( velocity, axis[0] );
9238 		legsAngles[PITCH] += side;
9239 	}
9240 
9241 	//legsAngles[YAW] = headAngles[YAW] + (movementOffsets[ dir ]*speed_dif);
9242 
9243 	//rww - crazy velocity-based leg angle calculation
9244 	legsAngles[YAW] = headAngles[YAW];
9245 	velPos[0] = cent_lerpOrigin[0] + velocity[0];
9246 	velPos[1] = cent_lerpOrigin[1] + velocity[1];
9247 	velPos[2] = cent_lerpOrigin[2];// + velocity[2];
9248 
9249 	if ( cent->groundEntityNum == ENTITYNUM_NONE ||
9250 		 cent->forceFrame ||
9251 		 (cent->weapon == WP_EMPLACED_GUN && emplaced) )
9252 	{ //off the ground, no direction-based leg angles (same if in saberlock)
9253 		VectorCopy(cent_lerpOrigin, velPos);
9254 	}
9255 
9256 	VectorSubtract(cent_lerpOrigin, velPos, velAng);
9257 
9258 	if (!VectorCompare(velAng, vec3_origin))
9259 	{
9260 		vectoangles(velAng, velAng);
9261 
9262 		if (velAng[YAW] <= legsAngles[YAW])
9263 		{
9264 			degrees_negative = (legsAngles[YAW] - velAng[YAW]);
9265 			degrees_positive = (360 - legsAngles[YAW]) + velAng[YAW];
9266 		}
9267 		else
9268 		{
9269 			degrees_negative = legsAngles[YAW] + (360 - velAng[YAW]);
9270 			degrees_positive = (velAng[YAW] - legsAngles[YAW]);
9271 		}
9272 
9273 		if ( degrees_negative < degrees_positive )
9274 		{
9275 			dif = degrees_negative;
9276 			adddir = 0;
9277 		}
9278 		else
9279 		{
9280 			dif = degrees_positive;
9281 			adddir = 1;
9282 		}
9283 
9284 		if (dif > 90)
9285 		{
9286 			dif = (180 - dif);
9287 		}
9288 
9289 		if (dif > 60)
9290 		{
9291 			dif = 60;
9292 		}
9293 
9294 		//Slight hack for when playing is running backward
9295 		if (dir == 3 || dir == 5)
9296 		{
9297 			dif = -dif;
9298 		}
9299 
9300 		if (adddir)
9301 		{
9302 			legsAngles[YAW] -= dif;
9303 		}
9304 		else
9305 		{
9306 			legsAngles[YAW] += dif;
9307 		}
9308 	}
9309 
9310 	if (cent->m_iVehicleNum)
9311 	{ //swing instantly on vehicles
9312 		*lYawAngle = legsAngles[YAW];
9313 	}
9314 	else
9315 	{
9316 		BG_SwingAngles( legsAngles[YAW], /*40*/0, 90, 0.65f, lYawAngle, lYawing, frametime );
9317 	}
9318 	legsAngles[YAW] = *lYawAngle;
9319 
9320 	/*
9321 	// pain twitch
9322 	CG_AddPainTwitch( cent, torsoAngles );
9323 	*/
9324 
9325 	legsAngles[ROLL] = 0;
9326 	torsoAngles[ROLL] = 0;
9327 
9328 //	VectorCopy(legsAngles, turAngles);
9329 
9330 	// pull the angles back out of the hierarchial chain
9331 	AnglesSubtract( headAngles, torsoAngles, headAngles );
9332 	AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
9333 
9334 	legsAngles[PITCH] = 0;
9335 
9336 	if (cent->heldByClient)
9337 	{ //keep the base angles clear when doing the IK stuff, it doesn't compensate for it.
9338 	  //rwwFIXMEFIXME: Store leg angles off and add them to all the fed in angles for G2 functions?
9339 		VectorClear(legsAngles);
9340 		legsAngles[YAW] = cent_lerpAngles[YAW];
9341 	}
9342 
9343 #ifdef BONE_BASED_LEG_ANGLES
9344 	legBoneYaw = legsAngles[YAW];
9345 	VectorClear(legsAngles);
9346 	legsAngles[YAW] = cent_lerpAngles[YAW];
9347 #endif
9348 
9349 	VectorCopy(legsAngles, turAngles);
9350 
9351 	AnglesToAxis( legsAngles, legs );
9352 
9353 	VectorCopy( cent_lerpAngles, viewAngles );
9354 	viewAngles[YAW] = viewAngles[ROLL] = 0;
9355 	viewAngles[PITCH] *= 0.5;
9356 
9357 	VectorSet( angles, 0, legsAngles[1], 0 );
9358 
9359 	angles[0] = legsAngles[0];
9360 	if ( angles[0] > 30 )
9361 	{
9362 		angles[0] = 30;
9363 	}
9364 	else if ( angles[0] < -30 )
9365 	{
9366 		angles[0] = -30;
9367 	}
9368 
9369 	if (cent->weapon == WP_EMPLACED_GUN &&
9370 		emplaced)
9371 	{ //if using an emplaced gun, then we want to make sure we're angled to "hold" it right
9372 		vec3_t facingAngles;
9373 
9374 		VectorSubtract(emplaced->pos.trBase, cent_lerpOrigin, facingAngles);
9375 		vectoangles(facingAngles, facingAngles);
9376 
9377 		if (emplaced->weapon == WP_NONE)
9378 		{ //e-web
9379 			VectorCopy(facingAngles, legsAngles);
9380 			AnglesToAxis( legsAngles, legs );
9381 		}
9382 		else
9383 		{ //misc emplaced
9384 			float emplacedDif = AngleSubtract(cent_lerpAngles[YAW], facingAngles[YAW]);
9385 
9386 			/*
9387 			if (emplaced->weapon == WP_NONE)
9388 			{ //offset is a little bit different for the e-web
9389 				emplacedDif -= 16.0f;
9390 			}
9391 			*/
9392 
9393 			VectorSet(facingAngles, -16.0f, -emplacedDif, 0.0f);
9394 
9395 			if (cent->legsAnim == BOTH_STRAFE_LEFT1 || cent->legsAnim == BOTH_STRAFE_RIGHT1)
9396 			{ //try to adjust so it doesn't look wrong
9397 				if (crazySmoothFactor)
9398 				{ //want to smooth a lot during this because it chops around and looks like ass
9399 					*crazySmoothFactor = time + 1000;
9400 				}
9401 
9402 				BG_G2ClientSpineAngles(ghoul2, motionBolt, cent_lerpOrigin, cent_lerpAngles, cent, time, viewAngles, ciLegs, ciTorso, angles, thoracicAngles, ulAngles, llAngles, modelScale, tPitchAngle, tYawAngle, corrTime);
9403 				trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9404 				trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9405 				trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9406 
9407 				VectorAdd(facingAngles, thoracicAngles, facingAngles);
9408 
9409 				if (cent->legsAnim == BOTH_STRAFE_LEFT1)
9410 				{ //this one needs some further correction
9411 					facingAngles[YAW] -= 32.0f;
9412 				}
9413 			}
9414 			else
9415 			{
9416 			//	trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9417 			//	trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9418 				trap->G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9419 			}
9420 
9421 			VectorScale(facingAngles, 0.6f, facingAngles);	trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9422 			VectorScale(facingAngles, 0.8f, facingAngles);	trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9423 			VectorScale(facingAngles, 0.8f, facingAngles);	trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9424 
9425 			//Now we want the head angled toward where we are facing
9426 			VectorSet(facingAngles, 0.0f, dif, 0.0f);
9427 			VectorScale(facingAngles, 0.6f, facingAngles);
9428 			trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9429 
9430 			return; //don't have to bother with the rest then
9431 		}
9432 	}
9433 
9434 	BG_G2ClientSpineAngles(ghoul2, motionBolt, cent_lerpOrigin, cent_lerpAngles, cent, time,
9435 		viewAngles, ciLegs, ciTorso, angles, thoracicAngles, ulAngles, llAngles, modelScale,
9436 		tPitchAngle, tYawAngle, corrTime);
9437 
9438 	VectorCopy(cent_lerpAngles, eyeAngles);
9439 
9440 	for ( i = 0; i < 3; i++ )
9441 	{
9442 		lookAngles[i] = AngleNormalize180( lookAngles[i] );
9443 		eyeAngles[i] = AngleNormalize180( eyeAngles[i] );
9444 	}
9445 	AnglesSubtract( lookAngles, eyeAngles, lookAngles );
9446 
9447 	BG_UpdateLookAngles(lookTime, lastHeadAngles, time, lookAngles, lookSpeed, -50.0f, 50.0f, -70.0f, 70.0f, -30.0f, 30.0f);
9448 
9449 	BG_G2ClientNeckAngles(ghoul2, time, lookAngles, headAngles, neckAngles, thoracicAngles, headClampMinAngles, headClampMaxAngles);
9450 
9451 #ifdef BONE_BASED_LEG_ANGLES
9452 	{
9453 		vec3_t bLAngles;
9454 		VectorClear(bLAngles);
9455 		bLAngles[ROLL] = AngleNormalize180((legBoneYaw - cent_lerpAngles[YAW]));
9456 		strap_G2API_SetBoneAngles(ghoul2, 0, "model_root", bLAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9457 
9458 		if (!llAngles[YAW])
9459 		{
9460 			llAngles[YAW] -= bLAngles[ROLL];
9461 		}
9462 	}
9463 #endif
9464 
9465 	trap->G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9466 	trap->G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9467 	trap->G2API_SetBoneAngles(ghoul2, 0, "thoracic", thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9468 //	trap->G2API_SetBoneAngles(ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
9469 }
9470 
BG_G2ATSTAngles(void * ghoul2,int time,vec3_t cent_lerpAngles)9471 void BG_G2ATSTAngles(void *ghoul2, int time, vec3_t cent_lerpAngles )
9472 {//																							up			right		fwd
9473 	trap->G2API_SetBoneAngles( ghoul2, 0, "thoracic", cent_lerpAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time );
9474 }
9475 
PM_AdjustAnglesForDualJumpAttack(playerState_t * ps,usercmd_t * ucmd)9476 static qboolean PM_AdjustAnglesForDualJumpAttack( playerState_t *ps, usercmd_t *ucmd )
9477 {
9478 	//ucmd->angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - ps->delta_angles[PITCH];
9479 	//ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
9480 	return qtrue;
9481 }
9482 
PM_CmdForSaberMoves(usercmd_t * ucmd)9483 static QINLINE void PM_CmdForSaberMoves(usercmd_t *ucmd)
9484 {
9485 	//DUAL FORWARD+JUMP+ATTACK
9486 	if ( (pm->ps->legsAnim == BOTH_JUMPATTACK6		&& pm->ps->saberMove == LS_JUMPATTACK_DUAL) ||
9487 		 (pm->ps->legsAnim == BOTH_BUTTERFLY_FL1	&& pm->ps->saberMove == LS_JUMPATTACK_STAFF_LEFT) ||
9488 		 (pm->ps->legsAnim == BOTH_BUTTERFLY_FR1	&& pm->ps->saberMove == LS_JUMPATTACK_STAFF_RIGHT) ||
9489 		 (pm->ps->legsAnim == BOTH_BUTTERFLY_RIGHT	&& pm->ps->saberMove == LS_BUTTERFLY_RIGHT) ||
9490 		 (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT	&& pm->ps->saberMove == LS_BUTTERFLY_LEFT) )
9491 	{
9492 		int aLen = PM_AnimLength(0, BOTH_JUMPATTACK6);
9493 
9494 		ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
9495 
9496 		if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
9497 		{ //dual stance attack
9498 			if ( pm->ps->legsTimer >= 100 //not at end
9499 				&& (aLen - pm->ps->legsTimer) >= 250 ) //not in beginning
9500 			{ //middle of anim
9501 				//push forward
9502 				ucmd->forwardmove = 127;
9503 			}
9504 
9505 			if ( (pm->ps->legsTimer >= 900 //not at end
9506 					&& aLen - pm->ps->legsTimer >= 950 ) //not in beginning
9507 				|| ( pm->ps->legsTimer >= 1600
9508 					&& aLen - pm->ps->legsTimer >= 400 ) ) //not in beginning
9509 			{ //one of the two jumps
9510 				if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9511 				{ //still on ground?
9512 					if ( pm->ps->groundEntityNum >= MAX_CLIENTS )
9513 					{
9514 						//jump!
9515 						pm->ps->velocity[2] = 250;//400;
9516 						pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
9517 						//pm->ps->pm_flags |= PMF_JUMPING;
9518 						//FIXME: NPCs yell?
9519 						PM_AddEvent(EV_JUMP);
9520 						//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9521 					}
9522 				}
9523 				else
9524 				{ //FIXME: if this is the second jump, maybe we should just stop the anim?
9525 				}
9526 			}
9527 		}
9528 		else
9529 		{ //saberstaff attacks
9530 			float lenMin = 1700.0f;
9531 			float lenMax = 1800.0f;
9532 
9533 			aLen = PM_AnimLength(0, (animNumber_t)pm->ps->legsAnim);
9534 
9535 			if (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT)
9536 			{
9537 				lenMin = 1200.0f;
9538 				lenMax = 1400.0f;
9539 			}
9540 
9541 			//FIXME: don't slide off people/obstacles?
9542 			if ( pm->ps->legsAnim == BOTH_BUTTERFLY_RIGHT
9543 				|| pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT )
9544 			{
9545 				if ( pm->ps->legsTimer > 450 )
9546 				{
9547 					switch ( pm->ps->legsAnim )
9548 					{
9549 						case BOTH_BUTTERFLY_LEFT:
9550 							ucmd->rightmove = -127;
9551 							break;
9552 						case BOTH_BUTTERFLY_RIGHT:
9553 							ucmd->rightmove = 127;
9554 							break;
9555 						default:
9556 							break;
9557 					}
9558 				}
9559 			}
9560 			else
9561 			{
9562 				if ( pm->ps->legsTimer >= 100 //not at end
9563 					&& aLen - pm->ps->legsTimer >= 250 )//not in beginning
9564 				{//middle of anim
9565 					//push forward
9566 					ucmd->forwardmove = 127;
9567 				}
9568 			}
9569 
9570 			if ( pm->ps->legsTimer >= lenMin && pm->ps->legsTimer < lenMax )
9571 			{//one of the two jumps
9572 				if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9573 				{//still on ground?
9574 					//jump!
9575 					if (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT)
9576 					{
9577 						pm->ps->velocity[2] = 350;
9578 					}
9579 					else
9580 					{
9581 						pm->ps->velocity[2] = 250;
9582 					}
9583 					pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
9584 					//pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
9585 					//FIXME: NPCs yell?
9586 					PM_AddEvent(EV_JUMP);
9587 					//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9588 				}
9589 				else
9590 				{//FIXME: if this is the second jump, maybe we should just stop the anim?
9591 				}
9592 			}
9593 		}
9594 
9595 		if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
9596 		{//can only turn when your feet hit the ground
9597 			if (PM_AdjustAnglesForDualJumpAttack(pm->ps, ucmd))
9598 			{
9599 				PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
9600 			}
9601 		}
9602 		//rwwFIXMEFIXME: Bother with bbox resizing like sp?
9603 	}
9604 	//STAFF BACK+JUMP+ATTACK
9605 	else if (pm->ps->saberMove == LS_A_BACKFLIP_ATK &&
9606 		pm->ps->legsAnim == BOTH_JUMPATTACK7)
9607 	{
9608 		int aLen = PM_AnimLength(0, BOTH_JUMPATTACK7);
9609 
9610 		if ( pm->ps->legsTimer > 800 //not at end
9611 			&& aLen - pm->ps->legsTimer >= 400 )//not in beginning
9612 		{//middle of anim
9613 			if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9614 			{//still on ground?
9615 				vec3_t yawAngles, backDir;
9616 
9617 				//push backwards some?
9618 				VectorSet( yawAngles, 0, pm->ps->viewangles[YAW]+180, 0 );
9619 				AngleVectors( yawAngles, backDir, 0, 0 );
9620 				VectorScale( backDir, 100, pm->ps->velocity );
9621 
9622 				//jump!
9623 				pm->ps->velocity[2] = 300;
9624 				pm->ps->fd.forceJumpZStart = pm->ps->origin[2]; //so we don't take damage if we land at same height
9625 				//pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
9626 
9627 				//FIXME: NPCs yell?
9628 				PM_AddEvent(EV_JUMP);
9629 				//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9630 				ucmd->upmove = 0; //clear any actual jump command
9631 			}
9632 		}
9633 		ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
9634 	}
9635 	//STAFF/DUAL SPIN ATTACK
9636 	else if (pm->ps->saberMove == LS_SPINATTACK ||
9637 		pm->ps->saberMove == LS_SPINATTACK_DUAL)
9638 	{
9639 		ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
9640 		//lock their viewangles during these attacks.
9641 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
9642 	}
9643 }
9644 
9645 //constrain him based on the angles of his vehicle and the caps
PM_VehicleViewAngles(playerState_t * ps,bgEntity_t * veh,usercmd_t * ucmd)9646 void PM_VehicleViewAngles(playerState_t *ps, bgEntity_t *veh, usercmd_t *ucmd)
9647 {
9648 	Vehicle_t *pVeh = veh->m_pVehicle;
9649 	qboolean setAngles = qfalse;
9650 	vec3_t clampMin;
9651 	vec3_t clampMax;
9652 	int i;
9653 
9654 	if ( veh->m_pVehicle->m_pPilot
9655 		&& veh->m_pVehicle->m_pPilot->s.number == ps->clientNum )
9656 	{//set the pilot's viewangles to the vehicle's viewangles
9657 #ifdef VEH_CONTROL_SCHEME_4
9658 		if ( 1 )
9659 #else //VEH_CONTROL_SCHEME_4
9660 		if ( !BG_UnrestrainedPitchRoll( ps, veh->m_pVehicle ) )
9661 #endif //VEH_CONTROL_SCHEME_4
9662 		{//only if not if doing special free-roll/pitch control
9663 			setAngles = qtrue;
9664 			clampMin[PITCH] = -pVeh->m_pVehicleInfo->lookPitch;
9665 			clampMax[PITCH] = pVeh->m_pVehicleInfo->lookPitch;
9666 			clampMin[YAW] = clampMax[YAW] = 0;
9667 			clampMin[ROLL] = clampMax[ROLL] = -1;
9668 		}
9669 	}
9670 	else
9671 	{
9672 		//NOTE: passengers can look around freely, UNLESS they're controlling a turret!
9673 		for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
9674 		{
9675 			if ( veh->m_pVehicle->m_pVehicleInfo->turret[i].passengerNum == ps->generic1 )
9676 			{//this turret is my station
9677 				setAngles = qtrue;
9678 				clampMin[PITCH] = veh->m_pVehicle->m_pVehicleInfo->turret[i].pitchClampUp;
9679 				clampMax[PITCH] = veh->m_pVehicle->m_pVehicleInfo->turret[i].pitchClampDown;
9680 				clampMin[YAW] = veh->m_pVehicle->m_pVehicleInfo->turret[i].yawClampRight;
9681 				clampMax[YAW] = veh->m_pVehicle->m_pVehicleInfo->turret[i].yawClampLeft;
9682 				clampMin[ROLL] = clampMax[ROLL] = 0;
9683 				break;
9684 			}
9685 		}
9686 	}
9687 	if ( setAngles )
9688 	{
9689 		for ( i = 0; i < 3; i++ )
9690 		{//clamp viewangles
9691 			if ( clampMin[i] == -1 || clampMax[i] == -1 )
9692 			{//no clamp
9693 			}
9694 			else if ( !clampMin[i] && !clampMax[i] )
9695 			{//no allowance
9696 				//ps->viewangles[i] = veh->playerState->viewangles[i];
9697 			}
9698 			else
9699 			{//allowance
9700 				if (ps->viewangles[i] > clampMax[i])
9701 				{
9702 					ps->viewangles[i] = clampMax[i];
9703 				}
9704 				else if (ps->viewangles[i] < clampMin[i])
9705 				{
9706 					ps->viewangles[i] = clampMin[i];
9707 				}
9708 			}
9709 		}
9710 
9711 		PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
9712 	}
9713 }
9714 
9715 /*
9716 //constrain him based on the angles of his vehicle and the caps
9717 void PM_VehicleViewAngles(playerState_t *ps, bgEntity_t *veh, usercmd_t *ucmd)
9718 {
9719 	Vehicle_t *pVeh = veh->m_pVehicle;
9720 
9721 	//now set the viewangles to the vehicle's directly
9722 	ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
9723 
9724 	//constrain the viewangles pitch based on the vehicle properties
9725 	if ( !pVeh->m_pVehicleInfo->lookPitch )
9726 	{//not allowed to look up & down!  ....???
9727 		ps->viewangles[PITCH] = veh->playerState->viewangles[PITCH];
9728 	}
9729 	else
9730 	{//clamp
9731 		if (ps->viewangles[PITCH] > pVeh->m_pVehicleInfo->lookPitch)
9732 		{
9733 			ps->viewangles[PITCH] = pVeh->m_pVehicleInfo->lookPitch;
9734 		}
9735 		else if (ps->viewangles[PITCH] < -pVeh->m_pVehicleInfo->lookPitch)
9736 		{
9737 			ps->viewangles[PITCH] = -pVeh->m_pVehicleInfo->lookPitch;
9738 		}
9739 	}
9740 
9741 	PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
9742 }
9743 */
9744 //see if a weapon is ok to use on a vehicle
PM_WeaponOkOnVehicle(int weapon)9745 qboolean PM_WeaponOkOnVehicle( int weapon )
9746 {
9747 	//FIXME: check g_vehicleInfo for our vehicle?
9748 	switch ( weapon )
9749 	{
9750 	//case WP_NONE:
9751 	case WP_MELEE:
9752 	case WP_SABER:
9753 	case WP_BLASTER:
9754 	//case WP_THERMAL:
9755 		return qtrue;
9756 		break;
9757 	}
9758 	return qfalse;
9759 }
9760 
9761 //do we have a weapon that's ok for using on the vehicle?
PM_GetOkWeaponForVehicle(void)9762 int PM_GetOkWeaponForVehicle(void)
9763 {
9764 	int i = 0;
9765 
9766 	while (i < WP_NUM_WEAPONS)
9767 	{
9768 		if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) &&
9769 			PM_WeaponOkOnVehicle(i))
9770 		{ //this one's good
9771 			return i;
9772 		}
9773 
9774 		i++;
9775 	}
9776 
9777 	//oh dear!
9778 	//assert(!"No valid veh weaps");
9779 	return -1;
9780 }
9781 
9782 //force the vehicle to turn and travel to its forced destination point
PM_VehForcedTurning(bgEntity_t * veh)9783 void PM_VehForcedTurning(bgEntity_t *veh)
9784 {
9785 	bgEntity_t *dst = PM_BGEntForNum(veh->playerState->vehTurnaroundIndex);
9786 	float pitchD, yawD;
9787 	vec3_t dir;
9788 
9789 	if (!veh || !veh->m_pVehicle)
9790 	{
9791 		return;
9792 	}
9793 
9794 	if (!dst)
9795 	{ //can't find dest ent?
9796 		return;
9797 	}
9798 
9799 	pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
9800 	pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
9801 	pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
9802 
9803 	VectorSubtract(dst->s.origin, veh->playerState->origin, dir);
9804 	vectoangles(dir, dir);
9805 
9806 	yawD = AngleSubtract(pm->ps->viewangles[YAW], dir[YAW]);
9807 	pitchD = AngleSubtract(pm->ps->viewangles[PITCH], dir[PITCH]);
9808 
9809 	yawD *= 0.6f*pml.frametime;
9810 	pitchD *= 0.6f*pml.frametime;
9811 
9812 #ifdef VEH_CONTROL_SCHEME_4
9813 	veh->playerState->viewangles[YAW] = AngleSubtract(veh->playerState->viewangles[YAW], yawD);
9814 	veh->playerState->viewangles[PITCH] = AngleSubtract(veh->playerState->viewangles[PITCH], pitchD);
9815 	pm->ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
9816 	pm->ps->viewangles[PITCH] = 0;
9817 
9818 	PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9819 	PM_SetPMViewAngle(veh->playerState, veh->playerState->viewangles, &pm->cmd);
9820 	VectorClear( veh->m_pVehicle->m_vPrevRiderViewAngles );
9821 	veh->m_pVehicle->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(pm->ps->viewangles[YAW]);
9822 
9823 #else //VEH_CONTROL_SCHEME_4
9824 
9825 	pm->ps->viewangles[YAW] = AngleSubtract(pm->ps->viewangles[YAW], yawD);
9826 	pm->ps->viewangles[PITCH] = AngleSubtract(pm->ps->viewangles[PITCH], pitchD);
9827 
9828 	PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9829 #endif //VEH_CONTROL_SCHEME_4
9830 }
9831 
9832 #ifdef VEH_CONTROL_SCHEME_4
PM_VehFaceHyperspacePoint(bgEntity_t * veh)9833 void PM_VehFaceHyperspacePoint(bgEntity_t *veh)
9834 {
9835 
9836 	if (!veh || !veh->m_pVehicle)
9837 	{
9838 		return;
9839 	}
9840 	else
9841 	{
9842 		float timeFrac = ((float)(pm->cmd.serverTime-veh->playerState->hyperSpaceTime))/HYPERSPACE_TIME;
9843 		float	turnRate, aDelta;
9844 		int		i, matchedAxes = 0;
9845 
9846 		pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
9847 		pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
9848 		pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
9849 
9850 		turnRate = (90.0f*pml.frametime);
9851 		for ( i = 0; i < 3; i++ )
9852 		{
9853 			aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]);
9854 			if ( fabs( aDelta ) < turnRate )
9855 			{//all is good
9856 				veh->playerState->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9857 				matchedAxes++;
9858 			}
9859 			else
9860 			{
9861 				aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->playerState->viewangles[i]);
9862 				if ( fabs( aDelta ) < turnRate )
9863 				{
9864 					veh->playerState->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9865 				}
9866 				else if ( aDelta > 0 )
9867 				{
9868 					if ( i == YAW )
9869 					{
9870 						veh->playerState->viewangles[i] = AngleNormalize360( veh->playerState->viewangles[i]+turnRate );
9871 					}
9872 					else
9873 					{
9874 						veh->playerState->viewangles[i] = AngleNormalize180( veh->playerState->viewangles[i]+turnRate );
9875 					}
9876 				}
9877 				else
9878 				{
9879 					if ( i == YAW )
9880 					{
9881 						veh->playerState->viewangles[i] = AngleNormalize360( veh->playerState->viewangles[i]-turnRate );
9882 					}
9883 					else
9884 					{
9885 						veh->playerState->viewangles[i] = AngleNormalize180( veh->playerState->viewangles[i]-turnRate );
9886 					}
9887 				}
9888 			}
9889 		}
9890 
9891 		pm->ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
9892 		pm->ps->viewangles[PITCH] = 0.0f;
9893 
9894 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9895 		PM_SetPMViewAngle(veh->playerState, veh->playerState->viewangles, &pm->cmd);
9896 		VectorClear( veh->m_pVehicle->m_vPrevRiderViewAngles );
9897 		veh->m_pVehicle->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(pm->ps->viewangles[YAW]);
9898 
9899 		if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
9900 		{//haven't gone through yet
9901 			if ( matchedAxes < 3 )
9902 			{//not facing the right dir yet
9903 				//keep hyperspace time up to date
9904 				veh->playerState->hyperSpaceTime += pml.msec;
9905 			}
9906 			else if ( !(veh->playerState->eFlags2&EF2_HYPERSPACE))
9907 			{//flag us as ready to hyperspace!
9908 				veh->playerState->eFlags2 |= EF2_HYPERSPACE;
9909 			}
9910 		}
9911 	}
9912 }
9913 
9914 #else //VEH_CONTROL_SCHEME_4
9915 
PM_VehFaceHyperspacePoint(bgEntity_t * veh)9916 void PM_VehFaceHyperspacePoint(bgEntity_t *veh)
9917 {
9918 
9919 	if (!veh || !veh->m_pVehicle)
9920 	{
9921 		return;
9922 	}
9923 	else
9924 	{
9925 		float timeFrac = ((float)(pm->cmd.serverTime-veh->playerState->hyperSpaceTime))/HYPERSPACE_TIME;
9926 		float	turnRate, aDelta;
9927 		int		i, matchedAxes = 0;
9928 
9929 		pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
9930 		pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
9931 		pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
9932 
9933 		turnRate = (90.0f*pml.frametime);
9934 		for ( i = 0; i < 3; i++ )
9935 		{
9936 			aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]);
9937 			if ( fabs( aDelta ) < turnRate )
9938 			{//all is good
9939 				pm->ps->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9940 				matchedAxes++;
9941 			}
9942 			else
9943 			{
9944 				aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], pm->ps->viewangles[i]);
9945 				if ( fabs( aDelta ) < turnRate )
9946 				{
9947 					pm->ps->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
9948 				}
9949 				else if ( aDelta > 0 )
9950 				{
9951 					if ( i == YAW )
9952 					{
9953 						pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]+turnRate );
9954 					}
9955 					else
9956 					{
9957 						pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]+turnRate );
9958 					}
9959 				}
9960 				else
9961 				{
9962 					if ( i == YAW )
9963 					{
9964 						pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]-turnRate );
9965 					}
9966 					else
9967 					{
9968 						pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]-turnRate );
9969 					}
9970 				}
9971 			}
9972 		}
9973 
9974 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
9975 
9976 		if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
9977 		{//haven't gone through yet
9978 			if ( matchedAxes < 3 )
9979 			{//not facing the right dir yet
9980 				//keep hyperspace time up to date
9981 				veh->playerState->hyperSpaceTime += pml.msec;
9982 			}
9983 			else if ( !(veh->playerState->eFlags2&EF2_HYPERSPACE))
9984 			{//flag us as ready to hyperspace!
9985 				veh->playerState->eFlags2 |= EF2_HYPERSPACE;
9986 			}
9987 		}
9988 	}
9989 }
9990 
9991 #endif //VEH_CONTROL_SCHEME_4
9992 
BG_VehicleAdjustBBoxForOrientation(Vehicle_t * veh,vec3_t origin,vec3_t mins,vec3_t maxs,int clientNum,int tracemask,void (* localTrace)(trace_t * results,const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int passEntityNum,int contentMask))9993 void BG_VehicleAdjustBBoxForOrientation( Vehicle_t *veh, vec3_t origin, vec3_t mins, vec3_t maxs,
9994 										int clientNum, int tracemask,
9995 										void (*localTrace)(trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask))
9996 {
9997 	if ( !veh
9998 		|| !veh->m_pVehicleInfo->length
9999 		|| !veh->m_pVehicleInfo->width
10000 		|| !veh->m_pVehicleInfo->height )
10001 		//|| veh->m_LandTrace.fraction < 1.0f )
10002 	{
10003 		return;
10004 	}
10005 	else if ( veh->m_pVehicleInfo->type != VH_FIGHTER
10006 		//&& veh->m_pVehicleInfo->type != VH_SPEEDER
10007 		&& veh->m_pVehicleInfo->type != VH_FLIER )
10008 	{//only those types of vehicles have dynamic bboxes, the rest just use a static bbox
10009 		VectorSet( maxs, veh->m_pVehicleInfo->width/2.0f, veh->m_pVehicleInfo->width/2.0f, veh->m_pVehicleInfo->height+DEFAULT_MINS_2 );
10010 		VectorSet( mins, veh->m_pVehicleInfo->width/-2.0f, veh->m_pVehicleInfo->width/-2.0f, DEFAULT_MINS_2 );
10011 		return;
10012 	}
10013 	else
10014 	{
10015 		matrix3_t	axis;
10016 		vec3_t		point[8], newMins, newMaxs;
10017 		int			curAxis = 0, i;
10018 		trace_t		trace;
10019 
10020 		AnglesToAxis( veh->m_vOrientation, axis );
10021 		VectorMA( origin, veh->m_pVehicleInfo->length/2.0f, axis[0], point[0] );
10022 		VectorMA( origin, -veh->m_pVehicleInfo->length/2.0f, axis[0], point[1] );
10023 		//extrapolate each side up and down
10024 		VectorMA( point[0], veh->m_pVehicleInfo->height/2.0f, axis[2], point[0] );
10025 		VectorMA( point[0], -veh->m_pVehicleInfo->height, axis[2], point[2] );
10026 		VectorMA( point[1], veh->m_pVehicleInfo->height/2.0f, axis[2], point[1] );
10027 		VectorMA( point[1], -veh->m_pVehicleInfo->height, axis[2], point[3] );
10028 
10029 		VectorMA( origin, veh->m_pVehicleInfo->width/2.0f, axis[1], point[4] );
10030 		VectorMA( origin, -veh->m_pVehicleInfo->width/2.0f, axis[1], point[5] );
10031 		//extrapolate each side up and down
10032 		VectorMA( point[4], veh->m_pVehicleInfo->height/2.0f, axis[2], point[4] );
10033 		VectorMA( point[4], -veh->m_pVehicleInfo->height, axis[2], point[6] );
10034 		VectorMA( point[5], veh->m_pVehicleInfo->height/2.0f, axis[2], point[5] );
10035 		VectorMA( point[5], -veh->m_pVehicleInfo->height, axis[2], point[7] );
10036 		/*
10037 		VectorMA( origin, veh->m_pVehicleInfo->height/2.0f, axis[2], point[4] );
10038 		VectorMA( origin, -veh->m_pVehicleInfo->height/2.0f, axis[2], point[5] );
10039 		*/
10040 		//Now inflate a bbox around these points
10041 		VectorCopy( origin, newMins );
10042 		VectorCopy( origin, newMaxs );
10043 		for ( curAxis = 0; curAxis < 3; curAxis++ )
10044 		{
10045 			for ( i = 0; i < 8; i++ )
10046 			{
10047 				if ( point[i][curAxis] > newMaxs[curAxis] )
10048 				{
10049 					newMaxs[curAxis] = point[i][curAxis];
10050 				}
10051 				else if ( point[i][curAxis] < newMins[curAxis] )
10052 				{
10053 					newMins[curAxis] = point[i][curAxis];
10054 				}
10055 			}
10056 		}
10057 		VectorSubtract( newMins, origin, newMins );
10058 		VectorSubtract( newMaxs, origin, newMaxs );
10059 		//now see if that's a valid way to be
10060 		if (localTrace)
10061 		{
10062 			localTrace( &trace, origin, newMins, newMaxs, origin, clientNum, tracemask );
10063 		}
10064 		else
10065 		{ //don't care about solid stuff then
10066 			trace.startsolid = trace.allsolid = 0;
10067 		}
10068 		if ( !trace.startsolid && !trace.allsolid )
10069 		{//let's use it!
10070 			VectorCopy( newMins, mins );
10071 			VectorCopy( newMaxs, maxs );
10072 		}
10073 		//else: just use the last one, I guess...?
10074 		//FIXME: make it as close as possible?  Or actually prevent the change in m_vOrientation?  Or push away from anything we hit?
10075 	}
10076 }
10077 /*
10078 ================
10079 PmoveSingle
10080 
10081 ================
10082 */
10083 extern int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
10084 extern qboolean BG_FighterUpdate(Vehicle_t *pVeh, const usercmd_t *pUcmd, vec3_t trMins, vec3_t trMaxs, float gravity,
10085 					  void (*traceFunc)( trace_t *results, const vec3_t start, const vec3_t lmins, const vec3_t lmaxs, const vec3_t end, int passEntityNum, int contentMask )); //FighterNPC.c
10086 
10087 #define JETPACK_HOVER_HEIGHT	64
10088 
10089 //#define _TESTING_VEH_PREDICTION
10090 
PM_MoveForKata(usercmd_t * ucmd)10091 void PM_MoveForKata(usercmd_t *ucmd)
10092 {
10093 	if ( pm->ps->legsAnim == BOTH_A7_SOULCAL
10094 		&& pm->ps->saberMove == LS_STAFF_SOULCAL )
10095 	{//forward spinning staff attack
10096 		ucmd->upmove = 0;
10097 
10098 		if ( PM_CanRollFromSoulCal( pm->ps ) )
10099 		{
10100 			ucmd->upmove = -127;
10101 			ucmd->rightmove = 0;
10102 			if (ucmd->forwardmove < 0)
10103 			{
10104 				ucmd->forwardmove = 0;
10105 			}
10106 		}
10107 		else
10108 		{
10109 			ucmd->rightmove = 0;
10110 			//FIXME: don't slide off people/obstacles?
10111 			if ( pm->ps->legsTimer >= 2750 )
10112 			{//not at end
10113 				//push forward
10114 				ucmd->forwardmove = 64;
10115 			}
10116 			else
10117 			{
10118 				ucmd->forwardmove = 0;
10119 			}
10120 		}
10121 		if ( pm->ps->legsTimer >= 2650
10122 			&& pm->ps->legsTimer < 2850 )
10123 		{//the jump
10124 			if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
10125 			{//still on ground?
10126 				//jump!
10127 				pm->ps->velocity[2] = 250;
10128 				pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
10129 			//	pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
10130 				//FIXME: NPCs yell?
10131 				PM_AddEvent(EV_JUMP);
10132 			}
10133 		}
10134 	}
10135 	else if (pm->ps->legsAnim == BOTH_A2_SPECIAL)
10136 	{ //medium kata
10137 		pm->cmd.rightmove = 0;
10138 		pm->cmd.upmove = 0;
10139 		if (pm->ps->legsTimer < 2700 && pm->ps->legsTimer > 2300)
10140 		{
10141 			pm->cmd.forwardmove = 127;
10142 		}
10143 		else if (pm->ps->legsTimer < 900 && pm->ps->legsTimer > 500)
10144 		{
10145 			pm->cmd.forwardmove = 127;
10146 		}
10147 		else
10148 		{
10149 			pm->cmd.forwardmove = 0;
10150 		}
10151 	}
10152 	else if (pm->ps->legsAnim == BOTH_A3_SPECIAL)
10153 	{ //strong kata
10154 		pm->cmd.rightmove = 0;
10155 		pm->cmd.upmove = 0;
10156 		if (pm->ps->legsTimer < 1700 && pm->ps->legsTimer > 1000)
10157 		{
10158 			pm->cmd.forwardmove = 127;
10159 		}
10160 		else
10161 		{
10162 			pm->cmd.forwardmove = 0;
10163 		}
10164 	}
10165 	else
10166 	{
10167 		pm->cmd.forwardmove = 0;
10168 		pm->cmd.rightmove = 0;
10169 		pm->cmd.upmove = 0;
10170 	}
10171 }
10172 
PmoveSingle(pmove_t * pmove)10173 void PmoveSingle (pmove_t *pmove) {
10174 	qboolean stiffenedUp = qfalse;
10175 	float gDist = 0;
10176 	qboolean noAnimate = qfalse;
10177 	int savedGravity = 0;
10178 
10179 	pm = pmove;
10180 
10181 	if (pm->cmd.buttons & BUTTON_ATTACK && pm->cmd.buttons & BUTTON_USE_HOLDABLE)
10182 	{
10183 		pm->cmd.buttons &= ~BUTTON_ATTACK;
10184 		pm->cmd.buttons &= ~BUTTON_USE_HOLDABLE;
10185 	}
10186 	if (pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->cmd.buttons & BUTTON_USE_HOLDABLE)
10187 	{
10188 		pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
10189 		pm->cmd.buttons &= ~BUTTON_USE_HOLDABLE;
10190 	}
10191 
10192 	if (pm->ps->emplacedIndex)
10193 	{
10194 		if (pm->cmd.buttons & BUTTON_ALT_ATTACK)
10195 		{ //hackerrific.
10196 			pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
10197 			pm->cmd.buttons |= BUTTON_ATTACK;
10198 		}
10199 	}
10200 
10201 	//set up these "global" bg ents
10202 	pm_entSelf = PM_BGEntForNum(pm->ps->clientNum);
10203 	if (pm->ps->m_iVehicleNum)
10204 	{
10205 		if (pm->ps->clientNum < MAX_CLIENTS)
10206 		{ //player riding vehicle
10207 			pm_entVeh = PM_BGEntForNum(pm->ps->m_iVehicleNum);
10208 		}
10209 		else
10210 		{ //vehicle with player pilot
10211 			pm_entVeh = PM_BGEntForNum(pm->ps->m_iVehicleNum-1);
10212 		}
10213 	}
10214 	else
10215 	{ //no vehicle ent
10216 		pm_entVeh = NULL;
10217 	}
10218 
10219 	gPMDoSlowFall = PM_DoSlowFall();
10220 
10221 	// this counter lets us debug movement problems with a journal
10222 	// by setting a conditional breakpoint fot the previous frame
10223 	c_pmove++;
10224 
10225 	// clear results
10226 	pm->numtouch = 0;
10227 	pm->watertype = 0;
10228 	pm->waterlevel = 0;
10229 
10230 	if (PM_IsRocketTrooper())
10231 	{ //kind of nasty, don't let them crouch or anything if nonhumanoid (probably a rockettrooper)
10232 		if (pm->cmd.upmove < 0)
10233 		{
10234 			pm->cmd.upmove = 0;
10235 		}
10236 	}
10237 
10238 	if (pm->ps->pm_type == PM_FLOAT)
10239 	{ //You get no control over where you go in grip movement
10240 		stiffenedUp = qtrue;
10241 	}
10242 	else if (pm->ps->eFlags & EF_DISINTEGRATION)
10243 	{
10244 		stiffenedUp = qtrue;
10245 	}
10246 	else if ( BG_SaberLockBreakAnim( pm->ps->legsAnim )
10247 		|| BG_SaberLockBreakAnim( pm->ps->torsoAnim )
10248 		|| pm->ps->saberLockTime >= pm->cmd.serverTime )
10249 	{//can't move or turn
10250 		stiffenedUp = qtrue;
10251 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10252 	}
10253 	else if ( pm->ps->saberMove == LS_A_BACK || pm->ps->saberMove == LS_A_BACK_CR ||
10254 		pm->ps->saberMove == LS_A_BACKSTAB || pm->ps->saberMove == LS_A_FLIP_STAB ||
10255 		pm->ps->saberMove == LS_A_FLIP_SLASH || pm->ps->saberMove == LS_A_JUMP_T__B_ ||
10256 		pm->ps->saberMove == LS_DUAL_LR || pm->ps->saberMove == LS_DUAL_FB)
10257 	{
10258 		if (pm->ps->legsAnim == BOTH_JUMPFLIPSTABDOWN ||
10259 			pm->ps->legsAnim == BOTH_JUMPFLIPSLASHDOWN1)
10260 		{ //flipover medium stance attack
10261 			if (pm->ps->legsTimer < 1600 && pm->ps->legsTimer > 900)
10262 			{
10263 				pm->ps->viewangles[YAW] += pml.frametime*240.0f;
10264 				PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10265 			}
10266 		}
10267 		stiffenedUp = qtrue;
10268 	}
10269 	else if ((pm->ps->legsAnim) == (BOTH_A2_STABBACK1) ||
10270 		(pm->ps->legsAnim) == (BOTH_ATTACK_BACK) ||
10271 		(pm->ps->legsAnim) == (BOTH_CROUCHATTACKBACK1) ||
10272 		(pm->ps->legsAnim) == (BOTH_FORCELEAP2_T__B_) ||
10273 		(pm->ps->legsAnim) == (BOTH_JUMPFLIPSTABDOWN) ||
10274 		(pm->ps->legsAnim) == (BOTH_JUMPFLIPSLASHDOWN1))
10275 	{
10276 		stiffenedUp = qtrue;
10277 	}
10278 	else if (pm->ps->legsAnim == BOTH_ROLL_STAB)
10279 	{
10280 		stiffenedUp = qtrue;
10281 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10282 	}
10283 	else if (pm->ps->heldByClient)
10284 	{
10285 		stiffenedUp = qtrue;
10286 	}
10287 	else if (BG_KickMove(pm->ps->saberMove) || BG_KickingAnim(pm->ps->legsAnim))
10288 	{
10289 		stiffenedUp = qtrue;
10290 	}
10291 	else if (BG_InGrappleMove(pm->ps->torsoAnim))
10292 	{
10293 		stiffenedUp = qtrue;
10294 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10295 	}
10296 	else if ( pm->ps->saberMove == LS_STABDOWN_DUAL ||
10297 			pm->ps->saberMove == LS_STABDOWN_STAFF ||
10298 			pm->ps->saberMove == LS_STABDOWN)
10299 	{//FIXME: need to only move forward until we bump into our target...?
10300 		if (pm->ps->legsTimer < 800)
10301 		{ //freeze movement near end of anim
10302 			stiffenedUp = qtrue;
10303 			PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10304 		}
10305 		else
10306 		{ //force forward til then
10307             pm->cmd.rightmove = 0;
10308 			pm->cmd.upmove = 0;
10309 			pm->cmd.forwardmove = 64;
10310 		}
10311 	}
10312 	else if (pm->ps->saberMove == LS_PULL_ATTACK_STAB ||
10313 		pm->ps->saberMove == LS_PULL_ATTACK_SWING)
10314 	{
10315 		stiffenedUp = qtrue;
10316 	}
10317 	else if (BG_SaberInKata(pm->ps->saberMove) ||
10318 			 BG_InKataAnim(pm->ps->torsoAnim) ||
10319 			 BG_InKataAnim(pm->ps->legsAnim))
10320 	{
10321 		PM_MoveForKata(&pm->cmd);
10322 	}
10323 	else if ( BG_FullBodyTauntAnim( pm->ps->legsAnim )
10324 		&& BG_FullBodyTauntAnim( pm->ps->torsoAnim ) )
10325 	{
10326 		if ( (pm->cmd.buttons&BUTTON_ATTACK)
10327 			|| (pm->cmd.buttons&BUTTON_ALT_ATTACK)
10328 			|| (pm->cmd.buttons&BUTTON_FORCEPOWER)
10329 			|| (pm->cmd.buttons&BUTTON_FORCEGRIP)
10330 			|| (pm->cmd.buttons&BUTTON_FORCE_LIGHTNING)
10331 			|| (pm->cmd.buttons&BUTTON_FORCE_DRAIN)
10332 			|| pm->cmd.upmove )
10333 		{//stop the anim
10334 			if ( pm->ps->legsAnim == BOTH_MEDITATE
10335 				&& pm->ps->torsoAnim == BOTH_MEDITATE )
10336 			{
10337 				PM_SetAnim( SETANIM_BOTH, BOTH_MEDITATE_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10338 			}
10339 			else
10340 			{
10341 				pm->ps->legsTimer = pm->ps->torsoTimer = 0;
10342 			}
10343 			if ( pm->ps->forceHandExtend == HANDEXTEND_TAUNT )
10344 			{
10345 				pm->ps->forceHandExtend = 0;
10346 			}
10347 		}
10348 		else
10349 		{
10350 			if ( pm->ps->legsAnim == BOTH_MEDITATE )
10351 			{
10352 				if ( pm->ps->legsTimer < 100 )
10353 				{
10354 					pm->ps->legsTimer = 100;
10355 				}
10356 			}
10357 			if ( pm->ps->torsoAnim == BOTH_MEDITATE )
10358 			{
10359 				if ( pm->ps->torsoTimer < 100 )
10360 				{
10361 					pm->ps->legsTimer = 100;
10362 				}
10363 				pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
10364 				pm->ps->forceHandExtendTime = pm->cmd.serverTime + 100;
10365 			}
10366 			if ( pm->ps->legsTimer > 0 || pm->ps->torsoTimer > 0 )
10367 			{
10368 				stiffenedUp = qtrue;
10369 				PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10370 				pm->cmd.rightmove = 0;
10371 				pm->cmd.upmove = 0;
10372 				pm->cmd.forwardmove = 0;
10373 				pm->cmd.buttons = 0;
10374 			}
10375 		}
10376 	}
10377 	else if ( pm->ps->legsAnim == BOTH_MEDITATE_END
10378 		&& pm->ps->legsTimer > 0 )
10379 	{
10380 		stiffenedUp = qtrue;
10381 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10382         pm->cmd.rightmove = 0;
10383 		pm->cmd.upmove = 0;
10384 		pm->cmd.forwardmove = 0;
10385 		pm->cmd.buttons = 0;
10386 	}
10387 	else if (pm->ps->legsAnim == BOTH_FORCELAND1 ||
10388 		pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
10389 		pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
10390 		pm->ps->legsAnim == BOTH_FORCELANDLEFT1)
10391 	{ //can't move while in a force land
10392 		stiffenedUp = qtrue;
10393 	}
10394 
10395 	if ( pm->ps->saberMove == LS_A_LUNGE )
10396 	{//can't move during lunge
10397 		pm->cmd.rightmove = pm->cmd.upmove = 0;
10398 		if ( pm->ps->legsTimer > 500 )
10399 		{
10400 			pm->cmd.forwardmove = 127;
10401 		}
10402 		else
10403 		{
10404 			pm->cmd.forwardmove = 0;
10405 		}
10406 	}
10407 
10408 	if ( pm->ps->saberMove == LS_A_JUMP_T__B_ )
10409 	{//can't move during leap
10410 		if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
10411 		{//hit the ground
10412 			pm->cmd.forwardmove = 0;
10413 		}
10414 		pm->cmd.rightmove = pm->cmd.upmove = 0;
10415 	}
10416 
10417 #if 0
10418 	if ((pm->ps->legsAnim) == BOTH_KISSER1LOOP ||
10419 		(pm->ps->legsAnim) == BOTH_KISSEE1LOOP)
10420 	{
10421 		stiffenedUp = qtrue;
10422 	}
10423 #endif
10424 
10425 	if (pm->ps->emplacedIndex)
10426 	{
10427 		if (pm->cmd.forwardmove < 0 || PM_GroundDistance() > 32.0f)
10428 		{
10429 			pm->ps->emplacedIndex = 0;
10430 			pm->ps->saberHolstered = 0;
10431 		}
10432 		else
10433 		{
10434 			stiffenedUp = qtrue;
10435 		}
10436 	}
10437 
10438 	/*
10439 	if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_CHARGING_ALT)
10440 	{ //not allowed to move while charging the disruptor
10441 		pm->cmd.forwardmove = 0;
10442 		pm->cmd.rightmove = 0;
10443 		if (pm->cmd.upmove > 0)
10444 		{
10445 			pm->cmd.upmove = 0;
10446 		}
10447 	}
10448 	*/
10449 
10450 	if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_CHARGING_ALT)
10451 	{ //not allowed to move while charging the disruptor
10452 		if (pm->cmd.forwardmove ||
10453 			pm->cmd.rightmove ||
10454 			pm->cmd.upmove > 0)
10455 		{ //get out
10456 			pm->ps->weaponstate = WEAPON_READY;
10457 			pm->ps->weaponTime = 1000;
10458 			PM_AddEventWithParm(EV_WEAPON_CHARGE, WP_DISRUPTOR); //cut the weapon charge sound
10459 			pm->cmd.upmove = 0;
10460 		}
10461 	}
10462 	else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
10463 	{ //can't jump
10464 		if (pm->cmd.upmove > 0)
10465 		{
10466 			pm->cmd.upmove = 0;
10467 		}
10468 	}
10469 
10470 	if (stiffenedUp)
10471 	{
10472 		pm->cmd.forwardmove = 0;
10473 		pm->cmd.rightmove = 0;
10474 		pm->cmd.upmove = 0;
10475 	}
10476 
10477 	if (pm->ps->fd.forceGripCripple)
10478 	{ //don't let attack or alt attack if being gripped I guess
10479 		pm->cmd.buttons &= ~BUTTON_ATTACK;
10480 		pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
10481 	}
10482 
10483 	if ( BG_InRoll( pm->ps, pm->ps->legsAnim ) )
10484 	{ //can't roll unless you're able to move normally
10485 		BG_CmdForRoll( pm->ps, pm->ps->legsAnim, &pm->cmd );
10486 	}
10487 
10488 	PM_CmdForSaberMoves(&pm->cmd);
10489 
10490 	BG_AdjustClientSpeed(pm->ps, &pm->cmd, pm->cmd.serverTime);
10491 
10492 	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
10493 		pm->tracemask &= ~CONTENTS_BODY;	// corpses can fly through bodies
10494 	}
10495 
10496 	// make sure walking button is clear if they are running, to avoid
10497 	// proxy no-footsteps cheats
10498 	if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
10499 		pm->cmd.buttons &= ~BUTTON_WALKING;
10500 	}
10501 
10502 	// set the talk balloon flag
10503 	if ( pm->cmd.buttons & BUTTON_TALK ) {
10504 		pm->ps->eFlags |= EF_TALK;
10505 	} else {
10506 		pm->ps->eFlags &= ~EF_TALK;
10507 	}
10508 
10509 	pm_cancelOutZoom = qfalse;
10510 	if (pm->ps->weapon == WP_DISRUPTOR &&
10511 		pm->ps->zoomMode == 1)
10512 	{
10513 		if ((pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
10514 			!(pm->cmd.buttons & BUTTON_ATTACK) &&
10515 			pm->ps->zoomLocked)
10516 		{
10517 			pm_cancelOutZoom = qtrue;
10518 		}
10519 	}
10520 	// In certain situations, we may want to control which attack buttons are pressed and what kind of functionality
10521 	//	is attached to them
10522 	PM_AdjustAttackStates( pm );
10523 
10524 	// clear the respawned flag if attack and use are cleared
10525 	if ( pm->ps->stats[STAT_HEALTH] > 0 &&
10526 		!( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
10527 		pm->ps->pm_flags &= ~PMF_RESPAWNED;
10528 	}
10529 
10530 	// if talk button is down, dissallow all other input
10531 	// this is to prevent any possible intercept proxy from
10532 	// adding fake talk balloons
10533 	if ( pmove->cmd.buttons & BUTTON_TALK ) {
10534 		// keep the talk button set tho for when the cmd.serverTime > 66 msec
10535 		// and the same cmd is used multiple times in Pmove
10536 		pmove->cmd.buttons = BUTTON_TALK;
10537 		pmove->cmd.forwardmove = 0;
10538 		pmove->cmd.rightmove = 0;
10539 		pmove->cmd.upmove = 0;
10540 	}
10541 
10542 	// clear all pmove local vars
10543 	memset (&pml, 0, sizeof(pml));
10544 
10545 	// determine the time
10546 	pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
10547 	if ( pml.msec < 1 ) {
10548 		pml.msec = 1;
10549 	} else if ( pml.msec > 200 ) {
10550 		pml.msec = 200;
10551 	}
10552 
10553 	/*
10554 	if (pm->ps->clientNum >= MAX_CLIENTS)
10555 	{
10556 #ifdef _GAME
10557 		Com_Printf( S_C0LOR_RED" SERVER N%i msec %d\n", pm->ps->clientNum, pml.msec );
10558 #else
10559 		Com_Printf( S_COLOR_GREEN" CLIENT N%i msec %d\n", pm->ps->clientNum, pml.msec );
10560 #endif
10561 	}
10562 	*/
10563 
10564 	pm->ps->commandTime = pmove->cmd.serverTime;
10565 
10566 	// save old org in case we get stuck
10567 	VectorCopy (pm->ps->origin, pml.previous_origin);
10568 
10569 	// save old velocity for crashlanding
10570 	VectorCopy (pm->ps->velocity, pml.previous_velocity);
10571 
10572 	pml.frametime = pml.msec * 0.001;
10573 
10574 	if (pm->ps->clientNum >= MAX_CLIENTS &&
10575 		pm_entSelf &&
10576 		pm_entSelf->s.NPC_class == CLASS_VEHICLE)
10577 	{ //we are a vehicle
10578 		bgEntity_t *veh = pm_entSelf;
10579 		assert( veh && veh->m_pVehicle);
10580 		if ( veh && veh->m_pVehicle )
10581 		{
10582 			veh->m_pVehicle->m_fTimeModifier = pml.frametime*60.0f;
10583 		}
10584 	}
10585 	else if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
10586 		&&pm->ps->m_iVehicleNum)
10587 	{
10588 		bgEntity_t *veh = pm_entVeh;
10589 
10590 		if (veh && veh->playerState &&
10591 			(pm->cmd.serverTime-veh->playerState->hyperSpaceTime) < HYPERSPACE_TIME)
10592 		{ //going into hyperspace, turn to face the right angles
10593             PM_VehFaceHyperspacePoint(veh);
10594 		}
10595 		else if (veh && veh->playerState &&
10596 			veh->playerState->vehTurnaroundIndex &&
10597 			veh->playerState->vehTurnaroundTime > pm->cmd.serverTime)
10598 		{ //riding this vehicle, turn my view too
10599             PM_VehForcedTurning(veh);
10600 		}
10601 	}
10602 
10603 	if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_ALT &&
10604 		pm->ps->legsTimer > 0 )
10605 	{
10606 		vec3_t vFwd, fwdAng;
10607 		VectorSet(fwdAng, 0.0f, pm->ps->viewangles[YAW], 0.0f);
10608 
10609 		AngleVectors( fwdAng, vFwd, NULL, NULL );
10610 		if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
10611 		{
10612 			float savZ = pm->ps->velocity[2];
10613 			VectorScale( vFwd, 100, pm->ps->velocity );
10614 			pm->ps->velocity[2] = savZ;
10615 		}
10616 		pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
10617 		PM_AdjustAnglesForWallRunUpFlipAlt( &pm->cmd );
10618 	}
10619 
10620 //	PM_AdjustAngleForWallRun(pm->ps, &pm->cmd, qtrue);
10621 //	PM_AdjustAnglesForStabDown( pm->ps, &pm->cmd );
10622 	PM_AdjustAngleForWallJump( pm->ps, &pm->cmd, qtrue );
10623 	PM_AdjustAngleForWallRunUp( pm->ps, &pm->cmd, qtrue );
10624 	PM_AdjustAngleForWallRun( pm->ps, &pm->cmd, qtrue );
10625 
10626 	if (pm->ps->saberMove == LS_A_JUMP_T__B_ || pm->ps->saberMove == LS_A_LUNGE ||
10627 		pm->ps->saberMove == LS_A_BACK_CR || pm->ps->saberMove == LS_A_BACK ||
10628 		pm->ps->saberMove == LS_A_BACKSTAB)
10629 	{
10630 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10631 	}
10632 
10633 #if 0
10634 	if ((pm->ps->legsAnim) == BOTH_KISSER1LOOP ||
10635 		(pm->ps->legsAnim) == BOTH_KISSEE1LOOP)
10636 	{
10637 		pm->ps->viewangles[PITCH] = 0;
10638 		PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
10639 	}
10640 #endif
10641 
10642 	PM_SetSpecialMoveValues();
10643 
10644 	// update the viewangles
10645 	PM_UpdateViewAngles( pm->ps, &pm->cmd );
10646 
10647 	AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
10648 
10649 	if ( pm->cmd.upmove < 10 && !(pm->ps->pm_flags & PMF_STUCK_TO_WALL)) {
10650 		// not holding jump
10651 		pm->ps->pm_flags &= ~PMF_JUMP_HELD;
10652 	}
10653 
10654 	// decide if backpedaling animations should be used
10655 	if ( pm->cmd.forwardmove < 0 ) {
10656 		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
10657 	} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
10658 		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
10659 	}
10660 
10661 	if ( pm->ps->pm_type >= PM_DEAD ) {
10662 		pm->cmd.forwardmove = 0;
10663 		pm->cmd.rightmove = 0;
10664 		pm->cmd.upmove = 0;
10665 	}
10666 
10667 	/*
10668 	if (pm->ps->fd.saberAnimLevel == SS_STAFF &&
10669 		(pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
10670 		pm->cmd.upmove > 0)
10671 	{ //this is how you do kick-for-condition
10672 		pm->cmd.upmove = 0;
10673 		pm->ps->pm_flags |= PMF_JUMP_HELD;
10674 	}
10675 	*/
10676 
10677 	if (pm->ps->saberLockTime >= pm->cmd.serverTime)
10678 	{
10679 		pm->cmd.upmove = 0;
10680 		pm->cmd.forwardmove = 0;//50;
10681 		pm->cmd.rightmove = 0;//*= 0.1;
10682 	}
10683 
10684 	if ( pm->ps->pm_type == PM_SPECTATOR ) {
10685 		PM_CheckDuck ();
10686 		if (!pm->noSpecMove)
10687 		{
10688 			PM_FlyMove ();
10689 		}
10690 		PM_DropTimers ();
10691 		return;
10692 	}
10693 
10694 	if ( pm->ps->pm_type == PM_NOCLIP ) {
10695 		if (pm->ps->clientNum < MAX_CLIENTS)
10696 		{
10697 			PM_NoclipMove ();
10698 			PM_DropTimers ();
10699 			return;
10700 		}
10701 	}
10702 
10703 	if (pm->ps->pm_type == PM_FREEZE) {
10704 		return;		// no movement at all
10705 	}
10706 
10707 	if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) {
10708 		return;		// no movement at all
10709 	}
10710 
10711 	// set watertype, and waterlevel
10712 	PM_SetWaterLevel();
10713 	pml.previous_waterlevel = pmove->waterlevel;
10714 
10715 	// set mins, maxs, and viewheight
10716 	PM_CheckDuck ();
10717 
10718 	if (pm->ps->pm_type == PM_JETPACK)
10719 	{
10720 		gDist = PM_GroundDistance();
10721 		savedGravity = pm->ps->gravity;
10722 
10723 		if (gDist < JETPACK_HOVER_HEIGHT+64)
10724 		{
10725 			pm->ps->gravity *= 0.1f;
10726 		}
10727 		else
10728 		{
10729 			pm->ps->gravity *= 0.25f;
10730 		}
10731 	}
10732 	else if (gPMDoSlowFall)
10733 	{
10734 		savedGravity = pm->ps->gravity;
10735 		pm->ps->gravity *= 0.5;
10736 	}
10737 
10738 	//if we're in jetpack mode then see if we should be jetting around
10739 	if (pm->ps->pm_type == PM_JETPACK)
10740 	{
10741 		if (pm->cmd.rightmove > 0)
10742 		{
10743 			PM_ContinueLegsAnim(BOTH_INAIRRIGHT1);
10744 		}
10745 		else if (pm->cmd.rightmove < 0)
10746 		{
10747             PM_ContinueLegsAnim(BOTH_INAIRLEFT1);
10748 		}
10749 		else if (pm->cmd.forwardmove > 0)
10750 		{
10751 			PM_ContinueLegsAnim(BOTH_INAIR1);
10752 		}
10753 		else if (pm->cmd.forwardmove < 0)
10754 		{
10755 			PM_ContinueLegsAnim(BOTH_INAIRBACK1);
10756 		}
10757 		else
10758 		{
10759 			PM_ContinueLegsAnim(BOTH_INAIR1);
10760 		}
10761 
10762 		if (pm->ps->weapon == WP_SABER &&
10763 			BG_SpinningSaberAnim( pm->ps->legsAnim ))
10764 		{ //make him stir around since he shouldn't have any real control when spinning
10765 			pm->ps->velocity[0] += Q_irand(-100, 100);
10766 			pm->ps->velocity[1] += Q_irand(-100, 100);
10767 		}
10768 
10769 		if (pm->cmd.upmove > 0 && pm->ps->velocity[2] < 256)
10770 		{ //cap upward velocity off at 256. Seems reasonable.
10771 			float addIn = 12.0f;
10772 
10773 /*
10774 			//Add based on our distance to the ground if we're already travelling upward
10775 			if (pm->ps->velocity[2] > 0)
10776 			{
10777 				while (gDist > 64)
10778 				{ //subtract 1 for every 64 units off the ground we get
10779 					addIn--;
10780 
10781 					gDist -= 64;
10782 
10783 					if (addIn <= 0)
10784 					{ //break out if we're not even going to add anything
10785 						break;
10786 					}
10787 				}
10788 			}
10789 */
10790 			if (pm->ps->velocity[2] > 0)
10791 			{
10792 				addIn = 12.0f - (gDist / 64.0f);
10793 			}
10794 
10795 			if (addIn > 0.0f)
10796 			{
10797 				pm->ps->velocity[2] += addIn;
10798 			}
10799 
10800 			pm->ps->eFlags |= EF_JETPACK_FLAMING; //going up
10801 		}
10802 		else
10803 		{
10804 			pm->ps->eFlags &= ~EF_JETPACK_FLAMING; //idling
10805 
10806 			if (pm->ps->velocity[2] < 256)
10807 			{
10808 				if (pm->ps->velocity[2] < -100)
10809 				{
10810 					pm->ps->velocity[2] = -100;
10811 				}
10812 				if (gDist < JETPACK_HOVER_HEIGHT)
10813 				{ //make sure we're always hovering off the ground somewhat while jetpack is active
10814 					pm->ps->velocity[2] += 2;
10815 				}
10816 			}
10817 		}
10818 	}
10819 
10820 	if (pm->ps->clientNum >= MAX_CLIENTS &&
10821 		pm_entSelf && pm_entSelf->m_pVehicle)
10822 	{ //Now update our mins/maxs to match our m_vOrientation based on our length, width & height
10823 		BG_VehicleAdjustBBoxForOrientation( pm_entSelf->m_pVehicle, pm->ps->origin, pm->mins, pm->maxs, pm->ps->clientNum, pm->tracemask, pm->trace );
10824 	}
10825 
10826 	// set groundentity
10827 	PM_GroundTrace();
10828 	if ( pm_flying == FLY_HOVER )
10829 	{//never stick to the ground
10830 		PM_HoverTrace();
10831 	}
10832 
10833 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
10834 	{//on ground
10835 		pm->ps->fd.forceJumpZStart = 0;
10836 	}
10837 
10838 	if ( pm->ps->pm_type == PM_DEAD ) {
10839 		if (pm->ps->clientNum >= MAX_CLIENTS &&
10840 			pm_entSelf &&
10841 			pm_entSelf->s.NPC_class == CLASS_VEHICLE &&
10842 			pm_entSelf->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL)
10843 		{//vehicles don't use deadmove
10844 		}
10845 		else
10846 		{
10847 			PM_DeadMove ();
10848 		}
10849 	}
10850 
10851 	PM_DropTimers();
10852 
10853 #ifdef _TESTING_VEH_PREDICTION
10854 #ifndef _GAME
10855 	{
10856 		vec3_t blah;
10857 		VectorMA(pm->ps->origin, 128.0f, pm->ps->moveDir, blah);
10858 		CG_TestLine(pm->ps->origin, blah, 1, 0x0000ff, 1);
10859 
10860 		VectorMA(pm->ps->origin, 1.0f, pm->ps->velocity, blah);
10861 		CG_TestLine(pm->ps->origin, blah, 1, 0xff0000, 1);
10862 	}
10863 #endif
10864 #endif
10865 
10866 	if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
10867 		&&pm->ps->m_iVehicleNum)
10868 	{ //a player riding a vehicle
10869 		bgEntity_t *veh = pm_entVeh;
10870 
10871 		if ( veh && veh->m_pVehicle &&
10872 			(veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) )
10873 		{//*sigh*, until we get forced weapon-switching working?
10874 			pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
10875 			pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
10876 			//pm->cmd.weapon = pm->ps->weapon;
10877 		}
10878 	}
10879 
10880 	if (!pm->ps->m_iVehicleNum &&
10881 		pm_entSelf->s.NPC_class!=CLASS_VEHICLE&&
10882 		pm_entSelf->s.NPC_class!=CLASS_RANCOR&&
10883 		pm->ps->groundEntityNum < ENTITYNUM_WORLD &&
10884 		pm->ps->groundEntityNum >= MAX_CLIENTS)
10885 	{ //I am a player client, not riding on a vehicle, and potentially standing on an NPC
10886 		bgEntity_t *pEnt = PM_BGEntForNum(pm->ps->groundEntityNum);
10887 
10888 		if (pEnt && pEnt->s.eType == ET_NPC &&
10889 			pEnt->s.NPC_class != CLASS_VEHICLE) //don't bounce on vehicles
10890 		{ //this is actually an NPC, let's try to bounce of its head to make sure we can't just stand around on top of it.
10891 			if (pm->ps->velocity[2] < 270)
10892 			{ //try forcing velocity up and also force him to jump
10893 				pm->ps->velocity[2] = 270; //seems reasonable
10894 				pm->cmd.upmove = 127;
10895 			}
10896 		}
10897 #ifdef _GAME
10898 		else if ( !pm->ps->zoomMode &&
10899 			pm_entSelf //I exist
10900 			&& pEnt->m_pVehicle )//ent has a vehicle
10901 		{
10902 			gentity_t *gEnt = (gentity_t*)pEnt;
10903 			if ( gEnt->client
10904 				&& !gEnt->client->ps.m_iVehicleNum //vehicle is empty
10905 				&& (gEnt->spawnflags&2) )//SUSPENDED
10906 			{//it's a vehicle, see if we should get in it
10907 				//if land on an empty, suspended vehicle, get in it
10908 				pEnt->m_pVehicle->m_pVehicleInfo->Board( pEnt->m_pVehicle, (bgEntity_t *)pm_entSelf );
10909 			}
10910 		}
10911 #endif
10912 	}
10913 
10914 	if (pm->ps->clientNum >= MAX_CLIENTS &&
10915 		pm_entSelf &&
10916 		pm_entSelf->s.NPC_class == CLASS_VEHICLE)
10917 	{ //we are a vehicle
10918 		bgEntity_t *veh = pm_entSelf;
10919 
10920 		assert(veh && veh->playerState && veh->m_pVehicle && veh->s.number >= MAX_CLIENTS);
10921 
10922 		if (veh->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
10923 		{ //kind of hacky, don't want to do this for flying vehicles
10924 			veh->m_pVehicle->m_vOrientation[PITCH] = pm->ps->viewangles[PITCH];
10925 		}
10926 
10927 		if (!pm->ps->m_iVehicleNum)
10928 		{ //no one is driving, just update and get out
10929 #ifdef _GAME
10930 			veh->m_pVehicle->m_pVehicleInfo->Update(veh->m_pVehicle, &pm->cmd);
10931 		    veh->m_pVehicle->m_pVehicleInfo->Animate(veh->m_pVehicle);
10932 #endif
10933 		}
10934 		else
10935 		{
10936 			bgEntity_t *self = pm_entVeh;
10937 #ifdef _GAME
10938 			int i = 0;
10939 #endif
10940 
10941 			assert(self && self->playerState && self->s.number < MAX_CLIENTS);
10942 
10943 			if (pm->ps->pm_type == PM_DEAD &&
10944 				(veh->m_pVehicle->m_ulFlags & VEH_CRASHING))
10945 			{
10946 				veh->m_pVehicle->m_ulFlags &= ~VEH_CRASHING;
10947 			}
10948 
10949 			if (self->playerState->m_iVehicleNum)
10950 			{ //only do it if they still have a vehicle (didn't get ejected this update or something)
10951 				PM_VehicleViewAngles(self->playerState, veh, &veh->m_pVehicle->m_ucmd);
10952 			}
10953 
10954 #ifdef _GAME
10955 			veh->m_pVehicle->m_pVehicleInfo->Update(veh->m_pVehicle, &veh->m_pVehicle->m_ucmd);
10956 			veh->m_pVehicle->m_pVehicleInfo->Animate(veh->m_pVehicle);
10957 
10958 			veh->m_pVehicle->m_pVehicleInfo->UpdateRider(veh->m_pVehicle, self, &veh->m_pVehicle->m_ucmd);
10959 			//update the passengers
10960 			while (i < veh->m_pVehicle->m_iNumPassengers)
10961 			{
10962 				if (veh->m_pVehicle->m_ppPassengers[i])
10963 				{
10964 					gentity_t *thePassenger = (gentity_t *)veh->m_pVehicle->m_ppPassengers[i]; //yes, this is, in fact, ass.
10965 					if (thePassenger->inuse && thePassenger->client)
10966 					{
10967 						veh->m_pVehicle->m_pVehicleInfo->UpdateRider(veh->m_pVehicle, veh->m_pVehicle->m_ppPassengers[i], &thePassenger->client->pers.cmd);
10968 					}
10969 				}
10970 				i++;
10971 			}
10972 #else
10973 			if (!veh->playerState->vehBoarding )//|| veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
10974 			{
10975 				if (veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
10976 				{ //client must explicitly call this for prediction
10977 					BG_FighterUpdate(veh->m_pVehicle, &veh->m_pVehicle->m_ucmd, pm->mins, pm->maxs, self->playerState->gravity, pm->trace);
10978 				}
10979 
10980 				if (veh->m_pVehicle->m_iBoarding == 0)
10981 				{
10982 					vec3_t vRollAng;
10983 
10984 					//make sure we are set as its pilot cgame side
10985 					veh->m_pVehicle->m_pPilot = self;
10986 
10987 					// Keep track of the old orientation.
10988 					VectorCopy( veh->m_pVehicle->m_vOrientation, veh->m_pVehicle->m_vPrevOrientation );
10989 
10990 					veh->m_pVehicle->m_pVehicleInfo->ProcessOrientCommands(veh->m_pVehicle);
10991 					PM_SetPMViewAngle(veh->playerState, veh->m_pVehicle->m_vOrientation, &veh->m_pVehicle->m_ucmd);
10992 					veh->m_pVehicle->m_pVehicleInfo->ProcessMoveCommands(veh->m_pVehicle);
10993 
10994 					vRollAng[YAW] = self->playerState->viewangles[YAW];
10995 					vRollAng[PITCH] = self->playerState->viewangles[PITCH];
10996 					vRollAng[ROLL] = veh->m_pVehicle->m_vOrientation[ROLL];
10997 					PM_SetPMViewAngle(self->playerState, vRollAng, &pm->cmd);
10998 
10999 					// Setup the move direction.
11000 					if ( veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
11001 					{
11002 						AngleVectors( veh->m_pVehicle->m_vOrientation, veh->playerState->moveDir, NULL, NULL );
11003 					}
11004 					else
11005 					{
11006 						vec3_t vVehAngles;
11007 
11008 						VectorSet(vVehAngles, 0, veh->m_pVehicle->m_vOrientation[YAW], 0);
11009 						AngleVectors( vVehAngles, veh->playerState->moveDir, NULL, NULL );
11010 					}
11011 				}
11012 			}
11013 			/*
11014 			else
11015 			{
11016 				veh->playerState->speed = 0.0f;
11017 				PM_SetPMViewAngle(self->playerState, veh->playerState->viewangles, &veh->m_pVehicle->m_ucmd);
11018 			}
11019 			*/
11020 			else if (veh->playerState)
11021 			{
11022 				veh->playerState->speed = 0.0f;
11023 				if (veh->m_pVehicle)
11024 				{
11025 					PM_SetPMViewAngle(self->playerState, veh->m_pVehicle->m_vOrientation, &pm->cmd);
11026 					PM_SetPMViewAngle(veh->playerState, veh->m_pVehicle->m_vOrientation, &pm->cmd);
11027 				}
11028 			}
11029 #endif
11030 		}
11031 		noAnimate = qtrue;
11032 	}
11033 
11034 	if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
11035 		&&pm->ps->m_iVehicleNum)
11036 	{//don't even run physics on a player if he's on a vehicle - he goes where the vehicle goes
11037 	}
11038 	else
11039 	{ //don't even run physics on a player if he's on a vehicle - he goes where the vehicle goes
11040 		if (pm->ps->pm_type == PM_FLOAT
11041 			||pm_flying == FLY_NORMAL)
11042 		{
11043 			PM_FlyMove ();
11044 		}
11045 		else if ( pm_flying == FLY_VEHICLE )
11046 		{
11047 			PM_FlyVehicleMove();
11048 		}
11049 		else
11050 		{
11051 			if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
11052 				PM_WaterJumpMove();
11053 			} else if ( pm->waterlevel > 1 ) {
11054 				// swimming
11055 				PM_WaterMove();
11056 			} else if ( pml.walking ) {
11057 				// walking on ground
11058 				PM_WalkMove();
11059 			} else {
11060 				// airborne
11061 				PM_AirMove();
11062 			}
11063 		}
11064 	}
11065 
11066 	if (!noAnimate)
11067 	{
11068 		PM_Animate();
11069 	}
11070 
11071 	// set groundentity, watertype, and waterlevel
11072 	PM_GroundTrace();
11073 	if ( pm_flying == FLY_HOVER )
11074 	{//never stick to the ground
11075 		PM_HoverTrace();
11076 	}
11077 	PM_SetWaterLevel();
11078 	if (pm->cmd.forcesel != (byte)-1 && (pm->ps->fd.forcePowersKnown & (1 << pm->cmd.forcesel)))
11079 	{
11080 		pm->ps->fd.forcePowerSelected = pm->cmd.forcesel;
11081 	}
11082 	if (pm->cmd.invensel != (byte)-1 && (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << pm->cmd.invensel)))
11083 	{
11084 		pm->ps->stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(pm->cmd.invensel, IT_HOLDABLE);
11085 	}
11086 
11087 	if (pm->ps->m_iVehicleNum
11088 		/*&&pm_entSelf->s.NPC_class!=CLASS_VEHICLE*/
11089 		&& pm->ps->clientNum < MAX_CLIENTS)
11090 	{//a client riding a vehicle
11091 		if ( (pm->ps->eFlags&EF_NODRAW) )
11092 		{//inside the vehicle, do nothing
11093 		}
11094 		else if (!PM_WeaponOkOnVehicle(pm->cmd.weapon) || !PM_WeaponOkOnVehicle(pm->ps->weapon))
11095 		{ //this weapon is not legal for the vehicle, force to our current one
11096             if (!PM_WeaponOkOnVehicle(pm->ps->weapon))
11097 			{ //uh-oh!
11098 				int weap = PM_GetOkWeaponForVehicle();
11099 
11100 				if (weap != -1)
11101 				{
11102 					pm->cmd.weapon = weap;
11103 					pm->ps->weapon = weap;
11104 				}
11105 			}
11106 			else
11107 			{
11108 				pm->cmd.weapon = pm->ps->weapon;
11109 			}
11110 		}
11111 	}
11112 
11113 	if (!pm->ps->m_iVehicleNum //not a vehicle and not riding one
11114 		|| pm_entSelf->s.NPC_class==CLASS_VEHICLE //you are a vehicle NPC
11115 		|| (!(pm->ps->eFlags&EF_NODRAW)&&PM_WeaponOkOnVehicle(pm->cmd.weapon)) )//you're not inside the vehicle and the weapon you're holding can be used when riding this vehicle
11116 	{ //only run weapons if a valid weapon is selected
11117 		// weapons
11118 		PM_Weapon();
11119 	}
11120 
11121 	PM_Use();
11122 
11123 	if (!pm->ps->m_iVehicleNum &&
11124 		(pm->ps->clientNum < MAX_CLIENTS ||
11125 		!pm_entSelf ||
11126 		pm_entSelf->s.NPC_class != CLASS_VEHICLE))
11127 	{ //don't do this if we're on a vehicle, or we are one
11128 		// footstep events / legs animations
11129 		PM_Footsteps();
11130 	}
11131 
11132 	// entering / leaving water splashes
11133 	PM_WaterEvents();
11134 
11135 	// snap velocity to integer coordinates to save network bandwidth
11136 	if ( !pm->pmove_float )
11137 		trap->SnapVector( pm->ps->velocity );
11138 
11139  	if (pm->ps->pm_type == PM_JETPACK || gPMDoSlowFall )
11140 	{
11141 		pm->ps->gravity = savedGravity;
11142 	}
11143 
11144 	if (//pm->ps->m_iVehicleNum &&
11145 		pm->ps->clientNum >= MAX_CLIENTS &&
11146 		pm_entSelf &&
11147 		pm_entSelf->s.NPC_class == CLASS_VEHICLE)
11148 	{ //a vehicle with passengers
11149 		bgEntity_t *veh;
11150 
11151 		veh = pm_entSelf;
11152 
11153 		assert(veh->m_pVehicle);
11154 
11155 		//this could be kind of "inefficient" because it's called after every passenger pmove too.
11156 		//Maybe instead of AttachRiders we should have each rider call attach for himself?
11157 		if (veh->m_pVehicle && veh->ghoul2)
11158 		{
11159 			veh->m_pVehicle->m_pVehicleInfo->AttachRiders( veh->m_pVehicle );
11160 		}
11161 	}
11162 
11163 	if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
11164 		&& pm->ps->m_iVehicleNum)
11165 	{ //riding a vehicle, see if we should do some anim overrides
11166 		PM_VehicleWeaponAnimate();
11167 	}
11168 }
11169 
11170 
11171 /*
11172 ================
11173 Pmove
11174 
11175 Can be called by either the server or the client
11176 ================
11177 */
Pmove(pmove_t * pmove)11178 void Pmove (pmove_t *pmove) {
11179 	int			finalTime;
11180 
11181 	finalTime = pmove->cmd.serverTime;
11182 
11183 	if ( finalTime < pmove->ps->commandTime ) {
11184 		return;	// should not happen
11185 	}
11186 
11187 	if ( finalTime > pmove->ps->commandTime + 1000 ) {
11188 		pmove->ps->commandTime = finalTime - 1000;
11189 	}
11190 
11191 	if (pmove->ps->fallingToDeath)
11192 	{
11193 		pmove->cmd.forwardmove = 0;
11194 		pmove->cmd.rightmove = 0;
11195 		pmove->cmd.upmove = 0;
11196 		pmove->cmd.buttons = 0;
11197 	}
11198 
11199 	pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
11200 
11201 	// chop the move up if it is too long, to prevent framerate
11202 	// dependent behavior
11203 	while ( pmove->ps->commandTime != finalTime ) {
11204 		int		msec;
11205 
11206 		msec = finalTime - pmove->ps->commandTime;
11207 
11208 		if ( pmove->pmove_fixed ) {
11209 			if ( msec > pmove->pmove_msec ) {
11210 				msec = pmove->pmove_msec;
11211 			}
11212 		}
11213 		else {
11214 			if ( msec > 66 ) {
11215 				msec = 66;
11216 			}
11217 		}
11218 		pmove->cmd.serverTime = pmove->ps->commandTime + msec;
11219 
11220 		PmoveSingle( pmove );
11221 
11222 		if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
11223 			pmove->cmd.upmove = 20;
11224 		}
11225 	}
11226 }
11227 
11228