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 #include "common_headers.h"
25 
26 #include "../rd-common/tr_public.h"
27 
28 
29 // bg_pmove.c -- both games player movement code
30 // takes a playerstate and a usercmd as input and returns a modifed playerstate
31 
32 // define GAME_INCLUDE so that g_public.h does not define the
33 // short, server-visible gclient_t and gentity_t structures,
34 // because we define the full size ones in this file
35 
36 #define	GAME_INCLUDE
37 #include "../qcommon/q_shared.h"
38 #include "g_shared.h"
39 #include "bg_local.h"
40 #include "g_local.h"
41 #include "g_functions.h"
42 #include "anims.h"
43 #include "../cgame/cg_local.h"	// yeah I know this is naughty, but we're shipping soon...
44 
45 #include "wp_saber.h"
46 #include "g_vehicles.h"
47 #include <float.h>
48 
49 extern qboolean G_DoDismemberment( gentity_t *self, vec3_t point, int mod, int damage, int hitLoc, qboolean force = qfalse );
50 extern qboolean G_EntIsUnlockedDoor( int entityNum );
51 extern qboolean G_EntIsDoor( int entityNum );
52 extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f );
53 extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
54 extern	qboolean	Q3_TaskIDPending( gentity_t *ent, taskID_t taskType );
55 extern qboolean WP_SaberLose( gentity_t *self, vec3_t throwDir );
56 extern int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType );
57 extern qboolean PM_HasAnimation( gentity_t *ent, int animation );
58 extern saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove );
59 extern saberMoveName_t PM_AttackMoveForQuad( int quad );
60 extern qboolean PM_SaberInTransition( int move );
61 extern qboolean PM_SaberInTransitionAny( int move );
62 extern qboolean PM_SaberInBounce( int move );
63 extern qboolean PM_SaberInSpecialAttack( int anim );
64 extern qboolean PM_SaberInAttack( int move );
65 extern qboolean PM_InAnimForSaberMove( int anim, int saberMove );
66 extern saberMoveName_t PM_SaberBounceForAttack( int move );
67 extern saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove );
68 extern saberMoveName_t PM_BrokenParryForParry( int move );
69 extern saberMoveName_t PM_KnockawayForParry( int move );
70 extern qboolean PM_SaberInParry( int move );
71 extern qboolean PM_SaberInKnockaway( int move );
72 extern qboolean PM_SaberInBrokenParry( int move );
73 extern qboolean PM_SaberInReflect( int move );
74 extern qboolean PM_SaberInIdle( int move );
75 extern qboolean PM_SaberInStart( int move );
76 extern qboolean PM_SaberInReturn( int move );
77 extern qboolean PM_SaberKataDone( int curmove, int newmove );
78 extern qboolean PM_SaberInSpecial( int move );
79 extern qboolean PM_InDeathAnim ( void );
80 extern qboolean PM_StandingAnim( int anim );
81 extern qboolean PM_KickMove( int move );
82 extern qboolean PM_KickingAnim( int anim );
83 extern qboolean PM_InAirKickingAnim( int anim );
84 extern qboolean PM_SuperBreakLoseAnim( int anim );
85 extern qboolean PM_SuperBreakWinAnim( int anim );
86 extern qboolean PM_InCartwheel( int anim );
87 extern qboolean PM_InButterfly( int anim );
88 extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
89 extern saberMoveName_t PM_SaberFlipOverAttackMove( void );
90 extern qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy );
91 extern saberMoveName_t PM_SaberJumpForwardAttackMove( void );
92 extern qboolean PM_CheckJumpForwardAttackMove( void );
93 extern saberMoveName_t PM_SaberBackflipAttackMove( void );
94 extern qboolean PM_CheckBackflipAttackMove( void );
95 extern saberMoveName_t PM_SaberDualJumpAttackMove( void );
96 extern qboolean PM_CheckDualJumpAttackMove( void );
97 extern saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge );
98 extern qboolean PM_CheckLungeAttackMove( void );
99 extern qboolean PM_InSecondaryStyle( void );
100 extern qboolean PM_KnockDownAnimExtended( int anim );
101 extern void G_StartMatrixEffect( gentity_t *ent, int meFlags = 0, int length = 1000, float timeScale = 0.0f, int spinTime = 0 );
102 extern void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower );
103 extern qboolean WP_ForcePowerAvailable( gentity_t *self, forcePowers_t forcePower, int overrideAmt );
104 extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt );
105 extern float G_ForceWallJumpStrength( void );
106 extern qboolean G_CheckRollSafety( gentity_t *self, int anim, float testDist );
107 extern saberMoveName_t PM_CheckDualSpinProtect( void );
108 extern saberMoveName_t PM_CheckPullAttack( void );
109 extern qboolean JET_Flying( gentity_t *self );
110 extern void JET_FlyStart( gentity_t *self );
111 extern void JET_FlyStop( gentity_t *self );
112 extern qboolean PM_LockedAnim( int anim );
113 extern qboolean G_TryingKataAttack( gentity_t *self, usercmd_t *cmd );
114 extern qboolean G_TryingCartwheel( gentity_t *self, usercmd_t *cmd );
115 extern qboolean G_TryingSpecial( gentity_t *self, usercmd_t *cmd );
116 extern qboolean G_TryingJumpAttack( gentity_t *self, usercmd_t *cmd );
117 extern qboolean G_TryingJumpForwardAttack( gentity_t *self, usercmd_t *cmd );
118 extern void WP_SaberSwingSound( gentity_t *ent, int saberNum, swingType_t swingType );
119 extern qboolean WP_UseFirstValidSaberStyle( gentity_t *ent, int *saberAnimLevel );
120 extern qboolean WP_SaberStyleValidForSaber( gentity_t *ent, int saberAnimLevel );
121 
122 qboolean PM_InKnockDown( playerState_t *ps );
123 qboolean PM_InKnockDownOnGround( playerState_t *ps );
124 qboolean PM_InGetUp( playerState_t *ps );
125 qboolean PM_InRoll( playerState_t *ps );
126 qboolean PM_SpinningSaberAnim( int anim );
127 qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight );
128 qboolean PM_SpinningAnim( int anim );
129 qboolean PM_FlippingAnim( int anim );
130 qboolean PM_PainAnim( int anim );
131 qboolean PM_RollingAnim( int anim );
132 qboolean PM_SwimmingAnim( int anim );
133 qboolean PM_InReboundJump( int anim );
134 qboolean PM_ForceJumpingAnim( int anim );
135 void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd );
136 
137 extern int parryDebounce[];
138 extern qboolean cg_usingInFrontOf;
139 extern qboolean		player_locked;
140 extern qboolean		MatrixMode;
141 qboolean		waterForceJump;
142 extern cvar_t	*g_timescale;
143 extern cvar_t	*g_speederControlScheme;
144 extern cvar_t	*d_slowmodeath;
145 extern cvar_t	*g_debugMelee;
146 extern cvar_t	*g_saberNewControlScheme;
147 extern cvar_t	*g_stepSlideFix;
148 extern cvar_t	*g_saberAutoBlocking;
149 
150 static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype );
151 
152 #define		FLY_NONE	0
153 #define		FLY_NORMAL	1
154 #define		FLY_VEHICLE	2
155 #define		FLY_HOVER	3
156 int			Flying = FLY_NONE;
157 
158 pmove_t		*pm;
159 pml_t		pml;
160 
161 // movement parameters
162 const float	pm_stopspeed = 100.0f;
163 const float	pm_duckScale = 0.50f;
164 const float	pm_swimScale = 0.50f;
165 float	pm_ladderScale = 0.7f;
166 
167 const float	pm_vehicleaccelerate = 36.0f;
168 const float	pm_accelerate = 12.0f;
169 const float	pm_airaccelerate = 4.0f;
170 const float	pm_wateraccelerate = 4.0f;
171 const float	pm_flyaccelerate = 8.0f;
172 
173 const float	pm_friction = 6.0f;
174 const float	pm_waterfriction = 1.0f;
175 const float	pm_flightfriction = 3.0f;
176 
177 //const float	pm_frictionModifier	= 3.0f;	//Used for "careful" mode (when pressing use)
178 const float pm_airDecelRate = 1.35f;	//Used for air decelleration away from current movement velocity
179 
180 int	c_pmove = 0;
181 
182 extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
183 extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
184 extern void PM_TorsoAnimation( void );
185 extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame );
186 extern int PM_AnimLength( int index, animNumber_t anim );
187 extern qboolean PM_InDeathAnim ( void );
188 extern qboolean PM_InOnGroundAnim ( playerState_t *ps );
189 extern	weaponInfo_t	cg_weapons[MAX_WEAPONS];
190 extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim );
191 
192 extern void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t *trace );
193 
194 #define	PHASER_RECHARGE_TIME	100
195 extern saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS];
196 
197 extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
PM_RidingVehicle(void)198 Vehicle_t *PM_RidingVehicle( void )
199 {
200 	return (G_IsRidingVehicle( pm->gent ));
201 }
202 
203 extern qboolean G_ControlledByPlayer( gentity_t *self );
PM_ControlledByPlayer(void)204 qboolean PM_ControlledByPlayer( void )
205 {
206 	return G_ControlledByPlayer( pm->gent );
207 }
208 
BG_UnrestrainedPitchRoll(playerState_t * ps,Vehicle_t * pVeh)209 qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh )
210 {
211 /*
212 	if ( 0 && ps->clientNum < MAX_CLIENTS //real client
213 		&& ps->m_iVehicleNum//in a vehicle
214 		&& pVeh //valid vehicle data pointer
215 		&& pVeh->m_pVehicleInfo//valid vehicle info
216 		&& pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
217 		//FIXME: specify per vehicle instead of assuming true for all fighters
218 		//FIXME: map/server setting?
219 	{//can roll and pitch without limitation!
220 		return qtrue;
221 	}*/
222 	return qfalse;
223 }
224 
225 
226 /*
227 ===============
228 PM_AddEvent
229 
230 ===============
231 */
PM_AddEvent(int newEvent)232 void PM_AddEvent( int newEvent )
233 {
234 	AddEventToPlayerstate( newEvent, 0, pm->ps );
235 }
236 
PM_PredictJumpSafe(vec3_t jumpHorizDir,float jumpHorizSpeed,float jumpVertSpeed,int predictTimeLength)237 qboolean PM_PredictJumpSafe( vec3_t jumpHorizDir, float jumpHorizSpeed, float jumpVertSpeed, int predictTimeLength )
238 {
239 	return qtrue;
240 }
241 
242 
PM_GrabWallForJump(int anim)243 void PM_GrabWallForJump( int anim )
244 {//NOTE!!! assumes an appropriate anim is being passed in!!!
245 	PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
246 	PM_AddEvent( EV_JUMP );//make sound for grab
247 	pm->ps->pm_flags |= PMF_STUCK_TO_WALL;
248 }
249 
PM_CheckGrabWall(trace_t * trace)250 qboolean PM_CheckGrabWall( trace_t *trace )
251 {
252 	if ( !pm->gent || !pm->gent->client )
253 	{
254 		return qfalse;
255 	}
256 	if ( pm->gent->health <= 0 )
257 	{//must be alive
258 		return qfalse;
259 	}
260 	if ( pm->gent->client->ps.groundEntityNum != ENTITYNUM_NONE )
261 	{//must be in air
262 		return qfalse;
263 	}
264 	if ( trace->plane.normal[2] != 0 )
265 	{//must be a flat wall
266 		return qfalse;
267 	}
268 	if ( !trace->plane.normal[0] && !trace->plane.normal[1] )
269 	{//invalid normal
270 		return qfalse;
271 	}
272 	if ( (trace->contents&(CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP)) )
273 	{//can't jump off of clip brushes
274 		return qfalse;
275 	}
276 	if ( pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1 )
277 	{//must have at least FJ 1
278 		return qfalse;
279 	}
280 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
281 		&& pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_3 )
282 	{//player must have force jump 3
283 		return qfalse;
284 	}
285 	if ( (pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB) )
286 	{
287 		return qfalse;
288 	}
289 	if ( pm->ps->dualSabers
290 		&& (pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB) )
291 	{
292 		return qfalse;
293 	}
294 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
295 	{//player
296 		//only if we were in a longjump
297 		if ( pm->ps->legsAnim != BOTH_FORCELONGLEAP_START
298 			&& pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK )
299 		{
300 			return qfalse;
301 		}
302 		//hit a flat wall during our long jump, see if we should grab it
303 		vec3_t moveDir;
304 		VectorCopy( pm->ps->velocity, moveDir );
305 		VectorNormalize( moveDir );
306 		if ( DotProduct( moveDir, trace->plane.normal ) > -0.65f )
307 		{//not enough of a direct impact, just slide off
308 			return qfalse;
309 		}
310 		if ( fabs(trace->plane.normal[2]) > MAX_WALL_GRAB_SLOPE )
311 		{
312 			return qfalse;
313 		}
314 		//grab it!
315 		//FIXME: stop Matrix effect!
316 		VectorClear( pm->ps->velocity );
317 		//FIXME: stop slidemove!
318 		//NOTE: we know it's forward, so...
319         PM_GrabWallForJump( BOTH_FORCEWALLREBOUND_FORWARD );
320 		return qtrue;
321 	}
322 	else
323 	{//NPCs
324 		if ( PM_InReboundJump( pm->ps->legsAnim ) )
325 		{//already in a rebound!
326 			return qfalse;
327 		}
328 		if ( (pm->ps->eFlags&EF_FORCE_GRIPPED) )
329 		{//being gripped!
330 			return qfalse;
331 		}
332 		/*
333 		if ( pm->gent->painDebounceTime > level.time )
334 		{//can't move!
335 			return qfalse;
336 		}
337 		if ( (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) )
338 		{//being thrown back!
339 			return qfalse;
340 		}
341 		*/
342 		if ( pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
343 		{//faling to our death!
344 			return qfalse;
345 		}
346 		//FIXME: random chance, based on skill/rank?
347 		if ( pm->ps->legsAnim != BOTH_FORCELONGLEAP_START
348 			&& pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK )
349 		{//not in a long-jump
350 			if ( !pm->gent->enemy )
351 			{//no enemy
352 				return qfalse;
353 			}
354 			else
355 			{//see if the enemy is in the direction of the wall or above us
356 				//if ( pm->gent->enemy->currentOrigin[2] < (pm->ps->origin[2]-128) )
357 				{//enemy is way below us
358 					vec3_t enemyDir;
359 					VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir );
360 					enemyDir[2] = 0;
361 					VectorNormalize( enemyDir );
362 					if ( DotProduct( enemyDir, trace->plane.normal ) < 0.65f )
363 					{//jumping off this wall would not launch me in the general direction of my enemy
364 						return qfalse;
365 					}
366 				}
367 			}
368 		}
369 		//FIXME: check for ground close beneath us?
370 		//FIXME: check for obstructions in the dir we're going to jump
371 		//		- including "do not enter" brushes!
372 		//hit a flat wall during our long jump, see if we should grab it
373 		vec3_t moveDir;
374 		VectorCopy( pm->ps->velocity, moveDir );
375 		VectorNormalize( moveDir );
376 		if ( DotProduct( moveDir, trace->plane.normal ) > -0.65f )
377 		{//not enough of a direct impact, just slide off
378 			return qfalse;
379 		}
380 
381 		//Okay, now see if jumping off this thing would send us into a do not enter brush
382 		if ( !PM_PredictJumpSafe( trace->plane.normal, JUMP_OFF_WALL_SPEED, G_ForceWallJumpStrength(), 1500 ) )
383 		{//we would hit a do not enter brush, so don't grab the wall
384 			return qfalse;
385 		}
386 
387 		//grab it!
388 		//Pick the proper anim
389 		int anim = BOTH_FORCEWALLREBOUND_FORWARD;
390 		vec3_t	facingAngles, wallDir, fwdDir, rtDir;
391 		VectorSubtract( trace->endpos, pm->gent->lastOrigin, wallDir );
392 		wallDir[2] = 0;
393 		VectorNormalize( wallDir );
394 		VectorSet( facingAngles, 0, pm->ps->viewangles[YAW], 0 );
395 		AngleVectors( facingAngles, fwdDir, rtDir, NULL );
396 		float fDot = DotProduct( fwdDir, wallDir );
397 		if ( fabs( fDot ) >= 0.5f )
398 		{//hit a wall in front/behind
399 			if ( fDot > 0.0f )
400 			{//in front
401 				anim = BOTH_FORCEWALLREBOUND_FORWARD;
402 			}
403 			else
404 			{//behind
405 				anim = BOTH_FORCEWALLREBOUND_BACK;
406 			}
407 		}
408 		else if ( DotProduct( rtDir, wallDir ) > 0 )
409 		{//hit a wall on the right
410 			anim = BOTH_FORCEWALLREBOUND_RIGHT;
411 		}
412 		else
413 		{//hit a wall on the left
414 			anim = BOTH_FORCEWALLREBOUND_LEFT;
415 		}
416 		VectorClear( pm->ps->velocity );
417 		//FIXME: stop slidemove!
418         PM_GrabWallForJump( anim );
419 		return qtrue;
420 	}
421 	//return qfalse;
422 }
423 /*
424 ===============
425 qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf )
426 
427 ===============
428 */
PM_ClientImpact(trace_t * trace,qboolean damageSelf)429 qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf )
430 {
431 	gentity_t	*traceEnt;
432 	int			otherEntityNum = trace->entityNum;
433 
434 	if ( !pm->gent )
435 	{
436 		return qfalse;
437 	}
438 
439 	traceEnt = &g_entities[otherEntityNum];
440 
441 	if ( otherEntityNum == ENTITYNUM_WORLD
442 		|| (traceEnt->bmodel && traceEnt->s.pos.trType == TR_STATIONARY ) )
443 	{//hit world or a non-moving brush
444 		if ( PM_CheckGrabWall( trace ) )
445 		{//stopped on the wall
446 			return qtrue;
447 		}
448 	}
449 
450 	if( (VectorLength( pm->ps->velocity )*(pm->gent->mass/10)) >= 100 && (pm->gent->client->NPC_class == CLASS_VEHICLE || pm->ps->lastOnGround+100<level.time) )//was 300 ||(other->material>=MAT_GLASS&&pm->gent->lastImpact+100<=level.time))
451 	{
452 		DoImpact( pm->gent, &g_entities[otherEntityNum], damageSelf, trace );
453 	}
454 
455 	if ( otherEntityNum >= ENTITYNUM_WORLD )
456 	{
457 		return qfalse;
458 	}
459 
460 	if ( !traceEnt || !(traceEnt->contents&pm->tracemask) )
461 	{//it's dead or not in my way anymore
462 		return qtrue;
463 	}
464 
465 	return qfalse;
466 }
467 /*
468 ===============
469 PM_AddTouchEnt
470 ===============
471 */
PM_AddTouchEnt(int entityNum)472 void PM_AddTouchEnt( int entityNum ) {
473 	int		i;
474 
475 	if ( entityNum == ENTITYNUM_WORLD ) {
476 		return;
477 	}
478 	if ( pm->numtouch == MAXTOUCH ) {
479 		return;
480 	}
481 
482 	// see if it is already added
483 	for ( i = 0 ; i < pm->numtouch ; i++ ) {
484 		if ( pm->touchents[ i ] == entityNum ) {
485 			return;
486 		}
487 	}
488 
489 	// add it
490 	pm->touchents[pm->numtouch] = entityNum;
491 	pm->numtouch++;
492 }
493 
494 
495 
496 
497 /*
498 ==================
499 PM_ClipVelocity
500 
501 Slide off of the impacting surface
502 
503   This will pull you down onto slopes if heading away from
504   them and push you up them as you go up them.
505   Also stops you when you hit walls.
506 
507 ==================
508 */
PM_ClipVelocity(vec3_t in,vec3_t normal,vec3_t out,float overbounce)509 void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
510 	float	backoff;
511 	float	change;
512 	float	oldInZ;
513 	int		i;
514 
515 	if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
516 	{//no sliding!
517 		VectorCopy( in, out );
518 		return;
519 	}
520 	oldInZ = in[2];
521 
522 	backoff = DotProduct (in, normal);
523 
524 	if ( backoff < 0 ) {
525 		backoff *= overbounce;
526 	} else {
527 		backoff /= overbounce;
528 	}
529 
530 	for ( i=0 ; i<3 ; i++ )
531 	{
532 		change = normal[i]*backoff;
533 		/*
534 		if ( i == 2 && Flying == FLY_HOVER && change > 0 )
535 		{//don't pull a hovercraft down
536 			change = 0;
537 		}
538 		else
539 		*/
540 		{
541 			out[i] = in[i] - change;
542 		}
543 	}
544 	if ( g_stepSlideFix->integer )
545 	{
546 		if ( pm->ps->clientNum < MAX_CLIENTS//normal player
547 			&& normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope
548 		{
549 			if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )//on the ground
550 			{//if walking on the ground, don't slide up slopes that are too steep to walk on
551 				out[2] = oldInZ;
552 			}
553 		}
554 	}
555 }
556 
557 
558 /*
559 ==================
560 PM_Friction
561 
562 Handles both ground friction and water friction
563 ==================
564 */
PM_Friction(void)565 static void PM_Friction( void ) {
566 	vec3_t	vec;
567 	float	*vel;
568 	float	speed, newspeed, control;
569 	float	drop, friction = pm->ps->friction;
570 
571 	vel = pm->ps->velocity;
572 
573 	VectorCopy( vel, vec );
574 	if ( pml.walking ) {
575 		vec[2] = 0;	// ignore slope movement
576 	}
577 
578 	speed = VectorLength(vec);
579 	if (speed < 1) {
580 		vel[0] = 0;
581 		vel[1] = 0;		// allow sinking underwater
582 		// FIXME: still have z friction underwater?
583 		return;
584 	}
585 
586 	drop = 0;
587 
588 	// apply ground friction, even if on ladder
589 	if ( pm->gent
590 		&& pm->gent->client
591 		&& pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle
592 		&& pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
593 	{
594 		friction = pm->gent->m_pVehicle->m_pVehicleInfo->friction;
595 
596 		if ( pm->gent->m_pVehicle && pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
597 		{//in a hovering vehicle, have air control
598 			if ( pm->gent->m_pVehicle->m_ulFlags & VEH_FLYING )
599 			{
600 				friction = 0.10f;
601 			}
602 		}
603 
604 		if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) )
605 		{
606 			control = speed < pm_stopspeed ? pm_stopspeed : speed;
607 			drop += control*friction*pml.frametime;
608 			/*
609 			if ( Flying == FLY_HOVER )
610 			{
611 				if ( pm->cmd.rightmove )
612 				{//if turning, increase friction
613 					control *= 2.0f;
614 				}
615 				if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
616 				{//on the ground
617 					drop += control*friction*pml.frametime;
618 				}
619 				else if ( pml.groundPlane )
620 				{//on a slope
621 					drop += control*friction*2.0f*pml.frametime;
622 				}
623 				else
624 				{//in air
625 					drop += control*2.0f*friction*pml.frametime;
626 				}
627 			}
628 			*/
629 		}
630 	}
631 	else if ( Flying != FLY_NORMAL )
632 	{
633 		if ( (pm->watertype & CONTENTS_LADDER) || pm->waterlevel <= 1 )
634 		{
635 			if ( (pm->watertype & CONTENTS_LADDER) || (pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK)) )
636 			{
637 				// if getting knocked back, no friction
638 				if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) )
639 				{
640 					if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
641 						|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
642 						|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND )
643 					{//super forward jump
644 						if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
645 						{//not in air
646 							if ( pm->cmd.forwardmove < 0 )
647 							{//trying to hold back some
648 								friction *= 0.5f;//0.25f;
649 							}
650 							else
651 							{//free slide
652 								friction *= 0.2f;//0.1f;
653 							}
654 							pm->cmd.forwardmove = pm->cmd.rightmove = 0;
655 							if ( pml.groundPlane && pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND )
656 							{
657 								//slide effect
658 								G_PlayEffect( "env/slide_dust", pml.groundTrace.endpos, pml.groundTrace.plane.normal );
659 								//FIXME: slide sound
660 							}
661 						}
662 					}
663 					/*
664 					else if ( pm->cmd.buttons & BUTTON_USE )
665 					{//If the use key is pressed. slow the player more quickly
666 						if ( pm->gent->client->NPC_class != CLASS_VEHICLE )	// if not in a vehicle...
667 						{//in a vehicle, use key makes you turbo-boost
668 							friction *= pm_frictionModifier;
669 						}
670 					}
671 					*/
672 
673 					control = speed < pm_stopspeed ? pm_stopspeed : speed;
674 					drop += control*friction*pml.frametime;
675 				}
676 			}
677 		}
678 	}
679 	else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
680 		&& pm->gent
681 		&& pm->gent->client
682 		&& (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) && pm->gent->client->moveType == MT_FLYSWIM )
683 	{//player as Boba
684 		drop += speed*pm_waterfriction*pml.frametime;
685 	}
686 
687 	if ( Flying == FLY_VEHICLE )
688 	{
689 		if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) )
690 		{
691 			control = speed < pm_stopspeed ? pm_stopspeed : speed;
692 			drop += control*friction*pml.frametime;
693 		}
694 	}
695 
696 	// apply water friction even if just wading
697 	if ( !waterForceJump )
698 	{
699 		if ( pm->waterlevel && !(pm->watertype & CONTENTS_LADDER))
700 		{
701 			drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
702 		}
703 	}
704 
705 	// apply flying friction
706 	if ( pm->ps->pm_type == PM_SPECTATOR )
707 	{
708 		drop += speed*pm_flightfriction*pml.frametime;
709 	}
710 
711 	// scale the velocity
712 	newspeed = speed - drop;
713 	if (newspeed < 0)
714 		newspeed = 0;
715 
716 	newspeed /= speed;
717 
718 	vel[0] = vel[0] * newspeed;
719 	vel[1] = vel[1] * newspeed;
720 	vel[2] = vel[2] * newspeed;
721 }
722 
723 
724 /*
725 ==============
726 PM_Accelerate
727 
728 Handles user intended acceleration
729 ==============
730 */
731 
PM_Accelerate(vec3_t wishdir,float wishspeed,float accel)732 static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
733 {
734 	int			i;
735 	float		addspeed, accelspeed, currentspeed;
736 
737 	currentspeed = DotProduct (pm->ps->velocity, wishdir);
738 
739 	addspeed = wishspeed - currentspeed;
740 
741 	if (addspeed <= 0) {
742 		return;
743 	}
744 	accelspeed = ( accel * pml.frametime ) * wishspeed;
745 
746 	if (accelspeed > addspeed) {
747 		accelspeed = addspeed;
748 	}
749 	for (i=0 ; i<3 ; i++) {
750 		pm->ps->velocity[i] += accelspeed * wishdir[i];
751 	}
752 }
753 
754 /*
755 ============
756 PM_CmdScale
757 
758 Returns the scale factor to apply to cmd movements
759 This allows the clients to use axial -127 to 127 values for all directions
760 without getting a sqrt(2) distortion in speed.
761 ============
762 */
PM_CmdScale(usercmd_t * cmd)763 static float PM_CmdScale( usercmd_t *cmd )
764 {
765 	int		max;
766 	float	total;
767 	float	scale;
768 
769 	max = abs( cmd->forwardmove );
770 
771 	if ( abs( cmd->rightmove ) > max ) {
772 		max = abs( cmd->rightmove );
773 	}
774 	if ( abs( cmd->upmove ) > max ) {
775 		max = abs( cmd->upmove );
776 	}
777 	if ( !max ) {
778 		return 0;
779 	}
780 	total = sqrt(	(float)(( cmd->forwardmove * cmd->forwardmove )
781 				  + ( cmd->rightmove * cmd->rightmove )
782 				  + ( cmd->upmove * cmd->upmove )) );
783 
784 	scale = (float) pm->ps->speed * max / ( 127.0f * total );
785 
786 	return scale;
787 }
788 
789 
790 /*
791 ================
792 PM_SetMovementDir
793 
794 Determine the rotation of the legs reletive
795 to the facing dir
796 ================
797 */
PM_SetMovementDir(void)798 static void PM_SetMovementDir( void ) {
799 	if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
800 		if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
801 			pm->ps->movementDir = 0;
802 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
803 			pm->ps->movementDir = 1;
804 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
805 			pm->ps->movementDir = 2;
806 		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
807 			pm->ps->movementDir = 3;
808 		} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
809 			pm->ps->movementDir = 4;
810 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
811 			pm->ps->movementDir = 5;
812 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
813 			pm->ps->movementDir = 6;
814 		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
815 			pm->ps->movementDir = 7;
816 		}
817 	} else {
818 		// if they aren't actively going directly sideways,
819 		// change the animation to the diagonal so they
820 		// don't stop too crooked
821 		if ( pm->ps->movementDir == 2 ) {
822 			pm->ps->movementDir = 1;
823 		} else if ( pm->ps->movementDir == 6 ) {
824 			pm->ps->movementDir = 7;
825 		}
826 	}
827 }
828 
829 
830 /*
831 =============
832 PM_CheckJump
833 =============
834 */
835 #define METROID_JUMP 1
PM_InReboundJump(int anim)836 qboolean PM_InReboundJump( int anim )
837 {
838 	switch ( anim )
839 	{
840 	case BOTH_FORCEWALLREBOUND_FORWARD:
841 	case BOTH_FORCEWALLREBOUND_LEFT:
842 	case BOTH_FORCEWALLREBOUND_BACK:
843 	case BOTH_FORCEWALLREBOUND_RIGHT:
844 		return qtrue;
845 		break;
846 	}
847 	return qfalse;
848 }
849 
PM_InReboundHold(int anim)850 qboolean PM_InReboundHold( int anim )
851 {
852 	switch ( anim )
853 	{
854 	case BOTH_FORCEWALLHOLD_FORWARD:
855 	case BOTH_FORCEWALLHOLD_LEFT:
856 	case BOTH_FORCEWALLHOLD_BACK:
857 	case BOTH_FORCEWALLHOLD_RIGHT:
858 		return qtrue;
859 		break;
860 	}
861 	return qfalse;
862 }
863 
PM_InReboundRelease(int anim)864 qboolean PM_InReboundRelease( int anim )
865 {
866 	switch ( anim )
867 	{
868 	case BOTH_FORCEWALLRELEASE_FORWARD:
869 	case BOTH_FORCEWALLRELEASE_LEFT:
870 	case BOTH_FORCEWALLRELEASE_BACK:
871 	case BOTH_FORCEWALLRELEASE_RIGHT:
872 		return qtrue;
873 		break;
874 	}
875 	return qfalse;
876 }
877 
PM_InBackFlip(int anim)878 qboolean PM_InBackFlip( int anim )
879 {
880 	switch ( anim )
881 	{
882 	case BOTH_FLIP_BACK1:
883 	case BOTH_FLIP_BACK2:
884 	case BOTH_FLIP_BACK3:
885 	case BOTH_ALORA_FLIP_B:
886 		return qtrue;
887 		break;
888 	}
889 	return qfalse;
890 }
891 
PM_InSpecialJump(int anim)892 qboolean PM_InSpecialJump( int anim )
893 {
894 	switch ( anim )
895 	{
896 	case BOTH_WALL_RUN_RIGHT:
897 	case BOTH_WALL_RUN_RIGHT_STOP:
898 	case BOTH_WALL_RUN_RIGHT_FLIP:
899 	case BOTH_WALL_RUN_LEFT:
900 	case BOTH_WALL_RUN_LEFT_STOP:
901 	case BOTH_WALL_RUN_LEFT_FLIP:
902 	case BOTH_WALL_FLIP_RIGHT:
903 	case BOTH_WALL_FLIP_LEFT:
904 	case BOTH_WALL_FLIP_BACK1:
905 	case BOTH_BUTTERFLY_LEFT:
906 	case BOTH_BUTTERFLY_RIGHT:
907 	case BOTH_BUTTERFLY_FL1:
908 	case BOTH_BUTTERFLY_FR1:
909 	case BOTH_FJSS_TR_BL:
910 	case BOTH_FJSS_TL_BR:
911 	case BOTH_FORCELEAP2_T__B_:
912 	case BOTH_JUMPFLIPSLASHDOWN1://#
913 	case BOTH_JUMPFLIPSTABDOWN://#
914 	case BOTH_JUMPATTACK6:
915 	case BOTH_JUMPATTACK7:
916 	case BOTH_ARIAL_LEFT:
917 	case BOTH_ARIAL_RIGHT:
918 	case BOTH_ARIAL_F1:
919 	case BOTH_CARTWHEEL_LEFT:
920 	case BOTH_CARTWHEEL_RIGHT:
921 
922 	case BOTH_FORCELONGLEAP_START:
923 	case BOTH_FORCELONGLEAP_ATTACK:
924 	case BOTH_FORCEWALLRUNFLIP_START:
925 	case BOTH_FORCEWALLRUNFLIP_END:
926 	case BOTH_FORCEWALLRUNFLIP_ALT:
927 	case BOTH_FLIP_ATTACK7:
928 	case BOTH_FLIP_HOLD7:
929 	case BOTH_FLIP_LAND:
930 	case BOTH_A7_SOULCAL:
931 		return qtrue;
932 	}
933 	if ( PM_InReboundJump( anim ) )
934 	{
935 		return qtrue;
936 	}
937 	if ( PM_InReboundHold( anim ) )
938 	{
939 		return qtrue;
940 	}
941 	if ( PM_InReboundRelease( anim ) )
942 	{
943 		return qtrue;
944 	}
945 	if ( PM_InBackFlip( anim ) )
946 	{
947 		return qtrue;
948 	}
949 	return qfalse;
950 }
951 
952 extern void CG_PlayerLockedWeaponSpeech( int jumping );
PM_ForceJumpingUp(gentity_t * gent)953 qboolean PM_ForceJumpingUp( gentity_t *gent )
954 {
955 	if ( !gent || !gent->client )
956 	{
957 		return qfalse;
958 	}
959 
960 	if ( gent->NPC )
961 	{//this is ONLY for the player
962 		if ( player
963 			&& player->client
964 			&& player->client->ps.viewEntity == gent->s.number )
965 		{//okay to jump if an NPC controlled by the player
966 		}
967 		else
968 		{
969 			return qfalse;
970 		}
971 	}
972 
973 	if ( !(gent->client->ps.forcePowersActive&(1<<FP_LEVITATION)) && gent->client->ps.forceJumpCharge )
974 	{//already jumped and let go
975 		return qfalse;
976 	}
977 
978 	if ( PM_InSpecialJump( gent->client->ps.legsAnim ) )
979 	{
980 		return qfalse;
981 	}
982 
983 	if ( PM_InKnockDown( &gent->client->ps ) )
984 	{
985 		return qfalse;
986 	}
987 
988 	if ( (gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(gent)) && in_camera )
989 	{//player can't use force powers in cinematic
990 		return qfalse;
991 	}
992 	if ( gent->client->ps.groundEntityNum == ENTITYNUM_NONE //in air
993 		&& ( (gent->client->ps.pm_flags&PMF_JUMPING) && gent->client->ps.velocity[2] > 0 )//jumped & going up or at water surface///*(gent->client->ps.waterHeightLevel==WHL_SHOULDERS&&gent->client->usercmd.upmove>0) ||*/
994 		&& gent->client->ps.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 //force-jump capable
995 		&& !(gent->client->ps.pm_flags&PMF_TRIGGER_PUSHED) )//not pushed by a trigger
996 	{
997 		if( gent->flags & FL_LOCK_PLAYER_WEAPONS ) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one
998 		{
999 			CG_PlayerLockedWeaponSpeech( qtrue );
1000 			return qfalse;
1001 		}
1002 		return qtrue;
1003 	}
1004 	return qfalse;
1005 }
1006 
PM_JumpForDir(void)1007 static void PM_JumpForDir( void )
1008 {
1009 	int anim = BOTH_JUMP1;
1010 	if ( pm->cmd.forwardmove > 0 )
1011 	{
1012 		anim = BOTH_JUMP1;
1013 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1014 	}
1015 	else if ( pm->cmd.forwardmove < 0 )
1016 	{
1017 		anim = BOTH_JUMPBACK1;
1018 		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
1019 	}
1020 	else if ( pm->cmd.rightmove > 0 )
1021 	{
1022 		anim = BOTH_JUMPRIGHT1;
1023 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1024 	}
1025 	else if ( pm->cmd.rightmove < 0 )
1026 	{
1027 		anim = BOTH_JUMPLEFT1;
1028 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1029 	}
1030 	else
1031 	{
1032 		anim = BOTH_JUMP1;
1033 		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
1034 	}
1035 	if(!PM_InDeathAnim())
1036 	{
1037 		PM_SetAnim(pm,SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE, 100);		// Only blend over 100ms
1038 	}
1039 }
1040 
PM_GentCantJump(gentity_t * gent)1041 qboolean PM_GentCantJump( gentity_t *gent )
1042 {//FIXME: ugh, hacky, set a flag on NPC or something, please...
1043 	if ( gent && gent->client &&
1044 		( gent->client->NPC_class == CLASS_ATST ||
1045 		gent->client->NPC_class == CLASS_GONK ||
1046 		gent->client->NPC_class == CLASS_MARK1 ||
1047 		gent->client->NPC_class == CLASS_MARK2 ||
1048 		gent->client->NPC_class == CLASS_MOUSE ||
1049 		gent->client->NPC_class == CLASS_PROBE ||
1050 		gent->client->NPC_class == CLASS_PROTOCOL ||
1051 		gent->client->NPC_class == CLASS_R2D2 ||
1052 		gent->client->NPC_class == CLASS_R5D2 ||
1053 		gent->client->NPC_class == CLASS_SEEKER ||
1054 		gent->client->NPC_class == CLASS_REMOTE ||
1055 		gent->client->NPC_class == CLASS_SENTRY ) )
1056 	{
1057 		return qtrue;
1058 	}
1059 	return qfalse;
1060 }
1061 
PM_CheckJump(void)1062 static qboolean PM_CheckJump( void )
1063 {
1064 	//Don't allow jump until all buttons are up
1065 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
1066 		return qfalse;
1067 	}
1068 
1069 	if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ) )
1070 	{//in knockdown
1071 		return qfalse;
1072 	}
1073 
1074 	if ( PM_GentCantJump( pm->gent ) )
1075 	{
1076 		return qfalse;
1077 	}
1078 
1079 	if ( PM_KickingAnim( pm->ps->legsAnim ) && !PM_InAirKickingAnim( pm->ps->legsAnim ) )
1080 	{//can't jump when in a kicking anim
1081 		return qfalse;
1082 	}
1083 	/*
1084 	if ( pm->cmd.buttons & BUTTON_FORCEJUMP )
1085 	{
1086 		pm->ps->pm_flags |= PMF_JUMP_HELD;
1087 	}
1088 	*/
1089 
1090 #if METROID_JUMP
1091 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
1092 		&& pm->gent && pm->gent->client
1093 		&& (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) )
1094 	{//player playing as boba fett
1095 		if ( pm->cmd.upmove > 0 )
1096 		{//turn on/go up
1097 			if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && !(pm->ps->pm_flags&PMF_JUMP_HELD) )
1098 			{//double-tap - must activate while in air
1099 				if ( !JET_Flying( pm->gent ) )
1100 				{
1101 					JET_FlyStart( pm->gent );
1102 				}
1103 			}
1104 		}
1105 		else if ( pm->cmd.upmove < 0 )
1106 		{//turn it off (or should we just go down)?
1107 			/*
1108 			if ( JET_Flying( pm->gent ) )
1109 			{
1110 				JET_FlyStop( pm->gent );
1111 			}
1112 			*/
1113 		}
1114 	}
1115 	else if ( pm->waterlevel < 3 )//|| (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) )
1116 	{
1117 		if ( pm->ps->gravity > 0 )
1118 		{//can't do this in zero-G
1119 			if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
1120 				|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
1121 				|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND )
1122 			{//in the middle of a force long-jump
1123 				/*
1124 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
1125 					&& pm->cmd.upmove > 0
1126 					&& pm->cmd.forwardmove > 0 )
1127 				*/
1128 				if ( (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK)
1129 					&& pm->ps->legsAnimTimer > 0 )
1130 				{//in the air
1131 					//FIXME: need an actual set time so it doesn't matter when the attack happens
1132 					//FIXME: make sure we don't jump further than force jump 3 allows
1133 					vec3_t	jFwdAngs, jFwdVec;
1134 					VectorSet( jFwdAngs, 0, pm->ps->viewangles[YAW], 0 );
1135 					AngleVectors( jFwdAngs, jFwdVec, NULL, NULL );
1136 					float oldZVel = pm->ps->velocity[2];
1137 					if ( pm->ps->legsAnimTimer > 150 && oldZVel < 0 )
1138 					{
1139 						oldZVel = 0;
1140 					}
1141 					VectorScale( jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity );
1142 					pm->ps->velocity[2] = oldZVel;
1143 					pm->ps->pm_flags |= PMF_JUMP_HELD;
1144 					pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1145 					pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1146 					return qtrue;
1147 				}
1148 				else
1149 				{//landing-slide
1150 					if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
1151 						|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK )
1152 					{//still in start anim, but it's run out
1153 						pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1154 						if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
1155 						{//still in air?
1156 							//hold it for another 50ms
1157 							//PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1158 						}
1159 					}
1160 					else
1161 					{//in land-slide anim
1162 						//FIXME: force some forward movement?  Less if holding back?
1163 					}
1164 					if ( pm->ps->groundEntityNum == ENTITYNUM_NONE//still in air
1165 						&& pm->ps->origin[2] < pm->ps->jumpZStart )//dropped below original jump start
1166 					{//slow down
1167 						pm->ps->velocity[0] *= 0.75f;
1168 						pm->ps->velocity[1] *= 0.75f;
1169 						if ( (pm->ps->velocity[0]+pm->ps->velocity[1])*0.5f<=10.0f )
1170 						{//falling straight down
1171 							PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE );
1172 						}
1173 					}
1174 					return qfalse;
1175 				}
1176 			}
1177 			else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) //player-only for now
1178 				&& pm->cmd.upmove > 0 //trying to jump
1179 				&& pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_3 //force jump 3 or better
1180 				&& pm->ps->forcePower >= FORCE_LONGJUMP_POWER //this costs 20 force to do
1181 				&& (pm->ps->forcePowersActive&(1<<FP_SPEED)) //force-speed is on
1182 				&& pm->cmd.forwardmove > 0 //pushing forward
1183 				&& !pm->cmd.rightmove //not strafing
1184 				&& pm->ps->groundEntityNum != ENTITYNUM_NONE//not in mid-air
1185 				&& !(pm->ps->pm_flags&PMF_JUMP_HELD)
1186 				//&& (float)(level.time-pm->ps->lastStationary) >= (3000.0f*g_timescale->value)//have to have a 3 second running start - relative to force speed slowdown
1187 				&& (level.time-pm->ps->forcePowerDebounce[FP_SPEED]) <= 250//have to have just started the force speed within the last half second
1188 				&& pm->gent )
1189 			{//start a force long-jump!
1190 				vec3_t	jFwdAngs, jFwdVec;
1191 				//BOTH_FORCELONGLEAP_ATTACK if holding attack, too?
1192 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1193 				VectorSet( jFwdAngs, 0, pm->ps->viewangles[YAW], 0 );
1194 				AngleVectors( jFwdAngs, jFwdVec, NULL, NULL );
1195 				VectorScale( jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity );
1196 				pm->ps->velocity[2] = 320;
1197 				pml.groundPlane = qfalse;
1198 				pml.walking = qfalse;
1199 				pm->ps->groundEntityNum = ENTITYNUM_NONE;
1200 				pm->ps->jumpZStart = pm->ps->origin[2];
1201 				pm->ps->pm_flags |= PMF_JUMP_HELD;
1202 				pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
1203 				//start force jump
1204 				pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1205 				pm->cmd.upmove = 0;
1206 				// keep track of force jump stat
1207 				if(pm->ps->clientNum == 0)
1208 				{
1209 					if( pm->gent && pm->gent->client )
1210 					{
1211 						pm->gent->client->sess.missionStats.forceUsed[(int)FP_LEVITATION]++;
1212 					}
1213 				}
1214 				G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1215 				WP_ForcePowerStop( pm->gent, FP_SPEED );
1216 				WP_ForcePowerDrain( pm->gent, FP_LEVITATION, FORCE_LONGJUMP_POWER );//drain the required force power
1217 				G_StartMatrixEffect( pm->gent, 0, pm->ps->legsAnimTimer+500 );
1218 				return qtrue;
1219 			}
1220 			else if ( PM_InCartwheel( pm->ps->legsAnim )
1221 				|| PM_InButterfly( pm->ps->legsAnim ) )
1222 			{//can't keep jumping up in cartwheels, ariels and butterflies
1223 			}
1224 			//FIXME: still able to pogo-jump...
1225 			else if ( PM_ForceJumpingUp( pm->gent ) && (pm->ps->pm_flags&PMF_JUMP_HELD) )//||pm->ps->waterHeightLevel==WHL_SHOULDERS) )
1226 			{//force jumping && holding jump
1227 				/*
1228 				if ( !pm->ps->forceJumpZStart && (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) )
1229 				{
1230 					pm->ps->forceJumpZStart = pm->ps->origin[2];
1231 				}
1232 				*/
1233 				float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart;
1234 				//check for max force jump level and cap off & cut z vel
1235 				if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height
1236 						(pm->ps->forcePower&&pm->cmd.upmove>=10) ) &&////still have force power available and still trying to jump up
1237 					curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] )//still below maximum jump height
1238 				{//can still go up
1239 					//FIXME: after a certain amount of time of held jump, play force jump sound and flip if a dir is being held
1240 					//FIXME: if hit a wall... should we cut velocity or allow them to slide up it?
1241 					//FIXME: constantly drain force power at a rate by which the usage for maximum height would use up the full cost of force jump
1242 					if ( curHeight > forceJumpHeight[0] )
1243 					{//passed normal jump height  *2?
1244 						if ( !(pm->ps->forcePowersActive&(1<<FP_LEVITATION)) )//haven't started forcejump yet
1245 						{
1246 							//start force jump
1247    							pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
1248 							if ( pm->gent )
1249 							{
1250 								G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
1251 								// keep track of force jump stat
1252 								if(pm->ps->clientNum == 0 && pm->gent->client)
1253 								{
1254 									pm->gent->client->sess.missionStats.forceUsed[(int)FP_LEVITATION]++;
1255 								}
1256 							}
1257 							//play flip
1258 							//FIXME: do this only when they stop the jump (below) or when they're just about to hit the peak of the jump
1259 							if ( PM_InAirKickingAnim( pm->ps->legsAnim )
1260 								&& pm->ps->legsAnimTimer )
1261 							{//still in kick
1262 							}
1263 							else if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir
1264 								//pm->ps->legsAnim != BOTH_ARIAL_F1 &&//not already flipping
1265 								pm->ps->legsAnim != BOTH_FLIP_F &&
1266 								pm->ps->legsAnim != BOTH_FLIP_B &&
1267 								pm->ps->legsAnim != BOTH_FLIP_R &&
1268 								pm->ps->legsAnim != BOTH_FLIP_L &&
1269 								pm->ps->legsAnim != BOTH_ALORA_FLIP_1 &&
1270 								pm->ps->legsAnim != BOTH_ALORA_FLIP_2 &&
1271 								pm->ps->legsAnim != BOTH_ALORA_FLIP_3
1272 								&& cg.renderingThirdPerson//third person only
1273 								&& !cg.zoomMode //not zoomed in
1274 								&& !(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS)//okay to do flips with this saber
1275 								&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS) )//okay to do flips with this saber
1276 								)
1277 							{//FIXME: this could end up playing twice if the jump is very long...
1278 								int anim = BOTH_FORCEINAIR1;
1279 								int	parts = SETANIM_BOTH;
1280 
1281 								if ( pm->cmd.forwardmove > 0 )
1282 								{
1283 									/*
1284 									if ( pm->ps->forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_2 )
1285 									{
1286 										anim = BOTH_ARIAL_F1;
1287 									}
1288 									else
1289 									*/
1290 									if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && Q_irand( 0, 3 ) )
1291 									{
1292 										anim = Q_irand( BOTH_ALORA_FLIP_1, BOTH_ALORA_FLIP_3 );
1293 									}
1294 									else
1295 									{
1296 										anim = BOTH_FLIP_F;
1297 									}
1298 								}
1299 								else if ( pm->cmd.forwardmove < 0 )
1300 								{
1301 									anim = BOTH_FLIP_B;
1302 								}
1303 								else if ( pm->cmd.rightmove > 0 )
1304 								{
1305 									anim = BOTH_FLIP_R;
1306 								}
1307 								else if ( pm->cmd.rightmove < 0 )
1308 								{
1309 									anim = BOTH_FLIP_L;
1310 								}
1311 								if ( pm->ps->weaponTime )
1312 								{//FIXME: really only care if we're in a saber attack anim...
1313 									parts = SETANIM_LEGS;
1314 								}
1315 
1316 								PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1317 							}
1318 							else if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1319 							{//FIXME: really want to know how far off ground we are, probably...
1320 								vec3_t facingFwd, facingRight, facingAngles = {0, pm->ps->viewangles[YAW], 0};
1321 								int	anim = -1;
1322 								AngleVectors( facingAngles, facingFwd, facingRight, NULL );
1323 								float dotR = DotProduct( facingRight, pm->ps->velocity );
1324 								float dotF = DotProduct( facingFwd, pm->ps->velocity );
1325 								if ( fabs(dotR) > fabs(dotF) * 1.5 )
1326 								{
1327 									if ( dotR > 150 )
1328 									{
1329 										anim = BOTH_FORCEJUMPRIGHT1;
1330 									}
1331 									else if ( dotR < -150 )
1332 									{
1333 										anim = BOTH_FORCEJUMPLEFT1;
1334 									}
1335 								}
1336 								else
1337 								{
1338 									if ( dotF > 150 )
1339 									{
1340 										anim = BOTH_FORCEJUMP1;
1341 									}
1342 									else if ( dotF < -150 )
1343 									{
1344 										anim = BOTH_FORCEJUMPBACK1;
1345 									}
1346 								}
1347 								if ( anim != -1 )
1348 								{
1349 									int parts = SETANIM_BOTH;
1350 									if ( pm->ps->weaponTime )
1351 									{//FIXME: really only care if we're in a saber attack anim...
1352 										parts = SETANIM_LEGS;
1353 									}
1354 
1355 									PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1356 								}
1357 							}
1358 						}
1359 						else
1360 						{
1361 							if ( !pm->ps->legsAnimTimer )
1362 							{//not in the middle of a legsAnim
1363 								int anim = pm->ps->legsAnim;
1364 								int newAnim = -1;
1365 								switch ( anim )
1366 								{
1367 								case BOTH_FORCEJUMP1:
1368 									newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
1369 									break;
1370 								case BOTH_FORCEJUMPBACK1:
1371 									newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
1372 									break;
1373 								case BOTH_FORCEJUMPLEFT1:
1374 									newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
1375 									break;
1376 								case BOTH_FORCEJUMPRIGHT1:
1377 									newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
1378 									break;
1379 								}
1380 								if ( newAnim != -1 )
1381 								{
1382 									int parts = SETANIM_BOTH;
1383 									if ( pm->ps->weaponTime )
1384 									{//FIXME: really only care if we're in a saber attack anim...
1385 										parts = SETANIM_LEGS;
1386 									}
1387 
1388 									PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1389 								}
1390 							}
1391 						}
1392 					}
1393 
1394 					//need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel
1395 					pm->ps->velocity[2] = (forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY;
1396 					pm->ps->velocity[2] /= 10;
1397 					pm->ps->velocity[2] += JUMP_VELOCITY;
1398 					pm->ps->pm_flags |= PMF_JUMP_HELD;
1399 				}
1400 				else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] )
1401 				{//still have some headroom, don't totally stop it
1402 					if ( pm->ps->velocity[2] > JUMP_VELOCITY )
1403 					{
1404 						pm->ps->velocity[2] = JUMP_VELOCITY;
1405 					}
1406 				}
1407 				else
1408 				{
1409 					pm->ps->velocity[2] = 0;
1410 				}
1411 				pm->cmd.upmove = 0;
1412 				return qfalse;
1413 			}
1414 		}
1415 	}
1416 
1417 #endif
1418 
1419 	//Not jumping
1420 	if ( pm->cmd.upmove < 10 ) {
1421 		return qfalse;
1422 	}
1423 
1424 	// must wait for jump to be released
1425 	if ( pm->ps->pm_flags & PMF_JUMP_HELD )
1426 	{
1427 		// clear upmove so cmdscale doesn't lower running speed
1428 		pm->cmd.upmove = 0;
1429 		return qfalse;
1430 	}
1431 
1432 	if ( pm->ps->gravity <= 0 )
1433 	{//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of
1434 		vec3_t	forward, back;
1435 		trace_t	trace;
1436 
1437 		AngleVectors( pm->ps->viewangles, forward, NULL, NULL );
1438 		VectorMA( pm->ps->origin, -8, forward, back );
1439 		pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask&~(CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP), (EG2_Collision)0, 0 );
1440 
1441 		pm->cmd.upmove = 0;
1442 
1443 		if ( trace.fraction < 1.0f )
1444 		{
1445 			VectorMA( pm->ps->velocity, JUMP_VELOCITY/2, forward, pm->ps->velocity );
1446 			//FIXME: kicking off wall anim?  At least check what anim we're in?
1447 			PM_SetAnim(pm,SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
1448 		}
1449 		else
1450 		{//else no surf close enough to push off of
1451 			return qfalse;
1452 		}
1453 
1454 		if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
1455 		{//need to set some things and return
1456 			//Jumping
1457 			pm->ps->forceJumpZStart = 0;
1458 			pml.groundPlane = qfalse;
1459 			pml.walking = qfalse;
1460 			pm->ps->pm_flags |= (PMF_JUMPING|PMF_JUMP_HELD);
1461 			pm->ps->groundEntityNum = ENTITYNUM_NONE;
1462 			pm->ps->jumpZStart = pm->ps->origin[2];
1463 
1464 			if ( pm->gent )
1465 			{
1466 				if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) )
1467 				{
1468 					PM_AddEvent( EV_JUMP );
1469 				}
1470 			}
1471 			else
1472 			{
1473 				PM_AddEvent( EV_JUMP );
1474 			}
1475 
1476 			return qtrue;
1477 		}//else no surf close enough to push off of
1478 	}
1479 	else if ( pm->cmd.upmove > 0 //want to jump
1480 		&& pm->waterlevel < 2 //not in water above ankles
1481 		&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 //have force jump ability
1482 		&& !(pm->ps->pm_flags&PMF_JUMP_HELD)//not holding jump from a previous jump
1483 		//&& !PM_InKnockDown( pm->ps )//not in a knockdown
1484 		&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime	//not in a force Rage recovery period
1485 		&& pm->gent && WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 0 ) //have enough force power to jump
1486 		&& ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode	&& !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) )) )// yes this locked weapons check also includes force powers, if we need a separate check later I'll make one
1487 	{
1488 		if ( pm->gent->NPC && pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank <= RANK_LT_JG )
1489 		{//reborn who are not acrobats can't do any of these acrobatics
1490 			//FIXME: extern these abilities in the .npc file!
1491 		}
1492 		else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
1493 		{//on the ground
1494 			//check for left-wall and right-wall special jumps
1495 			int anim = -1;
1496 			float	vertPush = 0;
1497 			int		forcePowerCostOverride = 0;
1498 
1499 			// Cartwheels/ariels/butterflies
1500 			if ( (pm->ps->weapon==WP_SABER&&G_TryingCartwheel(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/&&(pm->cmd.buttons&BUTTON_ATTACK))//using saber and holding focus + attack
1501 //					  ||(pm->ps->weapon!=WP_SABER&&((pm->cmd.buttons&BUTTON_ATTACK)||(pm->cmd.buttons&BUTTON_ALT_ATTACK)) ) )//using any other weapon and hitting either attack button
1502 				&& (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0&& pm->ps->velocity[2] >= 0 )//jumping NPC, going up already
1503 					||((pm->ps->clientNum<MAX_CLIENTS||PM_ControlledByPlayer())&&G_TryingCartwheel(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/))//focus-holding player
1504 				&& G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR*/ )// have enough power
1505 			{//holding attack and jumping
1506 				if ( pm->cmd.rightmove > 0 )
1507 				{
1508 					// If they're using the staff we do different anims.
1509 					if ( pm->ps->saberAnimLevel == SS_STAFF
1510 						&& pm->ps->weapon == WP_SABER )
1511 					{
1512 						if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1513 							|| pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
1514 						{
1515 							anim = BOTH_BUTTERFLY_RIGHT;
1516 							forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1517 						}
1518 					}
1519 					else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1520 						|| pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1521 					{
1522 						if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS)
1523 							&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) )
1524 						{//okay to do cartwheels with this saber
1525 							if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )
1526 							{//player: since we're on the ground, always do a cartwheel
1527 								/*
1528 								anim = BOTH_CARTWHEEL_RIGHT;
1529 								forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1530 								*/
1531 							}
1532 							else
1533 							{
1534 								vertPush = JUMP_VELOCITY;
1535 								if ( Q_irand( 0, 1 ) )
1536 								{
1537 									anim = BOTH_ARIAL_RIGHT;
1538 									forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1539 								}
1540 								else
1541 								{
1542 									anim = BOTH_CARTWHEEL_RIGHT;
1543 									forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1544 								}
1545 							}
1546 						}
1547 					}
1548 				}
1549 				else if ( pm->cmd.rightmove < 0 )
1550 				{
1551 					// If they're using the staff we do different anims.
1552 					if ( pm->ps->saberAnimLevel == SS_STAFF
1553 						&& pm->ps->weapon == WP_SABER )
1554 					{
1555 						if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1556 							|| pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
1557 						{
1558 							anim = BOTH_BUTTERFLY_LEFT;
1559 							forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1560 						}
1561 					}
1562 					else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())
1563 						|| pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1564 					{
1565 						if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS)
1566 							&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) )
1567 						{//okay to do cartwheels with this saber
1568 							if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )
1569 							{//player: since we're on the ground, always do a cartwheel
1570 								/*
1571 								anim = BOTH_CARTWHEEL_LEFT;
1572 								forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1573 								*/
1574 							}
1575 							else
1576 							{
1577 								vertPush = JUMP_VELOCITY;
1578 								if ( Q_irand( 0, 1 ) )
1579 								{
1580 									anim = BOTH_ARIAL_LEFT;
1581 									forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1582 								}
1583 								else
1584 								{
1585 									anim = BOTH_CARTWHEEL_LEFT;
1586 									forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR );
1587 								}
1588 							}
1589 						}
1590 					}
1591 				}
1592 			}
1593 			else if ( pm->cmd.rightmove > 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1594 			{//strafing right
1595 				if ( pm->cmd.forwardmove > 0 )
1596 				{//wall-run
1597 					if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS)
1598 						&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) )
1599 					{//okay to do wall-runs with this saber
1600 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
1601 						anim = BOTH_WALL_RUN_RIGHT;
1602 					}
1603 				}
1604 				else if ( pm->cmd.forwardmove == 0 )
1605 				{//wall-flip
1606 					if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS)
1607 						&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) )
1608 					{//okay to do wall-flips with this saber
1609 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1610 						anim = BOTH_WALL_FLIP_RIGHT;
1611 					}
1612 				}
1613 			}
1614 			else if ( pm->cmd.rightmove < 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
1615 			{//strafing left
1616 				if ( pm->cmd.forwardmove > 0 )
1617 				{//wall-run
1618 					if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS)
1619 						&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) )
1620 					{//okay to do wall-runs with this saber
1621 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
1622 						anim = BOTH_WALL_RUN_LEFT;
1623 					}
1624 				}
1625 				else if ( pm->cmd.forwardmove == 0 )
1626 				{//wall-flip
1627 					if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS)
1628 						&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) )
1629 					{//okay to do wall-flips with this saber
1630 						vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1631 						anim = BOTH_WALL_FLIP_LEFT;
1632 					}
1633 				}
1634 			}
1635 			else if ( /*pm->ps->clientNum >= MAX_CLIENTS//not the player
1636 				&& !PM_ControlledByPlayer() //not controlled by player
1637 				&&*/ pm->cmd.forwardmove > 0 //pushing forward
1638 				&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )//have jump 2 or higher
1639 			{//step off wall, flip backwards
1640 				if ( VectorLengthSquared( pm->ps->velocity ) > 40000 /*200*200*/)
1641 				{//have to be moving... FIXME: make sure it's opposite the wall... or at least forward?
1642 					if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
1643 					{//run all the way up wwall
1644 						if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS)
1645 							&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) )
1646 						{//okay to do wall-runs with this saber
1647 							vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
1648 							anim = BOTH_FORCEWALLRUNFLIP_START;
1649 						}
1650 					}
1651 					else
1652 					{//run just a couple steps up
1653 						if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS)
1654 							&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) )
1655 						{//okay to do wall-flips with this saber
1656 							vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1657 							anim = BOTH_WALL_FLIP_BACK1;
1658 						}
1659 					}
1660 				}
1661 			}
1662 			else if ( pm->cmd.forwardmove < 0 //pushing back
1663 				//&& pm->ps->clientNum//not the player
1664 				&& !(pm->cmd.buttons&BUTTON_ATTACK) )//not attacking
1665 			{//back-jump does backflip... FIXME: always?!  What about just doing a normal jump backwards?
1666 				if ( pm->ps->velocity[2] >= 0 )
1667 				{//must be going up already
1668 					if ( !(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS)
1669 						&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS)) )
1670 					{//okay to do backstabs with this saber
1671 						vertPush = JUMP_VELOCITY;
1672 						if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 2 ) )
1673 						{
1674 							anim = BOTH_ALORA_FLIP_B;
1675 						}
1676 						else
1677 						{
1678 							anim = PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 );
1679 						}
1680 					}
1681 				}
1682 			}
1683 			else if ( VectorLengthSquared( pm->ps->velocity ) < 256 /*16 squared*/)
1684 			{//not moving
1685 				if ( pm->ps->weapon == WP_SABER && (pm->cmd.buttons & BUTTON_ATTACK) )
1686 				{
1687 					saberMoveName_t overrideJumpAttackUpMove = LS_INVALID;
1688 					if ( pm->ps->saber[0].jumpAtkUpMove != LS_INVALID )
1689 					{
1690 						if ( pm->ps->saber[0].jumpAtkUpMove != LS_NONE )
1691 						{//actually overriding
1692 							overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkUpMove;
1693 						}
1694 						else if ( pm->ps->dualSabers
1695 							&& pm->ps->saber[1].jumpAtkUpMove > LS_NONE )
1696 						{//would be cancelling it, but check the second saber, too
1697 							overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkUpMove;
1698 						}
1699 						else
1700 						{//nope, just cancel it
1701 							overrideJumpAttackUpMove = LS_NONE;
1702 						}
1703 					}
1704 					else if ( pm->ps->dualSabers
1705 						&& pm->ps->saber[1].jumpAtkUpMove != LS_INVALID )
1706 					{//first saber not overridden, check second
1707 						overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkUpMove;
1708 					}
1709 					if ( overrideJumpAttackUpMove != LS_INVALID )
1710 					{//do this move instead
1711 						if ( overrideJumpAttackUpMove != LS_NONE )
1712 						{
1713 							anim = saberMoveData[overrideJumpAttackUpMove].animToUse;
1714 						}
1715 					}
1716 					else if ( pm->ps->saberAnimLevel == SS_MEDIUM )
1717 					{
1718 						/*
1719 						//Only tavion does these now
1720 						if ( pm->ps->clientNum && Q_irand( 0, 1 ) )
1721 						{//butterfly... FIXME: does direction matter?
1722 							vertPush = JUMP_VELOCITY;
1723 							if ( Q_irand( 0, 1 ) )
1724 							{
1725 								anim = BOTH_BUTTERFLY_LEFT;
1726 							}
1727 							else
1728 							{
1729 								anim = BOTH_BUTTERFLY_RIGHT;
1730 							}
1731 						}
1732 						else
1733 						*/if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() )//NOTE: pretty much useless, so player never does these
1734 						{//jump-spin FIXME: does direction matter?
1735 							vertPush = forceJumpStrength[FORCE_LEVEL_2]/1.5f;
1736 							if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA )
1737 							{
1738 								anim = BOTH_ALORA_SPIN;
1739 							}
1740 							else
1741 							{
1742 								anim = Q_irand( BOTH_FJSS_TR_BL, BOTH_FJSS_TL_BR );
1743 							}
1744 						}
1745 					}
1746 				}
1747 			}
1748 
1749 			if ( anim != -1 && PM_HasAnimation( pm->gent, anim ) )
1750 			{
1751 				vec3_t fwd, right, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
1752 				trace_t	trace;
1753 				qboolean doTrace = qfalse;
1754 				int contents = CONTENTS_SOLID;
1755 
1756 				AngleVectors( fwdAngles, fwd, right, NULL );
1757 
1758 				//trace-check for a wall, if necc.
1759 				switch ( anim )
1760 				{
1761 				case BOTH_WALL_FLIP_LEFT:
1762 					if ( g_debugMelee->integer )
1763 					{
1764 						contents |= CONTENTS_BODY;
1765 					}
1766 					//NOTE: purposely falls through to next case!
1767 				case BOTH_WALL_RUN_LEFT:
1768 					doTrace = qtrue;
1769 					VectorMA( pm->ps->origin, -16, right, traceto );
1770 					break;
1771 
1772 				case BOTH_WALL_FLIP_RIGHT:
1773 					if ( g_debugMelee->integer )
1774 					{
1775 						contents |= CONTENTS_BODY;
1776 					}
1777 					//NOTE: purposely falls through to next case!
1778 				case BOTH_WALL_RUN_RIGHT:
1779 					doTrace = qtrue;
1780 					VectorMA( pm->ps->origin, 16, right, traceto );
1781 					break;
1782 
1783 				case BOTH_WALL_FLIP_BACK1:
1784 					if ( g_debugMelee->integer )
1785 					{
1786 						contents |= CONTENTS_BODY;
1787 					}
1788 					doTrace = qtrue;
1789 					VectorMA( pm->ps->origin, 32, fwd, traceto );//was 16
1790 					break;
1791 
1792 				case BOTH_FORCEWALLRUNFLIP_START:
1793 					if ( g_debugMelee->integer )
1794 					{
1795 						contents |= CONTENTS_BODY;
1796 					}
1797 					doTrace = qtrue;
1798 					VectorMA( pm->ps->origin, 32, fwd, traceto );//was 16
1799 					break;
1800 				}
1801 
1802 				vec3_t	idealNormal={0}, wallNormal={0};
1803 				if ( doTrace )
1804 				{
1805 					//FIXME: all these jump ones should check for head clearance
1806 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1807 					VectorCopy( trace.plane.normal, wallNormal );
1808 					VectorNormalize( wallNormal );
1809 					VectorSubtract( pm->ps->origin, traceto, idealNormal );
1810 					VectorNormalize( idealNormal );
1811 					if ( anim == BOTH_WALL_FLIP_LEFT )
1812 					{//sigh.. check for bottomless pit to the right
1813 						trace_t	trace2;
1814 						vec3_t	start;
1815 						VectorMA( pm->ps->origin, 128, right, traceto );
1816 						pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1817 						if ( !trace2.allsolid && !trace2.startsolid )
1818 						{
1819 							VectorCopy( trace2.endpos, traceto );
1820 							VectorCopy( traceto, start );
1821 							traceto[2] -= 384;
1822 							pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1823 							if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f )
1824 							{//bottomless pit!
1825 								trace.fraction = 1.0f;//way to stop it from doing the side-flip
1826 							}
1827 						}
1828 					}
1829 					else if ( anim == BOTH_WALL_FLIP_RIGHT )
1830 					{//sigh.. check for bottomless pit to the left
1831 						trace_t	trace2;
1832 						vec3_t	start;
1833 						VectorMA( pm->ps->origin, -128, right, traceto );
1834 						pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1835 						if ( !trace2.allsolid && !trace2.startsolid )
1836 						{
1837 							VectorCopy( trace2.endpos, traceto );
1838 							VectorCopy( traceto, start );
1839 							traceto[2] -= 384;
1840 							pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1841 							if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f )
1842 							{//bottomless pit!
1843 								trace.fraction = 1.0f;//way to stop it from doing the side-flip
1844 							}
1845 						}
1846 					}
1847 					else
1848 					{
1849 						if ( anim == BOTH_WALL_FLIP_BACK1
1850 							|| anim == BOTH_FORCEWALLRUNFLIP_START )
1851 						{//trace up and forward a little to make sure the wall it at least 64 tall
1852 							if ( (contents&CONTENTS_BODY)//included entitied
1853 								&& (trace.contents&CONTENTS_BODY) //hit an entity
1854 								&& g_entities[trace.entityNum].client )//hit a client
1855 							{//no need to trace up, it's all good...
1856 								if ( PM_InOnGroundAnim( &g_entities[trace.entityNum].client->ps ) )//on the ground, no jump
1857 								{//can't jump off guys on ground
1858 									trace.fraction = 1.0f;//way to stop if from doing the jump
1859 								}
1860 								else if ( anim == BOTH_FORCEWALLRUNFLIP_START )
1861 								{//instead of wall-running up, do the backflip
1862 									anim = BOTH_WALL_FLIP_BACK1;
1863 									vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
1864 								}
1865 							}
1866 							else if ( anim == BOTH_WALL_FLIP_BACK1 )
1867 							{
1868 								trace_t	trace2;
1869 								vec3_t	start;
1870 								VectorCopy( pm->ps->origin, start );
1871 								start[2] += 64;
1872 								VectorMA( start, 32, fwd, traceto );
1873 								pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1874 								if ( trace2.allsolid
1875 									|| trace2.startsolid
1876 									|| trace2.fraction >= 1.0f )
1877 								{//no room above or no wall in front at that height
1878 									trace.fraction = 1.0f;//way to stop if from doing the jump
1879 								}
1880 							}
1881 						}
1882 						if ( trace.fraction < 1.0f )
1883 						{//still valid to jump
1884 							if ( anim == BOTH_WALL_FLIP_BACK1 )
1885 							{//sigh.. check for bottomless pit to the rear
1886 								trace_t	trace2;
1887 								vec3_t	start;
1888 								VectorMA( pm->ps->origin, -128, fwd, traceto );
1889 								pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1890 								if ( !trace2.allsolid && !trace2.startsolid )
1891 								{
1892 									VectorCopy( trace2.endpos, traceto );
1893 									VectorCopy( traceto, start );
1894 									traceto[2] -= 384;
1895 									pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 );
1896 									if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f )
1897 									{//bottomless pit!
1898 										trace.fraction = 1.0f;//way to stop it from doing the side-flip
1899 									}
1900 								}
1901 							}
1902 						}
1903 					}
1904 				}
1905 				gentity_t *traceEnt = &g_entities[trace.entityNum];
1906 
1907 				if ( !doTrace || (trace.fraction < 1.0f&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(wallNormal,idealNormal)>0.7)) )
1908 				{//there is a wall there
1909 
1910 					if ( (anim != BOTH_WALL_RUN_LEFT
1911 							&& anim != BOTH_WALL_RUN_RIGHT
1912 							&& anim != BOTH_FORCEWALLRUNFLIP_START)
1913 						|| (wallNormal[2] >= 0.0f && wallNormal[2] <= MAX_WALL_RUN_Z_NORMAL) )
1914 					{//wall-runs can only run on relatively flat walls, sorry.
1915 						if ( anim == BOTH_ARIAL_LEFT || anim == BOTH_CARTWHEEL_LEFT )
1916 						{
1917 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1918 							VectorMA( pm->ps->velocity, -185, right, pm->ps->velocity );
1919 						}
1920 						else if ( anim == BOTH_ARIAL_RIGHT || anim == BOTH_CARTWHEEL_RIGHT )
1921 						{
1922 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1923 							VectorMA( pm->ps->velocity, 185, right, pm->ps->velocity );
1924 						}
1925 						else if ( anim == BOTH_BUTTERFLY_LEFT )
1926 						{
1927 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1928 							VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity );
1929 						}
1930 						else if ( anim == BOTH_BUTTERFLY_RIGHT )
1931 						{
1932 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1933 							VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity );
1934 						}
1935 						//move me to side
1936 						else if ( anim == BOTH_WALL_FLIP_LEFT )
1937 						{
1938 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1939 							VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
1940 						}
1941 						else if ( anim == BOTH_WALL_FLIP_RIGHT )
1942 						{
1943 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1944 							VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
1945 						}
1946 						else if ( anim == BOTH_FLIP_BACK1
1947 							|| anim == BOTH_FLIP_BACK2
1948 							|| anim == BOTH_FLIP_BACK3
1949 							|| anim == BOTH_ALORA_FLIP_B
1950 							|| anim == BOTH_WALL_FLIP_BACK1 )
1951 						{
1952 							pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
1953 							VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
1954 						}
1955 						//kick if jumping off an ent
1956 						if ( doTrace
1957 							&& anim != BOTH_WALL_RUN_LEFT
1958 							&& anim != BOTH_WALL_RUN_RIGHT
1959 							&& anim != BOTH_FORCEWALLRUNFLIP_START)
1960 						{
1961 							if ( pm->gent && trace.entityNum < ENTITYNUM_WORLD )
1962 							{
1963 								if ( traceEnt
1964 									&& traceEnt->client
1965 									&& traceEnt->health > 0
1966 									&& traceEnt->takedamage
1967 									&& traceEnt->client->NPC_class != CLASS_GALAKMECH
1968 									&& traceEnt->client->NPC_class != CLASS_DESANN
1969 									&& !(traceEnt->flags&FL_NO_KNOCKBACK) )
1970 								{//push them away and do pain
1971 									vec3_t oppDir, fxDir;
1972 									float strength = VectorNormalize2( pm->ps->velocity, oppDir );
1973 									VectorScale( oppDir, -1, oppDir );
1974 									//FIXME: need knockdown anim
1975 									G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
1976 									VectorCopy( fwd, fxDir );
1977 									VectorScale( fxDir, -1, fxDir );
1978 									G_PlayEffect( G_EffectIndex( "melee/kick_impact" ), trace.endpos, fxDir );
1979 									//G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
1980 									if ( traceEnt->health > 0 )
1981 									{//didn't kill him
1982 										if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer))
1983 											|| (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)<RANK_ENSIGN) )
1984 										{
1985 											NPC_SetAnim( traceEnt, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
1986 											G_Throw( traceEnt, oppDir, strength );
1987 										}
1988 									}
1989 								}
1990 							}
1991 						}
1992 						//up
1993 						if ( vertPush )
1994 						{
1995 							pm->ps->velocity[2] = vertPush;
1996 						}
1997 						//animate me
1998 						if ( anim == BOTH_BUTTERFLY_RIGHT )
1999 						{
2000 							PM_SetSaberMove( LS_BUTTERFLY_RIGHT );
2001 						}
2002 						else if ( anim == BOTH_BUTTERFLY_LEFT )
2003 						{
2004 							PM_SetSaberMove( LS_BUTTERFLY_LEFT );
2005 						}
2006 						else
2007 						{//not a proper saberMove, so do set all the details manually
2008 							int parts = SETANIM_LEGS;
2009 							if ( /*anim == BOTH_BUTTERFLY_LEFT ||
2010 								anim == BOTH_BUTTERFLY_RIGHT ||*/
2011 								anim == BOTH_FJSS_TR_BL ||
2012 								anim == BOTH_FJSS_TL_BR )
2013 							{
2014 								parts = SETANIM_BOTH;
2015 								pm->cmd.buttons&=~BUTTON_ATTACK;
2016 								pm->ps->saberMove = LS_NONE;
2017 								pm->gent->client->ps.SaberActivateTrail( 300 );
2018 							}
2019 							else if ( !pm->ps->weaponTime )
2020 							{
2021 								parts = SETANIM_BOTH;
2022 							}
2023 							PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2024 							if ( /*anim == BOTH_BUTTERFLY_LEFT
2025 								|| anim == BOTH_BUTTERFLY_RIGHT
2026 								||*/ anim == BOTH_FJSS_TR_BL
2027 								|| anim == BOTH_FJSS_TL_BR
2028 								|| anim == BOTH_FORCEWALLRUNFLIP_START )
2029 							{
2030 								pm->ps->weaponTime = pm->ps->torsoAnimTimer;
2031 							}
2032 							else if ( anim == BOTH_WALL_FLIP_LEFT
2033 									|| anim == BOTH_WALL_FLIP_RIGHT
2034 									|| anim == BOTH_WALL_FLIP_BACK1 )
2035 							{//let us do some more moves after this
2036 								pm->ps->saberAttackChainCount = 0;
2037 							}
2038 						}
2039 						pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2040 						pm->ps->pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL);
2041 						pm->cmd.upmove = 0;
2042 						G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2043 						WP_ForcePowerDrain( pm->gent, FP_LEVITATION, forcePowerCostOverride );
2044 					}
2045 				}
2046 			}
2047 		}
2048 		else
2049 		{//in the air
2050 			int legsAnim = pm->ps->legsAnim;
2051 
2052 			if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT )
2053 			{//running on a wall
2054 				vec3_t right, traceto, mins = {pm->mins[0],pm->mins[0],0}, maxs = {pm->maxs[0],pm->maxs[0],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2055 				trace_t	trace;
2056 				int		anim = -1;
2057 
2058 				AngleVectors( fwdAngles, NULL, right, NULL );
2059 
2060 				if ( legsAnim == BOTH_WALL_RUN_LEFT )
2061 				{
2062 					if ( pm->ps->legsAnimTimer > 400 )
2063 					{//not at the end of the anim
2064 						float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_LEFT );
2065 						if ( pm->ps->legsAnimTimer < animLen - 400 )
2066 						{//not at start of anim
2067 							VectorMA( pm->ps->origin, -16, right, traceto );
2068 							anim = BOTH_WALL_RUN_LEFT_FLIP;
2069 						}
2070 					}
2071 				}
2072 				else if ( legsAnim == BOTH_WALL_RUN_RIGHT )
2073 				{
2074 					if ( pm->ps->legsAnimTimer > 400 )
2075 					{//not at the end of the anim
2076 						float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_RIGHT );
2077 						if ( pm->ps->legsAnimTimer < animLen - 400 )
2078 						{//not at start of anim
2079 							VectorMA( pm->ps->origin, 16, right, traceto );
2080 							anim = BOTH_WALL_RUN_RIGHT_FLIP;
2081 						}
2082 					}
2083 				}
2084 				if ( anim != -1 )
2085 				{
2086 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 );
2087 					if ( trace.fraction < 1.0f )
2088 					{//flip off wall
2089 						if ( anim == BOTH_WALL_RUN_LEFT_FLIP )
2090 						{
2091 							pm->ps->velocity[0] *= 0.5f;
2092 							pm->ps->velocity[1] *= 0.5f;
2093 							VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
2094 						}
2095 						else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP )
2096 						{
2097 							pm->ps->velocity[0] *= 0.5f;
2098 							pm->ps->velocity[1] *= 0.5f;
2099 							VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
2100 						}
2101 						PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2102 						pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2103 						pm->cmd.upmove = 0;
2104 					}
2105 				}
2106 				if ( pm->cmd.upmove != 0 )
2107 				{//jump failed, so don't try to do normal jump code, just return
2108 					return qfalse;
2109 				}
2110 			}
2111 			else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
2112 			{//want to jump off wall
2113 				vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[0],0}, maxs = {pm->maxs[0],pm->maxs[0],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2114 				trace_t	trace;
2115 				int		anim = -1;
2116 
2117 				AngleVectors( fwdAngles, fwd, NULL, NULL );
2118 
2119 				float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_FORCEWALLRUNFLIP_START );
2120 				if ( pm->ps->legsAnimTimer < animLen - 250 )//was 400
2121 				{//not at start of anim
2122 					VectorMA( pm->ps->origin, 16, fwd, traceto );
2123 					anim = BOTH_FORCEWALLRUNFLIP_END;
2124 				}
2125 				if ( anim != -1 )
2126 				{
2127 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 );
2128 					if ( trace.fraction < 1.0f )
2129 					{//flip off wall
2130 						pm->ps->velocity[0] *= 0.5f;
2131 						pm->ps->velocity[1] *= 0.5f;
2132 						VectorMA( pm->ps->velocity, WALL_RUN_UP_BACKFLIP_SPEED, fwd, pm->ps->velocity );
2133 						pm->ps->velocity[2] += 200;
2134 						int parts = SETANIM_LEGS;
2135 						if ( !pm->ps->weaponTime )
2136 						{//not attacking, set anim on both
2137 							parts = SETANIM_BOTH;
2138 						}
2139 						PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2140 						//FIXME: do damage to traceEnt, like above?
2141 						pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2142 						pm->cmd.upmove = 0;
2143 						PM_AddEvent( EV_JUMP );
2144 					}
2145 				}
2146 				if ( pm->cmd.upmove != 0 )
2147 				{//jump failed, so don't try to do normal jump code, just return
2148 					return qfalse;
2149 				}
2150 			}
2151 			/*
2152 			else if ( pm->cmd.forwardmove < 0
2153 				&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
2154 				&& !(pm->ps->pm_flags&PMF_JUMP_HELD) //not holding jump
2155 				&& (level.time - pm->ps->lastOnGround) <= 250 //just jumped
2156 				)//&& !(pm->cmd.buttons&BUTTON_ATTACK) )
2157 			{//double-tap back-jump does backflip
2158 				vec3_t fwd, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2159 
2160 				AngleVectors( fwdAngles, fwd, NULL, NULL );
2161 				pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2162 				VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2163 				//pm->ps->velocity[2] = JUMP_VELOCITY;
2164 				int parts = SETANIM_LEGS;
2165 				if ( !pm->ps->weaponTime )
2166 				{
2167 					parts = SETANIM_BOTH;
2168 				}
2169 				PM_SetAnim( pm, parts, PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2170 				pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2171 				pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2172 				pm->cmd.upmove = 0;
2173 				G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2174 				WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 );
2175 			}
2176 			*/
2177 			/*
2178 			else if ( pm->cmd.forwardmove > 0 //pushing forward
2179 				&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime	//not in a force Rage recovery period
2180 				&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //have force jump 2 or higher
2181 				&& (level.time - pm->ps->lastOnGround) <= 250//just jumped
2182 				&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 )//not in a flip or spin or anything
2183 			{//run up wall, flip backwards
2184 				//FIXME: have to be moving... make sure it's opposite the wall... or at least forward?
2185 				int wallWalkAnim = BOTH_WALL_FLIP_BACK1;
2186 				int parts = SETANIM_LEGS;
2187 				int contents = CONTENTS_SOLID;
2188 				qboolean kick = qtrue;
2189 				if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
2190 				{
2191 					wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START;
2192 					parts = SETANIM_BOTH;
2193 					kick = qfalse;
2194 				}
2195 				else
2196 				{
2197 					contents |= CONTENTS_BODY;
2198 					if ( !pm->ps->weaponTime )
2199 					{
2200 						parts = SETANIM_BOTH;
2201 					}
2202 				}
2203 				if ( PM_HasAnimation( pm->gent, wallWalkAnim ) )
2204 				{
2205 					vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2206 					trace_t	trace;
2207 					vec3_t	idealNormal;
2208 
2209 					AngleVectors( fwdAngles, fwd, NULL, NULL );
2210 					VectorMA( pm->ps->origin, 32, fwd, traceto );
2211 
2212 					pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too?
2213 					VectorSubtract( pm->ps->origin, traceto, idealNormal );
2214 					VectorNormalize( idealNormal );
2215 					gentity_t *traceEnt = &g_entities[trace.entityNum];
2216 
2217 					if ( trace.fraction < 1.0f
2218 						&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2219 					{//there is a wall there
2220 						pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
2221 						if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START )
2222 						{
2223 							pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f;
2224 						}
2225 						else
2226 						{
2227 							VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
2228 						}
2229 						//animate me
2230 						PM_SetAnim( pm, parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
2231 						pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2232 						pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
2233 						pm->cmd.upmove = 0;
2234 						G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
2235 						WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 );
2236 						//kick if jumping off an ent
2237 						if ( kick && pm->gent && trace.entityNum < ENTITYNUM_WORLD )
2238 						{
2239 							if ( traceEnt
2240 								&& traceEnt->client
2241 								&& traceEnt->health > 0
2242 								&& traceEnt->takedamage
2243 								&& traceEnt->client->NPC_class != CLASS_GALAKMECH
2244 								&& traceEnt->client->NPC_class != CLASS_DESANN
2245 								&& !(traceEnt->flags&FL_NO_KNOCKBACK) )
2246 							{//push them away and do pain
2247 								vec3_t oppDir;
2248 								float strength = VectorNormalize2( pm->ps->velocity, oppDir );
2249 								VectorScale( oppDir, -1, oppDir );
2250 								//FIXME: need knockdown anim
2251 								G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE );
2252 								G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
2253 								if ( trace.fraction <= 0.5f )
2254 								{//close to him
2255 									if ( traceEnt->health > 0 )
2256 									{//didn't kill him
2257 										if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer))
2258 											|| (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)<RANK_ENSIGN) )
2259 										{
2260 											NPC_SetAnim( traceEnt, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
2261 											G_Throw( traceEnt, oppDir, strength );
2262 										}
2263 									}
2264 								}
2265 							}
2266 						}
2267 					}
2268 				}
2269 			}
2270 			*/
2271 			else if ( (pm->ps->legsAnimTimer<=100
2272 						||!PM_InSpecialJump( legsAnim )//not in a special jump anim
2273 						||PM_InReboundJump( legsAnim )//we're already in a rebound
2274 						||PM_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you)
2275 					//&& pm->ps->velocity[2] <= 0
2276 					&& pm->ps->velocity[2] > -1200 //not falling down very fast
2277 					&& !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press
2278 					&& (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction
2279 					//&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime	//not in a force Rage recovery period
2280 					&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better
2281 					&& pm->ps->forcePower > 10 //have enough force power to do another one
2282 					&& (level.time-pm->ps->lastOnGround) > 250 //haven't been on the ground in the last 1/4 of a second
2283 					&& (!(pm->ps->pm_flags&PMF_JUMPING)//not jumping
2284 						|| ( (level.time-pm->ps->lastOnGround) > 250 //we are jumping, but have been in the air for at least half a second
2285 							 &&( g_debugMelee->integer//if you know kung fu, no height cap on wall-grab-jumps
2286 								|| ((pm->ps->origin[2]-pm->ps->forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(G_ForceWallJumpStrength()/2.0f))) )//can fit at least one more wall jump in (yes, using "magic numbers"... for now)
2287 							)
2288 						)
2289 					//&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
2290 					)
2291 			{//see if we're pushing at a wall and jump off it if so
2292 				if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB)
2293 					&& ( !pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB) ) )
2294 				{//okay to do wall-grabs with this saber
2295 					//FIXME: make sure we have enough force power
2296 					//FIXME: check  to see if we can go any higher
2297 					//FIXME: limit to a certain number of these in a row?
2298 					//FIXME: maybe don't require a ucmd direction, just check all 4?
2299 					//FIXME: should stick to the wall for a second, then push off...
2300 					vec3_t checkDir, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0};
2301 					trace_t	trace;
2302 					vec3_t	idealNormal;
2303 					int		anim = -1;
2304 
2305 					if ( pm->cmd.rightmove )
2306 					{
2307 						if ( pm->cmd.rightmove > 0 )
2308 						{
2309 							anim = BOTH_FORCEWALLREBOUND_RIGHT;
2310 							AngleVectors( fwdAngles, NULL, checkDir, NULL );
2311 						}
2312 						else if ( pm->cmd.rightmove < 0 )
2313 						{
2314 							anim = BOTH_FORCEWALLREBOUND_LEFT;
2315 							AngleVectors( fwdAngles, NULL, checkDir, NULL );
2316 							VectorScale( checkDir, -1, checkDir );
2317 						}
2318 					}
2319 					else if ( pm->cmd.forwardmove > 0 )
2320 					{
2321 						anim = BOTH_FORCEWALLREBOUND_FORWARD;
2322 						AngleVectors( fwdAngles, checkDir, NULL, NULL );
2323 					}
2324 					else if ( pm->cmd.forwardmove < 0 )
2325 					{
2326 						anim = BOTH_FORCEWALLREBOUND_BACK;
2327 						AngleVectors( fwdAngles, checkDir, NULL, NULL );
2328 						VectorScale( checkDir, -1, checkDir );
2329 					}
2330 					if ( anim != -1 )
2331 					{//trace in the dir we're pushing in and see if there's a vertical wall there
2332 						VectorMA( pm->ps->origin, 16, checkDir, traceto );//was 8
2333 						pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0 );//FIXME: clip brushes too?
2334 						VectorSubtract( pm->ps->origin, traceto, idealNormal );
2335 						VectorNormalize( idealNormal );
2336 						gentity_t *traceEnt = &g_entities[trace.entityNum];
2337 						if ( trace.fraction < 1.0f
2338 							&& fabs(trace.plane.normal[2]) <= MAX_WALL_GRAB_SLOPE
2339 							&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
2340 						{//there is a wall there
2341 							float dot = DotProduct( pm->ps->velocity, trace.plane.normal );
2342 							if ( dot < 1.0f )
2343 							{//can't be heading *away* from the wall!
2344 								//grab it!
2345 								PM_GrabWallForJump( anim );
2346 							}
2347 						}
2348 					}
2349 				}
2350 			}
2351 			else
2352 			{
2353 				//FIXME: if in a butterfly, kick people away?
2354 			}
2355 		}
2356 	}
2357 
2358 	if ( pm->gent
2359 		//&& pm->cmd.upmove > 0
2360 		&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime	//not in a force Rage recovery period
2361 		&& pm->ps->weapon == WP_SABER
2362 		&& (pm->ps->weaponTime > 0||(pm->cmd.buttons&BUTTON_ATTACK))
2363 		&& ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode)) )
2364 	{//okay, we just jumped and we're in an attack
2365 		if ( !PM_RollingAnim( pm->ps->legsAnim )
2366 			&& !PM_InKnockDown( pm->ps )
2367 			&& !PM_InDeathAnim()
2368 			&& !PM_PainAnim( pm->ps->torsoAnim )
2369 			&& !PM_FlippingAnim( pm->ps->legsAnim )
2370 			&& !PM_SpinningAnim( pm->ps->legsAnim )
2371 			&& !PM_SaberInSpecialAttack( pm->ps->torsoAnim ) )
2372 		{//HMM... do NPCs need this logic?
2373 			if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim
2374 				&& !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim
2375 				&& pm->ps->weaponTime <= 0//not busy
2376 				&& (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack
2377 			{//not in an attack or finishing/starting/transitioning one
2378 				if ( PM_CheckBackflipAttackMove() )
2379 				{
2380 					PM_SetSaberMove( PM_SaberBackflipAttackMove() );//backflip attack
2381 				}
2382 				/*
2383 				else if ( PM_CheckSaberDualJumpAttackMove() )
2384 				{
2385 					PM_SetSaberMove( PM_SaberDualJumpAttackMove() );//jump forward sideways flip attack
2386 				}
2387 				*/
2388 			}
2389 			/*
2390 			else if ( ( PM_SaberInTransitionAny( pm->ps->saberMove ) || PM_SaberInAttack( pm->ps->saberMove ) )
2391 				&& PM_InAnimForSaberMove( pm->ps->torsoAnim, pm->ps->saberMove ) )
2392 			{//not in an anim we shouldn't interrupt
2393 				//see if it's not too late to start a special jump-attack
2394 				float animLength = PM_AnimLength( g_entities[pm->ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)pm->ps->torsoAnim );
2395 				if ( animLength - pm->ps->torsoAnimTimer < 500 )
2396 				{//just started the saberMove
2397 					//check for special-case jump attacks
2398 					if ( PM_CheckFlipOverAttackMove( qtrue ) )
2399 					{
2400 						PM_SetSaberMove( PM_SaberFlipOverAttackMove() );
2401 					}
2402 					else if ( PM_CheckJumpAttackMove() )
2403 					{
2404 						PM_SetSaberMove( PM_SaberJumpAttackMove() );
2405 					}
2406 				}
2407 			}
2408 			*/
2409 		}
2410 	}
2411 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
2412 	{
2413 		return qfalse;
2414 	}
2415 	if ( pm->cmd.upmove > 0 )
2416 	{//no special jumps
2417 		/*
2418 		gentity_t *groundEnt = &g_entities[pm->ps->groundEntityNum];
2419 		if ( groundEnt && groundEnt->NPC )
2420 		{//Can't jump off of someone's head
2421 			return qfalse;
2422 		}
2423 		*/
2424 
2425 		pm->ps->velocity[2] = JUMP_VELOCITY;
2426 		pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
2427 		pm->ps->pm_flags |= PMF_JUMPING;
2428 	}
2429 
2430 	if ( d_JediAI->integer )
2431 	{
2432 		if ( pm->ps->clientNum && pm->ps->weapon == WP_SABER )
2433 		{
2434 			Com_Printf( "jumping\n" );
2435 		}
2436 	}
2437 	//Jumping
2438 	pml.groundPlane = qfalse;
2439 	pml.walking = qfalse;
2440 	pm->ps->pm_flags |= PMF_JUMP_HELD;
2441 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
2442 	pm->ps->jumpZStart = pm->ps->origin[2];
2443 
2444 	if ( pm->gent )
2445 	{
2446 		if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) )
2447 		{
2448 			PM_AddEvent( EV_JUMP );
2449 		}
2450 	}
2451 	else
2452 	{
2453 		PM_AddEvent( EV_JUMP );
2454 	}
2455 
2456 	//Set the animations
2457 	if ( pm->ps->gravity > 0 && !PM_InSpecialJump( pm->ps->legsAnim ) && !PM_InGetUp( pm->ps ) )
2458 	{
2459 		PM_JumpForDir();
2460 	}
2461 
2462 	return qtrue;
2463 }
2464 
2465 /*
2466 =============
2467 PM_CheckWaterJump
2468 =============
2469 */
PM_CheckWaterJump(void)2470 static qboolean	PM_CheckWaterJump( void ) {
2471 	vec3_t	spot;
2472 	int		cont;
2473 	vec3_t	flatforward;
2474 
2475 	if (pm->ps->pm_time) {
2476 		return qfalse;
2477 	}
2478 
2479 	if ( pm->cmd.forwardmove <= 0 && pm->cmd.upmove <= 0 )
2480 	{//they must not want to get out?
2481 		return qfalse;
2482 	}
2483 	// check for water jump
2484 	if ( pm->waterlevel != 2 ) {
2485 		return qfalse;
2486 	}
2487 
2488 	if ( pm->watertype & CONTENTS_LADDER ) {
2489 		if (pm->ps->velocity[2] <= 0)
2490 			return qfalse;
2491 	}
2492 
2493 	flatforward[0] = pml.forward[0];
2494 	flatforward[1] = pml.forward[1];
2495 	flatforward[2] = 0;
2496 	VectorNormalize( flatforward );
2497 
2498 	VectorMA( pm->ps->origin, 30, flatforward, spot );
2499 	spot[2] += 24;
2500 	cont = pm->pointcontents (spot, pm->ps->clientNum );
2501 	if ( !(cont & CONTENTS_SOLID) ) {
2502 		return qfalse;
2503 	}
2504 
2505 	spot[2] += 16;
2506 	cont = pm->pointcontents( spot, pm->ps->clientNum );
2507 	if ( cont&(CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_BODY) ) {
2508 		return qfalse;
2509 	}
2510 
2511 	// jump out of water
2512 	VectorScale( pml.forward, 200, pm->ps->velocity );
2513 	pm->ps->velocity[2] = 350+((pm->ps->waterheight-pm->ps->origin[2])*2);
2514 
2515 	pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
2516 	pm->ps->pm_time = 2000;
2517 
2518 	return qtrue;
2519 }
2520 
2521 //============================================================================
2522 
2523 
2524 /*
2525 ===================
2526 PM_WaterJumpMove
2527 
2528 Flying out of the water
2529 ===================
2530 */
PM_WaterJumpMove(void)2531 static void PM_WaterJumpMove( void )
2532 {
2533 	// waterjump has no control, but falls
2534 
2535 	PM_StepSlideMove( 1 );
2536 
2537 	pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2538 	if (pm->ps->velocity[2] < 0)
2539 	{
2540 		// cancel as soon as we are falling down again
2541 		pm->ps->pm_flags &= ~PMF_ALL_TIMES;
2542 		pm->ps->pm_time = 0;
2543 	}
2544 }
2545 
2546 /*
2547 ===================
2548 PM_WaterMove
2549 
2550 ===================
2551 */
PM_WaterMove(void)2552 static void PM_WaterMove( void ) {
2553 	int		i;
2554 	vec3_t	wishvel;
2555 	float	wishspeed;
2556 	vec3_t	wishdir;
2557 	float	scale;
2558 	float	vel;
2559 
2560 	if ( PM_CheckWaterJump() ) 	{
2561 		PM_WaterJumpMove();
2562 		return;
2563 	}
2564 	else if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && pm->waterlevel < 3 )
2565 	{
2566 		if ( PM_CheckJump () ) {
2567 			// jumped away
2568 			return;
2569 		}
2570 	}
2571 #if 0
2572 	// jump = head for surface
2573 	if ( pm->cmd.upmove >= 10 ) {
2574 		if (pm->ps->velocity[2] > -300) {
2575 			if ( pm->watertype == CONTENTS_WATER ) {
2576 				pm->ps->velocity[2] = 100;
2577 			} else if (pm->watertype == CONTENTS_SLIME) {
2578 				pm->ps->velocity[2] = 80;
2579 			} else {
2580 				pm->ps->velocity[2] = 50;
2581 			}
2582 		}
2583 	}
2584 #endif
2585 	PM_Friction ();
2586 
2587 	scale = PM_CmdScale( &pm->cmd );
2588 	//
2589 	// user intentions
2590 	//
2591 	if ( !scale ) {
2592 		wishvel[0] = 0;
2593 		wishvel[1] = 0;
2594 		if ( pm->watertype & CONTENTS_LADDER ) {
2595 			wishvel[2] = 0;
2596 		} else {
2597 			wishvel[2] = -60;		// sink towards bottom
2598 		}
2599 	} else {
2600 		for (i=0 ; i<3 ; i++) {
2601 			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
2602 		}
2603 		wishvel[2] += scale * pm->cmd.upmove;
2604 		if ( !(pm->watertype&CONTENTS_LADDER) )	//ladder
2605 		{
2606 			float depth = (pm->ps->origin[2]+pm->gent->client->standheight)-pm->ps->waterheight;
2607 			if ( depth >= 12 )
2608 			{//too high!
2609 				wishvel[2] -= 120;		// sink towards bottom
2610 				if ( wishvel[2] > 0 )
2611 				{
2612 					wishvel[2] = 0;
2613 				}
2614 			}
2615 			else if ( pm->ps->waterHeightLevel >= WHL_UNDER )//!depth && pm->waterlevel == 3 )
2616 			{
2617 			}
2618 			else if ( depth < 12 )
2619 			{//still deep
2620 				wishvel[2] -= 60;		// sink towards bottom
2621 				if ( wishvel[2] > 30 )
2622 				{
2623 					wishvel[2] = 30;
2624 				}
2625 			}
2626 		}
2627 	}
2628 
2629 	VectorCopy (wishvel, wishdir);
2630 	wishspeed = VectorNormalize(wishdir);
2631 
2632 	if ( pm->watertype & CONTENTS_LADDER )	//ladder
2633 	{
2634 		if ( wishspeed > pm->ps->speed * pm_ladderScale ) {
2635 			wishspeed = pm->ps->speed * pm_ladderScale;
2636 		}
2637 		PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );
2638 	} else {
2639 		if ( pm->ps->gravity < 0 )
2640 		{//float up
2641 			pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2642 		}
2643 		if ( wishspeed > pm->ps->speed * pm_swimScale ) {
2644 			wishspeed = pm->ps->speed * pm_swimScale;
2645 		}
2646 		PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate );
2647 	}
2648 
2649 	// make sure we can go up slopes easily under water
2650 	if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
2651 		vel = VectorLength(pm->ps->velocity);
2652 		// slide along the ground plane
2653 		PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
2654 			pm->ps->velocity, OVERCLIP );
2655 
2656 		VectorNormalize(pm->ps->velocity);
2657 		VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
2658 	}
2659 
2660 	PM_SlideMove( qfalse );
2661 }
2662 
2663 
2664 /*
2665 ===================
2666 PM_FlyVehicleMove
2667 
2668 ===================
2669 */
PM_FlyVehicleMove(void)2670 static void PM_FlyVehicleMove( void )
2671 {
2672 	int		i;
2673 	vec3_t	wishvel;
2674 	float	wishspeed;
2675 	vec3_t	wishdir;
2676 	float	scale;
2677 	float	zVel;
2678 	float	fmove = 0.0f, smove = 0.0f;
2679 
2680 	// We don't use these here because we pre-calculate the movedir in the vehicle update anyways, and if
2681 	// you leave this, you get strange motion during boarding (the player can move the vehicle).
2682 	//fmove = pm->cmd.forwardmove;
2683 	//smove = pm->cmd.rightmove;
2684 
2685 	// normal slowdown
2686 	if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE )
2687 	{//falling
2688 		zVel = pm->ps->velocity[2];
2689 		PM_Friction ();
2690 		pm->ps->velocity[2] = zVel;
2691 	}
2692 	else
2693 	{
2694 		PM_Friction ();
2695 		if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
2696 		{
2697 			pm->ps->velocity[2] = 0;	// ignore slope movement
2698 		}
2699 	}
2700 
2701 	scale = PM_CmdScale( &pm->cmd );
2702 
2703 	// Get The WishVel And WishSpeed
2704 	//-------------------------------
2705 	if ( pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare( pm->ps->moveDir, vec3_origin )) )
2706 	{//NPC
2707 
2708 		// If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
2709 		//--------------------------------------------------------------------------------------------
2710 		if ((fmove!=0.0f || smove!=0.0f) &&	VectorCompare(pm->ps->moveDir, vec3_origin))
2711 		{
2712 			//gi.Printf("Generating MoveDir\n");
2713 			for ( i = 0 ; i < 3 ; i++ )
2714 			{
2715 				wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2716 			}
2717 
2718 			VectorCopy( wishvel, wishdir );
2719 			wishspeed = VectorNormalize(wishdir);
2720 			wishspeed *= scale;
2721 		}
2722 		// Otherwise, Use The Move Dir
2723 		//-----------------------------
2724 		else
2725 		{
2726 			wishspeed = pm->ps->speed;
2727 			VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
2728 			VectorCopy( pm->ps->moveDir, wishdir );
2729 		}
2730 
2731 	}
2732 	else
2733 	{
2734 		for ( i = 0 ; i < 3 ; i++ ) {
2735 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2736 		}
2737 		// when going up or down slopes the wish velocity should Not be zero
2738 	//	wishvel[2] = 0;
2739 
2740 		VectorCopy (wishvel, wishdir);
2741 		wishspeed = VectorNormalize(wishdir);
2742 		wishspeed *= scale;
2743 	}
2744 
2745 	// Handle negative speed.
2746 	if ( wishspeed < 0 )
2747 	{
2748 		wishspeed = wishspeed * -1.0f;
2749 		VectorScale( wishvel, -1.0f, wishvel );
2750 		VectorScale( wishdir, -1.0f, wishdir );
2751 	}
2752 
2753 	VectorCopy( wishvel, wishdir );
2754 	wishspeed = VectorNormalize( wishdir );
2755 
2756 	PM_Accelerate( wishdir, wishspeed, 100 );
2757 
2758 	PM_StepSlideMove( 1 );
2759 }
2760 
2761 /*
2762 ===================
2763 PM_FlyMove
2764 
2765 Only with the flight powerup
2766 ===================
2767 */
PM_FlyMove(void)2768 static void PM_FlyMove( void )
2769 {
2770 	int		i;
2771 	vec3_t	wishvel;
2772 	float	wishspeed;
2773 	vec3_t	wishdir;
2774 	float	scale;
2775 	float	accel;
2776 	qboolean	lowGravMove = qfalse;
2777 	qboolean	jetPackMove = qfalse;
2778 
2779 	// normal slowdown
2780 	PM_Friction ();
2781 
2782 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
2783 		&& pm->gent
2784 		&& pm->gent->client
2785 		&& (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) && pm->gent->client->moveType == MT_FLYSWIM )
2786 	{//jetpack accel
2787 		accel = pm_flyaccelerate;
2788 		jetPackMove = qtrue;
2789 	}
2790 	else if ( pm->ps->gravity <= 0
2791 		&& ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent&&pm->gent->client&&pm->gent->client->moveType == MT_RUNJUMP)) )
2792 	{
2793 		PM_CheckJump();
2794 		accel = 1.0f;
2795 		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
2796 		pm->ps->jumpZStart = pm->ps->origin[2];//so we don't take a lot of damage when the gravity comes back on
2797 		lowGravMove = qtrue;
2798 	}
2799 	else
2800 	{
2801 		accel = pm_flyaccelerate;
2802 	}
2803 
2804 	scale = PM_CmdScale( &pm->cmd );
2805 	//
2806 	// user intentions
2807 	//
2808 	if ( !scale )
2809 	{
2810 		wishvel[0] = 0;
2811 		wishvel[1] = 0;
2812 		wishvel[2] = 0;
2813 	}
2814 	else
2815 	{
2816 		for (i=0 ; i<3 ; i++)
2817 		{
2818 			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
2819 		}
2820 		if ( jetPackMove )
2821 		{
2822 			wishvel[2] += pm->cmd.upmove;
2823 		}
2824 		else if ( lowGravMove )
2825 		{
2826 			wishvel[2] += scale * pm->cmd.upmove;
2827 			VectorScale( wishvel, 0.5f, wishvel );
2828 		}
2829 	}
2830 
2831 	VectorCopy( wishvel, wishdir );
2832 	wishspeed = VectorNormalize( wishdir );
2833 
2834 	PM_Accelerate( wishdir, wishspeed, accel );
2835 
2836 	PM_StepSlideMove( 1 );
2837 }
2838 
PM_GroundSlideOkay(float zNormal)2839 qboolean PM_GroundSlideOkay( float zNormal )
2840 {
2841 	if ( zNormal > 0 )
2842 	{
2843 		if ( pm->ps->velocity[2] > 0 )
2844 		{
2845 			if ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT
2846 				|| pm->ps->legsAnim == BOTH_WALL_RUN_LEFT
2847 				|| pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP
2848 				|| pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP
2849 				|| pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START
2850 				|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
2851 				|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
2852 				|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND
2853 				|| PM_InReboundJump( pm->ps->legsAnim ))
2854 			{
2855 				return qfalse;
2856 			}
2857 		}
2858 	}
2859 	return qtrue;
2860 }
2861 /*
2862 ===================
2863 PM_AirMove
2864 
2865 ===================
2866 */
PM_AirMove(void)2867 static void PM_AirMove( void ) {
2868 	int			i;
2869 	vec3_t		wishvel;
2870 	float		fmove, smove;
2871 	vec3_t		wishdir;
2872 	float		wishspeed;
2873 	usercmd_t	cmd;
2874 	float		gravMod = 1.0f;
2875 
2876 #if METROID_JUMP
2877 	PM_CheckJump();
2878 #endif
2879 
2880 	PM_Friction();
2881 
2882 	fmove = pm->cmd.forwardmove;
2883 	smove = pm->cmd.rightmove;
2884 
2885 	cmd = pm->cmd;
2886 	PM_CmdScale( &cmd );
2887 
2888 	// set the movementDir so clients can rotate the legs for strafing
2889 	PM_SetMovementDir();
2890 
2891 	// project moves down to flat plane
2892 	pml.forward[2] = 0;
2893 	pml.right[2] = 0;
2894 	VectorNormalize (pml.forward);
2895 	VectorNormalize (pml.right);
2896 
2897 	Vehicle_t *pVeh = NULL;
2898 
2899 	if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
2900 	{
2901 		pVeh = pm->gent->m_pVehicle;
2902 	}
2903 
2904 	if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 )
2905 	{//in a hovering vehicle, have air control
2906 
2907 		// Flying Or Breaking, No Control
2908 		//--------------------------------
2909 		if ( pVeh->m_ulFlags&VEH_FLYING || pVeh->m_ulFlags&VEH_SLIDEBREAKING)
2910 		{
2911 			wishspeed = 0.0f;
2912 			VectorClear( wishvel );
2913 			VectorClear( wishdir );
2914 		}
2915 
2916 		// Out Of Control - Maintain pos3 Velocity
2917 		//-----------------------------------------
2918 		else if ((pVeh->m_ulFlags&VEH_OUTOFCONTROL) || (pVeh->m_ulFlags&VEH_STRAFERAM))
2919 		{
2920  			VectorCopy(pm->gent->pos3, wishvel);
2921 			VectorCopy(wishvel, wishdir);
2922 			wishspeed = VectorNormalize(wishdir);
2923 		}
2924 
2925 		// Boarding - Maintain Boarding Velocity
2926 		//---------------------------------------
2927 		else if (pVeh->m_iBoarding)
2928 		{
2929 			VectorCopy(pVeh->m_vBoardingVelocity, wishvel);
2930 			VectorCopy(wishvel, wishdir);
2931 			wishspeed = VectorNormalize(wishdir);
2932 		}
2933 
2934 		// Otherwise, Normal Velocity
2935 		//----------------------------
2936 		else
2937 		{
2938 			wishspeed = pm->ps->speed;
2939 			VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
2940 			VectorCopy( pm->ps->moveDir, wishdir );
2941 		}
2942 	}
2943 	else if ( (pm->ps->pm_flags&PMF_SLOW_MO_FALL) )
2944 	{//no air-control
2945 		VectorClear( wishvel );
2946 	}
2947 	else
2948 	{
2949 		for ( i = 0 ; i < 2 ; i++ )
2950 		{
2951 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
2952 		}
2953 		wishvel[2] = 0;
2954 	}
2955 
2956 	VectorCopy (wishvel, wishdir);
2957  	wishspeed = VectorNormalize(wishdir);
2958 
2959 	if ( ( DotProduct (pm->ps->velocity, wishdir) ) < 0.0f )
2960 	{//Encourage deceleration away from the current velocity
2961 		wishspeed *= pm_airDecelRate;
2962 	}
2963 
2964 	// not on ground, so little effect on velocity
2965 	float accelerate = pm_airaccelerate;
2966  	if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER )
2967 	{//speeders have more control in air
2968 		//in mid-air
2969 		accelerate = pVeh->m_pVehicleInfo->acceleration;
2970 		if ( pml.groundPlane )
2971 		{//on a slope of some kind, shouldn't have much control and should slide a lot
2972 			accelerate *= 0.5f;
2973 		}
2974  		if (pVeh->m_ulFlags & VEH_SLIDEBREAKING)
2975 		{
2976 			VectorScale(pm->ps->velocity, 0.80f, pm->ps->velocity);
2977 		}
2978 		if (pm->ps->velocity[2]>1000.0f)
2979 		{
2980 			pm->ps->velocity[2] = 1000.0f;
2981 		}
2982 	}
2983 	PM_Accelerate( wishdir, wishspeed, accelerate );
2984 
2985 
2986 	// we may have a ground plane that is very steep, even
2987 	// though we don't have a groundentity
2988 	// slide along the steep plane
2989 	if ( pml.groundPlane )
2990 	{
2991 		if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) )
2992 		{
2993 			PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal,
2994 								pm->ps->velocity, OVERCLIP );
2995 		}
2996 	}
2997 
2998 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
2999 		&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0
3000 		&& pm->ps->forceJumpZStart
3001 		&& pm->ps->velocity[2] > 0 )
3002 	{//I am force jumping and I'm not holding the button anymore
3003 		float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart + (pm->ps->velocity[2]*pml.frametime);
3004 		float maxJumpHeight = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]];
3005 		if ( curHeight >= maxJumpHeight )
3006 		{//reached top, cut velocity
3007 			pm->ps->velocity[2] = 0;
3008 		}
3009 	}
3010 	if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
3011 	{
3012 		gravMod = 0.0f;
3013 	}
3014 	PM_StepSlideMove( gravMod );
3015 
3016   	if (pVeh && pm->ps->pm_flags&PMF_BUMPED)
3017 	{
3018 
3019 /*
3020 		// Turn Vehicle In Direction Of Collision
3021 		//----------------------------------------
3022 		vec3_t	nAngles;
3023  		vectoangles(pm->ps->velocity, nAngles);
3024 		nAngles[0] = pVeh->m_pParentEntity->client->ps.viewangles[0];
3025 		nAngles[2] = pVeh->m_pParentEntity->client->ps.viewangles[2];
3026 
3027 		// toggle the teleport bit so the client knows to not lerp
3028 		player->client->ps.eFlags ^= EF_TELEPORT_BIT;
3029 
3030 		// set angles
3031 		SetClientViewAngle( pVeh->m_pParentEntity, nAngles );
3032 		if (pVeh->m_pPilot)
3033 		{
3034 			SetClientViewAngle( pVeh->m_pPilot, nAngles );
3035 	 	}
3036 
3037 		VectorCopy(nAngles, pVeh->m_vPrevOrientation);
3038 		VectorCopy(nAngles, pVeh->m_vOrientation);
3039 		pVeh->m_vAngularVelocity = 0.0f;
3040 */
3041 
3042 		// Reduce "Bounce Up Wall" Velocity
3043 		//----------------------------------
3044 	 	if (pm->ps->velocity[2]>0)
3045 		{
3046 			pm->ps->velocity[2] *= 0.1f;
3047 		}
3048 	}
3049 }
3050 
3051 
3052 /*
3053 ===================
3054 PM_WalkMove
3055 
3056 ===================
3057 */
PM_WalkMove(void)3058 static void PM_WalkMove( void ) {
3059 	int			i;
3060 	vec3_t		wishvel;
3061 	float		fmove, smove;
3062 	vec3_t		wishdir;
3063 	float		wishspeed;
3064 	float		scale;
3065 	usercmd_t	cmd;
3066 	float		accelerate;
3067 	float		vel;
3068 
3069 	if ( pm->ps->gravity < 0 )
3070 	{//float away
3071 		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3072 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
3073 		pml.groundPlane = qfalse;
3074 		pml.walking = qfalse;
3075 		if ( pm->waterlevel > 1 ) {
3076 			PM_WaterMove();
3077 		} else {
3078 			PM_AirMove();
3079 		}
3080 		return;
3081 	}
3082 
3083 	if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
3084 		// begin swimming
3085 		PM_WaterMove();
3086 		return;
3087 	}
3088 
3089 
3090 	if ( PM_CheckJump () ) {
3091 		// jumped away
3092 		if ( pm->waterlevel > 1 ) {
3093 			PM_WaterMove();
3094 		} else {
3095 			PM_AirMove();
3096 		}
3097 		return;
3098 	}
3099 
3100 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE &&//on ground
3101 		pm->ps->velocity[2] <= 0 &&//not going up
3102 		pm->ps->pm_flags&PMF_TIME_KNOCKBACK )//knockback fimter on (stops friction)
3103 	{
3104 		pm->ps->pm_flags &= ~PMF_TIME_KNOCKBACK;
3105 	}
3106 
3107 	qboolean slide = qfalse;
3108 	if ( pm->ps->pm_type == PM_DEAD )
3109 	{//corpse
3110 		if ( g_entities[pm->ps->groundEntityNum].client )
3111 		{//on a client
3112 			if ( g_entities[pm->ps->groundEntityNum].health > 0 )
3113 			{//a living client
3114 				//no friction
3115 				slide = qtrue;
3116 			}
3117 		}
3118 	}
3119 	if ( !slide )
3120 	{
3121 		PM_Friction ();
3122 	}
3123 
3124 	if ( g_debugMelee->integer )
3125 	{
3126 		if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//player
3127 			&& cg.renderingThirdPerson//in third person
3128 			&& ((pm->cmd.buttons&BUTTON_USE)||pm->ps->leanStopDebounceTime)//holding use or leaning
3129 			//&& (pm->ps->forcePowersActive&(1<<FP_SPEED))
3130 			&& pm->ps->groundEntityNum != ENTITYNUM_NONE//on ground
3131 			&& !cg_usingInFrontOf )//nothing to use
3132 		{//holding use stops you from moving
3133 			return;
3134 		}
3135 	}
3136 	fmove = pm->cmd.forwardmove;
3137 	smove = pm->cmd.rightmove;
3138 
3139 	cmd = pm->cmd;
3140 	scale = PM_CmdScale( &cmd );
3141 
3142 	// set the movementDir so clients can rotate the legs for strafing
3143 	PM_SetMovementDir();
3144 
3145 	// project moves down to flat plane
3146 	pml.forward[2] = 0;
3147 	pml.right[2] = 0;
3148 
3149 	// project the forward and right directions onto the ground plane
3150 	PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
3151 	PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
3152 	//
3153 	VectorNormalize (pml.forward);
3154 	VectorNormalize (pml.right);
3155 
3156 /*	if ( ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) && pm->gent->NPC )
3157 	{//speeder control scheme
3158 		vec3_t	vfwd, vrt;
3159 		AngleVectors( ((CVehicleNPC *)pm->gent->NPC)->m_vOrientation, vfwd, vrt, NULL );
3160 
3161 		float speed = pm->ps->speed;
3162 		if ( fmove < 0 )
3163 		{//going backwards
3164 			if ( speed < 0 )
3165 			{//speed is negative, but since our command is reverse, make speed positive
3166 				speed = fabs( speed );
3167 			}
3168 			else if ( speed > 0 )
3169 			{//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually)
3170 				speed *= -1;
3171 			}
3172 		}
3173 		VectorScale( vfwd, speed*fmove/127.0f, wishvel );
3174 		//VectorMA( wishvel, pm->ps->speed*smove/127.0f, vrt, wishvel );
3175 		wishspeed = VectorNormalize2( wishvel, wishdir );
3176 	}
3177 	else*/
3178 
3179 	// Get The WishVel And WishSpeed
3180 	//-------------------------------
3181 	if ( pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare( pm->ps->moveDir, vec3_origin )) )
3182 	{//NPC
3183 
3184 		// If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
3185 		//--------------------------------------------------------------------------------------------
3186 		if ((fmove!=0.0f || smove!=0.0f) &&	VectorCompare(pm->ps->moveDir, vec3_origin))
3187 		{
3188 			//gi.Printf("Generating MoveDir\n");
3189 			for ( i = 0 ; i < 3 ; i++ )
3190 			{
3191 				wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3192 			}
3193 
3194 			VectorCopy( wishvel, wishdir );
3195 			wishspeed = VectorNormalize(wishdir);
3196 			wishspeed *= scale;
3197 		}
3198 		// Otherwise, Use The Move Dir
3199 		//-----------------------------
3200 		else
3201 		{
3202 			wishspeed = pm->ps->speed;
3203 			VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
3204 			VectorCopy( pm->ps->moveDir, wishdir );
3205 		}
3206 
3207 	}
3208 	else
3209 	{
3210 		for ( i = 0 ; i < 3 ; i++ ) {
3211 			wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3212 		}
3213 		// when going up or down slopes the wish velocity should Not be zero
3214 	//	wishvel[2] = 0;
3215 
3216 		VectorCopy (wishvel, wishdir);
3217 		wishspeed = VectorNormalize(wishdir);
3218 		wishspeed *= scale;
3219 	}
3220 
3221 	// Handle negative speed.
3222 	if ( wishspeed < 0 )
3223 	{
3224 		wishspeed = wishspeed * -1.0f;
3225 		VectorScale( wishvel, -1.0f, wishvel );
3226 		VectorScale( wishdir, -1.0f, wishdir );
3227 	}
3228 
3229 	// clamp the speed lower if ducking
3230 	if ( pm->ps->pm_flags & PMF_DUCKED && !PM_InKnockDown( pm->ps ) )
3231 	{
3232 		if ( wishspeed > pm->ps->speed * pm_duckScale )
3233 		{
3234 			wishspeed = pm->ps->speed * pm_duckScale;
3235 		}
3236 	}
3237 
3238 	// clamp the speed lower if wading or walking on the bottom
3239 	if ( pm->waterlevel ) {
3240 		float	waterScale;
3241 
3242 		waterScale = pm->waterlevel / 3.0;
3243 		waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale;
3244 		if ( wishspeed > pm->ps->speed * waterScale ) {
3245 			wishspeed = pm->ps->speed * waterScale;
3246 		}
3247 	}
3248 
3249 	// when a player gets hit, they temporarily lose
3250 	// full control, which allows them to be moved a bit
3251 	if ( Flying == FLY_HOVER )
3252 	{
3253 		accelerate = pm_vehicleaccelerate;
3254 	}
3255 	else if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) || (pm->ps->pm_flags&PMF_TIME_NOFRICTION) )
3256 	{
3257 		accelerate = pm_airaccelerate;
3258 	}
3259 	else
3260 	{
3261 		accelerate = pm_accelerate;
3262 
3263 		// Wind Affects Acceleration
3264 		//===================================================
3265 		if (wishspeed>0.0f && pm->gent && !pml.walking)
3266 		{
3267 			if (gi.WE_GetWindGusting(pm->gent->currentOrigin))
3268 			{
3269 				vec3_t	windDir;
3270 				if (gi.WE_GetWindVector(windDir, pm->gent->currentOrigin))
3271 				{
3272 					if (gi.WE_IsOutside(pm->gent->currentOrigin))
3273 					{
3274 						VectorScale(windDir, -1.0f, windDir);
3275 						accelerate *=  (1.0f - (DotProduct(wishdir, windDir)*0.55f));
3276 					}
3277 				}
3278 			}
3279 		}
3280 		//===================================================
3281 	}
3282 
3283 	PM_Accelerate (wishdir, wishspeed, accelerate);
3284 
3285 	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
3286 	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));
3287 
3288 	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK || (pm->ps->pm_flags&PMF_TIME_NOFRICTION) ) {
3289 		if ( pm->ps->gravity >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE && !VectorLengthSquared( pm->ps->velocity ) && pml.groundTrace.plane.normal[2] == 1.0 )
3290 		{//on ground and not moving and on level ground, no reason to do stupid fucking gravity with the clipvelocity!!!!
3291 		}
3292 		else
3293 		{
3294 			if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) && !(pm->ps->eFlags&EF_FORCE_DRAINED) )
3295 			{
3296 				pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
3297 			}
3298 		}
3299 	} else {
3300 		// don't reset the z velocity for slopes
3301 //		pm->ps->velocity[2] = 0;
3302 	}
3303 
3304 	vel = VectorLength(pm->ps->velocity);
3305 
3306 	// slide along the ground plane
3307 	PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
3308 		pm->ps->velocity, OVERCLIP );
3309 
3310 	// don't decrease velocity when going up or down a slope
3311 	VectorNormalize(pm->ps->velocity);
3312 	VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
3313 
3314 	// don't do anything if standing still
3315 	if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {
3316 		return;
3317 	}
3318 
3319 	if ( pm->ps->gravity <= 0 )
3320 	{//need to apply gravity since we're going to float up from ground
3321 		PM_StepSlideMove( 1 );
3322 	}
3323 	else
3324 	{
3325 		PM_StepSlideMove( 0 );
3326 	}
3327 
3328 	//Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
3329 
3330 }
3331 
3332 
3333 /*
3334 ==============
3335 PM_DeadMove
3336 ==============
3337 */
PM_DeadMove(void)3338 static void PM_DeadMove( void ) {
3339 	float	forward;
3340 
3341 	// If this is a vehicle, tell him he's dead, but give him a little while to do his things.
3342 /*	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->NPC && pm->gent->health != -99999 )
3343 	{
3344 		pm->gent->health = 1;
3345 		((CVehicleNPC *)pm->gent->NPC)->StartDeathDelay( 0 );
3346 	}
3347 	else
3348 	{
3349 		pm->gent->health = 0;
3350 	}*/
3351 
3352 	if ( !pml.walking ) {
3353 		return;
3354 	}
3355 
3356 	// extra friction
3357 
3358 	forward = VectorLength (pm->ps->velocity);
3359 	forward -= 20;
3360 	if ( forward <= 0 ) {
3361 		VectorClear (pm->ps->velocity);
3362 	} else {
3363 		VectorNormalize (pm->ps->velocity);
3364 		VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
3365 	}
3366 }
3367 
3368 
3369 /*
3370 ===============
3371 PM_NoclipMove
3372 ===============
3373 */
PM_NoclipMove(void)3374 static void PM_NoclipMove( void ) {
3375 	float	speed, drop, friction, control, newspeed;
3376 	int			i;
3377 	vec3_t		wishvel;
3378 	float		fmove, smove;
3379 	vec3_t		wishdir;
3380 	float		wishspeed;
3381 	float		scale;
3382 
3383 	if(pm->gent && pm->gent->client)
3384 	{
3385 		pm->ps->viewheight = pm->gent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET;
3386 //		if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
3387 //		{
3388 //			assert(0);
3389 //		}
3390 
3391 		VectorCopy( pm->gent->mins, pm->mins );
3392 		VectorCopy( pm->gent->maxs, pm->maxs );
3393 	}
3394 	else
3395 	{
3396 		pm->ps->viewheight = DEFAULT_MAXS_2 + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
3397 
3398 		if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
3399 		{
3400 			assert(0);
3401 		}
3402 
3403 		pm->mins[0] = DEFAULT_MINS_0;
3404 		pm->mins[1] = DEFAULT_MINS_1;
3405 		pm->mins[2] = DEFAULT_MINS_2;
3406 
3407 		pm->maxs[0] = DEFAULT_MAXS_0;
3408 		pm->maxs[1] = DEFAULT_MAXS_1;
3409 		pm->maxs[2] = DEFAULT_MAXS_2;
3410 	}
3411 
3412 	// friction
3413 
3414 	speed = VectorLength (pm->ps->velocity);
3415 	if (speed < 1)
3416 	{
3417 		VectorCopy (vec3_origin, pm->ps->velocity);
3418 	}
3419 	else
3420 	{
3421 		drop = 0;
3422 
3423 		friction = pm_friction*1.5;	// extra friction
3424 		control = speed < pm_stopspeed ? pm_stopspeed : speed;
3425 		drop += control*friction*pml.frametime;
3426 
3427 		// scale the velocity
3428 		newspeed = speed - drop;
3429 		if (newspeed < 0)
3430 			newspeed = 0;
3431 		newspeed /= speed;
3432 
3433 		VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
3434 	}
3435 
3436 	// accelerate
3437 	scale = PM_CmdScale( &pm->cmd );
3438 	if (pm->cmd.buttons & BUTTON_ATTACK) {	//turbo boost
3439 		scale *= 10;
3440 	}
3441 	if (pm->cmd.buttons & BUTTON_ALT_ATTACK) {	//turbo boost
3442 		scale *= 10;
3443 	}
3444 
3445 	fmove = pm->cmd.forwardmove;
3446 	smove = pm->cmd.rightmove;
3447 
3448 	for (i=0 ; i<3 ; i++)
3449 		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
3450 	wishvel[2] += pm->cmd.upmove;
3451 
3452 	VectorCopy (wishvel, wishdir);
3453 	wishspeed = VectorNormalize(wishdir);
3454 	wishspeed *= scale;
3455 
3456 	PM_Accelerate( wishdir, wishspeed, pm_accelerate );
3457 
3458 	// move
3459 	VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
3460 }
3461 
3462 //============================================================================
3463 
PM_DamageForDelta(int delta)3464 static float PM_DamageForDelta( int delta )
3465 {
3466 	float damage = delta;
3467 	if ( pm->gent->NPC )
3468 	{
3469 		if ( pm->ps->weapon == WP_SABER
3470 			|| (pm->gent->client && pm->gent->client->NPC_class == CLASS_REBORN) )
3471 		{//FIXME: for now Jedi take no falling damage, but really they should if pushed off?
3472 			damage = 0;
3473 		}
3474 	}
3475 	else if ( pm->ps->clientNum < MAX_CLIENTS )
3476 	{
3477 		if ( damage < 50 )
3478 		{
3479 			if ( damage > 24 )
3480 			{
3481 				damage = damage - 25;
3482 			}
3483 		}
3484 		else
3485 		{
3486 			damage *= 0.5f;
3487 		}
3488 	}
3489 	return damage * 0.5f;
3490 }
3491 
PM_CrashLandDamage(int damage)3492 static void PM_CrashLandDamage( int damage )
3493 {
3494 	if ( pm->gent )
3495 	{
3496 		int dflags = DAMAGE_NO_ARMOR;
3497 		if ( pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
3498 		{
3499 			damage = 1000;
3500 			dflags |= DAMAGE_DIE_ON_IMPACT;
3501 		}
3502 		else
3503 		{
3504 			damage = PM_DamageForDelta( damage );
3505 
3506 			if ( (pm->gent->flags&FL_NO_IMPACT_DMG) )
3507 				return;
3508 		}
3509 
3510 		if ( damage )
3511 		{
3512 			pm->gent->painDebounceTime = level.time + 200;	// no normal pain sound
3513 			G_Damage( pm->gent, NULL, player, NULL, NULL, damage, dflags, MOD_FALLING );
3514 		}
3515 	}
3516 }
3517 
3518 /*
3519 static float PM_CrashLandDelta( vec3_t org, vec3_t prevOrg, vec3_t prev_vel, float grav, int waterlevel )
3520 {
3521 	float		delta;
3522 	float		dist;
3523 	float		vel, acc;
3524 	float		t;
3525 	float		a, b, c, den;
3526 
3527 	// calculate the exact velocity on landing
3528 	dist = org[2] - prevOrg[2];
3529 	vel = prev_vel[2];
3530 	acc = -grav;
3531 
3532 	a = acc / 2;
3533 	b = vel;
3534 	c = -dist;
3535 
3536 	den =  b * b - 4 * a * c;
3537 	if ( den < 0 )
3538 	{
3539 		return 0;
3540 	}
3541 	t = (-b - sqrt( den ) ) / ( 2 * a );
3542 
3543 	delta = vel + t * acc;
3544 	delta = delta*delta * 0.0001;
3545 
3546 	// never take falling damage if completely underwater
3547 	if ( waterlevel == 3 )
3548 	{
3549 		return 0;
3550 	}
3551 	// reduce falling damage if there is standing water
3552 	if ( waterlevel == 2 )
3553 	{
3554 		delta *= 0.25;
3555 	}
3556 	if ( waterlevel == 1 )
3557 	{
3558 		delta *= 0.5;
3559 	}
3560 
3561 	return delta;
3562 }
3563 */
3564 
PM_CrashLandDelta(vec3_t prev_vel,int waterlevel)3565 static float PM_CrashLandDelta( vec3_t prev_vel, int waterlevel )
3566 {
3567 	float delta;
3568 
3569 	if ( pm->waterlevel == 3 )
3570 	{
3571 		return 0;
3572 	}
3573 	delta = fabs(prev_vel[2])/10;//VectorLength( prev_vel )
3574 
3575 	// reduce falling damage if there is standing water
3576 	if ( pm->waterlevel == 2 )
3577 	{
3578 		delta *= 0.25;
3579 	}
3580 	if ( pm->waterlevel == 1 )
3581 	{
3582 		delta *= 0.5;
3583 	}
3584 
3585 	return delta;
3586 }
3587 
PM_GetLandingAnim(void)3588 int PM_GetLandingAnim( void )
3589 {
3590 	int anim = pm->ps->legsAnim;
3591 
3592 	//special cases:
3593 	if ( anim == BOTH_FLIP_ATTACK7
3594 		|| anim == BOTH_FLIP_HOLD7 )
3595 	{
3596         return BOTH_FLIP_LAND;
3597 	}
3598 	else if ( anim == BOTH_FLIP_LAND )
3599 	{
3600 		if ( !g_allowBunnyhopping->integer ) {
3601 			//stick landings some
3602 			pm->ps->velocity[0] *= 0.5f;
3603 			pm->ps->velocity[1] *= 0.5f;
3604 		}
3605 		return BOTH_LAND1;
3606 	}
3607 	else if ( PM_InAirKickingAnim( anim ) )
3608 	{
3609 		switch ( anim )
3610 		{
3611 		case BOTH_A7_KICK_F_AIR:
3612 			return BOTH_FORCELAND1;
3613 			break;
3614 		case BOTH_A7_KICK_B_AIR:
3615 			return BOTH_FORCELANDBACK1;
3616 			break;
3617 		case BOTH_A7_KICK_R_AIR:
3618 			return BOTH_FORCELANDRIGHT1;
3619 			break;
3620 		case BOTH_A7_KICK_L_AIR:
3621 			return BOTH_FORCELANDLEFT1;
3622 			break;
3623 		}
3624 	}
3625 
3626 	if ( PM_SpinningAnim( anim ) || PM_SaberInSpecialAttack( anim ) )
3627 	{
3628 		return -1;
3629 	}
3630 	switch ( anim )
3631 	{
3632 	case BOTH_FORCEJUMPLEFT1:
3633 	case BOTH_FORCEINAIRLEFT1:
3634 		anim = BOTH_FORCELANDLEFT1;
3635 		if ( !g_allowBunnyhopping->integer ) {
3636 			//stick landings some
3637 			pm->ps->velocity[0] *= 0.5f;
3638 			pm->ps->velocity[1] *= 0.5f;
3639 		}
3640 		break;
3641 	case BOTH_FORCEJUMPRIGHT1:
3642 	case BOTH_FORCEINAIRRIGHT1:
3643 		anim = BOTH_FORCELANDRIGHT1;
3644 		if ( !g_allowBunnyhopping->integer ) {
3645 			//stick landings some
3646 			pm->ps->velocity[0] *= 0.5f;
3647 			pm->ps->velocity[1] *= 0.5f;
3648 		}
3649 		break;
3650 	case BOTH_FORCEJUMP1:
3651 	case BOTH_FORCEINAIR1:
3652 		if ( !g_allowBunnyhopping->integer ) {
3653 			//stick landings some
3654 			pm->ps->velocity[0] *= 0.5f;
3655 			pm->ps->velocity[1] *= 0.5f;
3656 		}
3657 		anim = BOTH_FORCELAND1;
3658 		break;
3659 	case BOTH_FORCEJUMPBACK1:
3660 	case BOTH_FORCEINAIRBACK1:
3661 		if ( !g_allowBunnyhopping->integer ) {
3662 			//stick landings some
3663 			pm->ps->velocity[0] *= 0.5f;
3664 			pm->ps->velocity[1] *= 0.5f;
3665 		}
3666 		anim = BOTH_FORCELANDBACK1;
3667 		break;
3668 	case BOTH_JUMPLEFT1:
3669 	case BOTH_INAIRLEFT1:
3670 		anim = BOTH_LANDLEFT1;
3671 		if ( !g_allowBunnyhopping->integer ) {
3672 			//stick landings some
3673 			pm->ps->velocity[0] *= 0.5f;
3674 			pm->ps->velocity[1] *= 0.5f;
3675 		}
3676 		break;
3677 	case BOTH_JUMPRIGHT1:
3678 	case BOTH_INAIRRIGHT1:
3679 		anim = BOTH_LANDRIGHT1;
3680 		if ( !g_allowBunnyhopping->integer ) {
3681 			//stick landings some
3682 			pm->ps->velocity[0] *= 0.5f;
3683 			pm->ps->velocity[1] *= 0.5f;
3684 		}
3685 		break;
3686 	case BOTH_JUMP1:
3687 	case BOTH_INAIR1:
3688 		anim = BOTH_LAND1;
3689 		if ( !g_allowBunnyhopping->integer ) {
3690 			//stick landings some
3691 			pm->ps->velocity[0] *= 0.5f;
3692 			pm->ps->velocity[1] *= 0.5f;
3693 		}
3694 		break;
3695 	case BOTH_JUMPBACK1:
3696 	case BOTH_INAIRBACK1:
3697 		anim = BOTH_LANDBACK1;
3698 		if ( !g_allowBunnyhopping->integer ) {
3699 			//stick landings some
3700 			pm->ps->velocity[0] *= 0.5f;
3701 			pm->ps->velocity[1] *= 0.5f;
3702 		}
3703 		break;
3704 	case BOTH_BUTTERFLY_LEFT:
3705 	case BOTH_BUTTERFLY_RIGHT:
3706 	case BOTH_BUTTERFLY_FL1:
3707 	case BOTH_BUTTERFLY_FR1:
3708 	case BOTH_FJSS_TR_BL:
3709 	case BOTH_FJSS_TL_BR:
3710 	case BOTH_LUNGE2_B__T_:
3711 	case BOTH_FORCELEAP2_T__B_:
3712 	case BOTH_ARIAL_LEFT:
3713 	case BOTH_ARIAL_RIGHT:
3714 	case BOTH_ARIAL_F1:
3715 	case BOTH_CARTWHEEL_LEFT:
3716 	case BOTH_CARTWHEEL_RIGHT:
3717 	case BOTH_JUMPFLIPSLASHDOWN1://#
3718 	case BOTH_JUMPFLIPSTABDOWN://#
3719 	case BOTH_JUMPATTACK6:
3720 	case BOTH_JUMPATTACK7:
3721 	case BOTH_A7_KICK_F:
3722 	case BOTH_A7_KICK_B:
3723 	case BOTH_A7_KICK_R:
3724 	case BOTH_A7_KICK_L:
3725 	case BOTH_A7_KICK_S:
3726 	case BOTH_A7_KICK_BF:
3727 	case BOTH_A7_KICK_RL:
3728 	case BOTH_A7_KICK_F_AIR:
3729 	case BOTH_A7_KICK_B_AIR:
3730 	case BOTH_A7_KICK_R_AIR:
3731 	case BOTH_A7_KICK_L_AIR:
3732 	case BOTH_STABDOWN:
3733 	case BOTH_STABDOWN_STAFF:
3734 	case BOTH_STABDOWN_DUAL:
3735 	case BOTH_A6_SABERPROTECT:
3736 	case BOTH_A7_SOULCAL:
3737 	case BOTH_A1_SPECIAL:
3738 	case BOTH_A2_SPECIAL:
3739 	case BOTH_A3_SPECIAL:
3740 	case BOTH_PULL_IMPALE_STAB:
3741 	case BOTH_PULL_IMPALE_SWING:
3742 		anim = -1;
3743 		break;
3744 	case BOTH_FORCELONGLEAP_START:
3745 	case BOTH_FORCELONGLEAP_ATTACK:
3746 		return BOTH_FORCELONGLEAP_LAND;
3747 		break;
3748 	case BOTH_WALL_RUN_LEFT://#
3749 	case BOTH_WALL_RUN_RIGHT://#
3750 		if ( pm->ps->legsAnimTimer > 500 )
3751 		{//only land at end of anim
3752 			return -1;
3753 		}
3754 		//NOTE: falls through on purpose!
3755 	default:
3756 		if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP )
3757 		{
3758 			anim = BOTH_LANDBACK1;
3759 		}
3760 		else
3761 		{
3762 			anim = BOTH_LAND1;
3763 		}
3764 		if ( !g_allowBunnyhopping->integer ) {
3765 			//stick landings some
3766 			pm->ps->velocity[0] *= 0.5f;
3767 			pm->ps->velocity[1] *= 0.5f;
3768 		}
3769 		break;
3770 	}
3771 	return anim;
3772 }
3773 
G_StartRoll(gentity_t * ent,int anim)3774 void G_StartRoll( gentity_t *ent, int anim )
3775 {
3776 	NPC_SetAnim(ent,SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS);
3777 	ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer - 200;//just to make sure it's cleared when roll is done
3778 	G_AddEvent( ent, EV_ROLL, 0 );
3779 	ent->client->ps.saberMove = LS_NONE;
3780 }
3781 
PM_TryRoll(void)3782 static qboolean PM_TryRoll( void )
3783 {
3784 	float rollDist = 192;//was 64;
3785 	if ( PM_SaberInAttack( pm->ps->saberMove ) || PM_SaberInSpecialAttack( pm->ps->torsoAnim )
3786 		|| PM_SpinningSaberAnim( pm->ps->legsAnim )
3787 		|| ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&PM_SaberInStart( pm->ps->saberMove )) )
3788 	{//attacking or spinning (or, if player, starting an attack)
3789 		if ( PM_CanRollFromSoulCal( pm->ps ) )
3790 		{//hehe
3791 		}
3792 		else
3793 		{
3794 			return qfalse;
3795 		}
3796 	}
3797 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (!cg.renderingThirdPerson || cg.zoomMode) )
3798 	{//player can't do this in 1st person
3799 		return qfalse;
3800 	}
3801 	if ( !pm->gent )
3802 	{
3803 		return qfalse;
3804 	}
3805 	if ( (pm->ps->saber[0].saberFlags&SFL_NO_ROLLS) )
3806 	{
3807 		return qfalse;
3808 	}
3809 	if ( pm->ps->dualSabers
3810 		&& (pm->ps->saber[1].saberFlags&SFL_NO_ROLLS) )
3811 	{
3812 		return qfalse;
3813 	}
3814 	if ( pm->ps->clientNum && pm->gent->NPC )
3815 	{//NPC
3816 		if ( pm->gent->NPC->scriptFlags&SCF_NO_ACROBATICS )
3817 		{//scripted to never do acrobatics
3818 			return qfalse;
3819 		}
3820 
3821 		if ( pm->ps->weapon == WP_SABER )
3822 		{//jedi/reborn
3823 			if ( pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank < RANK_LT_JG )
3824 			{//reborn/jedi who are not acrobats or fencers can't do any of these acrobatics
3825 				return qfalse;
3826 			}
3827 		}
3828 		else
3829 		{//non-jedi/reborn
3830 			if ( pm->ps->weapon != WP_NONE )//not empty-handed...who would that be???
3831 			{//only jedi/reborn NPCs should be able to do rolls (with a few exceptions)
3832 				if ( !pm->gent
3833 					|| !pm->gent->client
3834 					|| (pm->gent->client->NPC_class != CLASS_BOBAFETT //boba can roll with it, baby
3835 						&& pm->gent->client->NPC_class != CLASS_REBORN //reborn using weapons other than saber can still roll
3836 					))
3837 				{//can't roll
3838 					return qfalse;
3839 				}
3840 			}
3841 		}
3842 	}
3843 
3844 	vec3_t fwd, right, traceto,
3845 		mins = { pm->mins[0], pm->mins[1], pm->mins[2] + STEPSIZE },
3846 		maxs = { pm->maxs[0], pm->maxs[1], (float)pm->gent->client->crouchheight },
3847 		fwdAngles = { 0, pm->ps->viewangles[YAW], 0 };
3848 	trace_t	trace;
3849 	int		anim = -1;
3850 	AngleVectors( fwdAngles, fwd, right, NULL );
3851 	//FIXME: trace ahead for clearance to roll
3852 	if ( pm->cmd.forwardmove )
3853 	{
3854 		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
3855 		{
3856 			anim = BOTH_ROLL_B;
3857 			VectorMA( pm->ps->origin, -rollDist, fwd, traceto );
3858 		}
3859 		else
3860 		{
3861 			anim = BOTH_ROLL_F;
3862 			VectorMA( pm->ps->origin, rollDist, fwd, traceto );
3863 		}
3864 	}
3865 	else if ( pm->cmd.rightmove > 0 )
3866 	{
3867 		anim = BOTH_ROLL_R;
3868 		VectorMA( pm->ps->origin, rollDist, right, traceto );
3869 	}
3870 	else if ( pm->cmd.rightmove < 0 )
3871 	{
3872 		anim = BOTH_ROLL_L;
3873 		VectorMA( pm->ps->origin, -rollDist, right, traceto );
3874 	}
3875 	else
3876 	{//???
3877 	}
3878 	if ( anim != -1 )
3879 	{
3880 		qboolean roll = qfalse;
3881 		int		clipmask = CONTENTS_SOLID;
3882 		if ( pm->ps->clientNum )
3883 		{
3884 			clipmask |= (CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP);
3885 		}
3886 		else
3887 		{
3888 			if ( pm->gent && pm->gent->enemy && pm->gent->enemy->health > 0 )
3889 			{//player can always roll in combat
3890 				roll = qtrue;
3891 			}
3892 			else
3893 			{
3894 				clipmask |= CONTENTS_PLAYERCLIP;
3895 			}
3896 		}
3897 		if ( !roll )
3898 		{
3899 			pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, clipmask, (EG2_Collision)0, 0 );
3900 			if ( trace.fraction >= 1.0f )
3901 			{//okay, clear, check for a bottomless drop
3902 				vec3_t	top;
3903 				VectorCopy( traceto, top );
3904 				traceto[2] -= 256;
3905 				pm->trace( &trace, top, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0 );
3906 				if ( trace.fraction < 1.0f )
3907 				{//not a bottomless drop
3908 					roll = qtrue;
3909 				}
3910 			}
3911 			else
3912 			{//hit an architectural obstruction
3913 				if ( pm->ps->clientNum )
3914 				{//NPCs don't care about rolling into walls, just off ledges
3915 					if ( !(trace.contents&CONTENTS_BOTCLIP) )
3916 					{
3917 						roll = qtrue;
3918 					}
3919 				}
3920 				else if ( G_EntIsDoor( trace.entityNum ) )
3921 				{//okay to roll into a door
3922 					if ( G_EntIsUnlockedDoor( trace.entityNum ) )
3923 					{//if it's an auto-door
3924 						roll = qtrue;
3925 					}
3926 				}
3927 				else
3928 				{//check other conditions
3929 					gentity_t *traceEnt = &g_entities[trace.entityNum];
3930 					if ( traceEnt && (traceEnt->svFlags&SVF_GLASS_BRUSH) )
3931 					{//okay to roll through glass
3932 						roll = qtrue;
3933 					}
3934 				}
3935 			}
3936 		}
3937 		if ( roll )
3938 		{
3939 			G_StartRoll( pm->gent, anim );
3940 			return qtrue;
3941 		}
3942 	}
3943 	return qfalse;
3944 }
3945 
3946 extern void CG_LandingEffect( vec3_t origin, vec3_t normal, int material );
PM_CrashLandEffect(void)3947 static void PM_CrashLandEffect( void )
3948 {
3949 	if ( pm->waterlevel )
3950 	{
3951 		return;
3952 	}
3953 	float delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );?
3954 	if ( delta >= 30 )
3955 	{
3956 		vec3_t bottom = {pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1};
3957 		CG_LandingEffect( bottom, pml.groundTrace.plane.normal, (pml.groundTrace.surfaceFlags&MATERIAL_MASK) );
3958 	}
3959 }
3960 /*
3961 =================
3962 PM_CrashLand
3963 
3964 Check for hard landings that generate sound events
3965 =================
3966 */
PM_CrashLand(void)3967 static void PM_CrashLand( void )
3968 {
3969 	float		delta = 0;
3970 	qboolean	forceLanding = qfalse;
3971 
3972 	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
3973 	{
3974 		if ( pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
3975 		{
3976 			float dot = DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal );
3977 			//Com_Printf("%i:crashland %4.2f\n", c_pmove, pm->ps->velocity[2]);
3978 			if ( dot < -100.0f )
3979 			{
3980 				//NOTE: never hits this anyway
3981 				if ( pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX )
3982 				{//make sparks
3983 					if ( !Q_irand( 0, 3 ) )
3984 					{//FIXME: debounce
3985 						G_PlayEffect( pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX, pm->ps->origin, pml.groundTrace.plane.normal );
3986 					}
3987 				}
3988 				int damage = floor(fabs(dot+100)/10.0f);
3989 				if ( damage >= 0 )
3990 				{
3991 					G_Damage( pm->gent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING );
3992 				}
3993 			}
3994 		}
3995 		return;
3996 	}
3997 
3998 	if ( (pm->ps->pm_flags&PMF_TRIGGER_PUSHED) )
3999 	{
4000 		delta = 21;//?
4001 		forceLanding = qtrue;
4002 	}
4003 	else
4004 	{
4005 		if ( pm->gent && pm->gent->NPC && pm->gent->NPC->aiFlags & NPCAI_DIE_ON_IMPACT )
4006 		{//have to do death on impact if we are falling to our death, FIXME: should we avoid any additional damage this func?
4007 			PM_CrashLandDamage( 1000 );
4008 		}
4009 		else if ( pm->gent
4010 			&& pm->gent->client
4011 			&& (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) )
4012 		{
4013 			if ( JET_Flying( pm->gent ) )
4014 			{
4015 				if ( pm->gent->client->NPC_class == CLASS_BOBAFETT
4016 					|| (pm->gent->client->NPC_class == CLASS_ROCKETTROOPER&&pm->gent->NPC&&pm->gent->NPC->rank<RANK_LT) )
4017 				{
4018 					JET_FlyStop( pm->gent );
4019 				}
4020 				else
4021 				{
4022 					pm->ps->velocity[2] += Q_flrand( 100, 200 );
4023 				}
4024 				PM_AddEvent( EV_FALL_SHORT );
4025 			}
4026 			if ( pm->ps->forceJumpZStart )
4027 			{//we were force-jumping
4028 				forceLanding = qtrue;
4029 			}
4030 			delta = 1;
4031 		}
4032 		else if ( pm->ps->jumpZStart && (pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_1||(pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) )
4033 		{//we were force-jumping
4034 			if ( pm->ps->origin[2] >= pm->ps->jumpZStart )
4035 			{//we landed at same height or higher than we landed
4036 				if ( pm->ps->forceJumpZStart )
4037 				{//we were force-jumping
4038 					forceLanding = qtrue;
4039 				}
4040 				delta = 0;
4041 			}
4042 			else
4043 			{//take off some of it, at least
4044 				delta = (pm->ps->jumpZStart-pm->ps->origin[2]);
4045 				float dropAllow = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]];
4046 				if ( dropAllow < 128 )
4047 				{//always allow a drop from 128, at least
4048 					dropAllow = 128;
4049 				}
4050 				if ( delta > forceJumpHeight[FORCE_LEVEL_1] )
4051 				{//will have to use force jump ability to absorb some of it
4052 					forceLanding = qtrue;//absorbed some - just to force the correct animation to play below
4053 				}
4054 				delta = (delta - dropAllow)/2;
4055 			}
4056 			if ( delta < 1 )
4057 			{
4058 				delta = 1;
4059 			}
4060 		}
4061 
4062 		if ( !delta )
4063 		{
4064 			delta = PM_CrashLandDelta( pml.previous_velocity, pm->waterlevel );
4065 		}
4066 	}
4067 
4068 	PM_CrashLandEffect();
4069 
4070 	if ( (pm->ps->pm_flags&PMF_DUCKED) && (level.time-pm->ps->lastOnGround)>500 )
4071 	{//must be crouched and have been inthe air for half a second minimum
4072 		if( !PM_InOnGroundAnim( pm->ps ) && !PM_InKnockDown( pm->ps ) )
4073 		{//roll!
4074 			if ( PM_TryRoll() )
4075 			{//absorb some impact
4076 				delta *= 0.5f;
4077 			}
4078 		}
4079 	}
4080 
4081 	// If he just entered the water (from a fall presumably), absorb some of the impact.
4082 	if ( pm->waterlevel >= 2 )
4083 	{
4084 		delta *= 0.4f;
4085 	}
4086 
4087 	if ( delta < 1 )
4088 	{
4089 		AddSoundEvent( pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue );
4090 		return;
4091 	}
4092 
4093 	qboolean deadFallSound = qfalse;
4094 	if( !PM_InDeathAnim() )
4095 	{
4096 		if ( PM_InAirKickingAnim( pm->ps->legsAnim )
4097 			&& pm->ps->torsoAnim == pm->ps->legsAnim )
4098 		{
4099 			int anim = PM_GetLandingAnim();
4100 			if ( anim != -1 )
4101 			{//interrupting a kick clears everything
4102 				PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4103 				pm->ps->saberMove = LS_READY;
4104 				pm->ps->weaponTime = 0;
4105 				if ( !g_allowBunnyhopping->integer ) {
4106 					//stick landings some
4107 					pm->ps->velocity[0] *= 0.5f;
4108 					pm->ps->velocity[1] *= 0.5f;
4109 				}
4110 			}
4111 		}
4112 		else if ( pm->gent
4113 			&& pm->gent->client
4114 			&& pm->gent->client->NPC_class == CLASS_ROCKETTROOPER )
4115 		{//rockettroopers are simpler
4116 			int anim = PM_GetLandingAnim();
4117 			if ( anim != -1 )
4118 			{
4119 				if ( pm->gent->NPC
4120 					&& pm->gent->NPC->rank < RANK_LT )
4121 				{//special case: ground-based rocket troopers *always* play land anim on whole body
4122 					PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4123 				}
4124 				else
4125 				{
4126 					PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4127 				}
4128 			}
4129 		}
4130 		else if ( pm->cmd.upmove >= 0 && !PM_InKnockDown( pm->ps ) && !PM_InRoll( pm->ps ))
4131 		{//not crouching
4132 			if ( delta > 10
4133 				|| pm->ps->pm_flags & PMF_BACKWARDS_JUMP
4134 				|| (pm->ps->forcePowersActive&(1<<FP_LEVITATION))
4135 				|| forceLanding ) //EV_FALL_SHORT or jumping back or force-land
4136 			{// decide which landing animation to use
4137 				if ( pm->gent
4138 					&& pm->gent->client
4139 					&& (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA ) )
4140 				{
4141 				}
4142 				else
4143 				{
4144 					int anim = PM_GetLandingAnim();
4145 					if ( anim != -1 )
4146 					{
4147 						if ( PM_FlippingAnim( pm->ps->torsoAnim )
4148 							|| PM_SpinningAnim( pm->ps->torsoAnim )
4149 							|| pm->ps->torsoAnim == BOTH_FLIP_LAND )
4150 						{//interrupt these if we're going to play a land
4151 							pm->ps->torsoAnimTimer = 0;
4152 						}
4153 						if ( anim == BOTH_FORCELONGLEAP_LAND )
4154 						{
4155 							if ( pm->gent )
4156 							{
4157 								G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/player/slide.wav" );
4158 							}
4159 							PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4160 						}
4161 						else if ( anim == BOTH_FLIP_LAND
4162 							|| (pm->ps->torsoAnim == BOTH_FLIP_LAND) )
4163 						{
4164 							PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4165 						}
4166 						else if ( PM_InAirKickingAnim( pm->ps->legsAnim )
4167 							&& pm->ps->torsoAnim == pm->ps->legsAnim )
4168 						{//interrupting a kick clears everything
4169 							PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4170 							pm->ps->saberMove = LS_READY;
4171 							pm->ps->weaponTime = 0;
4172 						}
4173 						else if ( PM_ForceJumpingAnim( pm->ps->legsAnim )
4174 							&& pm->ps->torsoAnim == pm->ps->legsAnim )
4175 						{//special case: if land during one of these, set the torso, too, if it's not doing something else
4176 							PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4177 						}
4178 						else
4179 						{
4180 							PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 );	// Only blend over 100ms
4181 						}
4182 					}
4183 				}
4184 			}
4185 		}
4186 	}
4187 	else
4188 	{
4189 		pm->ps->gravity = 1.0;
4190 		//PM_CrashLandDamage( delta );
4191 		if ( pm->gent )
4192 		{
4193 			if ((!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) &&
4194 //				(!(pml.groundTrace.contents & CONTENTS_NODROP)) &&
4195 				(!(pm->pointcontents(pm->ps->origin,pm->ps->clientNum) & CONTENTS_NODROP)))
4196 			{
4197 				if ( pm->waterlevel < 2 )
4198 				{//don't play fallsplat when impact in the water
4199 					deadFallSound = qtrue;
4200 					if ( !(pm->ps->eFlags&EF_NODRAW) )
4201 					{//no sound if no draw
4202 						if ( delta >= 75 )
4203 						{
4204 							G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/player/fallsplat.wav" );
4205 						}
4206 						else
4207 						{
4208 							G_SoundOnEnt( pm->gent, CHAN_BODY, va("sound/player/bodyfall_human%d.wav",Q_irand(1,3)) );
4209 						}
4210 					}
4211 				}
4212 				else
4213 				{
4214 					G_SoundOnEnt( pm->gent, CHAN_BODY, va("sound/player/bodyfall_water%d.wav",Q_irand(1,3)) );
4215 				}
4216 				if ( gi.VoiceVolume[pm->ps->clientNum]
4217 					&& pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
4218 				{//I was talking, so cut it off... with a jump sound?
4219 					if ( !(pm->ps->eFlags&EF_NODRAW) )
4220 					{//no sound if no draw
4221 						G_SoundOnEnt( pm->gent, CHAN_VOICE_ATTEN, "*pain100.wav" );
4222 					}
4223 				}
4224 			}
4225 		}
4226 		if( pm->ps->legsAnim == BOTH_FALLDEATH1 || pm->ps->legsAnim == BOTH_FALLDEATH1INAIR)
4227 		{//FIXME: add a little bounce?
4228 			//FIXME: cut voice channel?
4229 			int old_pm_type = pm->ps->pm_type;
4230 			pm->ps->pm_type = PM_NORMAL;
4231 			//Hack because for some reason PM_SetAnim just returns if you're dead...???
4232 			PM_SetAnim(pm, SETANIM_BOTH, BOTH_FALLDEATH1LAND, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4233 			pm->ps->pm_type = old_pm_type;
4234 			AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_SUSPICIOUS, qfalse, qtrue );
4235 			return;
4236 		}
4237 	}
4238 
4239 	// create a local entity event to play the sound
4240 
4241 	if ( pm->gent && pm->gent->client && pm->gent->client->respawnTime >= level.time - 500 )
4242 	{//just spawned in, don't make a noise
4243 		return;
4244 	}
4245 
4246 	if ( delta >= 75 )
4247 	{
4248 		if ( !deadFallSound )
4249 		{
4250 			if ( !(pm->ps->eFlags&EF_NODRAW) )
4251 			{//no sound if no draw
4252 				PM_AddEvent( EV_FALL_FAR );
4253 			}
4254 		}
4255 		if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
4256 		{
4257 			PM_CrashLandDamage( delta );
4258 		}
4259 		if ( pm->gent )
4260 		{
4261 			if ( pm->gent->s.number == 0 )
4262 			{
4263 				vec3_t	bottom;
4264 
4265 				VectorCopy( pm->ps->origin, bottom );
4266 				bottom[2] += pm->mins[2];
4267 			//	if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
4268 				{
4269 					AddSoundEvent( pm->gent, bottom, 256, AEL_SUSPICIOUS, qfalse, qtrue );
4270 				}
4271 			}
4272 			else if ( pm->ps->stats[STAT_HEALTH] <= 0 && pm->gent && pm->gent->enemy )
4273 			{
4274 				AddSoundEvent( pm->gent->enemy, pm->ps->origin, 256, AEL_DISCOVERED, qfalse, qtrue );
4275 			}
4276 		}
4277 	}
4278 	else if ( delta >= 50 )
4279 	{
4280 		// this is a pain grunt, so don't play it if dead
4281 		if ( pm->ps->stats[STAT_HEALTH] > 0 )
4282 		{
4283 			if ( !deadFallSound )
4284 			{
4285 				if ( !(pm->ps->eFlags&EF_NODRAW) )
4286 				{//no sound if no draw
4287 					PM_AddEvent( EV_FALL_MEDIUM );//damage is dealt in g_active, ClientEvents
4288 				}
4289 			}
4290 			if ( pm->gent )
4291 			{
4292 				if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
4293 				{
4294 					PM_CrashLandDamage( delta );
4295 				}
4296 				if ( pm->gent->s.number == 0 )
4297 				{
4298 					vec3_t	bottom;
4299 
4300 					VectorCopy( pm->ps->origin, bottom );
4301 					bottom[2] += pm->mins[2];
4302 				//	if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
4303 					{
4304 						AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR, qfalse, qtrue );
4305 					}
4306 				}
4307 			}
4308 		}
4309 	}
4310 	else if ( delta >= 30 )
4311 	{
4312 		if ( !deadFallSound )
4313 		{
4314 			if ( !(pm->ps->eFlags&EF_NODRAW) )
4315 			{//no sound if no draw
4316 				PM_AddEvent( EV_FALL_SHORT );
4317 			}
4318 		}
4319 		if ( pm->gent )
4320 		{
4321 			if ( pm->gent->s.number == 0 )
4322 			{
4323 				vec3_t	bottom;
4324 
4325 				VectorCopy( pm->ps->origin, bottom );
4326 				bottom[2] += pm->mins[2];
4327 		//		if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
4328 				{
4329 					AddSoundEvent( pm->gent, bottom, 128, AEL_MINOR, qfalse, qtrue );
4330 				}
4331 			}
4332 			else
4333 			{
4334 				if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )
4335 				{
4336 					PM_CrashLandDamage( delta );
4337 				}
4338 			}
4339 		}
4340 	}
4341 	else
4342 	{
4343 		if ( !deadFallSound )
4344 		{
4345 			if ( !(pm->ps->pm_flags&PMF_DUCKED) || !Q_irand( 0, 3 ) )
4346 			{//only 25% chance of making landing alert when ducked
4347 				AddSoundEvent( pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue );
4348 			}
4349 			if ( !(pm->ps->eFlags&EF_NODRAW) )
4350 			{//no sound if no draw
4351 				if ( forceLanding )
4352 				{//we were force-jumping
4353 					PM_AddEvent( EV_FALL_SHORT );
4354 				}
4355 				else
4356 				{
4357 //moved to cg_player				PM_AddEvent( PM_FootstepForSurface() );
4358 				}
4359 			}
4360 		}
4361 	}
4362 
4363 	// start footstep cycle over
4364 	pm->ps->bobCycle = 0;
4365 	if ( pm->gent && pm->gent->client )
4366 	{//stop the force push effect when you land
4367 		pm->gent->forcePushTime = 0;
4368 	}
4369 }
4370 
4371 
4372 
4373 /*
4374 =============
4375 PM_CorrectAllSolid
4376 =============
4377 */
PM_CorrectAllSolid(void)4378 static void PM_CorrectAllSolid( void ) {
4379 	if ( pm->debugLevel ) {
4380 		Com_Printf("%i:allsolid\n", c_pmove);	//NOTENOTE: If this ever happens, I'd really like to see this print!
4381 	}
4382 
4383 	// FIXME: jitter around
4384 
4385 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
4386 	pml.groundPlane = qfalse;
4387 	pml.walking = qfalse;
4388 }
4389 
FlyingCreature(gentity_t * ent)4390 qboolean FlyingCreature( gentity_t *ent )
4391 {
4392 	if ( ent->client->ps.gravity <= 0 && (ent->svFlags&SVF_CUSTOM_GRAVITY) )
4393 	{
4394 		return qtrue;
4395 	}
4396 	return qfalse;
4397 }
4398 
PM_RocketeersAvoidDangerousFalls(void)4399 qboolean PM_RocketeersAvoidDangerousFalls( void )
4400 {
4401 	if ( pm->gent->NPC
4402 		&& pm->gent->client
4403 		&& (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) )
4404 	{//fixme:  fall through if jetpack broken?
4405 		if ( JET_Flying( pm->gent ) )
4406 		{
4407 			if ( pm->gent->client->NPC_class == CLASS_BOBAFETT )
4408 			{
4409 				pm->gent->client->jetPackTime = level.time + 2000;
4410 				//Wait, what if the effect is already playing, how do we know???
4411 				//G_PlayEffect( G_EffectIndex( "boba/jetSP" ), pm->gent->playerModel, pm->gent->genericBolt1, pm->gent->s.number, pm->gent->currentOrigin, pm->gent->client->jetPackTime-level.time );
4412 			}
4413 			else
4414 			{
4415 				pm->gent->client->jetPackTime = Q3_INFINITE;
4416 			}
4417 		}
4418 		else
4419 		{
4420 			TIMER_Set( pm->gent, "jetRecharge", 0 );
4421 			JET_FlyStart( pm->gent );
4422 		}
4423 		return qtrue;
4424 	}
4425 	return qfalse;
4426 }
4427 
PM_FallToDeath(void)4428 static void PM_FallToDeath( void )
4429 {
4430 	if ( !pm->gent )
4431 	{
4432 		return;
4433 	}
4434 
4435 	if ( PM_RocketeersAvoidDangerousFalls() )
4436 	{
4437 		return;
4438 	}
4439 
4440 	// If this is a vehicle, more precisely an animal...
4441 	if ( pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
4442 	{
4443 		Vehicle_t *pVeh = pm->gent->m_pVehicle;
4444 		pVeh->m_pVehicleInfo->EjectAll( pVeh );
4445 	}
4446 	else
4447 	{
4448 		if ( PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 ) )
4449 		{
4450 			PM_SetAnim(pm,SETANIM_LEGS,BOTH_FALLDEATH1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4451 		}
4452 		else
4453 		{
4454 			PM_SetAnim(pm,SETANIM_LEGS,BOTH_DEATH1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
4455 		}
4456 		G_SoundOnEnt( pm->gent, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN
4457 	}
4458 
4459 	if ( pm->gent->NPC )
4460 	{
4461 		pm->gent->NPC->aiFlags |= NPCAI_DIE_ON_IMPACT;
4462 		pm->gent->NPC->nextBStateThink = Q3_INFINITE;
4463 	}
4464 	pm->ps->friction = 1;
4465 }
4466 
PM_ForceJumpAnimForJumpAnim(int anim)4467 int PM_ForceJumpAnimForJumpAnim( int anim )
4468 {
4469 	switch( anim )
4470 	{
4471 	case BOTH_JUMP1:				//# Jump - wind-up and leave ground
4472 		anim = BOTH_FORCEJUMP1;			//# Jump - wind-up and leave ground
4473 		break;
4474 	case BOTH_INAIR1:			//# In air loop (from jump)
4475 		anim = BOTH_FORCEINAIR1;			//# In air loop (from jump)
4476 		break;
4477 	case BOTH_LAND1:				//# Landing (from in air loop)
4478 		anim = BOTH_FORCELAND1;				//# Landing (from in air loop)
4479 		break;
4480 	case BOTH_JUMPBACK1:			//# Jump backwards - wind-up and leave ground
4481 		anim = BOTH_FORCEJUMPBACK1;			//# Jump backwards - wind-up and leave ground
4482 		break;
4483 	case BOTH_INAIRBACK1:		//# In air loop (from jump back)
4484 		anim = BOTH_FORCEINAIRBACK1;		//# In air loop (from jump back)
4485 		break;
4486 	case BOTH_LANDBACK1:			//# Landing backwards(from in air loop)
4487 		anim = BOTH_FORCELANDBACK1;			//# Landing backwards(from in air loop)
4488 		break;
4489 	case BOTH_JUMPLEFT1:			//# Jump left - wind-up and leave ground
4490 		anim = BOTH_FORCEJUMPLEFT1;			//# Jump left - wind-up and leave ground
4491 		break;
4492 	case BOTH_INAIRLEFT1:		//# In air loop (from jump left)
4493 		anim = BOTH_FORCEINAIRLEFT1;		//# In air loop (from jump left)
4494 		break;
4495 	case BOTH_LANDLEFT1:			//# Landing left(from in air loop)
4496 		anim = BOTH_FORCELANDLEFT1;			//# Landing left(from in air loop)
4497 		break;
4498 	case BOTH_JUMPRIGHT1:		//# Jump right - wind-up and leave ground
4499 		anim = BOTH_FORCEJUMPRIGHT1;		//# Jump right - wind-up and leave ground
4500 		break;
4501 	case BOTH_INAIRRIGHT1:		//# In air loop (from jump right)
4502 		anim = BOTH_FORCEINAIRRIGHT1;		//# In air loop (from jump right)
4503 		break;
4504 	case BOTH_LANDRIGHT1:		//# Landing right(from in air loop)
4505 		anim = BOTH_FORCELANDRIGHT1;		//# Landing right(from in air loop)
4506 		break;
4507 	}
4508 	return anim;
4509 }
4510 
PM_SetVehicleAngles(vec3_t normal)4511 static void PM_SetVehicleAngles( vec3_t normal )
4512 {
4513 	if ( !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE )
4514 		return;
4515 
4516 	Vehicle_t *pVeh = pm->gent->m_pVehicle;
4517 
4518 	//float	curVehicleBankingSpeed;
4519 	float	vehicleBankingSpeed = pVeh->m_pVehicleInfo->bankingSpeed;//0.25f
4520 	vec3_t	vAngles;
4521 
4522 	if ( vehicleBankingSpeed <= 0
4523 		|| ( pVeh->m_pVehicleInfo->pitchLimit <= 0 && pVeh->m_pVehicleInfo->rollLimit <= 0 ) )
4524 	{//don't bother, this vehicle doesn't bank
4525 		return;
4526 	}
4527 	//FIXME: do 3 traces to define a plane and use that... smoothes it out some, too...
4528 	//pitch_roll_for_slope( pm->gent, normal, vAngles );
4529 	//FIXME: maybe have some pitch control in water and/or air?
4530 
4531 	//center of gravity affects pitch in air/water (FIXME: what about roll?)
4532 	//float 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
4533 
4534 	VectorClear( vAngles );
4535 
4536 
4537  	if (pm->waterlevel>0 || (normal && (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA))))
4538 	{//in water
4539 	//	vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5);
4540 	}
4541 	else if ( normal )
4542 	{//have a valid surface below me
4543 		pitch_roll_for_slope( pm->gent, normal, vAngles );
4544 		float deltaPitch = (vAngles[PITCH] - pVeh->m_vOrientation[PITCH]);
4545  		if (deltaPitch< -10.0f)
4546 		{
4547 			vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 10.0f;
4548 		}
4549 		else if (deltaPitch>10.0f)
4550 		{
4551 			vAngles[PITCH] = pVeh->m_vOrientation[PITCH] + 10.0f;
4552 		}
4553 	}
4554 	else
4555 	{//in air, let pitch match view...?
4556 		//FIXME: take center of gravity into account
4557 	 	vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 1;
4558 		if (vAngles[PITCH]<-15)
4559 		{
4560 			vAngles[PITCH]=-15;
4561 		}
4562 		//don't bank so fast when in the air
4563 		vehicleBankingSpeed *= 0.125f;
4564 	}
4565 	//NOTE: if angles are flat and we're moving through air (not on ground),
4566 	//		then pitch/bank?
4567 	if (pVeh->m_ulFlags & VEH_SPINNING)
4568 	{
4569 		vAngles[ROLL] = pVeh->m_vOrientation[ROLL] - 25;
4570 	}
4571 	else if (pVeh->m_ulFlags & VEH_OUTOFCONTROL)
4572 	{
4573 		//vAngles[ROLL] = pVeh->m_vOrientation[ROLL] + 5;
4574 	}
4575 	else if ( pVeh->m_pVehicleInfo->rollLimit > 0 )
4576 	{
4577 		//roll when banking
4578 		vec3_t	velocity;
4579 		float	speed;
4580 		VectorCopy( pm->ps->velocity, velocity );
4581 		speed = VectorNormalize( velocity );
4582 		if ( speed>0.01f )
4583 		{
4584 			vec3_t	rt, tempVAngles;
4585 			float	side, dotp;
4586 
4587 
4588 			VectorCopy( pVeh->m_vOrientation, tempVAngles );
4589 			tempVAngles[ROLL] = 0;
4590 			AngleVectors( tempVAngles, NULL, rt, NULL );
4591 			dotp = DotProduct( velocity, rt );
4592 			//if (fabsf(dotp)>0.5f)
4593 			{
4594 				speed *= dotp;
4595 
4596 
4597 				// Magic number fun!  Speed is used for banking, so modulate the speed by a sine wave
4598 				//FIXME: this banks too early
4599 				//speed *= sin( (150 + pml.frametime) * 0.003 );
4600 				if (level.time < pVeh->m_iTurboTime)
4601 				{
4602 					speed /= pVeh->m_pVehicleInfo->turboSpeed;
4603 				}
4604 				else
4605 				{
4606 					speed /= pVeh->m_pVehicleInfo->speedMax;
4607 				}
4608 
4609 			///	if (pm->cmd.forwardmove==0)
4610 			//	{
4611 			//		speed *= 0.5;
4612 			//	}
4613 			//	if (pm->cmd.forwardmove<0)
4614 			//	{
4615 			//		speed *= 0.1f;
4616 			//	}
4617 				if (pVeh->m_ulFlags & VEH_SLIDEBREAKING)
4618 				{
4619 					speed *= 3.0f;
4620 				}
4621 
4622 
4623   				side = speed * 75.0f;
4624 	//			if (pVeh->m_ulFlags & VEH_STRAFERAM)
4625 	//			{
4626 	//	 			vAngles[ROLL] += side;
4627 	//			}
4628 	//			else
4629 				{
4630 		 			vAngles[ROLL] -= side;
4631 				}
4632 
4633 		 		//gi.Printf("VAngles(%f)", vAngles[2]);
4634 			}
4635 			if (fabsf(vAngles[ROLL])<0.001f)
4636 			{
4637 				vAngles[ROLL] = 0.0f;
4638 			}
4639 		}
4640 	}
4641 
4642 	//cap
4643  	if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
4644 	{
4645 		vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
4646 	}
4647 	else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit )
4648 	{
4649 		vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
4650 	}
4651 
4652 	if (!(pVeh->m_ulFlags & VEH_SPINNING))
4653 	{
4654 		if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
4655 		{
4656 			vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
4657 		}
4658 		else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit )
4659 		{
4660 			vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
4661 		}
4662 	}
4663 
4664 	//do it
4665  	for ( int i = 0; i < 3; i++ )
4666 	{
4667 		if ( i == YAW )
4668 		{//yawing done elsewhere
4669 			continue;
4670 		}
4671 
4672 		if ( i == ROLL && pVeh->m_ulFlags & VEH_STRAFERAM)
4673 		{//during strafe ram, roll is done elsewhere
4674 			continue;
4675 		}
4676 		//bank faster the higher the difference is
4677 		/*
4678 		else if ( i == PITCH )
4679 		{
4680 			curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f);
4681 		}
4682 		else if ( i == ROLL )
4683 		{
4684 			curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f);
4685 		}
4686 
4687 		if ( curVehicleBankingSpeed )
4688 		*/
4689 		{
4690 		//	if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed )
4691 		//	{
4692 		//		pVeh->m_vOrientation[i] -= vehicleBankingSpeed;
4693 		//	}
4694 		//	else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed )
4695 		//	{
4696 		//		pVeh->m_vOrientation[i] += vehicleBankingSpeed;
4697 		//	}
4698 		//	else
4699 			{
4700 				pVeh->m_vOrientation[i] = vAngles[i];
4701 			}
4702 		}
4703 	}
4704  	//gi.Printf("Orientation(%f)", pVeh->m_vOrientation[2]);
4705 }
4706 
BG_ExternThisSoICanRecompileInDebug(Vehicle_t * pVeh,playerState_t * riderPS)4707 void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS )
4708 {/*
4709 	float pitchSubtract, pitchDelta, yawDelta;
4710 	//Com_Printf( S_COLOR_RED"PITCH: %4.2f, YAW: %4.2f, ROLL: %4.2f\n", riderPS->viewangles[0],riderPS->viewangles[1],riderPS->viewangles[2]);
4711 	yawDelta = AngleSubtract(riderPS->viewangles[YAW],pVeh->m_vPrevRiderViewAngles[YAW]);
4712 #ifndef QAGAME
4713 	if ( !cg_paused.integer )
4714 	{
4715 		//Com_Printf( "%d - yawDelta %4.2f\n", pm->cmd.serverTime, yawDelta );
4716 	}
4717 #endif
4718 	yawDelta *= (4.0f*pVeh->m_fTimeModifier);
4719 	pVeh->m_vOrientation[ROLL] -= yawDelta;
4720 
4721 	pitchDelta = AngleSubtract(riderPS->viewangles[PITCH],pVeh->m_vPrevRiderViewAngles[PITCH]);
4722 	pitchDelta *= (2.0f*pVeh->m_fTimeModifier);
4723 	pitchSubtract = pitchDelta * (fabs(pVeh->m_vOrientation[ROLL])/90.0f);
4724 	pVeh->m_vOrientation[PITCH] += pitchDelta-pitchSubtract;
4725 	if ( pVeh->m_vOrientation[ROLL] > 0 )
4726 	{
4727 		pVeh->m_vOrientation[YAW] += pitchSubtract;
4728 	}
4729 	else
4730 	{
4731 		pVeh->m_vOrientation[YAW] -= pitchSubtract;
4732 	}
4733 	pVeh->m_vOrientation[PITCH] = AngleNormalize180( pVeh->m_vOrientation[PITCH] );
4734 	pVeh->m_vOrientation[YAW] = AngleNormalize360( pVeh->m_vOrientation[YAW] );
4735 	pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
4736 
4737 	VectorCopy( riderPS->viewangles, pVeh->m_vPrevRiderViewAngles );*/
4738 }
4739 
BG_VehicleTurnRateForSpeed(Vehicle_t * pVeh,float speed,float * mPitchOverride,float * mYawOverride)4740 void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride )
4741 {
4742 	if ( pVeh && pVeh->m_pVehicleInfo )
4743 	{
4744 		float speedFrac = 1.0f;
4745 		if ( pVeh->m_pVehicleInfo->speedDependantTurning )
4746 		{
4747 			if ( pVeh->m_LandTrace.fraction >= 1.0f
4748 				|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
4749 			{
4750 				speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f));
4751 				if ( speedFrac < 0.25f )
4752 				{
4753 					speedFrac = 0.25f;
4754 				}
4755 				else if ( speedFrac > 1.0f )
4756 				{
4757 					speedFrac = 1.0f;
4758 				}
4759 			}
4760 		}
4761 		if ( pVeh->m_pVehicleInfo->mousePitch )
4762 		{
4763 			*mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac;
4764 		}
4765 		if ( pVeh->m_pVehicleInfo->mouseYaw )
4766 		{
4767 			*mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac;
4768 		}
4769 	}
4770 }
4771 /*
4772 =============
4773 PM_GroundTraceMissed
4774 
4775 The ground trace didn't hit a surface, so we are in freefall
4776 =============
4777 */
PM_GroundTraceMissed(void)4778 static void PM_GroundTraceMissed( void ) {
4779 	trace_t		trace;
4780 	vec3_t		point;
4781 	qboolean	cliff_fall = qfalse;
4782 
4783 	if ( Flying != FLY_HOVER )
4784 	{
4785 		if ( !(pm->ps->eFlags&EF_FORCE_DRAINED) )
4786 		{
4787 			//FIXME: if in a contents_falldeath brush, play the falling death anim and sound?
4788 			if ( pm->ps->clientNum != 0 && pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->NPC_class != CLASS_DESANN )//desann never falls to his death
4789 			{
4790 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
4791 				{
4792 					if ( pm->ps->stats[STAT_HEALTH] > 0
4793 						&& !(pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT)
4794 						&& !(pm->gent->NPC->aiFlags&NPCAI_JUMP)					// doing a path jump
4795 						&& !(pm->gent->NPC->scriptFlags&SCF_NO_FALLTODEATH)
4796 						&& pm->gent->NPC->behaviorState != BS_JUMP )//not being scripted to jump
4797 					{
4798 						if ( (level.time - pm->gent->client->respawnTime > 2000)//been in the world for at least 2 seconds
4799 							&& (!pm->gent->NPC->timeOfDeath || level.time - pm->gent->NPC->timeOfDeath < 1000) && pm->gent->e_ThinkFunc != thinkF_NPC_RemoveBody //Have to do this now because timeOfDeath is used by thinkF_NPC_RemoveBody to debounce removal checks
4800 							&& !(pm->gent->client->ps.forcePowersActive&(1<<FP_LEVITATION)) )
4801 						{
4802 							if ( !FlyingCreature( pm->gent )
4803 								&& g_gravity->value > 0 )
4804 							{
4805 								if ( !(pm->gent->flags&FL_UNDYING)
4806 									&& !(pm->gent->flags&FL_GODMODE) )
4807 								{
4808 									if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED)
4809 										&& !(pm->ps->eFlags&EF_FORCE_DRAINED)
4810 										&& !(pm->ps->pm_flags&PMF_TRIGGER_PUSHED) )
4811 									{
4812 										if ( !pm->ps->forceJumpZStart || pm->ps->forceJumpZStart > pm->ps->origin[2] )// && fabs(pm->ps->velocity[0])<10 && fabs(pm->ps->velocity[1])<10 && pm->ps->velocity[2]<0)//either not force-jumping or force-jumped and now fell below original jump start height
4813 										{
4814 											/*if ( pm->ps->legsAnim = BOTH_FALLDEATH1
4815 											&& pm->ps->legsAnim != BOTH_DEATH1
4816 											&& PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 )*/
4817 											//New method: predict impact, 400 ahead
4818 											vec3_t	vel;
4819 											float	time;
4820 
4821 											VectorCopy( pm->ps->velocity, vel );
4822 											float speed = VectorLength( vel );
4823 											if ( !speed )
4824 											{//damn divide by zero
4825 												speed = 1;
4826 											}
4827 											time = 400/speed;
4828 											vel[2] -= 0.5 * time * pm->ps->gravity;
4829 											speed = VectorLength( vel );
4830 											if ( !speed )
4831 											{//damn divide by zero
4832 												speed = 1;
4833 											}
4834 											time = 400/speed;
4835 											VectorScale( vel, time, vel );
4836 											VectorAdd( pm->ps->origin, vel, point );
4837 
4838 											pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
4839 
4840 											if ( (trace.contents&CONTENTS_LAVA)
4841 												&& PM_RocketeersAvoidDangerousFalls() )
4842 											{//got out of it
4843 											}
4844 											else if ( !trace.allsolid && !trace.startsolid && (pm->ps->origin[2] - trace.endpos[2]) >= 128 )//>=128 so we don't die on steps!
4845 											{
4846 												if ( trace.fraction == 1.0 )
4847 												{//didn't hit, we're probably going to die
4848 													if ( pm->ps->velocity[2] < 0 && pm->ps->origin[2] - point[2] > 256 )
4849 													{//going down, into a bottomless pit, apparently
4850 														PM_FallToDeath();
4851 														cliff_fall = qtrue;
4852 													}
4853 												}
4854 												else if ( trace.entityNum < ENTITYNUM_NONE
4855 													&& pm->ps->weapon != WP_SABER
4856 													&& (!pm->gent || !pm->gent->client || (pm->gent->client->NPC_class != CLASS_BOBAFETT&&pm->gent->client->NPC_class!=CLASS_REBORN&&pm->gent->client->NPC_class!=CLASS_ROCKETTROOPER)) )
4857 												{//Jedi don't scream and die if they're heading for a hard impact
4858 													gentity_t *traceEnt = &g_entities[trace.entityNum];
4859 													if ( trace.entityNum == ENTITYNUM_WORLD || (traceEnt && traceEnt->bmodel) )
4860 													{//hit architecture, find impact force
4861 														float	dmg;
4862 
4863 														VectorCopy( pm->ps->velocity, vel );
4864 														time = Distance( trace.endpos, pm->ps->origin )/VectorLength( vel );
4865 														vel[2] -= 0.5 * time * pm->ps->gravity;
4866 
4867 														if ( trace.plane.normal[2] > 0.5 )
4868 														{//use falling damage
4869 															int	waterlevel, junk;
4870 															PM_SetWaterLevelAtPoint( trace.endpos, &waterlevel, &junk );
4871 															dmg = PM_CrashLandDelta( vel, waterlevel );
4872 															if ( dmg >= 30 )
4873 															{//there is a minimum fall threshhold
4874 																dmg = PM_DamageForDelta( dmg );
4875 															}
4876 															else
4877 															{
4878 																dmg = 0;
4879 															}
4880 														}
4881 														else
4882 														{//use impact damage
4883 															//guestimate
4884 															if ( pm->gent->client && pm->gent->client->ps.forceJumpZStart )
4885 															{//we were force-jumping
4886 																if ( pm->gent->currentOrigin[2] >= pm->gent->client->ps.forceJumpZStart )
4887 																{//we landed at same height or higher than we landed
4888 																	dmg = 0;
4889 																}
4890 																else
4891 																{//FIXME: take off some of it, at least?
4892 																	dmg = (pm->gent->client->ps.forceJumpZStart-pm->gent->currentOrigin[2])/3;
4893 																}
4894 															}
4895 															dmg = 10 * VectorLength( pm->ps->velocity );
4896 															if ( pm->ps->clientNum < MAX_CLIENTS )
4897 															{
4898 																dmg /= 2;
4899 															}
4900 															dmg *= 0.01875f;//magic number
4901 														}
4902 														float maxDmg = pm->ps->stats[STAT_HEALTH]>20?pm->ps->stats[STAT_HEALTH]:20;//a fall that would do less than 20 points of damage should never make us scream to our deaths
4903 														if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HOWLER )
4904 														{//howlers can survive long falls, despire their health
4905 															maxDmg *= 5;
4906 														}
4907 														if ( dmg >= pm->ps->stats[STAT_HEALTH] )//armor?
4908 														{
4909 															PM_FallToDeath();
4910 															cliff_fall = qtrue;
4911 														}
4912 													}
4913 												}
4914 											}
4915 
4916 											/*
4917 											vec3_t	start;
4918 											//okay, kind of expensive temp hack here, but let's check to see if we should scream
4919 											//FIXME: we should either do a better check (predict using actual velocity) or we should wait until they've been over a bottomless pit for a certain amount of time...
4920 											VectorCopy( pm->ps->origin, start );
4921 											if ( pm->ps->forceJumpZStart < start[2] )
4922 											{//Jedi who are force-jumping should only do this from landing point down?
4923 												start[2] = pm->ps->forceJumpZStart;
4924 											}
4925 											VectorCopy( start, point );
4926 											point[2] -= 400;//320
4927 											pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
4928 											//FIXME: somehow, people can still get stuck on ledges and not splat when hit...?
4929 											if ( !trace.allsolid && !trace.startsolid && trace.fraction == 1.0 )
4930 											{
4931 												PM_FallToDeath();
4932 												cliff_fall = qtrue;
4933 											}
4934 											*/
4935 										}
4936 									}
4937 								}
4938 							}
4939 						}
4940 					}
4941 				}
4942 			}
4943 			if ( !cliff_fall )
4944 			{
4945 				if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START
4946 					|| pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK
4947 					|| pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START
4948 					|| pm->ps->legsAnim == BOTH_FLIP_LAND )
4949 				{//let it stay on this anim
4950 				}
4951 				else if ( PM_KnockDownAnimExtended( pm->ps->legsAnim ) )
4952 				{//no in-air anims when in knockdown anim
4953 				}
4954 				else if ( ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT
4955 					|| pm->ps->legsAnim == BOTH_WALL_RUN_LEFT
4956 					|| pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP
4957 					|| pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP
4958 					|| pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_FLIP
4959 					|| pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_FLIP
4960 					|| pm->ps->legsAnim == BOTH_WALL_FLIP_RIGHT
4961 					|| pm->ps->legsAnim == BOTH_WALL_FLIP_LEFT
4962 					|| pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_FORWARD
4963 					|| pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_LEFT
4964 					|| pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_BACK
4965 					|| pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_RIGHT
4966 					|| pm->ps->legsAnim == BOTH_CEILING_DROP )
4967 					&& !pm->ps->legsAnimTimer )
4968 				{//if flip anim is done, okay to use inair
4969 					PM_SetAnim( pm, SETANIM_LEGS, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE, 350 );	// Only blend over 100ms
4970 				}
4971 				else if ( pm->ps->legsAnim == BOTH_FLIP_ATTACK7
4972 					|| pm->ps->legsAnim == BOTH_FLIP_HOLD7  )
4973 				{
4974 					if ( !pm->ps->legsAnimTimer )
4975 					{//done?
4976 						PM_SetAnim( pm, SETANIM_BOTH, BOTH_FLIP_HOLD7, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 350 );	// Only blend over 100ms
4977 					}
4978 				}
4979 				else if ( PM_InCartwheel( pm->ps->legsAnim ) )
4980 				{
4981 					if ( pm->ps->legsAnimTimer > 0 )
4982 					{//still playing on bottom
4983 					}
4984 					else
4985 					{
4986 						PM_SetAnim( pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350  );
4987 					}
4988 				}
4989 				else if ( PM_InAirKickingAnim( pm->ps->legsAnim ) )
4990 				{
4991 					if ( pm->ps->legsAnimTimer > 0 )
4992 					{//still playing on bottom
4993 					}
4994 					else
4995 					{
4996 						PM_SetAnim( pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350  );
4997 						pm->ps->saberMove = LS_READY;
4998 						pm->ps->weaponTime = 0;
4999 					}
5000 				}
5001 				else if ( !PM_InRoll( pm->ps )
5002 					&& !PM_SpinningAnim( pm->ps->legsAnim )
5003 					&& !PM_FlippingAnim( pm->ps->legsAnim )
5004 					&& !PM_InSpecialJump( pm->ps->legsAnim ) )
5005 				{
5006 					if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
5007 					{
5008 						// we just transitioned into freefall
5009 						if ( pm->debugLevel )
5010 						{
5011 							Com_Printf("%i:lift\n", c_pmove);
5012 						}
5013 
5014 						// if they aren't in a jumping animation and the ground is a ways away, force into it
5015 						// if we didn't do the trace, the player would be backflipping down staircases
5016 						VectorCopy( pm->ps->origin, point );
5017 						point[2] -= 64;
5018 
5019 						pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0);
5020 						if ( trace.fraction == 1.0 )
5021 						{//FIXME: if velocity[2] < 0 and didn't jump, use some falling anim
5022 							if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD))
5023 							{
5024 								if(!PM_InDeathAnim())
5025 								{
5026 									// If we're a vehicle, set our falling flag.
5027 									if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
5028 									{
5029 										// We're flying in the air.
5030 										if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
5031 										{
5032 											pm->gent->m_pVehicle->m_ulFlags |= VEH_FLYING;
5033 										}
5034 									}
5035 									else
5036 									{
5037 										vec3_t	moveDir, lookAngles, lookDir, lookRight;
5038 										int		anim = BOTH_INAIR1;
5039 
5040 										VectorCopy( pm->ps->velocity, moveDir );
5041 										moveDir[2] = 0;
5042 										VectorNormalize( moveDir );
5043 
5044 										VectorCopy( pm->ps->viewangles, lookAngles );
5045 										lookAngles[PITCH] = lookAngles[ROLL] = 0;
5046 										AngleVectors( lookAngles, lookDir, lookRight, NULL );
5047 
5048 										float dot = DotProduct( moveDir, lookDir );
5049 										if ( dot > 0.5 )
5050 										{//redundant
5051 											anim = BOTH_INAIR1;
5052 										}
5053 										else if ( dot < -0.5 )
5054 										{
5055 											anim = BOTH_INAIRBACK1;
5056 										}
5057 										else
5058 										{
5059 											dot = DotProduct( moveDir, lookRight );
5060 											if ( dot > 0.5 )
5061 											{
5062 												anim = BOTH_INAIRRIGHT1;
5063 											}
5064 											else if ( dot < -0.5 )
5065 											{
5066 												anim = BOTH_INAIRLEFT1;
5067 											}
5068 											else
5069 											{//redundant
5070 												anim = BOTH_INAIR1;
5071 											}
5072 										}
5073 										if ( pm->ps->forcePowersActive & ( 1 << FP_LEVITATION ) )
5074 										{
5075 											anim = PM_ForceJumpAnimForJumpAnim( anim );
5076 										}
5077 										PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE, 100 );	// Only blend over 100ms
5078 									}
5079 								}
5080 								pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
5081 							}
5082 							else if ( !(pm->ps->forcePowersActive&(1<<FP_LEVITATION)) )
5083 							{
5084 								if ( pm->cmd.forwardmove >= 0 )
5085 								{
5086 									if(!PM_InDeathAnim())
5087 									{
5088 										PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE, 100);	// Only blend over 100ms
5089 									}
5090 									pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
5091 								}
5092 								else if ( pm->cmd.forwardmove < 0 )
5093 								{
5094 									if(!PM_InDeathAnim())
5095 									{
5096 										PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE, 100);	// Only blend over 100ms
5097 									}
5098 									pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
5099 								}
5100 							}
5101 						}
5102 						else
5103 						{
5104 							// If we're a vehicle, set our falling flag.
5105 							if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
5106 							{
5107 								// We're on the ground.
5108 								if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL )
5109 								{
5110 									pm->gent->m_pVehicle->m_ulFlags &= ~VEH_FLYING;
5111 								}
5112 							}
5113 						}
5114 					}
5115 				}
5116 			}
5117 		}
5118 
5119 		if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
5120 		{
5121 			pm->ps->jumpZStart = pm->ps->origin[2];
5122 		}
5123 	}
5124 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
5125 	pml.groundPlane = qfalse;
5126 	pml.walking = qfalse;
5127 }
5128 
5129 
5130 /*
5131 =============
5132 PM_GroundTrace
5133 =============
5134 */
PM_GroundTrace(void)5135 static void PM_GroundTrace( void ) {
5136 	vec3_t		point;
5137 	trace_t		trace;
5138 
5139 	if ( (pm->ps->eFlags&EF_LOCKED_TO_WEAPON)
5140 		|| (pm->ps->eFlags&EF_HELD_BY_RANCOR)
5141 		|| (pm->ps->eFlags&EF_HELD_BY_WAMPA)
5142 		|| (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE)
5143 		|| PM_RidingVehicle() )
5144 	{
5145 		pml.groundPlane = qtrue;
5146 		pml.walking = qtrue;
5147 		pm->ps->groundEntityNum = ENTITYNUM_WORLD;
5148 		pm->ps->lastOnGround = level.time;
5149 		/*
5150 		pml.groundTrace.allsolid = qfalse;
5151 		pml.groundTrace.contents = CONTENTS_SOLID;
5152 		VectorCopy( pm->ps->origin, pml.groundTrace.endpos );
5153 		pml.groundTrace.entityNum = ENTITYNUM_WORLD;
5154 		pml.groundTrace.fraction = 0.0f;;
5155 		pml.groundTrace.G2CollisionMap = NULL;
5156 		pml.groundTrace.plane.dist = 0.0f;
5157 		VectorSet( pml.groundTrace.plane.normal, 0, 0, 1 );
5158 		pml.groundTrace.plane.pad = 0;
5159 		pml.groundTrace.plane.signbits = 0;
5160 		pml.groundTrace.plane.type = 0;;
5161 		pml.groundTrace.startsolid = qfalse;
5162 		pml.groundTrace.surfaceFlags = 0;
5163 		*/
5164 		return;
5165 	}
5166 	else if ( pm->ps->legsAnimTimer > 300
5167 		&& (pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT
5168 			|| pm->ps->legsAnim == BOTH_WALL_RUN_LEFT
5169 			|| pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START) )
5170 	{//wall-running forces you to be in the air
5171 		pml.groundPlane = qfalse;
5172 		pml.walking = qfalse;
5173 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
5174 		return;
5175 	}
5176 
5177 	float minNormal = (float)MIN_WALK_NORMAL;
5178 	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
5179 	{//FIXME: extern this as part of vehicle data
5180 		minNormal = pm->gent->m_pVehicle->m_pVehicleInfo->maxSlope;
5181 	}
5182 
5183 	point[0] = pm->ps->origin[0];
5184 	point[1] = pm->ps->origin[1];
5185 	point[2] = pm->ps->origin[2] - 0.25f;
5186 
5187 	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0);
5188 	pml.groundTrace = trace;
5189 
5190 	// do something corrective if the trace starts in a solid...
5191 	if ( trace.allsolid ) {
5192 		PM_CorrectAllSolid();
5193 		return;
5194 	}
5195 
5196 	// if the trace didn't hit anything, we are in free fall
5197 	if ( trace.fraction == 1.0 || g_gravity->value <= 0 )
5198 	{
5199 		PM_GroundTraceMissed();
5200 		pml.groundPlane = qfalse;
5201 		pml.walking = qfalse;
5202 		/*
5203 		if ( pm->ps->vehicleIndex != VEHICLE_NONE )
5204 		{
5205 			PM_SetVehicleAngles( NULL );
5206 		}
5207 		*/
5208 		return;
5209 	}
5210 
5211 	// Not a vehicle and not riding one.
5212 	if ( pm->gent
5213 		&& pm->gent->client
5214 		&& pm->gent->client->NPC_class != CLASS_SAND_CREATURE
5215 		&& (pm->gent->client->NPC_class != CLASS_VEHICLE && !PM_RidingVehicle() ) )
5216 	{
5217 		// check if getting thrown off the ground
5218 		if ( ((pm->ps->velocity[2]>0&&(pm->ps->pm_flags&PMF_TIME_KNOCKBACK))||pm->ps->velocity[2]>100) && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 )
5219 		{//either thrown off ground (PMF_TIME_KNOCKBACK) or going off the ground at a large velocity
5220 			if ( pm->debugLevel ) {
5221 				Com_Printf("%i:kickoff\n", c_pmove);
5222 			}
5223 			// go into jump animation
5224 			if ( PM_FlippingAnim( pm->ps->legsAnim) )
5225 			{//we're flipping
5226 			}
5227 			else if ( PM_InSpecialJump( pm->ps->legsAnim ) )
5228 			{//special jumps
5229 			}
5230 			else if ( PM_InKnockDown( pm->ps ) )
5231 			{//in knockdown
5232 			}
5233 			else if ( PM_InRoll( pm->ps ) )
5234 			{//in knockdown
5235 			}
5236 			else if ( PM_KickingAnim( pm->ps->legsAnim ) )
5237 			{//in kick
5238 			}
5239 			else if ( pm->gent
5240 				&& pm->gent->client
5241 				&& (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA) )
5242 			{
5243 			}
5244 			else
5245 			{
5246 				PM_JumpForDir();
5247 			}
5248 
5249 			pm->ps->groundEntityNum = ENTITYNUM_NONE;
5250 			pml.groundPlane = qfalse;
5251 			pml.walking = qfalse;
5252 			return;
5253 		}
5254 	}
5255 
5256 	/*
5257 	if ( pm->ps->vehicleIndex != VEHICLE_NONE )
5258 	{
5259 		PM_SetVehicleAngles( trace.plane.normal );
5260 	}
5261 	*/
5262 
5263 	// slopes that are too steep will not be considered onground
5264 	if ( trace.plane.normal[2] < minNormal ) {
5265 		if ( pm->debugLevel ) {
5266 			Com_Printf("%i:steep\n", c_pmove);
5267 		}
5268 		// FIXME: if they can't slide down the slope, let them
5269 		// walk (sharp crevices)
5270 		pm->ps->groundEntityNum = ENTITYNUM_NONE;
5271 		pml.groundPlane = qtrue;
5272 		pml.walking = qfalse;
5273 		return;
5274 	}
5275 
5276 	//FIXME: if the ground surface is a "cover surface (like tall grass), add a "cover" flag to me
5277 	pml.groundPlane = qtrue;
5278 	pml.walking = qtrue;
5279 
5280 	// hitting solid ground will end a waterjump
5281 	if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
5282 	{
5283 		pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
5284 		pm->ps->pm_time = 0;
5285 	}
5286 
5287 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
5288 		// just hit the ground
5289 		if ( pm->debugLevel ) {
5290 			Com_Printf("%i:Land\n", c_pmove);
5291 		}
5292 
5293 		//if ( !PM_ClientImpact( trace.entityNum, qtrue ) )
5294 		{
5295 			PM_CrashLand();
5296 
5297 			// don't do landing time if we were just going down a slope
5298 			if ( pml.previous_velocity[2] < -200 ) {
5299 				// don't allow another jump for a little while
5300 				pm->ps->pm_flags |= PMF_TIME_LAND;
5301 				pm->ps->pm_time = 250;
5302 			}
5303 			if (!pm->cmd.forwardmove && !pm->cmd.rightmove) {
5304 				if ( Flying != FLY_HOVER )
5305 				{
5306 					pm->ps->velocity[2] = 0;	//wouldn't normally want this because of slopes, but we aren't tyring to move...
5307 				}
5308 			}
5309 		}
5310 	}
5311 
5312 	pm->ps->groundEntityNum = trace.entityNum;
5313 	pm->ps->lastOnGround = level.time;
5314 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
5315 	{//if a player, clear the jumping "flag" so can't double-jump
5316 		pm->ps->forceJumpCharge = 0;
5317 	}
5318 
5319 	// don't reset the z velocity for slopes
5320 //	pm->ps->velocity[2] = 0;
5321 
5322 	PM_AddTouchEnt( trace.entityNum );
5323 }
5324 
5325 int		LastMatrixJumpTime = 0;
5326 #define	DEBUGMATRIXJUMP		0
5327 
PM_HoverTrace(void)5328 void PM_HoverTrace( void )
5329 {
5330 	if ( !pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE )
5331 	{
5332 		return;
5333 	}
5334 
5335 	Vehicle_t *pVeh = pm->gent->m_pVehicle;
5336 	float hoverHeight = pVeh->m_pVehicleInfo->hoverHeight;
5337 	vec3_t		point, vAng, fxAxis[3];
5338 	trace_t		*trace = &pml.groundTrace;
5339 	int			traceContents = pm->tracemask;
5340 
5341 	pml.groundPlane = qfalse;
5342 
5343 	float relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2]));
5344 	if ( pm->waterlevel && relativeWaterLevel >= 0 )
5345 	{//in water
5346 		if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f )
5347 		{//sink like a rock
5348 		}
5349 		else
5350 		{//rise up
5351 			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
5352 			if ( relativeWaterLevel > floatHeight )
5353 			{//too low, should rise up
5354 				pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier;
5355 			}
5356 		}
5357 		if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] )
5358 		{//part of us is sticking out of water
5359 			if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
5360 			{//moving at a decent speed
5361 				if ( Q_irand( pml.frametime, 100 ) >= 50 )
5362 				{//splash
5363 					vAng[PITCH] = vAng[ROLL] = 0;
5364 					vAng[YAW] = pVeh->m_vOrientation[YAW];
5365 					AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
5366 					vec3_t wakeOrg;
5367 					VectorCopy( pm->ps->origin, wakeOrg );
5368 					wakeOrg[2] = pm->ps->waterheight;
5369 					if ( pVeh->m_pVehicleInfo->iWakeFX )
5370 					{
5371 						G_PlayEffect( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis );
5372 					}
5373 				}
5374 			}
5375 			pml.groundPlane = qtrue;
5376 		}
5377 	}
5378 	else
5379 	{
5380 		float minNormal = (float)MIN_WALK_NORMAL;
5381 		minNormal = pVeh->m_pVehicleInfo->maxSlope;
5382 
5383 		point[0] = pm->ps->origin[0];
5384 		point[1] = pm->ps->origin[1];
5385 		point[2] = pm->ps->origin[2] - (hoverHeight*3.0f);
5386 
5387 		//FIXME: check for water, too?  If over water, go slower and make wave effect
5388 		//		If *in* water, go really slow and use bouyancy stat to determine how far below surface to float
5389 
5390 		//NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground.
5391 		//		if it's 1.0f, you sink halfway into water.  If it's 0, you sink...
5392 		if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f )
5393 		{//sit on water
5394 			traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
5395 		}
5396  		pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0 );
5397 		if ( trace->plane.normal[2] >= minNormal )
5398 		{//not a steep slope, so push us up
5399 			if ( trace->fraction < 0.3f )
5400 			{//push up off ground
5401 	 			float hoverForce = pVeh->m_pVehicleInfo->hoverStrength;
5402 				pm->ps->velocity[2] += (0.3f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier;
5403 
5404 			//	if (pm->ps->velocity[2]>60.0f)
5405 			//	{
5406 			//		pm->ps->velocity[2] = 60.0f;
5407 			//	}
5408 
5409 				if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
5410 				{//hovering on water, make a spash if moving
5411 					if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
5412 					{//moving at a decent speed
5413 						if ( Q_irand( pml.frametime, 100 ) >= 50 )
5414 						{//splash
5415 							vAng[PITCH] = vAng[ROLL] = 0;
5416 							vAng[YAW] = pVeh->m_vOrientation[YAW];
5417 							AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
5418 							if ( pVeh->m_pVehicleInfo->iWakeFX )
5419 							{
5420 								G_PlayEffect( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis );
5421 							}
5422 						}
5423 					}
5424 				}
5425 
5426 				if (pVeh->m_ulFlags & VEH_SLIDEBREAKING)
5427 				{
5428   				 	if ( Q_irand( pml.frametime, 100 ) >= 50 )
5429 					{//dust
5430 						VectorClear(fxAxis[0]);
5431 					 	fxAxis[0][2] = 1;
5432 
5433 						VectorCopy(pm->ps->velocity, fxAxis[1]);
5434  						fxAxis[1][2] *= 0.01f;
5435 						VectorMA(pm->ps->origin, 0.25f, fxAxis[1], point);
5436 						G_PlayEffect("ships/swoop_dust", point, fxAxis[0]);
5437 					}
5438 				}
5439 				pml.groundPlane = qtrue;
5440 			}
5441 		}
5442 	}
5443 	if ( pml.groundPlane )
5444 	{
5445  		PM_SetVehicleAngles( pml.groundTrace.plane.normal );
5446 		// We're on the ground.
5447 //		if (pVeh->m_ulFlags&VEH_FLYING && level.time<pVeh->m_iTurboTime)
5448 //		{
5449 //			pVeh->m_iTurboTime = 0;	// stop turbo
5450 //		}
5451 		pVeh->m_ulFlags &= ~VEH_FLYING;
5452 		pVeh->m_vAngularVelocity = 0.0f;
5453 	}
5454 	else
5455 	{
5456 		PM_SetVehicleAngles( NULL );
5457 		// We're flying in the air.
5458 		pVeh->m_ulFlags |= VEH_FLYING;
5459 		//groundTrace
5460 
5461 		if (pVeh->m_vAngularVelocity==0.0f)
5462 		{
5463 			pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
5464 			if (pVeh->m_vAngularVelocity<-15.0f)
5465 			{
5466 				pVeh->m_vAngularVelocity = -15.0f;
5467 			}
5468 			if (pVeh->m_vAngularVelocity> 15.0f)
5469 			{
5470 				pVeh->m_vAngularVelocity =  15.0f;
5471 			}
5472 
5473 			// BEGIN MATRIX MODE INIT FOR JUMP
5474 			//=================================
5475   			if (pm->gent &&
5476 				pm->gent->owner &&
5477 				(pm->gent->owner->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent->owner)) &&
5478 				pVeh->m_pVehicleInfo->type==VH_SPEEDER &&
5479 				level.time>(LastMatrixJumpTime+5000) && VectorLength(pm->ps->velocity)>30.0f)
5480 			{
5481  				LastMatrixJumpTime = level.time;
5482 	 			vec3_t	predictedApx;
5483 				vec3_t	predictedFallVelocity;
5484 				vec3_t	predictedLandPosition;
5485 
5486  				VectorScale(pm->ps->velocity, 2.0f, predictedFallVelocity);					// take friction into account
5487 	 			predictedFallVelocity[2] = -(pm->ps->gravity * 1.1f);	// take gravity into account
5488 
5489 				VectorMA(pm->ps->origin,	0.25f, pm->ps->velocity,			predictedApx);
5490 				VectorMA(predictedApx,		0.25f, predictedFallVelocity,	predictedLandPosition);
5491 
5492 
5493 
5494 
5495 				trace_t trace2;
5496  				gi.trace( &trace2, predictedApx, pm->mins, pm->maxs, predictedLandPosition, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0);
5497  				if (!trace2.startsolid && !trace2.allsolid && trace2.fraction>0.75 && Q_irand(0, 3)==0)
5498 				{
5499 					LastMatrixJumpTime += 20000;
5500  				 	G_StartMatrixEffect(pm->gent, MEF_HIT_GROUND_STOP);
5501 //					CG_DrawEdge(pm->ps->origin, predictedApx,			EDGE_WHITE_TWOSECOND);
5502 //					CG_DrawEdge(predictedApx, predictedLandPosition,	EDGE_WHITE_TWOSECOND);
5503 				}
5504 //				else
5505 //				{
5506 //					CG_DrawEdge(pm->ps->origin, predictedApx,			EDGE_RED_TWOSECOND);
5507 //					CG_DrawEdge(predictedApx, predictedLandPosition,	EDGE_RED_TWOSECOND);
5508 //				}
5509 			}
5510 			//=================================
5511 		}
5512 		pVeh->m_vAngularVelocity *= 0.95f;		// Angular Velocity Decays Over Time
5513 	}
5514 	PM_GroundTraceMissed();
5515 }
5516 
5517 /*
5518 =============
5519 PM_SetWaterLevelAtPoint	FIXME: avoid this twice?  certainly if not moving
5520 =============
5521 */
PM_SetWaterLevelAtPoint(vec3_t org,int * waterlevel,int * watertype)5522 static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype )
5523 {
5524 	vec3_t		point;
5525 	int			cont;
5526 	int			sample1;
5527 	int			sample2;
5528 
5529 	//
5530 	// get waterlevel, accounting for ducking
5531 	//
5532 	*waterlevel = 0;
5533 	*watertype = 0;
5534 
5535 	point[0] = org[0];
5536 	point[1] = org[1];
5537 	point[2] = org[2] + DEFAULT_MINS_2 + 1;
5538 	if (gi.totalMapContents() & (MASK_WATER|CONTENTS_LADDER))
5539 	{
5540 		cont = pm->pointcontents( point, pm->ps->clientNum );
5541 
5542 		if ( cont & (MASK_WATER|CONTENTS_LADDER) )
5543 		{
5544 			sample2 = pm->ps->viewheight - DEFAULT_MINS_2;
5545 			sample1 = sample2 / 2;
5546 
5547 			*watertype = cont;
5548 			*waterlevel = 1;
5549 			point[2] = org[2] + DEFAULT_MINS_2 + sample1;
5550 			cont = pm->pointcontents( point, pm->ps->clientNum );
5551 			if ( cont & (MASK_WATER|CONTENTS_LADDER) )
5552 			{
5553 				*waterlevel = 2;
5554 				point[2] = org[2] + DEFAULT_MINS_2 + sample2;
5555 				cont = pm->pointcontents( point, pm->ps->clientNum );
5556 				if ( cont & (MASK_WATER|CONTENTS_LADDER) )
5557 				{
5558 					*waterlevel = 3;
5559 				}
5560 			}
5561 		}
5562 	}
5563 }
5564 
PM_SetWaterHeight(void)5565 void PM_SetWaterHeight( void )
5566 {
5567 	pm->ps->waterHeightLevel = WHL_NONE;
5568 	if ( pm->waterlevel < 1 )
5569 	{
5570 		pm->ps->waterheight = pm->ps->origin[2] + DEFAULT_MINS_2 - 4;
5571 		return;
5572 	}
5573 	trace_t trace;
5574 	vec3_t	top, bottom;
5575 
5576 	VectorCopy( pm->ps->origin, top );
5577 	VectorCopy( pm->ps->origin, bottom );
5578 	top[2] += pm->gent->client->standheight;
5579 	bottom[2] += DEFAULT_MINS_2;
5580 
5581 	gi.trace( &trace, top, pm->mins, pm->maxs, bottom, pm->ps->clientNum, MASK_WATER, (EG2_Collision)0, 0 );
5582 
5583 	if ( trace.startsolid )
5584 	{//under water
5585 		pm->ps->waterheight = top[2] + 4;
5586 	}
5587 	else if ( trace.fraction < 1.0f )
5588 	{//partially in and partially out of water
5589 		pm->ps->waterheight = trace.endpos[2]+pm->mins[2];
5590 	}
5591 	else if ( trace.contents&MASK_WATER )
5592 	{//water is above me
5593 		pm->ps->waterheight = top[2] + 4;
5594 	}
5595 	else
5596 	{//water is below me
5597 		pm->ps->waterheight = bottom[2] - 4;
5598 	}
5599 	float distFromEyes = (pm->ps->origin[2]+pm->gent->client->standheight)-pm->ps->waterheight;
5600 
5601 	if ( distFromEyes < 0 )
5602 	{
5603 		pm->ps->waterHeightLevel = WHL_UNDER;
5604 	}
5605 	else if ( distFromEyes < 6 )
5606 	{
5607 		pm->ps->waterHeightLevel = WHL_HEAD;
5608 	}
5609 	else if ( distFromEyes < 18 )
5610 	{
5611 		pm->ps->waterHeightLevel = WHL_SHOULDERS;
5612 	}
5613 	else if ( distFromEyes < pm->gent->client->standheight-8 )
5614 	{//at least 8 above origin
5615 		pm->ps->waterHeightLevel = WHL_TORSO;
5616 	}
5617 	else
5618 	{
5619 		float distFromOrg = pm->ps->origin[2]-pm->ps->waterheight;
5620 		if ( distFromOrg < 6 )
5621 		{
5622 			pm->ps->waterHeightLevel = WHL_WAIST;
5623 		}
5624 		else if ( distFromOrg < 16 )
5625 		{
5626 			pm->ps->waterHeightLevel = WHL_KNEES;
5627 		}
5628 		else if ( distFromOrg > fabs(pm->mins[2]) )
5629 		{
5630 			pm->ps->waterHeightLevel = WHL_NONE;
5631 		}
5632 		else
5633 		{
5634 			pm->ps->waterHeightLevel = WHL_ANKLES;
5635 		}
5636 	}
5637 }
5638 
5639 
5640 /*
5641 ==============
5642 PM_SetBounds
5643 
5644 Sets mins, maxs
5645 ==============
5646 */
PM_SetBounds(void)5647 static void PM_SetBounds (void)
5648 {
5649 	if ( pm->gent && pm->gent->client )
5650 	{
5651 		if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
5652 		{
5653 			//assert(0);
5654 		}
5655 
5656 		VectorCopy( pm->gent->mins, pm->mins );
5657 		VectorCopy( pm->gent->maxs, pm->maxs );
5658 	}
5659 	else
5660 	{
5661 		if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
5662 		{
5663 			assert(0);
5664 		}
5665 
5666 		pm->mins[0] = DEFAULT_MINS_0;
5667 		pm->mins[1] = DEFAULT_MINS_1;
5668 
5669 		pm->maxs[0] = DEFAULT_MAXS_0;
5670 		pm->maxs[1] = DEFAULT_MAXS_1;
5671 
5672 		pm->mins[2] = DEFAULT_MINS_2;
5673 		pm->maxs[2] = DEFAULT_MAXS_2;
5674 	}
5675 }
5676 
5677 /*
5678 ==============
5679 PM_CheckDuck
5680 
5681 Sets mins, maxs, and pm->ps->viewheight
5682 ==============
5683 */
PM_CheckDuck(void)5684 static void PM_CheckDuck (void)
5685 {
5686 	trace_t	trace;
5687 	int		standheight;
5688 	int		crouchheight;
5689 	int		oldHeight;
5690 
5691 	if ( pm->gent && pm->gent->client )
5692 	{
5693 		if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
5694 		{
5695 			//assert(0);
5696 		}
5697 
5698 		if ( pm->ps->clientNum < MAX_CLIENTS
5699 			&& (pm->gent->client->NPC_class == CLASS_ATST ||pm->gent->client->NPC_class == CLASS_RANCOR)
5700 			&& !cg.renderingThirdPerson )
5701 		{
5702 			standheight = crouchheight = 128;
5703 		}
5704 		else
5705 		{
5706 			standheight = pm->gent->client->standheight;
5707 			crouchheight = pm->gent->client->crouchheight;
5708 		}
5709 	}
5710 	else
5711 	{
5712 		if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
5713 		{
5714 			assert(0);
5715 		}
5716 
5717 		standheight = DEFAULT_MAXS_2;
5718 		crouchheight = CROUCH_MAXS_2;
5719 	}
5720 
5721 	if ( PM_RidingVehicle() || (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) )
5722 	{//riding a vehicle or are a vehicle
5723 		//no ducking or rolling when on a vehicle
5724 		//right?  not even on ones that you just ride on top of?
5725 		pm->ps->pm_flags &= ~PMF_DUCKED;
5726 		pm->maxs[2] = standheight;
5727 		//FIXME: have a crouchviewheight and standviewheight on ent?
5728 		pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
5729 		//NOTE: we don't clear the pm->cmd.upmove here because
5730 		//the vehicle code may need it later... but, for riders,
5731 		//it should have already been copied over to the vehicle, right?
5732 		return;
5733 	}
5734 
5735 	if ( PM_InGetUp( pm->ps ) )
5736 	{//can't do any kind of crouching when getting up
5737 		if ( pm->ps->legsAnim == BOTH_GETUP_CROUCH_B1 || pm->ps->legsAnim == BOTH_GETUP_CROUCH_F1 )
5738 		{//crouched still
5739 			pm->ps->pm_flags |= PMF_DUCKED;
5740 			pm->maxs[2] = crouchheight;
5741 		}
5742 		pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5743 		return;
5744 	}
5745 
5746 	oldHeight = pm->maxs[2];
5747 
5748 	if ( PM_InRoll( pm->ps ) )
5749 	{
5750 		/*
5751 		if ( pm->ps->clientNum && pm->gent && pm->gent->client )
5752 		{
5753 			pm->maxs[2] = pm->gent->client->renderInfo.eyePoint[2]-pm->ps->origin[2] + 4;
5754 			if ( crouchheight > pm->maxs[2] )
5755 			{
5756 				pm->maxs[2] = crouchheight;
5757 			}
5758 		}
5759 		else
5760 		*/
5761 		{
5762 			pm->maxs[2] = crouchheight;
5763 		}
5764 		pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5765 		pm->ps->pm_flags |= PMF_DUCKED;
5766 		return;
5767 	}
5768 	if ( PM_GettingUpFromKnockDown( standheight, crouchheight ) )
5769 	{
5770 		pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5771 		return;
5772 	}
5773 	if ( PM_InKnockDown( pm->ps ) )
5774 	{//forced crouch
5775 		if ( pm->gent && pm->gent->client )
5776 		{//interrupted any potential delayed weapon fires
5777 			pm->gent->client->fireDelay = 0;
5778 		}
5779 		pm->maxs[2] = crouchheight;
5780 		pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
5781 		pm->ps->pm_flags |= PMF_DUCKED;
5782 		return;
5783 	}
5784 	if ( pm->cmd.upmove < 0 )
5785 	{	// trying to duck
5786 		pm->maxs[2] = crouchheight;
5787 		pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT;
5788 		if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && !PM_SwimmingAnim( pm->ps->legsAnim ) )
5789 		{//Not ducked already and trying to duck in mid-air
5790 			//will raise your feet, unducking whilst in air will drop feet
5791 			if ( !(pm->ps->pm_flags&PMF_DUCKED) )
5792 			{
5793 				pm->ps->eFlags ^= EF_TELEPORT_BIT;
5794 			}
5795 			if ( pm->gent )
5796 			{
5797 				pm->ps->origin[2] += oldHeight - pm->maxs[2];//diff will be zero if were already ducking
5798 				//Don't worry, we know we fit in a smaller size
5799 			}
5800 		}
5801 		pm->ps->pm_flags |= PMF_DUCKED;
5802 		if ( d_JediAI->integer )
5803 		{
5804 			if ( pm->ps->clientNum && pm->ps->weapon == WP_SABER )
5805 			{
5806 				Com_Printf( "ducking\n" );
5807 			}
5808 		}
5809 	}
5810 	else
5811 	{	// want to stop ducking, stand up if possible
5812 		if ( pm->ps->pm_flags & PMF_DUCKED )
5813 		{//Was ducking
5814 			if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
5815 			{//unducking whilst in air will try to drop feet
5816 				pm->maxs[2] = standheight;
5817 				pm->ps->origin[2] += oldHeight - pm->maxs[2];
5818 				pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
5819 				if ( !trace.allsolid )
5820 				{
5821 					pm->ps->eFlags ^= EF_TELEPORT_BIT;
5822 					pm->ps->pm_flags &= ~PMF_DUCKED;
5823 				}
5824 				else
5825 				{//Put us back
5826 					pm->ps->origin[2] -= oldHeight - pm->maxs[2];
5827 				}
5828 				//NOTE: this isn't the best way to check this, you may have room to unduck
5829 				//while in air, but your feet are close to landing.  Probably won't be a
5830 				//noticable shortcoming
5831 			}
5832 			else
5833 			{
5834 				// try to stand up
5835 				pm->maxs[2] = standheight;
5836 				pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
5837 				if ( !trace.allsolid )
5838 				{
5839 					pm->ps->pm_flags &= ~PMF_DUCKED;
5840 				}
5841 			}
5842 		}
5843 
5844 		if ( pm->ps->pm_flags & PMF_DUCKED )
5845 		{//Still ducking
5846 			pm->maxs[2] = crouchheight;
5847 			pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT;
5848 		}
5849 		else
5850 		{//standing now
5851 			pm->maxs[2] = standheight;
5852 			//FIXME: have a crouchviewheight and standviewheight on ent?
5853 			pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
5854 		}
5855 	}
5856 }
5857 
5858 
5859 
5860 //===================================================================
PM_SaberLockAnim(int anim)5861 qboolean PM_SaberLockAnim( int anim )
5862 {
5863 	switch ( anim )
5864 	{
5865 	case BOTH_BF2LOCK:	//#
5866 	case BOTH_BF1LOCK:	//#
5867 	case BOTH_CWCIRCLELOCK:	//#
5868 	case BOTH_CCWCIRCLELOCK:	//#
5869 		return qtrue;
5870 	}
5871 	return qfalse;
5872 }
5873 
PM_ForceAnim(int anim)5874 qboolean PM_ForceAnim( int anim )
5875 {
5876 	switch ( anim )
5877 	{
5878 	case BOTH_CHOKE1:			//being choked...???
5879 	case BOTH_GESTURE1:			//taunting...
5880 	case BOTH_RESISTPUSH:		//# plant yourself to resist force push/pulls.
5881 	case BOTH_FORCEPUSH:		//# Use off-hand to do force power.
5882 	case BOTH_FORCEPULL:		//# Use off-hand to do force power.
5883 	case BOTH_MINDTRICK1:		//# Use off-hand to do mind trick
5884 	case BOTH_MINDTRICK2:		//# Use off-hand to do distraction
5885 	case BOTH_FORCELIGHTNING:	//# Use off-hand to do lightning
5886 	case BOTH_FORCELIGHTNING_START:
5887 	case BOTH_FORCELIGHTNING_HOLD:	//# Use off-hand to do lightning
5888 	case BOTH_FORCELIGHTNING_RELEASE:	//# Use off-hand to do lightning
5889 	case BOTH_FORCEHEAL_START:	//# Healing meditation pose start
5890 	case BOTH_FORCEHEAL_STOP:	//# Healing meditation pose end
5891 	case BOTH_FORCEHEAL_QUICK:	//# Healing meditation gesture
5892 	case BOTH_FORCEGRIP1:		//# temp force-grip anim (actually re-using push)
5893 	case BOTH_FORCEGRIP_HOLD:		//# temp force-grip anim (actually re-using push)
5894 	case BOTH_FORCEGRIP_RELEASE:		//# temp force-grip anim (actually re-using push)
5895 	//case BOTH_FORCEGRIP3:		//# force-gripping
5896 	case BOTH_FORCE_RAGE:
5897 	case BOTH_FORCE_2HANDEDLIGHTNING:
5898 	case BOTH_FORCE_2HANDEDLIGHTNING_START:
5899 	case BOTH_FORCE_2HANDEDLIGHTNING_HOLD:
5900 	case BOTH_FORCE_2HANDEDLIGHTNING_RELEASE:
5901 	case BOTH_FORCE_DRAIN:
5902 	case BOTH_FORCE_DRAIN_START:
5903 	case BOTH_FORCE_DRAIN_HOLD:
5904 	case BOTH_FORCE_DRAIN_RELEASE:
5905 	case BOTH_FORCE_ABSORB:
5906 	case BOTH_FORCE_ABSORB_START:
5907 	case BOTH_FORCE_ABSORB_END:
5908 	case BOTH_FORCE_PROTECT:
5909 	case BOTH_FORCE_PROTECT_FAST:
5910 		return qtrue;
5911 		break;
5912 	}
5913 	return qfalse;
5914 }
5915 
PM_InSaberAnim(int anim)5916 qboolean PM_InSaberAnim( int anim )
5917 {
5918 	if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR )
5919 	{
5920 		return qtrue;
5921 	}
5922 	return qfalse;
5923 }
5924 
PM_InForceGetUp(playerState_t * ps)5925 qboolean PM_InForceGetUp( playerState_t *ps )
5926 {
5927 	switch ( ps->legsAnim )
5928 	{
5929 	case BOTH_FORCE_GETUP_F1:
5930 	case BOTH_FORCE_GETUP_F2:
5931 	case BOTH_FORCE_GETUP_B1:
5932 	case BOTH_FORCE_GETUP_B2:
5933 	case BOTH_FORCE_GETUP_B3:
5934 	case BOTH_FORCE_GETUP_B4:
5935 	case BOTH_FORCE_GETUP_B5:
5936 	case BOTH_FORCE_GETUP_B6:
5937 	case BOTH_GETUP_BROLL_B:
5938 	case BOTH_GETUP_BROLL_F:
5939 	case BOTH_GETUP_BROLL_L:
5940 	case BOTH_GETUP_BROLL_R:
5941 	case BOTH_GETUP_FROLL_B:
5942 	case BOTH_GETUP_FROLL_F:
5943 	case BOTH_GETUP_FROLL_L:
5944 	case BOTH_GETUP_FROLL_R:
5945 		if ( ps->legsAnimTimer )
5946 		{
5947 			return qtrue;
5948 		}
5949 		break;
5950 	}
5951 	return qfalse;
5952 }
5953 
PM_InGetUp(playerState_t * ps)5954 qboolean PM_InGetUp( playerState_t *ps )
5955 {
5956 	switch ( ps->legsAnim )
5957 	{
5958 	case BOTH_GETUP1:
5959 	case BOTH_GETUP2:
5960 	case BOTH_GETUP3:
5961 	case BOTH_GETUP4:
5962 	case BOTH_GETUP5:
5963 	case BOTH_GETUP_CROUCH_F1:
5964 	case BOTH_GETUP_CROUCH_B1:
5965 	case BOTH_GETUP_BROLL_B:
5966 	case BOTH_GETUP_BROLL_F:
5967 	case BOTH_GETUP_BROLL_L:
5968 	case BOTH_GETUP_BROLL_R:
5969 	case BOTH_GETUP_FROLL_B:
5970 	case BOTH_GETUP_FROLL_F:
5971 	case BOTH_GETUP_FROLL_L:
5972 	case BOTH_GETUP_FROLL_R:
5973 		if ( ps->legsAnimTimer )
5974 		{
5975 			return qtrue;
5976 		}
5977 		break;
5978 	default:
5979 		return PM_InForceGetUp( ps );
5980 		break;
5981 	}
5982 	//what the hell, redundant, but...
5983 	return qfalse;
5984 }
5985 
PM_InGetUpNoRoll(playerState_t * ps)5986 qboolean PM_InGetUpNoRoll( playerState_t *ps )
5987 {
5988 	switch ( ps->legsAnim )
5989 	{
5990 	case BOTH_GETUP1:
5991 	case BOTH_GETUP2:
5992 	case BOTH_GETUP3:
5993 	case BOTH_GETUP4:
5994 	case BOTH_GETUP5:
5995 	case BOTH_GETUP_CROUCH_F1:
5996 	case BOTH_GETUP_CROUCH_B1:
5997 	case BOTH_FORCE_GETUP_F1:
5998 	case BOTH_FORCE_GETUP_F2:
5999 	case BOTH_FORCE_GETUP_B1:
6000 	case BOTH_FORCE_GETUP_B2:
6001 	case BOTH_FORCE_GETUP_B3:
6002 	case BOTH_FORCE_GETUP_B4:
6003 	case BOTH_FORCE_GETUP_B5:
6004 	case BOTH_FORCE_GETUP_B6:
6005 		if ( ps->legsAnimTimer )
6006 		{
6007 			return qtrue;
6008 		}
6009 		break;
6010 	}
6011 	return qfalse;
6012 }
6013 
PM_InKnockDown(playerState_t * ps)6014 qboolean PM_InKnockDown( playerState_t *ps )
6015 {
6016 	switch ( ps->legsAnim )
6017 	{
6018 	case BOTH_KNOCKDOWN1:
6019 	case BOTH_KNOCKDOWN2:
6020 	case BOTH_KNOCKDOWN3:
6021 	case BOTH_KNOCKDOWN4:
6022 	case BOTH_KNOCKDOWN5:
6023 	//special anims:
6024 	case BOTH_RELEASED:
6025 		return qtrue;
6026 		break;
6027 	case BOTH_LK_DL_ST_T_SB_1_L:
6028 		if ( ps->legsAnimTimer < 550 )
6029 		{
6030 			return qtrue;
6031 		}
6032 		break;
6033 	case BOTH_PLAYER_PA_3_FLY:
6034 		if ( ps->legsAnimTimer < 300 )
6035 		{
6036 			return qtrue;
6037 		}
6038 		/*
6039 		else if ( ps->clientNum < MAX_CLIENTS
6040 			&& ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
6041 		{
6042 			return qtrue;
6043 		}
6044 		*/
6045 		break;
6046 	default:
6047 		return PM_InGetUp( ps );
6048 		break;
6049 	}
6050 	return qfalse;
6051 }
6052 
PM_InKnockDownNoGetup(playerState_t * ps)6053 qboolean PM_InKnockDownNoGetup( playerState_t *ps )
6054 {
6055 	switch ( ps->legsAnim )
6056 	{
6057 	case BOTH_KNOCKDOWN1:
6058 	case BOTH_KNOCKDOWN2:
6059 	case BOTH_KNOCKDOWN3:
6060 	case BOTH_KNOCKDOWN4:
6061 	case BOTH_KNOCKDOWN5:
6062 	//special anims:
6063 	case BOTH_RELEASED:
6064 		return qtrue;
6065 		break;
6066 	case BOTH_LK_DL_ST_T_SB_1_L:
6067 		if ( ps->legsAnimTimer < 550 )
6068 		{
6069 			return qtrue;
6070 		}
6071 		break;
6072 	case BOTH_PLAYER_PA_3_FLY:
6073 		if ( ps->legsAnimTimer < 300 )
6074 		{
6075 			return qtrue;
6076 		}
6077 		/*
6078 		else if ( ps->clientNum < MAX_CLIENTS
6079 			&& ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
6080 		{
6081 			return qtrue;
6082 		}
6083 		*/
6084 		break;
6085 	}
6086 	return qfalse;
6087 }
6088 
PM_InKnockDownOnGround(playerState_t * ps)6089 qboolean PM_InKnockDownOnGround( playerState_t *ps )
6090 {
6091 	switch ( ps->legsAnim )
6092 	{
6093 	case BOTH_KNOCKDOWN1:
6094 	case BOTH_KNOCKDOWN2:
6095 	case BOTH_KNOCKDOWN3:
6096 	case BOTH_KNOCKDOWN4:
6097 	case BOTH_KNOCKDOWN5:
6098 	case BOTH_RELEASED:
6099 		//if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer > 300 )
6100 		{//at end of fall down anim
6101 			return qtrue;
6102 		}
6103 		break;
6104 	case BOTH_GETUP1:
6105 	case BOTH_GETUP2:
6106 	case BOTH_GETUP3:
6107 	case BOTH_GETUP4:
6108 	case BOTH_GETUP5:
6109 	case BOTH_GETUP_CROUCH_F1:
6110 	case BOTH_GETUP_CROUCH_B1:
6111 	case BOTH_FORCE_GETUP_F1:
6112 	case BOTH_FORCE_GETUP_F2:
6113 	case BOTH_FORCE_GETUP_B1:
6114 	case BOTH_FORCE_GETUP_B2:
6115 	case BOTH_FORCE_GETUP_B3:
6116 	case BOTH_FORCE_GETUP_B4:
6117 	case BOTH_FORCE_GETUP_B5:
6118 	case BOTH_FORCE_GETUP_B6:
6119 		if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer < 500 )
6120 		{//at beginning of getup anim
6121 			return qtrue;
6122 		}
6123 		break;
6124 	case BOTH_GETUP_BROLL_B:
6125 	case BOTH_GETUP_BROLL_F:
6126 	case BOTH_GETUP_BROLL_L:
6127 	case BOTH_GETUP_BROLL_R:
6128 	case BOTH_GETUP_FROLL_B:
6129 	case BOTH_GETUP_FROLL_F:
6130 	case BOTH_GETUP_FROLL_L:
6131 	case BOTH_GETUP_FROLL_R:
6132 		if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer < 500 )
6133 		{//at beginning of getup anim
6134 			return qtrue;
6135 		}
6136 		break;
6137 	case BOTH_LK_DL_ST_T_SB_1_L:
6138 		if ( ps->legsAnimTimer < 1000 )
6139 		{
6140 			return qtrue;
6141 		}
6142 		break;
6143 	case BOTH_PLAYER_PA_3_FLY:
6144 		if ( ps->legsAnimTimer < 300 )
6145 		{
6146 			return qtrue;
6147 		}
6148 		/*
6149 		else if ( ps->clientNum < MAX_CLIENTS
6150 			&& ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
6151 		{
6152 			return qtrue;
6153 		}
6154 		*/
6155 		break;
6156 	}
6157 	return qfalse;
6158 }
6159 
PM_CrouchGetup(float crouchheight)6160 qboolean PM_CrouchGetup( float crouchheight )
6161 {
6162 	pm->maxs[2] = crouchheight;
6163 	pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
6164 	int	anim = -1;
6165 	switch ( pm->ps->legsAnim )
6166 	{
6167 	case BOTH_KNOCKDOWN1:
6168 	case BOTH_KNOCKDOWN2:
6169 	case BOTH_KNOCKDOWN4:
6170 	case BOTH_RELEASED:
6171 	case BOTH_PLAYER_PA_3_FLY:
6172 		anim = BOTH_GETUP_CROUCH_B1;
6173 		break;
6174 	case BOTH_KNOCKDOWN3:
6175 	case BOTH_KNOCKDOWN5:
6176 	case BOTH_LK_DL_ST_T_SB_1_L:
6177 		anim = BOTH_GETUP_CROUCH_F1;
6178 		break;
6179 	}
6180 	if ( anim == -1 )
6181 	{//WTF? stay down?
6182 		pm->ps->legsAnimTimer = 100;//hold this anim for another 10th of a second
6183 		return qfalse;
6184 	}
6185 	else
6186 	{//get up into crouch anim
6187 		if ( PM_LockedAnim( pm->ps->torsoAnim ) )
6188 		{//need to be able to override this anim
6189 			pm->ps->torsoAnimTimer = 0;
6190 		}
6191 		if ( PM_LockedAnim( pm->ps->legsAnim ) )
6192 		{//need to be able to override this anim
6193 			pm->ps->legsAnimTimer = 0;
6194 		}
6195 		PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS );
6196 		pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
6197 		pm->ps->saberBlocked = BLOCKED_NONE;
6198 		return qtrue;
6199 	}
6200 }
6201 
6202 extern qboolean PM_GoingToAttackDown( playerState_t *ps );
PM_CheckRollGetup(void)6203 qboolean PM_CheckRollGetup( void )
6204 {
6205 	if ( pm->ps->legsAnim == BOTH_KNOCKDOWN1
6206 		|| pm->ps->legsAnim == BOTH_KNOCKDOWN2
6207 		|| pm->ps->legsAnim == BOTH_KNOCKDOWN3
6208 		|| pm->ps->legsAnim == BOTH_KNOCKDOWN4
6209 		|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6210 		|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L
6211 		|| pm->ps->legsAnim == BOTH_PLAYER_PA_3_FLY
6212 		|| pm->ps->legsAnim == BOTH_RELEASED )
6213 	{//lying on back or front
6214 		if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && ( pm->cmd.rightmove || (pm->cmd.forwardmove&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) ) )//player pressing left or right
6215 			|| ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->gent->NPC//an NPC
6216 				&& pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have at least force jump 1
6217 				&& pm->gent->enemy //I have an enemy
6218 				&& pm->gent->enemy->client//a client
6219 				&& pm->gent->enemy->enemy == pm->gent//he's mad at me!
6220 				&& (PM_GoingToAttackDown( &pm->gent->enemy->client->ps )||!Q_irand(0,2))//he's attacking downward! (or we just feel like doing it this time)
6221 				&& ((pm->gent->client&&pm->gent->client->NPC_class==CLASS_ALORA)||Q_irand( 0, RANK_CAPTAIN )<pm->gent->NPC->rank) ) )//higher rank I am, more likely I am to roll away!
6222 		{//roll away!
6223 			int anim;
6224 			qboolean forceGetUp = qfalse;
6225 			if ( pm->cmd.forwardmove > 0 )
6226 			{
6227 				if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6228 					|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6229 					|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6230 				{
6231 					anim = BOTH_GETUP_FROLL_F;
6232 				}
6233 				else
6234 				{
6235 					anim = BOTH_GETUP_BROLL_F;
6236 				}
6237 				forceGetUp = qtrue;
6238 			}
6239 			else if ( pm->cmd.forwardmove < 0 )
6240 			{
6241 				if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6242 					|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6243 					|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6244 				{
6245 					anim = BOTH_GETUP_FROLL_B;
6246 				}
6247 				else
6248 				{
6249 					anim = BOTH_GETUP_BROLL_B;
6250 				}
6251 				forceGetUp = qtrue;
6252 			}
6253 			else if ( pm->cmd.rightmove > 0 )
6254 			{
6255 				if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6256 					|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6257 					|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6258 				{
6259 					anim = BOTH_GETUP_FROLL_R;
6260 				}
6261 				else
6262 				{
6263 					anim = BOTH_GETUP_BROLL_R;
6264 				}
6265 			}
6266 			else if ( pm->cmd.rightmove < 0 )
6267 			{
6268 				if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6269 					|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6270 					|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6271 				{
6272 					anim = BOTH_GETUP_FROLL_L;
6273 				}
6274 				else
6275 				{
6276 					anim = BOTH_GETUP_BROLL_L;
6277 				}
6278 			}
6279 			else
6280 			{
6281 				if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6282 					|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6283 					|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6284 				{//on your front
6285 					anim = Q_irand( BOTH_GETUP_FROLL_B, BOTH_GETUP_FROLL_R );
6286 				}
6287 				else
6288 				{
6289 					anim = Q_irand( BOTH_GETUP_BROLL_B, BOTH_GETUP_BROLL_R );
6290 				}
6291 			}
6292 			if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
6293 			{
6294 				if ( !G_CheckRollSafety( pm->gent, anim, 64 ) )
6295 				{//oops, try other one
6296 					if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3
6297 						|| pm->ps->legsAnim == BOTH_KNOCKDOWN5
6298 						|| pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6299 					{
6300 						if ( anim == BOTH_GETUP_FROLL_R )
6301 						{
6302 							anim = BOTH_GETUP_FROLL_L;
6303 						}
6304 						else if ( anim == BOTH_GETUP_FROLL_F )
6305 						{
6306 							anim = BOTH_GETUP_FROLL_B;
6307 						}
6308 						else if ( anim == BOTH_GETUP_FROLL_B )
6309 						{
6310 							anim = BOTH_GETUP_FROLL_F;
6311 						}
6312 						else
6313 						{
6314 							anim = BOTH_GETUP_FROLL_L;
6315 						}
6316 						if ( !G_CheckRollSafety( pm->gent, anim, 64 ) )
6317 						{//neither side is clear, screw it
6318 							return qfalse;
6319 						}
6320 					}
6321 					else
6322 					{
6323 						if ( anim == BOTH_GETUP_BROLL_R )
6324 						{
6325 							anim = BOTH_GETUP_BROLL_L;
6326 						}
6327 						else if ( anim == BOTH_GETUP_BROLL_F )
6328 						{
6329 							anim = BOTH_GETUP_BROLL_B;
6330 						}
6331 						else if ( anim == BOTH_GETUP_FROLL_B )
6332 						{
6333 							anim = BOTH_GETUP_BROLL_F;
6334 						}
6335 						else
6336 						{
6337 							anim = BOTH_GETUP_BROLL_L;
6338 						}
6339 						if ( !G_CheckRollSafety( pm->gent, anim, 64 ) )
6340 						{//neither side is clear, screw it
6341 							return qfalse;
6342 						}
6343 					}
6344 				}
6345 			}
6346 			pm->cmd.rightmove = pm->cmd.forwardmove = 0;
6347 			if ( PM_LockedAnim( pm->ps->torsoAnim ) )
6348 			{//need to be able to override this anim
6349 				pm->ps->torsoAnimTimer = 0;
6350 			}
6351 			if ( PM_LockedAnim( pm->ps->legsAnim ) )
6352 			{//need to be able to override this anim
6353 				pm->ps->legsAnimTimer = 0;
6354 			}
6355 			PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS );
6356 			pm->ps->weaponTime = pm->ps->torsoAnimTimer - 300;//don't attack until near end of this anim
6357 			pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
6358 			pm->ps->saberBlocked = BLOCKED_NONE;
6359 			if ( forceGetUp )
6360 			{
6361 				if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY
6362 					&& pm->gent->NPC && pm->gent->NPC->blockedSpeechDebounceTime < level.time
6363 					&& !Q_irand( 0, 1 ) )
6364 				{
6365 					PM_AddEvent( Q_irand( EV_COMBAT1, EV_COMBAT3 ) );
6366 					pm->gent->NPC->blockedSpeechDebounceTime = level.time + 1000;
6367 				}
6368 				G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
6369 				//launch off ground?
6370 				pm->ps->weaponTime = 300;//just to make sure it's cleared
6371 			}
6372 			return qtrue;
6373 		}
6374 	}
6375 	return qfalse;
6376 }
6377 
6378 extern int G_MinGetUpTime( gentity_t *ent );
PM_GettingUpFromKnockDown(float standheight,float crouchheight)6379 qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight )
6380 {
6381 	int legsAnim = pm->ps->legsAnim;
6382 	if ( legsAnim == BOTH_KNOCKDOWN1
6383 		||legsAnim == BOTH_KNOCKDOWN2
6384 		||legsAnim == BOTH_KNOCKDOWN3
6385 		||legsAnim == BOTH_KNOCKDOWN4
6386 		||legsAnim == BOTH_KNOCKDOWN5
6387 		||legsAnim == BOTH_PLAYER_PA_3_FLY
6388 		||legsAnim == BOTH_LK_DL_ST_T_SB_1_L
6389 		||legsAnim == BOTH_RELEASED )
6390 	{//in a knockdown
6391 		int minTimeLeft = G_MinGetUpTime( pm->gent );
6392 		if ( pm->ps->legsAnimTimer <= minTimeLeft )
6393 		{//if only a quarter of a second left, allow roll-aways
6394 			if ( PM_CheckRollGetup() )
6395 			{
6396 				pm->cmd.rightmove = pm->cmd.forwardmove = 0;
6397 				return qtrue;
6398 			}
6399 		}
6400 		if ( TIMER_Exists( pm->gent, "noGetUpStraight" ) )
6401 		{
6402 			if ( !TIMER_Done2( pm->gent, "noGetUpStraight", qtrue ) )
6403 			{//not allowed to do straight get-ups for another few seconds
6404 				if ( pm->ps->legsAnimTimer <= minTimeLeft )
6405 				{//hold it for a bit
6406 					pm->ps->legsAnimTimer = minTimeLeft+1;
6407 				}
6408 			}
6409 		}
6410 		if ( !pm->ps->legsAnimTimer	|| (pm->ps->legsAnimTimer<=minTimeLeft&&(pm->cmd.upmove>0||(pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_ALORA))) )
6411 		{//done with the knockdown - FIXME: somehow this is allowing an *instant* getup...???
6412 			//FIXME: if trying to crouch (holding button?), just get up into a crouch?
6413 			if ( pm->cmd.upmove < 0 )
6414 			{
6415 				return PM_CrouchGetup( crouchheight );
6416 			}
6417 			else
6418 			{
6419 				trace_t	trace;
6420 				// try to stand up
6421 				pm->maxs[2] = standheight;
6422 				pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
6423 				if ( !trace.allsolid )
6424 				{//stand up
6425 					int	anim = BOTH_GETUP1;
6426 					qboolean forceGetUp = qfalse;
6427 					pm->maxs[2] = standheight;
6428 					pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;
6429 					//NOTE: the force power checks will stop fencers and grunts from getting up using force jump
6430 					switch ( pm->ps->legsAnim )
6431 					{
6432 					case BOTH_KNOCKDOWN1:
6433 						if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6434 						{
6435 							anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end
6436 							forceGetUp = qtrue;
6437 						}
6438 						else
6439 						{
6440 							anim = BOTH_GETUP1;
6441 						}
6442 						break;
6443 					case BOTH_KNOCKDOWN2:
6444 					case BOTH_PLAYER_PA_3_FLY:
6445 						if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6446 						{
6447 							anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end
6448 							forceGetUp = qtrue;
6449 						}
6450 						else
6451 						{
6452 							anim = BOTH_GETUP2;
6453 						}
6454 						break;
6455 					case BOTH_KNOCKDOWN3:
6456 						if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )
6457 						{
6458 							anim = Q_irand( BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2 );
6459 							forceGetUp = qtrue;
6460 						}
6461 						else
6462 						{
6463 							anim = BOTH_GETUP3;
6464 						}
6465 						break;
6466 					case BOTH_KNOCKDOWN4:
6467 					case BOTH_RELEASED:
6468 						if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6469 						{
6470 							anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end
6471 							forceGetUp = qtrue;
6472 						}
6473 						else
6474 						{
6475 							anim = BOTH_GETUP4;
6476 						}
6477 						break;
6478 					case BOTH_KNOCKDOWN5:
6479 					case BOTH_LK_DL_ST_T_SB_1_L:
6480 						if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) )
6481 						{
6482 							anim = Q_irand( BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2 );
6483 							forceGetUp = qtrue;
6484 						}
6485 						else
6486 						{
6487 							anim = BOTH_GETUP5;
6488 						}
6489 						break;
6490 					}
6491 					//Com_Printf( "getupanim = %s\n", animTable[anim].name );
6492 					if ( forceGetUp )
6493 					{
6494 						if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY
6495 							&& pm->gent->NPC && pm->gent->NPC->blockedSpeechDebounceTime < level.time
6496 							&& !Q_irand( 0, 1 ) )
6497 						{
6498 							PM_AddEvent( Q_irand( EV_COMBAT1, EV_COMBAT3 ) );
6499 							pm->gent->NPC->blockedSpeechDebounceTime = level.time + 1000;
6500 						}
6501 						G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
6502 						//launch off ground?
6503 						pm->ps->weaponTime = 300;//just to make sure it's cleared
6504 					}
6505 					if ( PM_LockedAnim( pm->ps->torsoAnim ) )
6506 					{//need to be able to override this anim
6507 						pm->ps->torsoAnimTimer = 0;
6508 					}
6509 					if ( PM_LockedAnim( pm->ps->legsAnim ) )
6510 					{//need to be able to override this anim
6511 						pm->ps->legsAnimTimer = 0;
6512 					}
6513 					PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS );
6514 					pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in
6515 					pm->ps->saberBlocked = BLOCKED_NONE;
6516 					return qtrue;
6517 				}
6518 				else
6519 				{
6520 					return PM_CrouchGetup( crouchheight );
6521 				}
6522 			}
6523 		}
6524 		else
6525 		{
6526 			if ( pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
6527 			{
6528 				PM_CmdForRoll( pm->ps, &pm->cmd );
6529 			}
6530 			else
6531 			{
6532 				pm->cmd.rightmove = pm->cmd.forwardmove = 0;
6533 			}
6534 		}
6535 	}
6536 	return qfalse;
6537 }
6538 
PM_CmdForRoll(playerState_t * ps,usercmd_t * pCmd)6539 void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd )
6540 {
6541 	switch ( ps->legsAnim )
6542 	{
6543 	case BOTH_ROLL_F:
6544 		pCmd->forwardmove = 127;
6545 		pCmd->rightmove = 0;
6546 		break;
6547 	case BOTH_ROLL_B:
6548 		pCmd->forwardmove = -127;
6549 		pCmd->rightmove = 0;
6550 		break;
6551 	case BOTH_ROLL_R:
6552 		pCmd->forwardmove = 0;
6553 		pCmd->rightmove = 127;
6554 		break;
6555 	case BOTH_ROLL_L:
6556 		pCmd->forwardmove = 0;
6557 		pCmd->rightmove = -127;
6558 		break;
6559 
6560 	case BOTH_GETUP_BROLL_R:
6561 		pCmd->forwardmove = 0;
6562 		pCmd->rightmove = 48;
6563 		//NOTE: speed is 400
6564 		break;
6565 
6566 	case BOTH_GETUP_FROLL_R:
6567 		if ( ps->legsAnimTimer <= 250 )
6568 		{//end of anim
6569 			pCmd->forwardmove = pCmd->rightmove = 0;
6570 		}
6571 		else
6572 		{
6573 			pCmd->forwardmove = 0;
6574 			pCmd->rightmove = 48;
6575 			//NOTE: speed is 400
6576 		}
6577 		break;
6578 
6579 	case BOTH_GETUP_BROLL_L:
6580 		pCmd->forwardmove = 0;
6581 		pCmd->rightmove = -48;
6582 		//NOTE: speed is 400
6583 		break;
6584 
6585 	case BOTH_GETUP_FROLL_L:
6586 		if ( ps->legsAnimTimer <= 250 )
6587 		{//end of anim
6588 			pCmd->forwardmove = pCmd->rightmove = 0;
6589 		}
6590 		else
6591 		{
6592 			pCmd->forwardmove = 0;
6593 			pCmd->rightmove = -48;
6594 			//NOTE: speed is 400
6595 		}
6596 		break;
6597 
6598 	case BOTH_GETUP_BROLL_B:
6599 		if ( ps->torsoAnimTimer <= 250 )
6600 		{//end of anim
6601 			pCmd->forwardmove = pCmd->rightmove = 0;
6602 		}
6603 		else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 350 )
6604 		{//beginning of anim
6605 			pCmd->forwardmove = pCmd->rightmove = 0;
6606 		}
6607 		else
6608 		{
6609 			//FIXME: ramp down over length of anim
6610 			pCmd->forwardmove = -64;
6611 			pCmd->rightmove = 0;
6612 			//NOTE: speed is 400
6613 		}
6614 		break;
6615 
6616 	case BOTH_GETUP_FROLL_B:
6617 		if ( ps->torsoAnimTimer <= 100 )
6618 		{//end of anim
6619 			pCmd->forwardmove = pCmd->rightmove = 0;
6620 		}
6621 		else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 200 )
6622 		{//beginning of anim
6623 			pCmd->forwardmove = pCmd->rightmove = 0;
6624 		}
6625 		else
6626 		{
6627 			//FIXME: ramp down over length of anim
6628 			pCmd->forwardmove = -64;
6629 			pCmd->rightmove = 0;
6630 			//NOTE: speed is 400
6631 		}
6632 		break;
6633 
6634 	case BOTH_GETUP_BROLL_F:
6635 		if ( ps->torsoAnimTimer <= 550 )
6636 		{//end of anim
6637 			pCmd->forwardmove = pCmd->rightmove = 0;
6638 		}
6639 		else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 150 )
6640 		{//beginning of anim
6641 			pCmd->forwardmove = pCmd->rightmove = 0;
6642 		}
6643 		else
6644 		{
6645 			pCmd->forwardmove = 64;
6646 			pCmd->rightmove = 0;
6647 			//NOTE: speed is 400
6648 		}
6649 		break;
6650 
6651 	case BOTH_GETUP_FROLL_F:
6652 		if ( ps->torsoAnimTimer <= 100 )
6653 		{//end of anim
6654 			pCmd->forwardmove = pCmd->rightmove = 0;
6655 		}
6656 		else
6657 		{
6658 			//FIXME: ramp down over length of anim
6659 			pCmd->forwardmove = 64;
6660 			pCmd->rightmove = 0;
6661 			//NOTE: speed is 400
6662 		}
6663 		break;
6664 	case BOTH_LK_DL_ST_T_SB_1_L:
6665 		//kicked backwards
6666 		if ( ps->legsAnimTimer < 3050//at least 10 frames in
6667 			&& ps->legsAnimTimer > 550 )//at least 6 frames from end
6668 		{//move backwards
6669 			pCmd->forwardmove = -64;
6670 			pCmd->rightmove = 0;
6671 		}
6672 		else
6673 		{
6674 			pCmd->forwardmove = pCmd->rightmove = 0;
6675 		}
6676 		break;
6677 	}
6678 
6679 	pCmd->upmove = 0;
6680 }
6681 
PM_InRollIgnoreTimer(playerState_t * ps)6682 qboolean PM_InRollIgnoreTimer( playerState_t *ps )
6683 {
6684 	switch ( ps->legsAnim )
6685 	{
6686 	case BOTH_ROLL_F:
6687 	case BOTH_ROLL_B:
6688 	case BOTH_ROLL_R:
6689 	case BOTH_ROLL_L:
6690 	case BOTH_GETUP_BROLL_B:
6691 	case BOTH_GETUP_BROLL_F:
6692 	case BOTH_GETUP_BROLL_L:
6693 	case BOTH_GETUP_BROLL_R:
6694 	case BOTH_GETUP_FROLL_B:
6695 	case BOTH_GETUP_FROLL_F:
6696 	case BOTH_GETUP_FROLL_L:
6697 	case BOTH_GETUP_FROLL_R:
6698 		return qtrue;
6699 	}
6700 	return qfalse;
6701 }
6702 
PM_InRoll(playerState_t * ps)6703 qboolean PM_InRoll( playerState_t *ps )
6704 {
6705 	if ( ps->legsAnimTimer && PM_InRollIgnoreTimer( ps ) )
6706 	{
6707 		return qtrue;
6708 	}
6709 
6710 	return qfalse;
6711 }
6712 
PM_CrouchAnim(int anim)6713 qboolean PM_CrouchAnim( int anim )
6714 {
6715 	switch ( anim )
6716 	{
6717 	case BOTH_SIT1:				//# Normal chair sit.
6718 	case BOTH_SIT2:				//# Lotus position.
6719 	case BOTH_SIT3:				//# Sitting in tired position: elbows on knees
6720 	case BOTH_CROUCH1:			//# Transition from standing to crouch
6721 	case BOTH_CROUCH1IDLE:		//# Crouching idle
6722 	case BOTH_CROUCH1WALK:		//# Walking while crouched
6723 	case BOTH_CROUCH1WALKBACK:	//# Walking while crouched
6724 	case BOTH_CROUCH2TOSTAND1:	//# going from crouch2 to stand1
6725 	case BOTH_CROUCH3:			//# Desann crouching down to Kyle (cin 9)
6726 	case BOTH_KNEES1:			//# Tavion on her knees
6727 	case BOTH_CROUCHATTACKBACK1://FIXME: not if in middle of anim?
6728 	case BOTH_ROLL_STAB:
6729 	//???
6730 	case BOTH_STAND_TO_KNEEL:
6731 	case BOTH_KNEEL_TO_STAND:
6732 	case BOTH_TURNCROUCH1:
6733 	case BOTH_CROUCH4:
6734 	case BOTH_KNEES2:			//# Tavion on her knees looking down
6735 	case BOTH_KNEES2TO1:			//# Transition of KNEES2 to KNEES1
6736 		return qtrue;
6737 		break;
6738 	}
6739 	return qfalse;
6740 }
6741 
PM_PainAnim(int anim)6742 qboolean PM_PainAnim( int anim )
6743 {
6744 	switch ( anim )
6745 	{
6746 		case BOTH_PAIN1:				//# First take pain anim
6747 		case BOTH_PAIN2:				//# Second take pain anim
6748 		case BOTH_PAIN3:				//# Third take pain anim
6749 		case BOTH_PAIN4:				//# Fourth take pain anim
6750 		case BOTH_PAIN5:				//# Fifth take pain anim - from behind
6751 		case BOTH_PAIN6:				//# Sixth take pain anim - from behind
6752 		case BOTH_PAIN7:				//# Seventh take pain anim - from behind
6753 		case BOTH_PAIN8:				//# Eigth take pain anim - from behind
6754 		case BOTH_PAIN9:				//#
6755 		case BOTH_PAIN10:			//#
6756 		case BOTH_PAIN11:			//#
6757 		case BOTH_PAIN12:			//#
6758 		case BOTH_PAIN13:			//#
6759 		case BOTH_PAIN14:			//#
6760 		case BOTH_PAIN15:			//#
6761 		case BOTH_PAIN16:			//#
6762 		case BOTH_PAIN17:			//#
6763 		case BOTH_PAIN18:			//#
6764 		return qtrue;
6765 		break;
6766 	}
6767 	return qfalse;
6768 }
6769 
6770 
PM_DodgeHoldAnim(int anim)6771 qboolean PM_DodgeHoldAnim( int anim )
6772 {
6773 	switch ( anim )
6774 	{
6775 	case BOTH_DODGE_HOLD_FL:
6776 	case BOTH_DODGE_HOLD_FR:
6777 	case BOTH_DODGE_HOLD_BL:
6778 	case BOTH_DODGE_HOLD_BR:
6779 	case BOTH_DODGE_HOLD_L:
6780 	case BOTH_DODGE_HOLD_R:
6781 		return qtrue;
6782 		break;
6783 	}
6784 	return qfalse;
6785 }
6786 
PM_DodgeAnim(int anim)6787 qboolean PM_DodgeAnim( int anim )
6788 {
6789 	switch ( anim )
6790 	{
6791 	case BOTH_DODGE_FL:			//# lean-dodge forward left
6792 	case BOTH_DODGE_FR:			//# lean-dodge forward right
6793 	case BOTH_DODGE_BL:			//# lean-dodge backwards left
6794 	case BOTH_DODGE_BR:			//# lean-dodge backwards right
6795 	case BOTH_DODGE_L:			//# lean-dodge left
6796 	case BOTH_DODGE_R:			//# lean-dodge right
6797 		return qtrue;
6798 		break;
6799 	default:
6800 		return PM_DodgeHoldAnim( anim );
6801 		break;
6802 	}
6803 	//return qfalse;
6804 }
6805 
PM_ForceJumpingAnim(int anim)6806 qboolean PM_ForceJumpingAnim( int anim )
6807 {
6808 	switch ( anim )
6809 	{
6810 		case BOTH_FORCEJUMP1:				//# Jump - wind-up and leave ground
6811 		case BOTH_FORCEINAIR1:			//# In air loop (from jump)
6812 		case BOTH_FORCELAND1:				//# Landing (from in air loop)
6813 		case BOTH_FORCEJUMPBACK1:			//# Jump backwards - wind-up and leave ground
6814 		case BOTH_FORCEINAIRBACK1:		//# In air loop (from jump back)
6815 		case BOTH_FORCELANDBACK1:			//# Landing backwards(from in air loop)
6816 		case BOTH_FORCEJUMPLEFT1:			//# Jump left - wind-up and leave ground
6817 		case BOTH_FORCEINAIRLEFT1:		//# In air loop (from jump left)
6818 		case BOTH_FORCELANDLEFT1:			//# Landing left(from in air loop)
6819 		case BOTH_FORCEJUMPRIGHT1:		//# Jump right - wind-up and leave ground
6820 		case BOTH_FORCEINAIRRIGHT1:		//# In air loop (from jump right)
6821 		case BOTH_FORCELANDRIGHT1:		//# Landing right(from in air loop)
6822 		return qtrue;
6823 		break;
6824 	}
6825 	return qfalse;
6826 }
6827 
PM_JumpingAnim(int anim)6828 qboolean PM_JumpingAnim( int anim )
6829 {
6830 	switch ( anim )
6831 	{
6832 	case BOTH_JUMP1:				//# Jump - wind-up and leave ground
6833 	case BOTH_INAIR1:			//# In air loop (from jump)
6834 	case BOTH_LAND1:				//# Landing (from in air loop)
6835 	case BOTH_LAND2:				//# Landing Hard (from a great height)
6836 	case BOTH_JUMPBACK1:			//# Jump backwards - wind-up and leave ground
6837 	case BOTH_INAIRBACK1:		//# In air loop (from jump back)
6838 	case BOTH_LANDBACK1:			//# Landing backwards(from in air loop)
6839 	case BOTH_JUMPLEFT1:			//# Jump left - wind-up and leave ground
6840 	case BOTH_INAIRLEFT1:		//# In air loop (from jump left)
6841 	case BOTH_LANDLEFT1:			//# Landing left(from in air loop)
6842 	case BOTH_JUMPRIGHT1:		//# Jump right - wind-up and leave ground
6843 	case BOTH_INAIRRIGHT1:		//# In air loop (from jump right)
6844 	case BOTH_LANDRIGHT1:		//# Landing right(from in air loop)
6845 		return qtrue;
6846 		break;
6847 	default:
6848 		if ( PM_InAirKickingAnim( anim ) )
6849 		{
6850 			return qtrue;
6851 		}
6852 		return PM_ForceJumpingAnim( anim );
6853 		break;
6854 	}
6855 	//return qfalse;
6856 }
6857 
PM_LandingAnim(int anim)6858 qboolean PM_LandingAnim( int anim )
6859 {
6860 	switch ( anim )
6861 	{
6862 		case BOTH_LAND1:				//# Landing (from in air loop)
6863 		case BOTH_LAND2:				//# Landing Hard (from a great height)
6864 		case BOTH_LANDBACK1:			//# Landing backwards(from in air loop)
6865 		case BOTH_LANDLEFT1:			//# Landing left(from in air loop)
6866 		case BOTH_LANDRIGHT1:		//# Landing right(from in air loop)
6867 		case BOTH_FORCELAND1:		//# Landing (from in air loop)
6868 		case BOTH_FORCELANDBACK1:	//# Landing backwards(from in air loop)
6869 		case BOTH_FORCELANDLEFT1:	//# Landing left(from in air loop)
6870 		case BOTH_FORCELANDRIGHT1:	//# Landing right(from in air loop)
6871 		return qtrue;
6872 		break;
6873 	}
6874 	return qfalse;
6875 }
6876 
PM_FlippingAnim(int anim)6877 qboolean PM_FlippingAnim( int anim )
6878 {
6879 	switch ( anim )
6880 	{
6881 	case BOTH_FLIP_F:			//# Flip forward
6882 	case BOTH_FLIP_B:			//# Flip backwards
6883 	case BOTH_FLIP_L:			//# Flip left
6884 	case BOTH_FLIP_R:			//# Flip right
6885 	case BOTH_ALORA_FLIP_1:
6886 	case BOTH_ALORA_FLIP_2:
6887 	case BOTH_ALORA_FLIP_3:
6888 	case BOTH_WALL_RUN_RIGHT_FLIP:
6889 	case BOTH_WALL_RUN_LEFT_FLIP:
6890 	case BOTH_WALL_FLIP_RIGHT:
6891 	case BOTH_WALL_FLIP_LEFT:
6892 	case BOTH_FLIP_BACK1:
6893 	case BOTH_FLIP_BACK2:
6894 	case BOTH_FLIP_BACK3:
6895 	case BOTH_WALL_FLIP_BACK1:
6896 	case BOTH_ALORA_FLIP_B:
6897 	//Not really flips, but...
6898 	case BOTH_WALL_RUN_RIGHT:
6899 	case BOTH_WALL_RUN_LEFT:
6900 	case BOTH_WALL_RUN_RIGHT_STOP:
6901 	case BOTH_WALL_RUN_LEFT_STOP:
6902 	case BOTH_BUTTERFLY_LEFT:
6903 	case BOTH_BUTTERFLY_RIGHT:
6904 	case BOTH_BUTTERFLY_FL1:
6905 	case BOTH_BUTTERFLY_FR1:
6906 	//
6907 	case BOTH_ARIAL_LEFT:
6908 	case BOTH_ARIAL_RIGHT:
6909 	case BOTH_ARIAL_F1:
6910 	case BOTH_CARTWHEEL_LEFT:
6911 	case BOTH_CARTWHEEL_RIGHT:
6912 	case BOTH_JUMPFLIPSLASHDOWN1:
6913 	case BOTH_JUMPFLIPSTABDOWN:
6914 	case BOTH_JUMPATTACK6:
6915 	case BOTH_JUMPATTACK7:
6916 	//JKA
6917 	case BOTH_FORCEWALLRUNFLIP_END:
6918 	case BOTH_FORCEWALLRUNFLIP_ALT:
6919 	case BOTH_FLIP_ATTACK7:
6920 	case BOTH_A7_SOULCAL:
6921 		return qtrue;
6922 		break;
6923 	}
6924 	return qfalse;
6925 }
6926 
PM_WalkingAnim(int anim)6927 qboolean PM_WalkingAnim( int anim )
6928 {
6929 	switch ( anim )
6930 	{
6931 	case BOTH_WALK1:				//# Normal walk
6932 	case BOTH_WALK2:				//# Normal walk with saber
6933 	case BOTH_WALK_STAFF:			//# Normal walk with staff
6934 	case BOTH_WALK_DUAL:			//# Normal walk with staff
6935 	case BOTH_WALK5:				//# Tavion taunting Kyle (cin 22)
6936 	case BOTH_WALK6:				//# Slow walk for Luke (cin 12)
6937 	case BOTH_WALK7:				//# Fast walk
6938 	case BOTH_WALKBACK1:			//# Walk1 backwards
6939 	case BOTH_WALKBACK2:			//# Walk2 backwards
6940 	case BOTH_WALKBACK_STAFF:		//# Walk backwards with staff
6941 	case BOTH_WALKBACK_DUAL:		//# Walk backwards with dual
6942 		return qtrue;
6943 		break;
6944 	}
6945 	return qfalse;
6946 }
6947 
PM_RunningAnim(int anim)6948 qboolean PM_RunningAnim( int anim )
6949 {
6950 	switch ( anim )
6951 	{
6952 	case BOTH_RUN1:
6953 	case BOTH_RUN2:
6954 	case BOTH_RUN4:
6955 	case BOTH_RUN_STAFF:
6956 	case BOTH_RUN_DUAL:
6957 	case BOTH_RUNBACK1:
6958 	case BOTH_RUNBACK2:
6959 	case BOTH_RUNBACK_STAFF:
6960 	case BOTH_RUN1START:			//# Start into full run1
6961 	case BOTH_RUN1STOP:			//# Stop from full run1
6962 	case BOTH_RUNSTRAFE_LEFT1:	//# Sidestep left: should loop
6963 	case BOTH_RUNSTRAFE_RIGHT1:	//# Sidestep right: should loop
6964 		return qtrue;
6965 		break;
6966 	}
6967 	return qfalse;
6968 }
6969 
PM_RollingAnim(int anim)6970 qboolean PM_RollingAnim( int anim )
6971 {
6972 	switch ( anim )
6973 	{
6974 	case BOTH_ROLL_F:			//# Roll forward
6975 	case BOTH_ROLL_B:			//# Roll backward
6976 	case BOTH_ROLL_L:			//# Roll left
6977 	case BOTH_ROLL_R:			//# Roll right
6978 	case BOTH_GETUP_BROLL_B:
6979 	case BOTH_GETUP_BROLL_F:
6980 	case BOTH_GETUP_BROLL_L:
6981 	case BOTH_GETUP_BROLL_R:
6982 	case BOTH_GETUP_FROLL_B:
6983 	case BOTH_GETUP_FROLL_F:
6984 	case BOTH_GETUP_FROLL_L:
6985 	case BOTH_GETUP_FROLL_R:
6986 		return qtrue;
6987 		break;
6988 	}
6989 	return qfalse;
6990 }
6991 
PM_SwimmingAnim(int anim)6992 qboolean PM_SwimmingAnim( int anim )
6993 {
6994 	switch ( anim )
6995 	{
6996 	case BOTH_SWIM_IDLE1:		//# Swimming Idle 1
6997 	case BOTH_SWIMFORWARD:		//# Swim forward loop
6998 	case BOTH_SWIMBACKWARD:		//# Swim backward loop
6999 		return qtrue;
7000 		break;
7001 	}
7002 	return qfalse;
7003 }
7004 
PM_LeapingSaberAnim(int anim)7005 qboolean PM_LeapingSaberAnim( int anim )
7006 {
7007 	switch ( anim )
7008 	{
7009 	//level 7
7010 	case BOTH_T7_BR_TL:
7011 	case BOTH_T7__L_BR:
7012 	case BOTH_T7__L__R:
7013 	case BOTH_T7_BL_BR:
7014 	case BOTH_T7_BL__R:
7015 	case BOTH_T7_BL_TR:
7016 		return qtrue;
7017 		break;
7018 	}
7019 	return qfalse;
7020 }
7021 
PM_SpinningSaberAnim(int anim)7022 qboolean PM_SpinningSaberAnim( int anim )
7023 {
7024 	switch ( anim )
7025 	{
7026 	//level 1 - FIXME: level 1 will have *no* spins
7027 	case BOTH_T1_BR_BL:
7028 	case BOTH_T1__R__L:
7029 	case BOTH_T1__R_BL:
7030 	case BOTH_T1_TR_BL:
7031 	case BOTH_T1_BR_TL:
7032 	case BOTH_T1_BR__L:
7033 	case BOTH_T1_TL_BR:
7034 	case BOTH_T1__L_BR:
7035 	case BOTH_T1__L__R:
7036 	case BOTH_T1_BL_BR:
7037 	case BOTH_T1_BL__R:
7038 	case BOTH_T1_BL_TR:
7039 	//level 2
7040 	case BOTH_T2_BR__L:
7041 	case BOTH_T2_BR_BL:
7042 	case BOTH_T2__R_BL:
7043 	case BOTH_T2__L_BR:
7044 	case BOTH_T2_BL_BR:
7045 	case BOTH_T2_BL__R:
7046 	//level 3
7047 	case BOTH_T3_BR__L:
7048 	case BOTH_T3_BR_BL:
7049 	case BOTH_T3__R_BL:
7050 	case BOTH_T3__L_BR:
7051 	case BOTH_T3_BL_BR:
7052 	case BOTH_T3_BL__R:
7053 	//level 4
7054 	case BOTH_T4_BR__L:
7055 	case BOTH_T4_BR_BL:
7056 	case BOTH_T4__R_BL:
7057 	case BOTH_T4__L_BR:
7058 	case BOTH_T4_BL_BR:
7059 	case BOTH_T4_BL__R:
7060 	//level 5
7061 	case BOTH_T5_BR_BL:
7062 	case BOTH_T5__R__L:
7063 	case BOTH_T5__R_BL:
7064 	case BOTH_T5_TR_BL:
7065 	case BOTH_T5_BR_TL:
7066 	case BOTH_T5_BR__L:
7067 	case BOTH_T5_TL_BR:
7068 	case BOTH_T5__L_BR:
7069 	case BOTH_T5__L__R:
7070 	case BOTH_T5_BL_BR:
7071 	case BOTH_T5_BL__R:
7072 	case BOTH_T5_BL_TR:
7073 	//level 6
7074 	case BOTH_T6_BR_TL:
7075 	case BOTH_T6__R_TL:
7076 	case BOTH_T6__R__L:
7077 	case BOTH_T6__R_BL:
7078 	case BOTH_T6_TR_TL:
7079 	case BOTH_T6_TR__L:
7080 	case BOTH_T6_TR_BL:
7081 	case BOTH_T6_T__TL:
7082 	case BOTH_T6_T__BL:
7083 	case BOTH_T6_TL_BR:
7084 	case BOTH_T6__L_BR:
7085 	case BOTH_T6__L__R:
7086 	case BOTH_T6_TL__R:
7087 	case BOTH_T6_TL_TR:
7088 	case BOTH_T6__L_TR:
7089 	case BOTH_T6__L_T_:
7090 	case BOTH_T6_BL_T_:
7091 	case BOTH_T6_BR__L:
7092 	case BOTH_T6_BR_BL:
7093 	case BOTH_T6_BL_BR:
7094 	case BOTH_T6_BL__R:
7095 	case BOTH_T6_BL_TR:
7096 	//level 7
7097 	case BOTH_T7_BR_TL:
7098 	case BOTH_T7_BR__L:
7099 	case BOTH_T7_BR_BL:
7100 	case BOTH_T7__R__L:
7101 	case BOTH_T7__R_BL:
7102 	case BOTH_T7_TR__L:
7103 	case BOTH_T7_T___R:
7104 	case BOTH_T7_TL_BR:
7105 	case BOTH_T7__L_BR:
7106 	case BOTH_T7__L__R:
7107 	case BOTH_T7_BL_BR:
7108 	case BOTH_T7_BL__R:
7109 	case BOTH_T7_BL_TR:
7110 	case BOTH_T7_TL_TR:
7111 	case BOTH_T7_T__BR:
7112 	case BOTH_T7__L_TR:
7113 	case BOTH_V7_BL_S7:
7114 	//special
7115 	//case BOTH_A2_STABBACK1:
7116 	case BOTH_ATTACK_BACK:
7117 	case BOTH_CROUCHATTACKBACK1:
7118 	case BOTH_BUTTERFLY_LEFT:
7119 	case BOTH_BUTTERFLY_RIGHT:
7120 	case BOTH_FJSS_TR_BL:
7121 	case BOTH_FJSS_TL_BR:
7122 	case BOTH_JUMPFLIPSLASHDOWN1:
7123 	case BOTH_JUMPFLIPSTABDOWN:
7124 		return qtrue;
7125 		break;
7126 	}
7127 	return qfalse;
7128 }
7129 
PM_SpinningAnim(int anim)7130 qboolean PM_SpinningAnim( int anim )
7131 {
7132 	/*
7133 	switch ( anim )
7134 	{
7135 	//FIXME: list any other spinning anims
7136 	default:
7137 		break;
7138 	}
7139 	*/
7140 	return PM_SpinningSaberAnim( anim );
7141 }
7142 
PM_ResetAnkleAngles(void)7143 void PM_ResetAnkleAngles( void )
7144 {
7145 	if ( !pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_ATST )
7146 	{
7147 		return;
7148 	}
7149 	if ( pm->gent->footLBone != -1 )
7150 	{
7151 		gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7152 	}
7153 	if ( pm->gent->footRBone != -1 )
7154 	{
7155 		gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7156 	}
7157 }
7158 
PM_AnglesForSlope(const float yaw,const vec3_t slope,vec3_t angles)7159 void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles )
7160 {
7161 	vec3_t	nvf, ovf, ovr, new_angles;
7162 	float	pitch, mod, dot;
7163 
7164 	VectorSet( angles, 0, yaw, 0 );
7165 	AngleVectors( angles, ovf, ovr, NULL );
7166 
7167 	vectoangles( slope, new_angles );
7168 	pitch = new_angles[PITCH] + 90;
7169 	new_angles[ROLL] = new_angles[PITCH] = 0;
7170 
7171 	AngleVectors( new_angles, nvf, NULL, NULL );
7172 
7173 	mod = DotProduct( nvf, ovr );
7174 
7175 	if ( mod < 0 )
7176 		mod = -1;
7177 	else
7178 		mod = 1;
7179 
7180 	dot = DotProduct( nvf, ovf );
7181 
7182 	angles[YAW] = 0;
7183 	angles[PITCH] = dot * pitch;
7184 	angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
7185 }
7186 
PM_FootSlopeTrace(float * pDiff,float * pInterval)7187 void PM_FootSlopeTrace( float *pDiff, float *pInterval )
7188 {
7189 	vec3_t	footLOrg, footROrg, footLBot, footRBot;
7190 	trace_t	trace;
7191 	float	diff, interval;
7192 	if ( pm->gent->client->NPC_class == CLASS_ATST )
7193 	{
7194 		interval = 10;
7195 	}
7196 	else
7197 	{
7198 		interval = 4;//?
7199 	}
7200 
7201 	if ( pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1 )
7202 	{
7203 		if ( pDiff != NULL )
7204 		{
7205 			*pDiff = 0;
7206 		}
7207 		if ( pInterval != NULL )
7208 		{
7209 			*pInterval = interval;
7210 		}
7211 		return;
7212 	}
7213 #if 1
7214 	for ( int i = 0; i < 3; i++ )
7215 	{
7216 		if ( Q_isnan( pm->gent->client->renderInfo.footLPoint[i] )
7217 			|| Q_isnan( pm->gent->client->renderInfo.footRPoint[i] ) )
7218 		{
7219 			if ( pDiff != NULL )
7220 			{
7221 				*pDiff = 0;
7222 			}
7223 			if ( pInterval != NULL )
7224 			{
7225 				*pInterval = interval;
7226 			}
7227 			return;
7228 		}
7229 	}
7230 #else
7231 
7232 	//FIXME: these really should have been gotten on the cgame, but I guess sometimes they're not and we end up with qnan numbers!
7233 	mdxaBone_t	boltMatrix;
7234 	vec3_t		G2Angles = {0, pm->gent->client->ps.legsYaw, 0};
7235 	//get the feet
7236 	gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt,
7237 			&boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7238 					NULL, pm->gent->s.modelScale );
7239 	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint );
7240 
7241 	gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt,
7242 					&boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7243 					NULL, pm->gent->s.modelScale );
7244 	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint );
7245 #endif
7246 	//NOTE: on AT-STs, rotating the foot moves this point, so it will wiggle...
7247 	//		we have to do this extra work (more G2 transforms) to stop the wiggle... is it worth it?
7248 	/*
7249 	if ( pm->gent->client->NPC_class == CLASS_ATST )
7250 	{
7251 		mdxaBone_t	boltMatrix;
7252 		vec3_t		G2Angles = {0, pm->gent->client->ps.legsYaw, 0};
7253 		//get the feet
7254 		gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL );
7255 		gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt,
7256 				&boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7257 						NULL, pm->gent->s.modelScale );
7258 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint );
7259 
7260 		gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL );
7261 		gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt,
7262 						&boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time),
7263 						NULL, pm->gent->s.modelScale );
7264 		gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint );
7265 	}
7266 	*/
7267 	//get these on the cgame and store it, save ourselves a ghoul2 construct skel call
7268 	VectorCopy( pm->gent->client->renderInfo.footLPoint, footLOrg );
7269 	VectorCopy( pm->gent->client->renderInfo.footRPoint, footROrg );
7270 
7271 	//step 2: adjust foot tag z height to bottom of bbox+1
7272 	footLOrg[2] = pm->gent->currentOrigin[2] + pm->gent->mins[2] + 1;
7273 	footROrg[2] = pm->gent->currentOrigin[2] + pm->gent->mins[2] + 1;
7274 	VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 );
7275 	VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 );
7276 
7277 	//step 3: trace down from each, find difference
7278 	vec3_t footMins, footMaxs;
7279 	vec3_t footLSlope, footRSlope;
7280 	if ( pm->gent->client->NPC_class == CLASS_ATST )
7281 	{
7282 		VectorSet( footMins, -16, -16, 0 );
7283 		VectorSet( footMaxs, 16, 16, 1 );
7284 	}
7285 	else
7286 	{
7287 		VectorSet( footMins, -3, -3, 0 );
7288 		VectorSet( footMaxs, 3, 3, 1 );
7289 	}
7290 
7291 	pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
7292 	VectorCopy( trace.endpos, footLBot );
7293 	VectorCopy( trace.plane.normal, footLSlope );
7294 
7295 	pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 );
7296 	VectorCopy( trace.endpos, footRBot );
7297 	VectorCopy( trace.plane.normal, footRSlope );
7298 
7299 	diff = footLBot[2] - footRBot[2];
7300 
7301 	//optional step: for atst, tilt the footpads to match the slopes under it...
7302 	if ( pm->gent->client->NPC_class == CLASS_ATST )
7303 	{
7304 		vec3_t footAngles;
7305 		if ( !VectorCompare( footLSlope, vec3_origin ) )
7306 		{//rotate the ATST's left foot pad to match the slope
7307 			PM_AnglesForSlope( pm->gent->client->renderInfo.legsYaw, footLSlope, footAngles );
7308 			//Hmm... lerp this?
7309 			gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7310 		}
7311 		if ( !VectorCompare( footRSlope, vec3_origin ) )
7312 		{//rotate the ATST's right foot pad to match the slope
7313 			PM_AnglesForSlope( pm->gent->client->renderInfo.legsYaw, footRSlope, footAngles );
7314 			//Hmm... lerp this?
7315 			gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 );
7316 		}
7317 	}
7318 
7319 	if ( pDiff != NULL )
7320 	{
7321 		*pDiff = diff;
7322 	}
7323 	if ( pInterval != NULL )
7324 	{
7325 		*pInterval = interval;
7326 	}
7327 }
7328 
PM_InSlopeAnim(int anim)7329 qboolean PM_InSlopeAnim( int anim )
7330 {
7331 	switch ( anim )
7332 	{
7333 	case LEGS_LEFTUP1:			//# On a slope with left foot 4 higher than right
7334 	case LEGS_LEFTUP2:			//# On a slope with left foot 8 higher than right
7335 	case LEGS_LEFTUP3:			//# On a slope with left foot 12 higher than right
7336 	case LEGS_LEFTUP4:			//# On a slope with left foot 16 higher than right
7337 	case LEGS_LEFTUP5:			//# On a slope with left foot 20 higher than right
7338 	case LEGS_RIGHTUP1:			//# On a slope with RIGHT foot 4 higher than left
7339 	case LEGS_RIGHTUP2:			//# On a slope with RIGHT foot 8 higher than left
7340 	case LEGS_RIGHTUP3:			//# On a slope with RIGHT foot 12 higher than left
7341 	case LEGS_RIGHTUP4:			//# On a slope with RIGHT foot 16 higher than left
7342 	case LEGS_RIGHTUP5:			//# On a slope with RIGHT foot 20 higher than left
7343 	case LEGS_S1_LUP1:
7344 	case LEGS_S1_LUP2:
7345 	case LEGS_S1_LUP3:
7346 	case LEGS_S1_LUP4:
7347 	case LEGS_S1_LUP5:
7348 	case LEGS_S1_RUP1:
7349 	case LEGS_S1_RUP2:
7350 	case LEGS_S1_RUP3:
7351 	case LEGS_S1_RUP4:
7352 	case LEGS_S1_RUP5:
7353 	case LEGS_S3_LUP1:
7354 	case LEGS_S3_LUP2:
7355 	case LEGS_S3_LUP3:
7356 	case LEGS_S3_LUP4:
7357 	case LEGS_S3_LUP5:
7358 	case LEGS_S3_RUP1:
7359 	case LEGS_S3_RUP2:
7360 	case LEGS_S3_RUP3:
7361 	case LEGS_S3_RUP4:
7362 	case LEGS_S3_RUP5:
7363 	case LEGS_S4_LUP1:
7364 	case LEGS_S4_LUP2:
7365 	case LEGS_S4_LUP3:
7366 	case LEGS_S4_LUP4:
7367 	case LEGS_S4_LUP5:
7368 	case LEGS_S4_RUP1:
7369 	case LEGS_S4_RUP2:
7370 	case LEGS_S4_RUP3:
7371 	case LEGS_S4_RUP4:
7372 	case LEGS_S4_RUP5:
7373 	case LEGS_S5_LUP1:
7374 	case LEGS_S5_LUP2:
7375 	case LEGS_S5_LUP3:
7376 	case LEGS_S5_LUP4:
7377 	case LEGS_S5_LUP5:
7378 	case LEGS_S5_RUP1:
7379 	case LEGS_S5_RUP2:
7380 	case LEGS_S5_RUP3:
7381 	case LEGS_S5_RUP4:
7382 	case LEGS_S5_RUP5:
7383 	case LEGS_S6_LUP1:
7384 	case LEGS_S6_LUP2:
7385 	case LEGS_S6_LUP3:
7386 	case LEGS_S6_LUP4:
7387 	case LEGS_S6_LUP5:
7388 	case LEGS_S6_RUP1:
7389 	case LEGS_S6_RUP2:
7390 	case LEGS_S6_RUP3:
7391 	case LEGS_S6_RUP4:
7392 	case LEGS_S6_RUP5:
7393 	case LEGS_S7_LUP1:
7394 	case LEGS_S7_LUP2:
7395 	case LEGS_S7_LUP3:
7396 	case LEGS_S7_LUP4:
7397 	case LEGS_S7_LUP5:
7398 	case LEGS_S7_RUP1:
7399 	case LEGS_S7_RUP2:
7400 	case LEGS_S7_RUP3:
7401 	case LEGS_S7_RUP4:
7402 	case LEGS_S7_RUP5:
7403 		return qtrue;
7404 		break;
7405 	}
7406 	return qfalse;
7407 }
7408 
PM_SaberStanceAnim(int anim)7409 qboolean PM_SaberStanceAnim( int anim )
7410 {
7411 	switch ( anim )
7412 	{
7413 	case BOTH_STAND1://not really a saberstance anim, actually... "saber off" stance
7414 	case BOTH_STAND2://single-saber, medium style
7415 	case BOTH_SABERFAST_STANCE://single-saber, fast style
7416 	case BOTH_SABERSLOW_STANCE://single-saber, strong style
7417 	case BOTH_SABERSTAFF_STANCE://saber staff style
7418 	case BOTH_SABERDUAL_STANCE://dual saber style
7419 		return qtrue;
7420 		break;
7421 	}
7422 	return qfalse;
7423 }
7424 
PM_SaberDrawPutawayAnim(int anim)7425 qboolean PM_SaberDrawPutawayAnim( int anim )
7426 {
7427 	switch ( anim )
7428 	{
7429 	case BOTH_STAND1TO2:
7430 	case BOTH_STAND2TO1:
7431 	case BOTH_S1_S7:
7432 	case BOTH_S7_S1:
7433 	case BOTH_S1_S6:
7434 	case BOTH_S6_S1:
7435 		return qtrue;
7436 		break;
7437 	}
7438 	return qfalse;
7439 }
7440 
7441 #define	SLOPE_RECALC_INT 100
7442 extern qboolean G_StandardHumanoid( gentity_t *self );
PM_AdjustStandAnimForSlope(void)7443 qboolean PM_AdjustStandAnimForSlope( void )
7444 {
7445 	if ( !pm->gent || !pm->gent->client )
7446 	{
7447 		return qfalse;
7448 	}
7449 	if ( pm->gent->client->NPC_class != CLASS_ATST
7450 		&& (!pm->gent||!G_StandardHumanoid( pm->gent )) )
7451 	{//only ATST and player does this
7452 		return qfalse;
7453 	}
7454 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
7455 		&& (!cg.renderingThirdPerson || cg.zoomMode) )
7456 	{//first person doesn't do this
7457 		return qfalse;
7458 	}
7459 	if ( pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1 )
7460 	{//need these bolts!
7461 		return qfalse;
7462 	}
7463 	//step 1: find the 2 foot tags
7464 	float	diff;
7465 	float	interval;
7466 	PM_FootSlopeTrace( &diff, &interval );
7467 
7468 	//step 4: based on difference, choose one of the left/right slope-match intervals
7469 	int		destAnim;
7470 	if ( diff >= interval*5 )
7471 	{
7472 		destAnim = LEGS_LEFTUP5;
7473 	}
7474 	else if ( diff >= interval*4 )
7475 	{
7476 		destAnim = LEGS_LEFTUP4;
7477 	}
7478 	else if ( diff >= interval*3 )
7479 	{
7480 		destAnim = LEGS_LEFTUP3;
7481 	}
7482 	else if ( diff >= interval*2 )
7483 	{
7484 		destAnim = LEGS_LEFTUP2;
7485 	}
7486 	else if ( diff >= interval )
7487 	{
7488 		destAnim = LEGS_LEFTUP1;
7489 	}
7490 	else if ( diff <= interval*-5 )
7491 	{
7492 		destAnim = LEGS_RIGHTUP5;
7493 	}
7494 	else if ( diff <= interval*-4 )
7495 	{
7496 		destAnim = LEGS_RIGHTUP4;
7497 	}
7498 	else if ( diff <= interval*-3 )
7499 	{
7500 		destAnim = LEGS_RIGHTUP3;
7501 	}
7502 	else if ( diff <= interval*-2 )
7503 	{
7504 		destAnim = LEGS_RIGHTUP2;
7505 	}
7506 	else if ( diff <= interval*-1 )
7507 	{
7508 		destAnim = LEGS_RIGHTUP1;
7509 	}
7510 	else
7511 	{
7512 		return qfalse;
7513 	}
7514 
7515 	int legsAnim = pm->ps->legsAnim;
7516 	if ( pm->gent->client->NPC_class != CLASS_ATST )
7517 	{
7518 		//adjust for current legs anim
7519 		switch ( legsAnim )
7520 		{
7521 		case BOTH_STAND1:
7522 		case LEGS_S1_LUP1:
7523 		case LEGS_S1_LUP2:
7524 		case LEGS_S1_LUP3:
7525 		case LEGS_S1_LUP4:
7526 		case LEGS_S1_LUP5:
7527 		case LEGS_S1_RUP1:
7528 		case LEGS_S1_RUP2:
7529 		case LEGS_S1_RUP3:
7530 		case LEGS_S1_RUP4:
7531 		case LEGS_S1_RUP5:
7532 			destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1);
7533 			break;
7534 		case BOTH_STAND2:
7535 		case BOTH_SABERFAST_STANCE:
7536 		case BOTH_SABERSLOW_STANCE:
7537 		case BOTH_CROUCH1IDLE:
7538 		case BOTH_CROUCH1:
7539 		case LEGS_LEFTUP1:			//# On a slope with left foot 4 higher than right
7540 		case LEGS_LEFTUP2:			//# On a slope with left foot 8 higher than right
7541 		case LEGS_LEFTUP3:			//# On a slope with left foot 12 higher than right
7542 		case LEGS_LEFTUP4:			//# On a slope with left foot 16 higher than right
7543 		case LEGS_LEFTUP5:			//# On a slope with left foot 20 higher than right
7544 		case LEGS_RIGHTUP1:			//# On a slope with RIGHT foot 4 higher than left
7545 		case LEGS_RIGHTUP2:			//# On a slope with RIGHT foot 8 higher than left
7546 		case LEGS_RIGHTUP3:			//# On a slope with RIGHT foot 12 higher than left
7547 		case LEGS_RIGHTUP4:			//# On a slope with RIGHT foot 16 higher than left
7548 		case LEGS_RIGHTUP5:			//# On a slope with RIGHT foot 20 higher than left
7549 			//fine
7550 			break;
7551 		case BOTH_STAND3:
7552 		case LEGS_S3_LUP1:
7553 		case LEGS_S3_LUP2:
7554 		case LEGS_S3_LUP3:
7555 		case LEGS_S3_LUP4:
7556 		case LEGS_S3_LUP5:
7557 		case LEGS_S3_RUP1:
7558 		case LEGS_S3_RUP2:
7559 		case LEGS_S3_RUP3:
7560 		case LEGS_S3_RUP4:
7561 		case LEGS_S3_RUP5:
7562 			destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1);
7563 			break;
7564 		case BOTH_STAND4:
7565 		case LEGS_S4_LUP1:
7566 		case LEGS_S4_LUP2:
7567 		case LEGS_S4_LUP3:
7568 		case LEGS_S4_LUP4:
7569 		case LEGS_S4_LUP5:
7570 		case LEGS_S4_RUP1:
7571 		case LEGS_S4_RUP2:
7572 		case LEGS_S4_RUP3:
7573 		case LEGS_S4_RUP4:
7574 		case LEGS_S4_RUP5:
7575 			destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1);
7576 			break;
7577 		case BOTH_STAND5:
7578 		case LEGS_S5_LUP1:
7579 		case LEGS_S5_LUP2:
7580 		case LEGS_S5_LUP3:
7581 		case LEGS_S5_LUP4:
7582 		case LEGS_S5_LUP5:
7583 		case LEGS_S5_RUP1:
7584 		case LEGS_S5_RUP2:
7585 		case LEGS_S5_RUP3:
7586 		case LEGS_S5_RUP4:
7587 		case LEGS_S5_RUP5:
7588 			destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1);
7589 			break;
7590 		case BOTH_SABERDUAL_STANCE:
7591 		case LEGS_S6_LUP1:
7592 		case LEGS_S6_LUP2:
7593 		case LEGS_S6_LUP3:
7594 		case LEGS_S6_LUP4:
7595 		case LEGS_S6_LUP5:
7596 		case LEGS_S6_RUP1:
7597 		case LEGS_S6_RUP2:
7598 		case LEGS_S6_RUP3:
7599 		case LEGS_S6_RUP4:
7600 		case LEGS_S6_RUP5:
7601 			destAnim = LEGS_S6_LUP1 + (destAnim-LEGS_LEFTUP1);
7602 			break;
7603 		case BOTH_SABERSTAFF_STANCE:
7604 		case LEGS_S7_LUP1:
7605 		case LEGS_S7_LUP2:
7606 		case LEGS_S7_LUP3:
7607 		case LEGS_S7_LUP4:
7608 		case LEGS_S7_LUP5:
7609 		case LEGS_S7_RUP1:
7610 		case LEGS_S7_RUP2:
7611 		case LEGS_S7_RUP3:
7612 		case LEGS_S7_RUP4:
7613 		case LEGS_S7_RUP5:
7614 			destAnim = LEGS_S7_LUP1 + (destAnim-LEGS_LEFTUP1);
7615 			break;
7616 		case BOTH_STAND6:
7617 		default:
7618 			return qfalse;
7619 			break;
7620 		}
7621 	}
7622 	//step 5: based on the chosen interval and the current legsAnim, pick the correct anim
7623 	//step 6: increment/decrement to the dest anim, not instant
7624 	if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5)
7625 		|| (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5)
7626 		|| (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5)
7627 		|| (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5)
7628 		|| (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5)
7629 		|| (legsAnim >= LEGS_S6_LUP1 && legsAnim <= LEGS_S6_LUP5)
7630 		|| (legsAnim >= LEGS_S7_LUP1 && legsAnim <= LEGS_S7_LUP5) )
7631 	{//already in left-side up
7632 		if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7633 		{
7634 			legsAnim++;
7635 			pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7636 		}
7637 		else if ( destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7638 		{
7639 			legsAnim--;
7640 			pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7641 		}
7642 		else
7643 		{
7644 			destAnim = legsAnim;
7645 		}
7646 	}
7647 	else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5)
7648 		|| (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5)
7649 		|| (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5)
7650 		|| (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5)
7651 		|| (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5)
7652 		|| (legsAnim >= LEGS_S6_RUP1 && legsAnim <= LEGS_S6_RUP5)
7653 		|| (legsAnim >= LEGS_S7_RUP1 && legsAnim <= LEGS_S7_RUP5) )
7654 	{//already in right-side up
7655 		if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7656 		{
7657 			legsAnim++;
7658 			pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7659 		}
7660 		else if ( destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time )
7661 		{
7662 			legsAnim--;
7663 			pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7664 		}
7665 		else
7666 		{
7667 			destAnim = legsAnim;
7668 		}
7669 	}
7670 	else
7671 	{//in a stand of some sort?
7672 		if ( pm->gent->client->NPC_class == CLASS_ATST )
7673 		{
7674 			if ( legsAnim == BOTH_STAND1 || legsAnim == BOTH_STAND2 || legsAnim == BOTH_CROUCH1IDLE )
7675 			{
7676 				if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
7677 				{//going into left side up
7678 					destAnim = LEGS_LEFTUP1;
7679 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7680 				}
7681 				else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
7682 				{//going into right side up
7683 					destAnim = LEGS_RIGHTUP1;
7684 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7685 				}
7686 				else
7687 				{//will never get here
7688 					return qfalse;
7689 				}
7690 			}
7691 		}
7692 		else
7693 		{
7694 			switch ( legsAnim )
7695 			{
7696 			case BOTH_STAND1:
7697 				if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 )
7698 				{//going into left side up
7699 					destAnim = LEGS_S1_LUP1;
7700 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7701 				}
7702 				else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 )
7703 				{//going into right side up
7704 					destAnim = LEGS_S1_RUP1;
7705 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7706 				}
7707 				else
7708 				{//will never get here
7709 					return qfalse;
7710 				}
7711 				break;
7712 			case BOTH_STAND2:
7713 			case BOTH_SABERFAST_STANCE:
7714 			case BOTH_SABERSLOW_STANCE:
7715 			case BOTH_CROUCH1IDLE:
7716 				if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
7717 				{//going into left side up
7718 					destAnim = LEGS_LEFTUP1;
7719 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7720 				}
7721 				else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
7722 				{//going into right side up
7723 					destAnim = LEGS_RIGHTUP1;
7724 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7725 				}
7726 				else
7727 				{//will never get here
7728 					return qfalse;
7729 				}
7730 				break;
7731 			case BOTH_STAND3:
7732 				if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 )
7733 				{//going into left side up
7734 					destAnim = LEGS_S3_LUP1;
7735 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7736 				}
7737 				else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 )
7738 				{//going into right side up
7739 					destAnim = LEGS_S3_RUP1;
7740 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7741 				}
7742 				else
7743 				{//will never get here
7744 					return qfalse;
7745 				}
7746 				break;
7747 			case BOTH_STAND4:
7748 				if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 )
7749 				{//going into left side up
7750 					destAnim = LEGS_S4_LUP1;
7751 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7752 				}
7753 				else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 )
7754 				{//going into right side up
7755 					destAnim = LEGS_S4_RUP1;
7756 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7757 				}
7758 				else
7759 				{//will never get here
7760 					return qfalse;
7761 				}
7762 				break;
7763 			case BOTH_STAND5:
7764 				if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 )
7765 				{//going into left side up
7766 					destAnim = LEGS_S5_LUP1;
7767 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7768 				}
7769 				else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 )
7770 				{//going into right side up
7771 					destAnim = LEGS_S5_RUP1;
7772 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7773 				}
7774 				else
7775 				{//will never get here
7776 					return qfalse;
7777 				}
7778 				break;
7779 			case BOTH_SABERDUAL_STANCE:
7780 				if ( destAnim >= LEGS_S6_LUP1 && destAnim <= LEGS_S6_LUP5 )
7781 				{//going into left side up
7782 					destAnim = LEGS_S6_LUP1;
7783 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7784 				}
7785 				else if ( destAnim >= LEGS_S6_RUP1 && destAnim <= LEGS_S6_RUP5 )
7786 				{//going into right side up
7787 					destAnim = LEGS_S6_RUP1;
7788 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7789 				}
7790 				else
7791 				{//will never get here
7792 					return qfalse;
7793 				}
7794 				break;
7795 			case BOTH_SABERSTAFF_STANCE:
7796 				if ( destAnim >= LEGS_S7_LUP1 && destAnim <= LEGS_S7_LUP5 )
7797 				{//going into left side up
7798 					destAnim = LEGS_S7_LUP1;
7799 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7800 				}
7801 				else if ( destAnim >= LEGS_S7_RUP1 && destAnim <= LEGS_S7_RUP5 )
7802 				{//going into right side up
7803 					destAnim = LEGS_S7_RUP1;
7804 					pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT;
7805 				}
7806 				else
7807 				{//will never get here
7808 					return qfalse;
7809 				}
7810 				break;
7811 			case BOTH_STAND6:
7812 			default:
7813 				return qfalse;
7814 				break;
7815 			}
7816 		}
7817 	}
7818 	//step 7: set the anim
7819 	PM_SetAnim( pm, SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL );
7820 
7821 	return qtrue;
7822 }
7823 
PM_JetPackAnim(void)7824 void PM_JetPackAnim( void )
7825 {
7826 	if ( !PM_ForceJumpingAnim( pm->ps->legsAnim ) )//haven't started forcejump yet
7827 	{
7828 		vec3_t facingFwd, facingRight, facingAngles = {0, pm->ps->viewangles[YAW], 0};
7829 		int anim = BOTH_FORCEJUMP1;
7830 		AngleVectors( facingAngles, facingFwd, facingRight, NULL );
7831 		float dotR = DotProduct( facingRight, pm->ps->velocity );
7832 		float dotF = DotProduct( facingFwd, pm->ps->velocity );
7833 		if ( fabs(dotR) > fabs(dotF) * 1.5 )
7834 		{
7835 			if ( dotR > 150 )
7836 			{
7837 				anim = BOTH_FORCEJUMPRIGHT1;
7838 			}
7839 			else if ( dotR < -150 )
7840 			{
7841 				anim = BOTH_FORCEJUMPLEFT1;
7842 			}
7843 		}
7844 		else
7845 		{
7846 			if ( dotF > 150 )
7847 			{
7848 				anim = BOTH_FORCEJUMP1;
7849 			}
7850 			else if ( dotF < -150 )
7851 			{
7852 				anim = BOTH_FORCEJUMPBACK1;
7853 			}
7854 		}
7855 		int parts = SETANIM_BOTH;
7856 		if ( pm->ps->weaponTime )
7857 		{//FIXME: really only care if we're in a saber attack anim...
7858 			parts = SETANIM_LEGS;
7859 		}
7860 
7861 		PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7862 	}
7863 	/*
7864 	else
7865 	{
7866 		if ( !pm->ps->legsAnimTimer )
7867 		{//not in the middle of a legsAnim
7868 			int anim = pm->ps->legsAnim;
7869 			int newAnim = -1;
7870 			switch ( anim )
7871 			{
7872 			case BOTH_FORCEJUMP1:
7873 				newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
7874 				break;
7875 			case BOTH_FORCEJUMPBACK1:
7876 				newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
7877 				break;
7878 			case BOTH_FORCEJUMPLEFT1:
7879 				newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
7880 				break;
7881 			case BOTH_FORCEJUMPRIGHT1:
7882 				newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
7883 				break;
7884 			}
7885 			if ( newAnim != -1 )
7886 			{
7887 				int parts = SETANIM_BOTH;
7888 				if ( pm->ps->weaponTime )
7889 				{//FIXME: really only care if we're in a saber attack anim...
7890 					parts = SETANIM_LEGS;
7891 				}
7892 
7893 				PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7894 			}
7895 		}
7896 	}
7897 	*/
7898 }
PM_SwimFloatAnim(void)7899 void PM_SwimFloatAnim( void )
7900 {
7901 	int legsAnim = pm->ps->legsAnim;
7902 	//FIXME: no start or stop anims
7903 	if ( pm->cmd.forwardmove || pm->cmd.rightmove || pm->cmd.upmove )
7904 	{
7905 		PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL);
7906 	}
7907 	else
7908 	{//stopping
7909 		if ( legsAnim == BOTH_SWIMFORWARD )
7910 		{//I was swimming
7911 			if ( !pm->ps->legsAnimTimer )
7912 			{
7913 				PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL);
7914 			}
7915 		}
7916 		else
7917 		{//idle
7918 			if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 )
7919 			{//not crouching
7920 				PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL);
7921 			}
7922 		}
7923 	}
7924 }
7925 
7926 /*
7927 ===============
7928 PM_Footsteps
7929 ===============
7930 */
PM_Footsteps(void)7931 static void PM_Footsteps( void )
7932 {
7933 	float		bobmove;
7934 	int			old, oldAnim;
7935 	qboolean	footstep = qfalse;
7936 	qboolean	validNPC = qfalse;
7937 	qboolean	flipping = qfalse;
7938 	int			setAnimFlags = SETANIM_FLAG_NORMAL;
7939 
7940 	if( pm->gent == NULL || pm->gent->client == NULL )
7941 		return;
7942 
7943 	if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) )
7944 	{
7945 		PM_SetAnim( pm, SETANIM_BOTH, BOTH_HANG_IDLE, SETANIM_FLAG_NORMAL );
7946 		if ( pm->ps->legsAnim == BOTH_HANG_IDLE )
7947 		{
7948 			if ( pm->ps->torsoAnimTimer < 100 )
7949 			{
7950 				pm->ps->torsoAnimTimer = 100;
7951 			}
7952 			if ( pm->ps->legsAnimTimer < 100 )
7953 			{
7954 				pm->ps->legsAnimTimer = 100;
7955 			}
7956 		}
7957 		return;
7958 	}
7959 
7960 	if ( PM_SpinningSaberAnim( pm->ps->legsAnim ) && pm->ps->legsAnimTimer )
7961 	{//spinning
7962 		return;
7963 	}
7964 	if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ))
7965 	{//in knockdown
7966 		return;
7967 	}
7968 	if ( (pm->ps->eFlags&EF_FORCE_DRAINED) )
7969 	{//being drained
7970 		//PM_SetAnim( pm, SETANIM_LEGS, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7971 		return;
7972 	}
7973 	if ( (pm->ps->forcePowersActive&(1<<FP_DRAIN))
7974 		&& pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD )
7975 	{//draining
7976 		//PM_SetAnim( pm, SETANIM_LEGS, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
7977 		return;
7978 	}
7979 	else if (pm->gent->NPC && pm->gent->NPC->aiFlags & NPCAI_KNEEL)
7980 	{//kneeling
7981 		return;
7982 	}
7983 
7984 	if( pm->gent->NPC != NULL )
7985 	{
7986 		validNPC = qtrue;
7987 	}
7988 
7989 	pm->gent->client->renderInfo.legsFpsMod = 1.0f;
7990 	//PM_ResetAnkleAngles();
7991 
7992 	//
7993 	// calculate speed and cycle to be used for
7994 	// all cyclic walking effects
7995 	//
7996 	pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
7997 		+  pm->ps->velocity[1] * pm->ps->velocity[1] );
7998 
7999 	if ( pm->ps->legsAnim == BOTH_FLIP_F ||
8000 		pm->ps->legsAnim == BOTH_FLIP_B ||
8001 		pm->ps->legsAnim == BOTH_FLIP_L ||
8002 		pm->ps->legsAnim == BOTH_FLIP_R ||
8003 		pm->ps->legsAnim == BOTH_ALORA_FLIP_1 ||
8004 		pm->ps->legsAnim == BOTH_ALORA_FLIP_2 ||
8005 		pm->ps->legsAnim == BOTH_ALORA_FLIP_3 )
8006 	{
8007 		flipping = qtrue;
8008 	}
8009 
8010 	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
8011 		|| ( pm->watertype & CONTENTS_LADDER )
8012 		|| pm->ps->waterHeightLevel >= WHL_TORSO )
8013 	{//in air or submerged in water or in ladder
8014 		// airborne leaves position in cycle intact, but doesn't advance
8015 		if ( pm->waterlevel > 0 )
8016 		{
8017 			if ( pm->watertype & CONTENTS_LADDER )
8018 			{//FIXME: check for watertype, save waterlevel for whether to play
8019 				//the get off ladder transition anim!
8020 				if ( pm->ps->velocity[2]  )
8021 				{//going up or down it
8022 					int	anim;
8023 					if ( pm->ps->velocity[2] > 0 )
8024 					{
8025 						anim = BOTH_LADDER_UP1;
8026 					}
8027 					else
8028 					{
8029 						anim = BOTH_LADDER_DWN1;
8030 					}
8031 					PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
8032 					if ( pm->waterlevel >= 2 )	//arms on ladder
8033 					{
8034 						PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
8035 					}
8036 					if (fabs(pm->ps->velocity[2]) >5) {
8037 						bobmove = 0.005 * fabs(pm->ps->velocity[2]);	// climbing bobs slow
8038 						if (bobmove > 0.3)
8039 							bobmove = 0.3F;
8040 						goto DoFootSteps;
8041 					}
8042 				}
8043 				else
8044 				{
8045 					PM_SetAnim( pm, SETANIM_LEGS, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
8046 					pm->ps->legsAnimTimer += 300;
8047 					if ( pm->waterlevel >= 2 )	//arms on ladder
8048 					{
8049 						PM_SetAnim( pm, SETANIM_TORSO, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
8050 						pm->ps->torsoAnimTimer += 300;
8051 					}
8052 				}
8053 				return;
8054 			}
8055 			else if ( pm->ps->waterHeightLevel >= WHL_TORSO
8056 				&& ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
8057 					||pm->ps->weapon==WP_SABER||pm->ps->weapon==WP_NONE||pm->ps->weapon==WP_MELEE) )//pm->waterlevel > 1 )	//in deep water
8058 			{
8059 				if ( !PM_ForceJumpingUp( pm->gent ) )
8060 				{
8061 					if ( pm->ps->groundEntityNum != ENTITYNUM_NONE && (pm->ps->pm_flags&PMF_DUCKED) )
8062 					{
8063 						if ( !flipping )
8064 						{//you can crouch under water if feet are on ground
8065 							if ( pm->cmd.forwardmove || pm->cmd.rightmove )
8066 							{
8067 								if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
8068 								{
8069 									PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags);
8070 								}
8071 								else
8072 								{
8073 									PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags);
8074 								}
8075 							}
8076 							else
8077 							{
8078 								PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8079 							}
8080 							return;
8081 						}
8082 					}
8083 					PM_SwimFloatAnim();
8084 					if ( pm->ps->legsAnim != BOTH_SWIM_IDLE1 )
8085 					{//moving
8086 						old = pm->ps->bobCycle;
8087 						bobmove = 0.15f;	// swim is a slow cycle
8088 						pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
8089 
8090 						// if we just crossed a cycle boundary, play an apropriate footstep event
8091 						if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
8092 						{
8093 							PM_AddEvent( EV_SWIM );
8094 						}
8095 					}
8096 				}
8097 				return;
8098 			}
8099 			else
8100 			{//hmm, in water, but not high enough to swim
8101 				//fall through to walk/run/stand
8102 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
8103 				{//unless in the air
8104 					//NOTE: this is a dupe of the code just below... for when you are not in the water at all
8105 					if ( pm->ps->pm_flags & PMF_DUCKED )
8106 					{
8107 						if ( !flipping )
8108 						{
8109 							PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8110 						}
8111 					}
8112 					else if ( pm->ps->gravity <= 0 )//FIXME: or just less than normal?
8113 					{
8114 						if ( pm->gent
8115 							&& pm->gent->client
8116 							&& (pm->gent->client->NPC_class == CLASS_BOBAFETT ||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER)
8117 							&& pm->gent->client->moveType == MT_FLYSWIM )
8118 						{//flying around with jetpack
8119 							//do something else?
8120 							PM_JetPackAnim();
8121 						}
8122 						else
8123 						{
8124 							PM_SwimFloatAnim();
8125 						}
8126 					}
8127 					return;
8128 				}
8129 			}
8130 		}
8131 		else
8132 		{
8133 			if ( pm->ps->pm_flags & PMF_DUCKED )
8134 			{
8135 				if ( !flipping )
8136 				{
8137 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8138 				}
8139 			}
8140 			else if ( pm->ps->gravity <= 0 )//FIXME: or just less than normal?
8141 			{
8142 				if ( pm->gent
8143 					&& pm->gent->client
8144 					&& (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER)
8145 					&& pm->gent->client->moveType == MT_FLYSWIM )
8146 				{//flying around with jetpack
8147 					//do something else?
8148 					PM_JetPackAnim();
8149 				}
8150 				else
8151 				{
8152 					PM_SwimFloatAnim();
8153 				}
8154 			}
8155 			return;
8156 		}
8157 	}
8158 
8159 	if ( PM_SwimmingAnim( pm->ps->legsAnim ) && pm->waterlevel < 2 )
8160 	{//legs are in swim anim, and not swimming, be sure to override it
8161 		setAnimFlags |= SETANIM_FLAG_OVERRIDE;
8162 	}
8163 
8164 	// if not trying to move
8165 	if ( !pm->cmd.forwardmove && !pm->cmd.rightmove )
8166 	{
8167 		if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8168 		{
8169 			if ( !PM_AdjustStandAnimForSlope() )
8170 			{
8171 				PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8172 			}
8173 		}
8174 		else if ( pm->ps->pm_flags & PMF_DUCKED )
8175 		{
8176 			if( !PM_InOnGroundAnim( pm->ps ) )
8177 			{
8178 				if ( !PM_AdjustStandAnimForSlope() )
8179 				{
8180 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL);
8181 				}
8182 			}
8183 		}
8184 		else
8185 		{
8186 			if ( pm->ps->legsAnimTimer && PM_LandingAnim( pm->ps->legsAnim ) )
8187 			{//still in a landing anim, let it play
8188 				return;
8189 			}
8190 			if ( pm->ps->legsAnimTimer
8191 				&& (pm->ps->legsAnim == BOTH_THERMAL_READY
8192 					||pm->ps->legsAnim == BOTH_THERMAL_THROW
8193 					||pm->ps->legsAnim == BOTH_ATTACK10) )
8194 			{//still in a thermal anim, let it play
8195 				return;
8196 			}
8197 			qboolean saberInAir = qtrue;
8198 			if ( pm->ps->saberInFlight )
8199 			{//guiding saber
8200 				if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) )
8201 				{//we're stuck in a broken parry
8202 					saberInAir = qfalse;
8203 				}
8204 				if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
8205 				{//
8206 					if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
8207 					{//fell to the ground and we're not trying to pull it back
8208 						saberInAir = qfalse;
8209 					}
8210 				}
8211 			}
8212 			if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH )
8213 			{//NOTE: stand1 is with the helmet retracted, stand1to2 is the helmet going into place
8214 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_NORMAL );
8215 			}
8216 			else if ( pm->ps->weapon == WP_SABER
8217 				&& pm->ps->saberInFlight
8218 				&& saberInAir
8219 				&& (!pm->ps->dualSabers || !pm->ps->saber[1].Active()))
8220 			{
8221 				if ( !PM_AdjustStandAnimForSlope() )
8222 				{
8223 					if ( pm->ps->legsAnim != BOTH_LOSE_SABER
8224 						|| !pm->ps->legsAnimTimer )
8225 					{
8226 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_SABERPULL,SETANIM_FLAG_NORMAL);
8227 					}
8228 				}
8229 			}
8230 			else if ( (pm->ps->weapon == WP_SABER
8231 				&&pm->ps->SaberLength()>0
8232 				&&!pm->ps->saberInFlight
8233 				&&!PM_SaberDrawPutawayAnim( pm->ps->legsAnim )) )
8234 			{
8235 				if ( !PM_AdjustStandAnimForSlope() )
8236 				{
8237 					int legsAnim;
8238 					if ( (pm->ps->torsoAnim == BOTH_SPINATTACK6
8239 							|| pm->ps->torsoAnim == BOTH_SPINATTACK7
8240 							|| PM_SaberInAttack( pm->ps->saberMove )
8241 							|| PM_SaberInTransitionAny( pm->ps->saberMove ))
8242 						&& pm->ps->legsAnim != BOTH_FORCELONGLEAP_LAND
8243 						&& (pm->ps->groundEntityNum == ENTITYNUM_NONE//in air
8244 							|| (!PM_JumpingAnim( pm->ps->torsoAnim )&&!PM_InAirKickingAnim( pm->ps->torsoAnim ))) )//OR: on ground and torso not in a jump anim
8245 					{
8246 						legsAnim = pm->ps->torsoAnim;
8247 					}
8248 					else
8249 					{
8250 						switch ( pm->ps->saberAnimLevel )
8251 						{
8252 						case SS_FAST:
8253 						case SS_TAVION:
8254 							legsAnim = BOTH_SABERFAST_STANCE;
8255 							break;
8256 						case SS_STRONG:
8257 							legsAnim = BOTH_SABERSLOW_STANCE;
8258 							break;
8259 						case SS_DUAL:
8260 							legsAnim = BOTH_SABERDUAL_STANCE;
8261 							break;
8262 						case SS_STAFF:
8263 							legsAnim = BOTH_SABERSTAFF_STANCE;
8264 							break;
8265 						case SS_NONE:
8266 						case SS_MEDIUM:
8267 						case SS_DESANN:
8268 						default:
8269 							legsAnim = BOTH_STAND2;
8270 							break;
8271 						}
8272 					}
8273 					PM_SetAnim(pm,SETANIM_LEGS,legsAnim,SETANIM_FLAG_NORMAL);
8274 				}
8275 			}
8276 			else if( (validNPC && pm->ps->weapon > WP_SABER && pm->ps->weapon < WP_DET_PACK ))//Being careful or carrying a 2-handed weapon
8277 			{//Squadmates use BOTH_STAND3
8278 				oldAnim = pm->ps->legsAnim;
8279 				if(oldAnim != BOTH_GUARD_LOOKAROUND1 && oldAnim != BOTH_GUARD_IDLE1
8280 					&& oldAnim != BOTH_STAND2TO4
8281 					&& oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4 )
8282 				{//Don't auto-override the guard idles
8283 					if ( !PM_AdjustStandAnimForSlope() )
8284 					{
8285 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND3,SETANIM_FLAG_NORMAL);
8286 						//if(oldAnim != BOTH_STAND2 && pm->ps->legsAnim == BOTH_STAND2)
8287 						//{
8288 						//	pm->ps->legsAnimTimer = 500;
8289 						//}
8290 					}
8291 				}
8292 			}
8293 			else
8294 			{
8295 				if ( !PM_AdjustStandAnimForSlope() )
8296 				{
8297 					// FIXME: Do we need this here... The imps stand is 4, not 1...
8298 					if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_IMPERIAL )
8299 					{
8300 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
8301 					}
8302 					else if ( pm->ps->weapon == WP_TUSKEN_STAFF )
8303 					{
8304 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8305 					}
8306 					else
8307 					{
8308 						if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR )
8309 						{
8310 							if ( pm->gent->count )
8311 							{
8312 								PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
8313 							}
8314 							else if ( pm->gent->enemy || pm->gent->wait )
8315 							{//have an enemy or have had one since we spawned
8316 								PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
8317 							}
8318 							else
8319 							{
8320 								PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8321 							}
8322 						}
8323 						else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA )
8324 						{
8325 							if ( pm->gent->count )
8326 							{//holding a victim
8327 								PM_SetAnim(pm,SETANIM_LEGS,BOTH_HOLD_IDLE/*BOTH_STAND2*/,SETANIM_FLAG_NORMAL);
8328 							}
8329 							else
8330 							{//not holding a victim
8331 								PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8332 							}
8333 						}
8334 						else
8335 						{
8336 							PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
8337 						}
8338 					}
8339 				}
8340 			}
8341 		}
8342 		return;
8343 	}
8344 
8345 	//maybe call this every frame, even when moving?
8346 	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8347 	{
8348 		PM_FootSlopeTrace( NULL, NULL );
8349 	}
8350 
8351 	//trying to move laterally
8352 	if ( (pm->ps->eFlags&EF_IN_ATST)
8353 		|| (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_RANCOR)//does this catch NPCs, too?
8354 		|| (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_WAMPA) )//does this catch NPCs, too?
8355 	{//atst, Rancor & Wampa, only override turn anims on legs (no torso)
8356 		if ( pm->ps->legsAnim == BOTH_TURN_LEFT1 ||
8357 				pm->ps->legsAnim == BOTH_TURN_RIGHT1 )
8358 		{//moving overrides turning
8359 			setAnimFlags |= SETANIM_FLAG_OVERRIDE;
8360 		}
8361 	}
8362 	else
8363 	{//all other NPCs...
8364 		if ( (PM_InSaberAnim( pm->ps->legsAnim ) && !PM_SpinningSaberAnim( pm->ps->legsAnim ))
8365 			|| PM_SaberStanceAnim( pm->ps->legsAnim )
8366 			|| PM_SaberDrawPutawayAnim( pm->ps->legsAnim )
8367 			|| pm->ps->legsAnim == BOTH_SPINATTACK6//not a full-body spin, just spinning the saber
8368 			|| pm->ps->legsAnim == BOTH_SPINATTACK7//not a full-body spin, just spinning the saber
8369 			|| pm->ps->legsAnim == BOTH_BUTTON_HOLD
8370 			|| pm->ps->legsAnim == BOTH_BUTTON_RELEASE
8371 			|| pm->ps->legsAnim == BOTH_THERMAL_READY
8372 			|| pm->ps->legsAnim == BOTH_THERMAL_THROW
8373 			|| pm->ps->legsAnim == BOTH_ATTACK10
8374 			|| PM_LandingAnim( pm->ps->legsAnim )
8375 			|| PM_PainAnim( pm->ps->legsAnim )
8376 			|| PM_ForceAnim( pm->ps->legsAnim ))
8377 		{//legs are in a saber anim, and not spinning, be sure to override it
8378 			setAnimFlags |= SETANIM_FLAG_OVERRIDE;
8379 		}
8380 	}
8381 
8382 	if ( pm->ps->pm_flags & PMF_DUCKED )
8383 	{
8384 		bobmove = 0.5;	// ducked characters bob much faster
8385 		if( !PM_InOnGroundAnim( pm->ps ) //not on the ground
8386 			&& ( !PM_InRollIgnoreTimer( pm->ps )||(!pm->ps->legsAnimTimer&&pm->cmd.upmove<0) ) )//not in a roll (or you just finished one and you're still holding crouch)
8387 		{
8388 			qboolean rolled = qfalse;
8389 			if ( PM_RunningAnim( pm->ps->legsAnim )
8390 				|| pm->ps->legsAnim == BOTH_FORCEHEAL_START
8391 				|| PM_CanRollFromSoulCal( pm->ps ))
8392 			{//roll!
8393 				rolled = PM_TryRoll();
8394 			}
8395 			if ( !rolled )
8396 			{
8397 				if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
8398 				{
8399 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags);
8400 				}
8401 				else
8402 				{
8403 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags);
8404 				}
8405 				if ( !Q_irand( 0, 19 ) )
8406 				{//5% chance of making an alert
8407 					AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue );
8408 				}
8409 			}
8410 			else
8411 			{//rolling is a little noisy
8412 				AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue, qtrue );
8413 			}
8414 		}
8415 		// ducked characters never play footsteps
8416 	}
8417 	else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
8418 	{//Moving backwards
8419 		if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
8420 		{//running backwards
8421 			bobmove = 0.4F;	// faster speeds bob faster
8422 			if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8423 			{
8424 				/*
8425 				if ( pm->ps->saberAnimLevel == SS_STAFF )
8426 				{
8427 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK_STAFF,setAnimFlags);
8428 				}
8429 				else
8430 				*/
8431 				{
8432 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK2,setAnimFlags);
8433 				}
8434 			}
8435 			else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR )
8436 			{//no run anim
8437 				PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK1,setAnimFlags);
8438 			}
8439 			else
8440 			{
8441 				PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK1,setAnimFlags);
8442 			}
8443 			footstep = qtrue;
8444 		}
8445 		else
8446 		{//walking backwards
8447 			bobmove = 0.3F;	// faster speeds bob faster
8448 			if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8449 			{
8450 				if ( pm->ps->saberAnimLevel == SS_DUAL )
8451 				{
8452 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK_DUAL,setAnimFlags);
8453 				}
8454 				else if ( pm->ps->saberAnimLevel == SS_STAFF )
8455 				{
8456 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK_STAFF,setAnimFlags);
8457 				}
8458 				else
8459 				{
8460 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK2,setAnimFlags);
8461 				}
8462 			}
8463 			else
8464 			{
8465 				PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK1,setAnimFlags);
8466 			}
8467 			if ( !Q_irand( 0, 9 ) )
8468 			{//10% chance of a small alert, mainly for the sand_creature
8469 				AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue );
8470 			}
8471 		}
8472 	}
8473 	else
8474 	{
8475 		if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH )
8476 		{
8477 			bobmove = 0.3F;	// walking bobs slow
8478 			if ( pm->ps->weapon == WP_NONE )
8479 			{//helmet retracted
8480 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_WALK1, SETANIM_FLAG_NORMAL );
8481 			}
8482 			else
8483 			{//helmet in place
8484 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_WALK2, SETANIM_FLAG_NORMAL );
8485 			}
8486 			AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_SUSPICIOUS, qtrue, qtrue );
8487 		}
8488 		else
8489 		{
8490 			if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
8491 			{//running
8492 				bobmove = 0.4F;	// faster speeds bob faster
8493 				if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8494 				{
8495 					if ( pm->ps->saberAnimLevel == SS_DUAL )
8496 					{
8497 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN_DUAL,setAnimFlags);
8498 					}
8499 					else if ( pm->ps->saberAnimLevel == SS_STAFF )
8500 					{
8501 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN_STAFF,setAnimFlags);
8502 					}
8503 					else
8504 					{
8505 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,setAnimFlags);
8506 					}
8507 				}
8508 				else
8509 				{
8510 					if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_JAWA )
8511 					{
8512 						//if ( pm->gent->enemy && (pm->ps->weapon == WP_NONE || pm->ps->weapon == WP_MELEE) )
8513 						{
8514 							PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN4,setAnimFlags);
8515 						}
8516 						/*
8517 						else
8518 						{
8519 							PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,setAnimFlags);
8520 						}
8521 						*/
8522 					}
8523 					else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8524 					{
8525 						if ( pm->ps->legsAnim != BOTH_RUN1 )
8526 						{
8527 							if ( pm->ps->legsAnim != BOTH_RUN1START )
8528 							{//Hmm, he should really start slow and have to accelerate... also need to do this for stopping
8529 								PM_SetAnim( pm,SETANIM_LEGS, BOTH_RUN1START, setAnimFlags|SETANIM_FLAG_HOLD );
8530 							}
8531 							else if ( !pm->ps->legsAnimTimer )
8532 							{
8533 								PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags );
8534 							}
8535 						}
8536 						else
8537 						{
8538 							PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags );
8539 						}
8540 					}
8541 					else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA )
8542 					{
8543 						if ( pm->gent->NPC && pm->gent->NPC->stats.runSpeed == 300 )
8544 						{//full on run, on all fours
8545 							PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,SETANIM_FLAG_NORMAL);
8546 						}
8547 						else
8548 						{//regular, upright run
8549 							PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,SETANIM_FLAG_NORMAL);
8550 						}
8551 					}
8552 					else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR )
8553 					{//no run anim
8554 						PM_SetAnim( pm, SETANIM_LEGS, BOTH_WALK1, setAnimFlags );
8555 					}
8556 					else
8557 					{
8558 						PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags );
8559 					}
8560 				}
8561 				footstep = qtrue;
8562 			}
8563 			else
8564 			{//walking forward
8565 				bobmove = 0.3F;	// walking bobs slow
8566 				if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() )
8567 				{
8568 					if ( pm->ps->saberAnimLevel == SS_DUAL )
8569 					{
8570 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK_DUAL,setAnimFlags);
8571 					}
8572 					else if ( pm->ps->saberAnimLevel == SS_STAFF )
8573 					{
8574 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK_STAFF,setAnimFlags);
8575 					}
8576 					else
8577 					{
8578 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,setAnimFlags);
8579 					}
8580 				}
8581 				else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA )
8582 				{
8583 					if ( pm->gent->health <= 50 )
8584 					{//hurt walk
8585 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,SETANIM_FLAG_NORMAL);
8586 					}
8587 					else
8588 					{//normal walk
8589 						PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,SETANIM_FLAG_NORMAL);
8590 					}
8591 				}
8592 				else
8593 				{
8594 					PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,setAnimFlags);
8595 				}
8596 				//Enemy NPCs always make footsteps for the benefit of the player
8597 				if ( pm->gent
8598 					&& pm->gent->NPC
8599 					&& pm->gent->client
8600 					&& pm->gent->client->playerTeam != TEAM_PLAYER )
8601 				{
8602 					footstep = qtrue;
8603 				}
8604 				else if ( !Q_irand( 0, 9 ) )
8605 				{//10% chance of a small alert, mainly for the sand_creature
8606 					AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue );
8607 				}
8608 			}
8609 		}
8610 	}
8611 
8612 	if(pm->gent != NULL)
8613 	{
8614 		if( pm->gent->client->renderInfo.legsFpsMod > 2 )
8615 		{
8616 			pm->gent->client->renderInfo.legsFpsMod = 2;
8617 		}
8618 		else if(pm->gent->client->renderInfo.legsFpsMod < 0.5)
8619 		{
8620 			pm->gent->client->renderInfo.legsFpsMod = 0.5;
8621 		}
8622 	}
8623 
8624 DoFootSteps:
8625 
8626 	// check for footstep / splash sounds
8627 	old = pm->ps->bobCycle;
8628 	pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
8629 
8630 	// if we just crossed a cycle boundary, play an apropriate footstep event
8631 	if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
8632 	{
8633 		if ( pm->watertype & CONTENTS_LADDER )
8634 		{
8635 			if ( !pm->noFootsteps )
8636 			{
8637 				if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {// on ladder
8638 					 PM_AddEvent( EV_FOOTSTEP_METAL );
8639 				} else {
8640 					//PM_AddEvent( PM_FootstepForSurface() );	//still on ground
8641 				}
8642 			}
8643 			if ( pm->gent && pm->gent->s.number == 0 )
8644 			{
8645 //					if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8646 				{
8647 					AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue );
8648 				}
8649 			}
8650 		}
8651 		else if ( pm->waterlevel == 0 )
8652 		{
8653 			// on ground will only play sounds if running
8654 			if ( footstep )
8655 			{
8656 				if ( !pm->noFootsteps )
8657 				{
8658 					//PM_AddEvent( PM_FootstepForSurface() );
8659 				}
8660 				if ( pm->gent && pm->gent->s.number == 0 )
8661 				{
8662 					vec3_t	bottom;
8663 
8664 					VectorCopy( pm->ps->origin, bottom );
8665 					bottom[2] += pm->mins[2];
8666 //					if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8667 					{
8668 						AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR, qtrue, qtrue );
8669 					}
8670 				}
8671 			}
8672 		}
8673 		else if ( pm->waterlevel == 1 )
8674 		{
8675 			// splashing
8676 			if ( pm->ps->waterHeightLevel >= WHL_KNEES )
8677 			{
8678 				PM_AddEvent( EV_FOOTWADE );
8679 			}
8680 			else
8681 			{
8682 				PM_AddEvent( EV_FOOTSPLASH );
8683 			}
8684 			if ( pm->gent && pm->gent->s.number == 0 )
8685 			{
8686 				vec3_t	bottom;
8687 
8688 				VectorCopy( pm->ps->origin, bottom );
8689 				bottom[2] += pm->mins[2];
8690 //				if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8691 				{
8692 					AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS, qfalse, qtrue );//was bottom
8693 					AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_MINOR );
8694 				}
8695 			}
8696 		}
8697 		else if ( pm->waterlevel == 2 )
8698 		{
8699 			// wading / swimming at surface
8700 			/*
8701 			if ( pm->ps->waterHeightLevel >= WHL_TORSO )
8702 			{
8703 				PM_AddEvent( EV_SWIM );
8704 			}
8705 			else
8706 			*/
8707 			{
8708 				PM_AddEvent( EV_FOOTWADE );
8709 			}
8710 			if ( pm->gent && pm->gent->s.number == 0 )
8711 			{
8712 //				if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
8713 				{
8714 					AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR, qfalse, qtrue );
8715 					AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS );
8716 				}
8717 			}
8718 		}
8719 		else
8720 		{// or no sound when completely underwater...?
8721 			PM_AddEvent( EV_SWIM );
8722 		}
8723 	}
8724 }
8725 
8726 /*
8727 ==============
8728 PM_WaterEvents
8729 
8730 Generate sound events for entering and leaving water
8731 ==============
8732 */
PM_WaterEvents(void)8733 static void PM_WaterEvents( void ) {		// FIXME?
8734 
8735 	qboolean impact_splash = qfalse;
8736 
8737 	if ( pm->watertype & CONTENTS_LADDER )	//fake water for ladder
8738 	{
8739 		return;
8740 	}
8741 	//
8742 	// if just entered a water volume, play a sound
8743 	//
8744 	if (!pml.previous_waterlevel && pm->waterlevel)
8745 	{
8746 		if ( (pm->watertype&CONTENTS_LAVA) )
8747 		{
8748 			PM_AddEvent( EV_LAVA_TOUCH );
8749 		}
8750 		else
8751 		{
8752 			PM_AddEvent( EV_WATER_TOUCH );
8753 		}
8754 		if ( pm->gent )
8755 		{
8756 			if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
8757 			{
8758 				impact_splash = qtrue;
8759 			}
8760 
8761 			if ( pm->ps->clientNum < MAX_CLIENTS )
8762 			{
8763 				AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS );
8764 				AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS );
8765 			}
8766 		}
8767 	}
8768 
8769 	//
8770 	// if just completely exited a water volume, play a sound
8771 	//
8772 	if (pml.previous_waterlevel && !pm->waterlevel)
8773 	{
8774 		if ( (pm->watertype&CONTENTS_LAVA) )
8775 		{
8776 			PM_AddEvent( EV_LAVA_LEAVE );
8777 		}
8778 		else
8779 		{
8780 			PM_AddEvent( EV_WATER_LEAVE );
8781 		}
8782 		if ( pm->gent && VectorLengthSquared( pm->ps->velocity ) > 40000 )
8783 		{
8784 			impact_splash = qtrue;
8785 		}
8786 		if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS )
8787 		{
8788 			AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS );
8789 			AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS );
8790 		}
8791 	}
8792 
8793 	if ( impact_splash )
8794 	{
8795 		//play the splash effect
8796 		trace_t	tr;
8797 		vec3_t	axis[3], angs, start, end;
8798 
8799 		VectorSet( angs, 0, pm->gent->currentAngles[YAW], 0 );
8800 		AngleVectors( angs, axis[2], axis[1], axis[0] );
8801 
8802 		VectorCopy( pm->ps->origin, start );
8803 		VectorCopy( pm->ps->origin, end );
8804 
8805 		// FIXME: set start and end better
8806 		start[2] += 10;
8807 		end[2] -= 40;
8808 
8809 		gi.trace( &tr, start, vec3_origin, vec3_origin, end, pm->gent->s.number, MASK_WATER, (EG2_Collision)0, 0 );
8810 
8811 		if ( tr.fraction < 1.0f )
8812 		{
8813 			if ( (tr.contents&CONTENTS_LAVA) )
8814 			{
8815 				G_PlayEffect( "env/lava_splash", tr.endpos, axis );
8816 			}
8817 			else if ( (tr.contents&CONTENTS_SLIME) )
8818 			{
8819 				G_PlayEffect( "env/acid_splash", tr.endpos, axis );
8820 			}
8821 			else //must be water
8822 			{
8823 				G_PlayEffect( "env/water_impact", tr.endpos, axis );
8824 			}
8825 		}
8826 	}
8827 
8828 	//
8829 	// check for head just going under water
8830 	//
8831 	if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
8832 		if ( (pm->watertype&CONTENTS_LAVA) )
8833 		{
8834 			PM_AddEvent( EV_LAVA_UNDER );
8835 		}
8836 		else
8837 		{
8838 			PM_AddEvent( EV_WATER_UNDER );
8839 		}
8840 
8841 		if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS )
8842 		{
8843 			AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR );
8844 			AddSightEvent( pm->gent, pm->ps->origin, 384, AEL_MINOR );
8845 		}
8846 	}
8847 
8848 	//
8849 	// check for head just coming out of water
8850 	//
8851 	if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
8852 		if ( !pm->gent || !pm->gent->client || pm->gent->client->airOutTime < level.time + 2000 )
8853 		{//only do this if we were drowning or about to start drowning
8854 			PM_AddEvent( EV_WATER_CLEAR );
8855 		}
8856 		else
8857 		{
8858 			if ( (pm->watertype&CONTENTS_LAVA) )
8859 			{
8860 				PM_AddEvent( EV_LAVA_LEAVE );
8861 			}
8862 			else
8863 			{
8864 				PM_AddEvent( EV_WATER_LEAVE );
8865 			}
8866 		}
8867 		if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS )
8868 		{
8869 			AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR );
8870 			AddSightEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS );
8871 		}
8872 	}
8873 }
8874 
8875 
8876 /*
8877 ===============
8878 PM_BeginWeaponChange
8879 ===============
8880 */
PM_BeginWeaponChange(int weapon)8881 static void PM_BeginWeaponChange( int weapon ) {
8882 
8883 	if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 )
8884 	{//just entered map
8885 		if ( weapon == WP_NONE && pm->ps->weapon != weapon )
8886 		{//don't switch to weapon none if just entered map
8887 			return;
8888 		}
8889 	}
8890 
8891 	if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
8892 		return;
8893 	}
8894 
8895 	if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
8896 		return;
8897 	}
8898 
8899 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
8900 		return;
8901 	}
8902 
8903 	if ( cg.time > 0 )
8904 	{//this way we don't get that annoying change weapon sound every time a map starts
8905 		PM_AddEvent( EV_CHANGE_WEAPON );
8906 	}
8907 
8908 	pm->ps->weaponstate = WEAPON_DROPPING;
8909 	pm->ps->weaponTime += 200;
8910 	if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) && !G_IsRidingVehicle(pm->gent))
8911 	{
8912 		PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1,SETANIM_FLAG_HOLD);
8913 	}
8914 
8915 	// turn of any kind of zooming when weapon switching....except the LA Goggles
8916 	// eezstreet edit: also ignore if we change to WP_NONE..sorta hacky fix for binoculars using WP_SABER
8917 	if ( pm->ps->clientNum == 0 && cg.weaponSelect != WP_NONE )
8918 	{
8919 		if ( cg.zoomMode > 0 && cg.zoomMode < 3 )
8920 		{
8921 			cg.zoomMode = 0;
8922 			cg.zoomTime = cg.time;
8923 		}
8924 	}
8925 
8926 	if ( pm->gent
8927 		&& pm->gent->client
8928 		&& (pm->gent->client->NPC_class == CLASS_ATST||pm->gent->client->NPC_class == CLASS_RANCOR) )
8929 	{
8930 		if ( pm->ps->clientNum < MAX_CLIENTS )
8931 		{
8932 			gi.cvar_set( "cg_thirdperson", "1" );
8933 		}
8934 	}
8935 	else if ( weapon == WP_SABER )
8936 	{//going to switch to lightsaber
8937 	}
8938 	else
8939 	{
8940 		if ( pm->ps->weapon == WP_SABER )
8941 		{//going to switch away from saber
8942 			if ( pm->gent )
8943 			{
8944 				G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/saber/saberoffquick.wav" );
8945 			}
8946 			if (!G_IsRidingVehicle(pm->gent))
8947 			{
8948 				PM_SetSaberMove(LS_PUTAWAY);
8949 			}
8950 		}
8951 		//put this back in because saberActive isn't being set somewhere else anymore
8952 		pm->ps->SaberDeactivate();
8953 		pm->ps->SetSaberLength( 0.0f );
8954 	}
8955 }
8956 
8957 
8958 /*
8959 ===============
8960 PM_FinishWeaponChange
8961 ===============
8962 */
PM_FinishWeaponChange(void)8963 static void PM_FinishWeaponChange( void ) {
8964 	int		weapon;
8965 	qboolean	trueSwitch = qtrue;
8966 
8967 	if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 )
8968 	{//just entered map
8969 		if ( pm->cmd.weapon == WP_NONE && pm->ps->weapon != pm->cmd.weapon )
8970 		{//don't switch to weapon none if just entered map
8971 			return;
8972 		}
8973 	}
8974 	weapon = pm->cmd.weapon;
8975 	if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
8976 		weapon = WP_NONE;
8977 	}
8978 
8979 	if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
8980 		weapon = WP_NONE;
8981 	}
8982 
8983 	if ( pm->ps->weapon == weapon )
8984 	{
8985 		trueSwitch = qfalse;
8986 	}
8987 	//int oldWeap = pm->ps->weapon;
8988 	pm->ps->weapon = weapon;
8989 	pm->ps->weaponstate = WEAPON_RAISING;
8990 	pm->ps->weaponTime += 250;
8991 
8992 	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST )
8993 	{//do nothing
8994 	}
8995 	else if ( weapon == WP_SABER )
8996 	{//turn on the lightsaber
8997 		//FIXME: somehow sometimes I still end up with 2 weapons in hand... usually if I
8998 		//		cycle weapons fast enough that I end up in 1st person lightsaber, then
8999 		//		somehow throw the saber and switch to another weapon (all in 1st person),
9000 		//		making my saber drop to the ground... when I switch back to the saber, it
9001 		//		does not remove the current weapon model and then, when I pull the saber
9002 		//		back to my hand, I have 2 weaponModels active...?
9003 		if ( pm->gent )
9004 		{// remove gun if we had it.
9005 			G_RemoveWeaponModels( pm->gent );
9006 		}
9007 
9008 		if ( !pm->ps->saberInFlight || pm->ps->dualSabers )
9009 		{//if it's not in flight or lying around, turn it on!
9010 			//FIXME: AddSound/Sight Event
9011 			//FIXME: don't do this if just loaded a game
9012 			if ( trueSwitch )
9013 			{//actually did switch weapons, turn it on
9014 				if ( PM_RidingVehicle() )
9015 				{//only turn on the first saber's first blade...?
9016 					pm->ps->SaberBladeActivate( 0, 0 );
9017 				}
9018 				else
9019 				{
9020 					pm->ps->SaberActivate();
9021 				}
9022 				pm->ps->SetSaberLength( 0.0f );
9023 			}
9024 
9025 			if ( pm->gent )
9026 			{
9027 				WP_SaberAddG2SaberModels( pm->gent );
9028 			}
9029 		}
9030 		else
9031 		{//FIXME: pull it back to us?
9032 		}
9033 
9034 		if ( pm->gent )
9035 		{
9036 			WP_SaberInitBladeData( pm->gent );
9037 			if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
9038 			{
9039 				gi.cvar_set( "cg_thirdperson", "1" );
9040 			}
9041 		}
9042 		if ( trueSwitch )
9043 		{//actually did switch weapons, play anim
9044 			if (!G_IsRidingVehicle(pm->gent))
9045 			{
9046 				PM_SetSaberMove(LS_DRAW);
9047 			}
9048 		}
9049 	}
9050 	else
9051 	{//switched away from saber
9052 		if ( pm->gent )
9053 		{
9054 			// remove the sabre if we had it.
9055 			G_RemoveWeaponModels( pm->gent );
9056 			if (weaponData[weapon].weaponMdl[0]) {	//might be NONE, so check if it has a model
9057 				G_CreateG2AttachedWeaponModel( pm->gent, weaponData[weapon].weaponMdl, pm->gent->handRBolt, 0 );
9058 			}
9059 		}
9060 
9061 		if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) )
9062 		{
9063 			if ( pm->ps->weapon != WP_THERMAL
9064 				&& pm->ps->weapon != WP_TRIP_MINE
9065 				&& pm->ps->weapon != WP_DET_PACK
9066 				&& !G_IsRidingVehicle(pm->gent))
9067 			{
9068 				PM_SetAnim(pm,SETANIM_TORSO,TORSO_RAISEWEAP1,SETANIM_FLAG_HOLD);
9069 			}
9070 		}
9071 
9072 		if ( pm->ps->clientNum < MAX_CLIENTS
9073 			&& cg_gunAutoFirst.integer
9074 			&& !PM_RidingVehicle()
9075 //			&& oldWeap == WP_SABER
9076 			&& weapon != WP_NONE )
9077 		{
9078 			gi.cvar_set( "cg_thirdperson", "0" );
9079 		}
9080 		pm->ps->saberMove = LS_NONE;
9081 		pm->ps->saberBlocking = BLK_NO;
9082 		pm->ps->saberBlocked = BLOCKED_NONE;
9083 	}
9084 }
9085 
PM_ReadyPoseForSaberAnimLevel(void)9086 int PM_ReadyPoseForSaberAnimLevel( void )
9087 {
9088 	int anim = BOTH_STAND2;
9089  	if (PM_RidingVehicle())
9090 	{
9091 		return -1;
9092 	}
9093 	switch ( pm->ps->saberAnimLevel )
9094 	{
9095 	case SS_DUAL:
9096 		anim = BOTH_SABERDUAL_STANCE;
9097 		break;
9098 	case SS_STAFF:
9099 		anim = BOTH_SABERSTAFF_STANCE;
9100 		break;
9101 	case SS_FAST:
9102 	case SS_TAVION:
9103 		anim = BOTH_SABERFAST_STANCE;
9104 		break;
9105 	case SS_STRONG:
9106 		anim = BOTH_SABERSLOW_STANCE;
9107 		break;
9108 	case SS_NONE:
9109 	case SS_MEDIUM:
9110 	case SS_DESANN:
9111 	default:
9112 		anim = BOTH_STAND2;
9113 		break;
9114 	}
9115 	return anim;
9116 }
9117 
PM_CanDoDualDoubleAttacks(void)9118 qboolean PM_CanDoDualDoubleAttacks( void )
9119 {
9120 	if ( (pm->ps->saber[0].saberFlags&SFL_NO_MIRROR_ATTACKS) )
9121 	{
9122 		return qfalse;
9123 	}
9124 	if ( pm->ps->dualSabers
9125 		&& (pm->ps->saber[1].saberFlags&SFL_NO_MIRROR_ATTACKS) )
9126 	{
9127 		return qfalse;
9128 	}
9129 	//NOTE: assumes you're using SS_DUAL style and have both sabers on...
9130 	if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
9131 	{//player
9132 		return qtrue;
9133 	}
9134 	if ( pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= Q_irand( RANK_LT_COMM, RANK_CAPTAIN+2 ) )
9135 	{//high-rank NPC
9136 		return qtrue;
9137 	}
9138 	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA )
9139 	{//Alora
9140 		return qtrue;
9141 	}
9142 	return qfalse;
9143 }
9144 
PM_SetJumped(float height,qboolean force)9145 void PM_SetJumped( float height, qboolean force )
9146 {
9147 	pm->ps->velocity[2] = height;
9148 	pml.groundPlane = qfalse;
9149 	pml.walking = qfalse;
9150 	pm->ps->groundEntityNum = ENTITYNUM_NONE;
9151 	pm->ps->pm_flags |= PMF_JUMP_HELD;
9152 	pm->ps->pm_flags |= PMF_JUMPING;
9153 	pm->cmd.upmove = 0;
9154 
9155 	if ( force )
9156 	{
9157 		pm->ps->jumpZStart = pm->ps->origin[2];
9158 		pm->ps->pm_flags |= PMF_SLOW_MO_FALL;
9159 		//start force jump
9160 		pm->ps->forcePowersActive |= (1<<FP_LEVITATION);
9161 		G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
9162 	}
9163 	else
9164 	{
9165 		PM_AddEvent( EV_JUMP );
9166 	}
9167 }
9168 
9169 
PM_SetSaberMove(saberMoveName_t newMove)9170 void PM_SetSaberMove(saberMoveName_t newMove)
9171 {
9172 	unsigned int setflags;
9173 	int	anim;
9174 	int parts = SETANIM_TORSO;
9175 	qboolean manualBlocking = qfalse;
9176 
9177 	if ( newMove < LS_NONE || newMove >= LS_MOVE_MAX )
9178 	{
9179 		assert(0);
9180 		return;
9181 	}
9182 
9183 	setflags = saberMoveData[newMove].animSetFlags;
9184 	anim = saberMoveData[newMove].animToUse;
9185 
9186 	if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) )
9187 	{//no anim
9188 		return;
9189 	}
9190 
9191 	if ( cg_debugSaber.integer&0x01 && (newMove != LS_READY) )
9192 	{
9193 		Com_Printf("SetSaberMove:  From '%s' to '%s'\n",
9194 				saberMoveData[pm->ps->saberMove].name,
9195 				saberMoveData[newMove].name);
9196 	}
9197 
9198 	if ( newMove == LS_READY || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH )
9199 	{//finished with a kata (or in a special move) reset attack counter
9200 		pm->ps->saberAttackChainCount = 0;
9201 	}
9202 	else if ( PM_SaberInAttack( newMove ) )
9203 	{//continuing with a kata, increment attack counter
9204 		//FIXME: maybe some contextual/style-specific logic in here
9205 		pm->ps->saberAttackChainCount++;
9206 	}
9207 
9208 	if ( newMove == LS_READY )
9209 	{
9210 		if ( pm->ps->saberBlockingTime > cg.time )
9211 		{
9212 			manualBlocking = qtrue;
9213 			if ( !pm->ps->SaberActive() )
9214 			{//turn on all blades and sabers if none are currently on
9215 				pm->ps->SaberActivate();
9216 			}
9217 			if ( pm->ps->saber[0].type == SABER_CLAW )
9218 			{
9219 				anim = BOTH_INAIR1;//FIXME: is there a better anim for this?
9220 			}
9221 			else if ( pm->ps->dualSabers && pm->ps->saber[1].Active() )
9222 			{
9223 				anim = BOTH_INAIR1;
9224 			}
9225 			else
9226 			{
9227 				anim = BOTH_P1_S1_T_;
9228 			}
9229 		}
9230 		else if ( pm->ps->saber[0].readyAnim != -1 )
9231 		{
9232 			anim = pm->ps->saber[0].readyAnim;
9233 		}
9234 		else if ( pm->ps->dualSabers
9235 			&& pm->ps->saber[1].readyAnim != -1 )
9236 		{
9237 			anim = pm->ps->saber[1].readyAnim;
9238 		}
9239 		else if ( pm->ps->saber[0].type == SABER_ARC )
9240 		{//FIXME: need it's own style?
9241 			anim = BOTH_SABERFAST_STANCE;
9242 		}
9243 		else if ( (pm->ps->dualSabers && pm->ps->saber[1].Active()) )
9244 		{
9245 			anim = BOTH_SABERDUAL_STANCE;
9246 		}
9247 		else if ( (pm->ps->SaberStaff() && (!pm->ps->saber[0].singleBladeStyle||pm->ps->saber[0].blade[1].active))//saber staff with more than first blade active
9248 			|| pm->ps->saber[0].type == SABER_ARC )
9249 		{
9250 			anim = BOTH_SABERSTAFF_STANCE;
9251 		}
9252 		else if ( pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT )
9253 		{//FIXME: need some 2-handed forward-pointing anim
9254 			anim = BOTH_STAND1;
9255 		}
9256 		else
9257 		{
9258 			anim = PM_ReadyPoseForSaberAnimLevel();
9259 		}
9260 	}
9261 	else if ( newMove == LS_DRAW )
9262 	{
9263 		if ( PM_RunningAnim( pm->ps->torsoAnim ) )
9264 		{
9265 			pm->ps->saberMove = newMove;
9266 			return;
9267 		}
9268 		if ( pm->ps->saber[0].drawAnim != -1 )
9269 		{
9270 			anim = pm->ps->saber[0].drawAnim;
9271 		}
9272 		else if ( pm->ps->dualSabers
9273 			&& pm->ps->saber[1].drawAnim != -1 )
9274 		{
9275 			anim = pm->ps->saber[1].drawAnim;
9276 		}
9277 		else if ( pm->ps->saber[0].stylesLearned==(1<<SS_STAFF) )
9278 		{
9279 			anim = BOTH_S1_S7;
9280 		}
9281 		else if ( pm->ps->dualSabers
9282 			&& !(pm->ps->saber[0].stylesForbidden&(1<<SS_DUAL))
9283 			&& !(pm->ps->saber[1].stylesForbidden&(1<<SS_DUAL)) )
9284 		{
9285 			anim = BOTH_S1_S6;
9286 		}
9287 		if ( pm->ps->torsoAnim == BOTH_STAND1IDLE1 )
9288 		{
9289 			setflags |= SETANIM_FLAG_OVERRIDE;
9290 		}
9291 	}
9292 	else if ( newMove == LS_PUTAWAY )
9293 	{
9294 		if ( pm->ps->saber[0].putawayAnim != -1 )
9295 		{
9296 			anim = pm->ps->saber[0].putawayAnim;
9297 		}
9298 		else if ( pm->ps->dualSabers
9299 			&& pm->ps->saber[1].putawayAnim != -1 )
9300 		{
9301 			anim = pm->ps->saber[1].putawayAnim;
9302 		}
9303 		else if ( pm->ps->saber[0].stylesLearned==(1<<SS_STAFF)
9304 			&& pm->ps->saber[0].blade[1].active )
9305 		{
9306 			anim = BOTH_S7_S1;
9307 		}
9308 		else if ( pm->ps->dualSabers
9309 			&& !(pm->ps->saber[0].stylesForbidden&(1<<SS_DUAL))
9310 			&& !(pm->ps->saber[1].stylesForbidden&(1<<SS_DUAL))
9311 			&& pm->ps->saber[1].Active() )
9312 		{
9313 			anim = BOTH_S6_S1;
9314 		}
9315 		if ( PM_SaberStanceAnim( pm->ps->legsAnim ) && pm->ps->legsAnim != BOTH_STAND1 )
9316 		{
9317 			parts = SETANIM_BOTH;
9318 		}
9319 		else
9320 		{
9321 			if ( PM_RunningAnim( pm->ps->torsoAnim ) )
9322 			{
9323 				pm->ps->saberMove = newMove;
9324 				return;
9325 			}
9326 			parts = SETANIM_TORSO;
9327 		}
9328 		//FIXME: also dual
9329 	}
9330 	else if ( pm->ps->saberAnimLevel == SS_STAFF && newMove >= LS_S_TL2BR && newMove < LS_REFLECT_LL )
9331 	{//staff has an entirely new set of anims, besides special attacks
9332 		//FIXME: include ready and draw/putaway?
9333 		//FIXME: get hand-made bounces and deflections?
9334 		if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL )
9335 		{//there aren't 1-7, just 1, 6 and 7, so just set it
9336 			anim = BOTH_P7_S7_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set
9337 		}
9338 		else
9339 		{//add the appropriate animLevel
9340 			anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
9341 		}
9342 	}
9343 	else if ( (pm->ps->saberAnimLevel == SS_DUAL
9344 				|| (pm->ps->dualSabers&& pm->ps->saber[1].Active()))
9345 		&& newMove >= LS_S_TL2BR
9346 		&& newMove < LS_REFLECT_LL )
9347 	{//staff has an entirely new set of anims, besides special attacks
9348 		//FIXME: include ready and draw/putaway?
9349 		//FIXME: get hand-made bounces and deflections?
9350 		//FIXME: only do the dual FB & LR attacks when on ground?
9351 		if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL )
9352 		{//there aren't 1-7, just 1, 6 and 7, so just set it
9353 			anim = BOTH_P6_S6_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set
9354 		}
9355 		else if ( ( newMove == LS_A_R2L || newMove == LS_S_R2L
9356 					|| newMove == LS_A_L2R  || newMove == LS_S_L2R )
9357 			&& PM_CanDoDualDoubleAttacks()
9358 			&& G_CheckEnemyPresence( pm->gent, DIR_RIGHT, 150.0f )
9359 			&& G_CheckEnemyPresence( pm->gent, DIR_LEFT, 150.0f ) )
9360 		{//enemy both on left and right
9361 			newMove = LS_DUAL_LR;
9362 			anim = saberMoveData[newMove].animToUse;
9363 			//probably already moved, but...
9364 			pm->cmd.rightmove = 0;
9365 		}
9366 		else if ( (newMove == LS_A_T2B || newMove == LS_S_T2B
9367 					|| newMove == LS_A_BACK || newMove == LS_A_BACK_CR )
9368 			&& PM_CanDoDualDoubleAttacks()
9369 			&& G_CheckEnemyPresence( pm->gent, DIR_FRONT, 150.0f )
9370 			&& G_CheckEnemyPresence( pm->gent, DIR_BACK, 150.0f ) )
9371 		{//enemy both in front and back
9372 			newMove = LS_DUAL_FB;
9373 			anim = saberMoveData[newMove].animToUse;
9374 			//probably already moved, but...
9375 			pm->cmd.forwardmove = 0;
9376 		}
9377 		else
9378 		{//add the appropriate animLevel
9379 			anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
9380 		}
9381 	}
9382 	/*
9383 	else if ( newMove == LS_DRAW && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )
9384 	{//hold saber out front as we turn it on
9385 		//FIXME: need a real "draw" anim for this (and put-away)
9386 		anim = BOTH_SABERSTAFF_STANCE;
9387 	}
9388 	*/
9389 	else if ( pm->ps->saberAnimLevel > FORCE_LEVEL_1 &&
9390 		 !PM_SaberInIdle( newMove ) && !PM_SaberInParry( newMove ) && !PM_SaberInKnockaway( newMove ) && !PM_SaberInBrokenParry( newMove ) && !PM_SaberInReflect( newMove ) && !PM_SaberInSpecial( newMove ))
9391 	{//readies, parries and reflections have only 1 level
9392 		if ( pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT )
9393 		{//FIXME: hack for now - these use the fast anims, but slowed down.  Should have own style
9394 		}
9395 		else
9396 		{//increment the anim to the next level of saber anims
9397 			anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
9398 		}
9399 	}
9400 	else if ( newMove == LS_KICK_F_AIR
9401 		|| newMove == LS_KICK_B_AIR
9402 		|| newMove == LS_KICK_R_AIR
9403 		|| newMove == LS_KICK_L_AIR )
9404 	{
9405 		if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
9406 		{
9407 			PM_SetJumped( 200, qtrue );
9408 		}
9409 	}
9410 
9411 	// If the move does the same animation as the last one, we need to force a restart...
9412 //	if ( saberMoveData[pm->ps->saberMove].animToUse == anim && newMove > LS_PUTAWAY)
9413 	if ( ( pm->ps->torsoAnim == anim || pm->ps->legsAnim == anim )
9414 		&& newMove > LS_PUTAWAY )
9415 	{
9416 		setflags |= SETANIM_FLAG_RESTART;
9417 	}
9418 
9419 	if ( (anim == BOTH_STAND1 && (pm->ps->saber[0].type == SABER_ARC || (pm->ps->dualSabers && pm->ps->saber[1].Active())) )
9420 		|| anim == BOTH_STAND2
9421 		//FIXME: temp hack to stop it from using run2 with staff
9422 		|| (0 && anim == BOTH_SABERSTAFF_STANCE)
9423 		|| anim == BOTH_SABERDUAL_STANCE
9424 		|| anim == BOTH_SABERFAST_STANCE
9425 		|| anim == BOTH_SABERSLOW_STANCE )
9426 	{//match torso anim to walk/run anim if newMove is just LS_READY
9427 		//FIXME: play both_stand2_random1 when you've been idle for a while
9428 		switch ( pm->ps->legsAnim )
9429 		{
9430 		case BOTH_WALK1:
9431 		case BOTH_WALK2:
9432 		case BOTH_WALK_STAFF:
9433 		case BOTH_WALK_DUAL:
9434 		case BOTH_WALKBACK1:
9435 		case BOTH_WALKBACK2:
9436 		case BOTH_WALKBACK_STAFF:
9437 		case BOTH_WALKBACK_DUAL:
9438 		case BOTH_RUN1:
9439 		case BOTH_RUN2:
9440 		case BOTH_RUN_STAFF:
9441 		case BOTH_RUN_DUAL:
9442 		case BOTH_RUNBACK1:
9443 		case BOTH_RUNBACK2:
9444 		case BOTH_RUNBACK_STAFF:
9445 			anim = pm->ps->legsAnim;
9446 			break;
9447 		}
9448 	}
9449 
9450 	if ( !PM_RidingVehicle() )
9451 	{
9452 		if ( !manualBlocking )
9453 		{
9454 			if ( newMove == LS_A_LUNGE
9455 				|| newMove == LS_A_JUMP_T__B_
9456 				|| newMove == LS_A_BACKSTAB
9457 				|| newMove == LS_A_BACK
9458 				|| newMove == LS_A_BACK_CR
9459 				|| newMove == LS_ROLL_STAB
9460 				|| newMove == LS_A_FLIP_STAB
9461 				|| newMove == LS_A_FLIP_SLASH
9462 				|| newMove == LS_JUMPATTACK_DUAL
9463 				|| newMove == LS_JUMPATTACK_ARIAL_LEFT
9464 				|| newMove == LS_JUMPATTACK_ARIAL_RIGHT
9465 				|| newMove == LS_JUMPATTACK_CART_LEFT
9466 				|| newMove == LS_JUMPATTACK_CART_RIGHT
9467 				|| newMove == LS_JUMPATTACK_STAFF_LEFT
9468 				|| newMove == LS_JUMPATTACK_STAFF_RIGHT
9469 				|| newMove == LS_BUTTERFLY_LEFT
9470 				|| newMove == LS_BUTTERFLY_RIGHT
9471 				|| newMove == LS_A_BACKFLIP_ATK
9472 				|| newMove == LS_STABDOWN
9473 				|| newMove == LS_STABDOWN_STAFF
9474 				|| newMove == LS_STABDOWN_DUAL
9475 				|| newMove == LS_DUAL_SPIN_PROTECT
9476 				|| newMove == LS_STAFF_SOULCAL
9477 				|| newMove == LS_A1_SPECIAL
9478 				|| newMove == LS_A2_SPECIAL
9479 				|| newMove == LS_A3_SPECIAL
9480 				|| newMove == LS_UPSIDE_DOWN_ATTACK
9481 				|| newMove == LS_PULL_ATTACK_STAB
9482 				|| newMove == LS_PULL_ATTACK_SWING
9483 				|| PM_KickMove( newMove ) )
9484 			{
9485 				parts = SETANIM_BOTH;
9486 			}
9487 			else if ( PM_SpinningSaberAnim( anim ) )
9488 			{//spins must be played on entire body
9489 				parts = SETANIM_BOTH;
9490 			}
9491 			else if ( (!pm->cmd.forwardmove&&!pm->cmd.rightmove&&!pm->cmd.upmove))
9492 			{//not trying to run, duck or jump
9493 				if ( !PM_FlippingAnim( pm->ps->legsAnim ) &&
9494 					!PM_InRoll( pm->ps ) &&
9495 					!PM_InKnockDown( pm->ps ) &&
9496 					!PM_JumpingAnim( pm->ps->legsAnim ) &&
9497 					!PM_PainAnim( pm->ps->legsAnim ) &&
9498 					!PM_InSpecialJump( pm->ps->legsAnim ) &&
9499 					!PM_InSlopeAnim( pm->ps->legsAnim ) &&
9500 					//!PM_CrouchAnim( pm->ps->legsAnim ) &&
9501 					//pm->cmd.upmove >= 0 &&
9502 					!(pm->ps->pm_flags & PMF_DUCKED) &&
9503 					newMove != LS_PUTAWAY )
9504 				{
9505 					parts = SETANIM_BOTH;
9506 				}
9507 				else if ( !(pm->ps->pm_flags & PMF_DUCKED)
9508 					&& ( newMove == LS_SPINATTACK_DUAL || newMove == LS_SPINATTACK ) )
9509 				{
9510 					parts = SETANIM_BOTH;
9511 				}
9512 			}
9513 		}
9514 	}
9515 	else
9516 	{
9517 		if (!pm->ps->saberBlocked)
9518 		{
9519 			parts = SETANIM_BOTH;
9520 			setflags &= ~SETANIM_FLAG_RESTART;
9521 		}
9522 	}
9523 	if (anim!=-1)
9524 	{
9525 		PM_SetAnim( pm, parts, anim, setflags, saberMoveData[newMove].blendTime );
9526 	}
9527 
9528 	if ( pm->ps->torsoAnim == anim )
9529 	{//successfully changed anims
9530 	//special check for *starting* a saber swing
9531 		if ( pm->gent && pm->ps->SaberLength() > 1 )
9532 		{
9533 			if ( PM_SaberInAttack( newMove ) || PM_SaberInSpecialAttack( anim ) )
9534 			{//playing an attack
9535 				if ( pm->ps->saberMove != newMove )
9536 				{//wasn't playing that attack before
9537 					if ( PM_SaberInSpecialAttack( anim ) )
9538 					{
9539 						WP_SaberSwingSound( pm->gent, 0, SWING_FAST );
9540 						if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
9541 						{//can still attack during a cartwheel/arial
9542 							pm->ps->weaponTime = pm->ps->torsoAnimTimer;//so we know our weapon is busy
9543 						}
9544 					}
9545 					else
9546 					{
9547 						switch ( pm->ps->saberAnimLevel )
9548 						{
9549 						case SS_DESANN:
9550 						case SS_STRONG:
9551 							WP_SaberSwingSound( pm->gent, 0, SWING_STRONG );
9552 							break;
9553 						case SS_MEDIUM:
9554 						case SS_DUAL:
9555 						case SS_STAFF:
9556 							WP_SaberSwingSound( pm->gent, 0, SWING_MEDIUM );
9557 							break;
9558 						case SS_TAVION:
9559 						case SS_FAST:
9560 							WP_SaberSwingSound( pm->gent, 0, SWING_FAST );
9561 							break;
9562 						}
9563 					}
9564 				}
9565 				else if ( (setflags&SETANIM_FLAG_RESTART) && PM_SaberInSpecialAttack( anim ) )
9566 				{//sigh, if restarted a special, then set the weaponTime *again*
9567 					if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
9568 					{//can still attack during a cartwheel/arial
9569 						pm->ps->weaponTime = pm->ps->torsoAnimTimer;//so we know our weapon is busy
9570 					}
9571 				}
9572 			}
9573 			else if ( PM_SaberInStart( newMove ) )
9574 			{
9575 				//if ( g_saberRealisticCombat->integer < 1 )
9576 				{//don't damage on the first few frames of a start anim because it may pop from one position to some drastically different one, killing the enemy without hitting them.
9577 					int damageDelay = 150;
9578 					if ( pm->ps->torsoAnimTimer < damageDelay )
9579 					{
9580 						damageDelay = pm->ps->torsoAnimTimer;
9581 					}
9582 					//ent->client->ps.saberDamageDebounceTime = level.time + damageDelay;
9583 				}
9584 				if ( pm->ps->saberAnimLevel == SS_STRONG )
9585 				{
9586 					WP_SaberSwingSound( pm->gent, 0, SWING_FAST );
9587 				}
9588 			}
9589 		}
9590 
9591 		/*
9592 		//wtf... getting stuck with weaponTime set even though we're not in an attack...?
9593 		if ( PM_SaberInAttack( pm->ps->saberMove )
9594 			&& !PM_SaberInAttack( newMove ) )
9595 		{
9596 			pm->ps->weaponTime = 0;
9597 		}
9598 		*/
9599 
9600 
9601 		//Some special attacks can be started when sabers are off, make sure we turn them on, first!
9602 		switch ( newMove )
9603 		{//make sure the saber is on!
9604 		case LS_A_LUNGE:
9605 		case LS_ROLL_STAB:
9606 			if ( PM_InSecondaryStyle() )
9607 			{//staff as medium or dual as fast
9608 				if ( pm->ps->dualSabers )
9609 				{//only force on the first saber
9610 					pm->ps->saber[0].Activate();
9611 				}
9612 				else if ( pm->ps->saber[0].numBlades > 1 )
9613 				{//only force on the first saber's first blade
9614 					pm->ps->SaberBladeActivate(0,0);
9615 				}
9616 			}
9617 			else
9618 			{//turn on all blades on all sabers
9619 				pm->ps->SaberActivate();
9620 			}
9621 			break;
9622 		case LS_SPINATTACK_ALORA:
9623 		case LS_SPINATTACK_DUAL:
9624 		case LS_SPINATTACK:
9625 		case LS_A1_SPECIAL:
9626 		case LS_A2_SPECIAL:
9627 		case LS_A3_SPECIAL:
9628 		case LS_DUAL_SPIN_PROTECT:
9629 		case LS_STAFF_SOULCAL:
9630 			//FIXME: probably more...
9631 			pm->ps->SaberActivate();
9632 			break;
9633 		default:
9634 			break;
9635 		}
9636 
9637 		pm->ps->saberMove = newMove;
9638 		pm->ps->saberBlocking = saberMoveData[newMove].blocking;
9639 
9640 		if ( pm->ps->clientNum == 0 || PM_ControlledByPlayer() )
9641 		{
9642 			if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ
9643 				&& newMove >= LS_REFLECT_UP && newMove <= LS_REFLECT_LL )
9644 			{//don't clear it when blocking projectiles
9645 			}
9646 			else
9647 			{
9648 				pm->ps->saberBlocked = BLOCKED_NONE;
9649 			}
9650 		}
9651 		else if ( pm->ps->saberBlocked <= BLOCKED_ATK_BOUNCE || !pm->ps->SaberActive() || (newMove < LS_PARRY_UR || newMove > LS_REFLECT_LL) )
9652 		{//NPCs only clear blocked if not blocking?
9653 			pm->ps->saberBlocked = BLOCKED_NONE;
9654 		}
9655 
9656 		if ( pm->gent && pm->gent->client )
9657 		{
9658 			if ( saberMoveData[newMove].trailLength > 0 )
9659 			{
9660 				pm->gent->client->ps.SaberActivateTrail( saberMoveData[newMove].trailLength ); // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter
9661 			}
9662 			else
9663 			{
9664 				pm->gent->client->ps.SaberDeactivateTrail( 0 );
9665 			}
9666 		}
9667 	}
9668 }
9669 
9670 
9671 /*
9672 ==============
9673 PM_Use
9674 
9675 Generates a use event
9676 ==============
9677 */
9678 #define USE_DELAY 250
9679 
PM_Use(void)9680 void PM_Use( void )
9681 {
9682 	if ( pm->ps->useTime > 0 )
9683 	{
9684 		pm->ps->useTime -= pml.msec;
9685 		if ( pm->ps->useTime < 0 )
9686 		{
9687 			pm->ps->useTime = 0;
9688 		}
9689 	}
9690 
9691 	if ( pm->ps->useTime > 0 ) {
9692 		return;
9693 	}
9694 
9695 	if ( ! (pm->cmd.buttons & BUTTON_USE ) )
9696 	{
9697 		pm->useEvent = 0;
9698 		pm->ps->useTime = 0;
9699 		return;
9700 	}
9701 
9702 	pm->useEvent = EV_USE;
9703 	pm->ps->useTime = USE_DELAY;
9704 }
9705 
9706 extern saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown );
PM_NPCSaberAttackFromQuad(int quad)9707 saberMoveName_t PM_NPCSaberAttackFromQuad( int quad )
9708 {
9709 	//FIXME: this should be an AI decision
9710 	// It should be based on the enemy's current LS_ move, saberAnimLevel,
9711 	// the jedi's difficulty level, rank and FP_OFFENSE skill...
9712 	saberMoveName_t autoMove = LS_NONE;
9713 	if ( pm->gent && ((pm->gent->NPC && pm->gent->NPC->rank != RANK_ENSIGN && pm->gent->NPC->rank != RANK_CIVILIAN ) || (pm->gent->client && (pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA))) )
9714 	{
9715 		autoMove = PM_AttackForEnemyPos( qtrue, qtrue );
9716 	}
9717 	if ( autoMove != LS_NONE && PM_SaberInSpecial( autoMove ) )
9718 	{//if have opportunity to do a special attack, do one
9719 		return autoMove;
9720 	}
9721 	else
9722 	{//pick another one
9723 		saberMoveName_t newmove = LS_NONE;
9724 		switch( quad )
9725 		{
9726 		case Q_T://blocked top
9727 			if ( Q_irand( 0, 1 ) )
9728 			{
9729 				newmove = LS_A_T2B;
9730 			}
9731 			else
9732 			{
9733 				newmove = LS_A_TR2BL;
9734 			}
9735 			break;
9736 		case Q_TR:
9737 			if ( !Q_irand( 0, 2 ) )
9738 			{
9739 				newmove = LS_A_R2L;
9740 			}
9741 			else if ( !Q_irand( 0, 1 ) )
9742 			{
9743 				newmove = LS_A_TR2BL;
9744 			}
9745 			else
9746 			{
9747 				newmove = LS_T1_TR_BR;
9748 			}
9749 			break;
9750 		case Q_TL:
9751 			if ( !Q_irand( 0, 2 ) )
9752 			{
9753 				newmove = LS_A_L2R;
9754 			}
9755 			else if ( !Q_irand( 0, 1 ) )
9756 			{
9757 				newmove = LS_A_TL2BR;
9758 			}
9759 			else
9760 			{
9761 				newmove = LS_T1_TL_BL;
9762 			}
9763 			break;
9764 		case Q_BR:
9765 			if ( !Q_irand( 0, 2 ) )
9766 			{
9767 				newmove = LS_A_BR2TL;
9768 			}
9769 			else if ( !Q_irand( 0, 1 ) )
9770 			{
9771 				newmove = LS_T1_BR_TR;
9772 			}
9773 			else
9774 			{
9775 				newmove = LS_A_R2L;
9776 			}
9777 			break;
9778 		case Q_BL:
9779 			if ( !Q_irand( 0, 2 ) )
9780 			{
9781 				newmove = LS_A_BL2TR;
9782 			}
9783 			else if ( !Q_irand( 0, 1 ) )
9784 			{
9785 				newmove = LS_T1_BL_TL;
9786 			}
9787 			else
9788 			{
9789 				newmove = LS_A_L2R;
9790 			}
9791 			break;
9792 		case Q_L:
9793 			if ( !Q_irand( 0, 2 ) )
9794 			{
9795 				newmove = LS_A_L2R;
9796 			}
9797 			else if ( !Q_irand( 0, 1 ) )
9798 			{
9799 				newmove = LS_T1__L_T_;
9800 			}
9801 			else
9802 			{
9803 				newmove = LS_A_R2L;
9804 			}
9805 			break;
9806 		case Q_R:
9807 			if ( !Q_irand( 0, 2 ) )
9808 			{
9809 				newmove = LS_A_R2L;
9810 			}
9811 			else if ( !Q_irand( 0, 1 ) )
9812 			{
9813 				newmove = LS_T1__R_T_;
9814 			}
9815 			else
9816 			{
9817 				newmove = LS_A_L2R;
9818 			}
9819 			break;
9820 		case Q_B:
9821 			if ( pm->gent
9822 				&& pm->gent->NPC
9823 				&& pm->gent->NPC->rank >= RANK_LT_JG )
9824 			{//fencers and above can do bottom-up attack
9825 				if ( Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG )
9826 				{//but not overly likely
9827 					newmove = PM_SaberLungeAttackMove( qtrue );
9828 				}
9829 			}
9830 			break;
9831 		default:
9832 			break;
9833 		}
9834 		return newmove;
9835 	}
9836 }
9837 
PM_SaberMoveQuadrantForMovement(usercmd_t * ucmd)9838 int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd )
9839 {
9840 	if ( ucmd->rightmove > 0 )
9841 	{//moving right
9842 		if ( ucmd->forwardmove > 0 )
9843 		{//forward right = TL2BR slash
9844 			return Q_TL;
9845 		}
9846 		else if ( ucmd->forwardmove < 0 )
9847 		{//backward right = BL2TR uppercut
9848 			return Q_BL;
9849 		}
9850 		else
9851 		{//just right is a left slice
9852 			return Q_L;
9853 		}
9854 	}
9855 	else if ( ucmd->rightmove < 0 )
9856 	{//moving left
9857 		if ( ucmd->forwardmove > 0 )
9858 		{//forward left = TR2BL slash
9859 			return Q_TR;
9860 		}
9861 		else if ( ucmd->forwardmove < 0 )
9862 		{//backward left = BR2TL uppercut
9863 			return Q_BR;
9864 		}
9865 		else
9866 		{//just left is a right slice
9867 			return Q_R;
9868 		}
9869 	}
9870 	else
9871 	{//not moving left or right
9872 		if ( ucmd->forwardmove > 0 )
9873 		{//forward= T2B slash
9874 			return Q_T;
9875 		}
9876 		else if ( ucmd->forwardmove < 0 )
9877 		{//backward= T2B slash	//or B2T uppercut?
9878 			return Q_T;
9879 		}
9880 		else //if ( curmove == LS_READY )//???
9881 		{//Not moving at all
9882 			return Q_R;
9883 		}
9884 	}
9885 	//return Q_R;//????
9886 }
9887 
PM_SetAnimFrame(gentity_t * gent,int frame,qboolean torso,qboolean legs)9888 void PM_SetAnimFrame( gentity_t *gent, int frame, qboolean torso, qboolean legs )
9889 {
9890 	if ( !gi.G2API_HaveWeGhoul2Models( gent->ghoul2 ) )
9891 	{
9892 		return;
9893 	}
9894 	int	actualTime = (cg.time?cg.time:level.time);
9895 	if ( torso && gent->lowerLumbarBone != -1 )//gent->upperLumbarBone
9896 	{
9897 		gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, //gent->upperLumbarBone
9898 			frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 );
9899 		if ( gent->motionBone != -1 )
9900 		{
9901 			gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, //gent->upperLumbarBone
9902 				frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 );
9903 		}
9904 	}
9905 	if ( legs && gent->rootBone != -1 )
9906 	{
9907 		gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->rootBone,
9908 			frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 );
9909 	}
9910 }
9911 
PM_SaberLockWinAnim(saberLockResult_t result,int breakType)9912 int PM_SaberLockWinAnim( saberLockResult_t result, int breakType )
9913 {
9914 	int winAnim = -1;
9915 	switch ( pm->ps->torsoAnim )
9916 	{
9917 /*
9918 	default:
9919 #ifndef FINAL_BUILD
9920 		Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", pm->gent->NPC_type, pm->ps->torsoAnim, animTable[pm->ps->torsoAnim].name );
9921 #endif
9922 */
9923 	case BOTH_BF2LOCK:
9924 		if ( breakType == SABERLOCK_SUPERBREAK )
9925 		{
9926 			winAnim = BOTH_LK_S_S_T_SB_1_W;
9927 		}
9928 		else if ( result == LOCK_DRAW )
9929 		{
9930 			winAnim = BOTH_BF1BREAK;
9931 		}
9932 		else
9933 		{
9934 			pm->ps->saberMove = LS_A_T2B;
9935 			winAnim = BOTH_A3_T__B_;
9936 		}
9937 		break;
9938 	case BOTH_BF1LOCK:
9939 		if ( breakType == SABERLOCK_SUPERBREAK )
9940 		{
9941 			winAnim = BOTH_LK_S_S_T_SB_1_W;
9942 		}
9943 		else if ( result == LOCK_DRAW )
9944 		{
9945 			winAnim = BOTH_KNOCKDOWN4;
9946 		}
9947 		else
9948 		{
9949 			pm->ps->saberMove = LS_K1_T_;
9950 			winAnim = BOTH_K1_S1_T_;
9951 		}
9952 		break;
9953 	case BOTH_CWCIRCLELOCK:
9954 		if ( breakType == SABERLOCK_SUPERBREAK )
9955 		{
9956 			winAnim = BOTH_LK_S_S_S_SB_1_W;
9957 		}
9958 		else if ( result == LOCK_DRAW )
9959 		{
9960 			pm->ps->saberMove = pm->ps->saberBounceMove = LS_V1_BL;
9961 			pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN;
9962 			winAnim = BOTH_V1_BL_S1;
9963 		}
9964 		else
9965 		{
9966 			winAnim = BOTH_CWCIRCLEBREAK;
9967 		}
9968 		break;
9969 	case BOTH_CCWCIRCLELOCK:
9970 		if ( breakType == SABERLOCK_SUPERBREAK )
9971 		{
9972 			winAnim = BOTH_LK_S_S_S_SB_1_W;
9973 		}
9974 		else if ( result == LOCK_DRAW )
9975 		{
9976 			pm->ps->saberMove = pm->ps->saberBounceMove = LS_V1_BR;
9977 			pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN;
9978 			winAnim = BOTH_V1_BR_S1;
9979 		}
9980 		else
9981 		{
9982 			winAnim = BOTH_CCWCIRCLEBREAK;
9983 		}
9984 		break;
9985 	default:
9986 		//must be using new system:
9987 		break;
9988 	}
9989 	if ( winAnim != -1 )
9990 	{
9991 		PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
9992 		pm->ps->weaponTime = pm->ps->torsoAnimTimer;
9993 		pm->ps->saberBlocked = BLOCKED_NONE;
9994 		pm->ps->weaponstate = WEAPON_FIRING;
9995 		if ( breakType == SABERLOCK_SUPERBREAK
9996 			&& winAnim != BOTH_LK_ST_DL_T_SB_1_W )
9997 		{//going to attack with saber, do a saber trail
9998 			pm->ps->SaberActivateTrail( 200 );
9999 		}
10000 	}
10001 	return winAnim;
10002 }
10003 
PM_SaberLockLoseAnim(gentity_t * genemy,saberLockResult_t result,int breakType)10004 int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int breakType )
10005 {
10006 	int loseAnim = -1;
10007 	switch ( genemy->client->ps.torsoAnim )
10008 	{
10009 /*
10010 	default:
10011 #ifndef FINAL_BUILD
10012 		Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", genemy->NPC_type, genemy->client->ps.torsoAnim, animTable[genemy->client->ps.torsoAnim].name );
10013 #endif
10014 */
10015 	case BOTH_BF2LOCK:
10016 		if ( breakType == SABERLOCK_SUPERBREAK )
10017 		{
10018 			loseAnim = BOTH_LK_S_S_T_SB_1_L;
10019 		}
10020 		else if ( result == LOCK_DRAW )
10021 		{
10022 			loseAnim = BOTH_BF1BREAK;
10023 		}
10024 		else
10025 		{
10026 			if ( result == LOCK_STALEMATE )
10027 			{//no-one won
10028 				genemy->client->ps.saberMove = LS_K1_T_;
10029 				loseAnim = BOTH_K1_S1_T_;
10030 			}
10031 			else
10032 			{//FIXME: this anim needs to transition back to ready when done
10033 				loseAnim = BOTH_BF1BREAK;
10034 			}
10035 		}
10036 		break;
10037 	case BOTH_BF1LOCK:
10038 		if ( breakType == SABERLOCK_SUPERBREAK )
10039 		{
10040 			loseAnim = BOTH_LK_S_S_T_SB_1_L;
10041 		}
10042 		else if ( result == LOCK_DRAW )
10043 		{
10044 			loseAnim = BOTH_KNOCKDOWN4;
10045 		}
10046 		else
10047 		{
10048 			if ( result == LOCK_STALEMATE )
10049 			{//no-one won
10050 				genemy->client->ps.saberMove = LS_A_T2B;
10051 				loseAnim = BOTH_A3_T__B_;
10052 			}
10053 			else
10054 			{
10055 				loseAnim = BOTH_KNOCKDOWN4;
10056 			}
10057 		}
10058 		break;
10059 	case BOTH_CWCIRCLELOCK:
10060 		if ( breakType == SABERLOCK_SUPERBREAK )
10061 		{
10062 			loseAnim = BOTH_LK_S_S_S_SB_1_L;
10063 		}
10064 		else if ( result == LOCK_DRAW )
10065 		{
10066 			genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BL;
10067 			genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10068 			loseAnim = BOTH_V1_BL_S1;
10069 		}
10070 		else
10071 		{
10072 			if ( result == LOCK_STALEMATE )
10073 			{//no-one won
10074 				loseAnim = BOTH_CCWCIRCLEBREAK;
10075 			}
10076 			else
10077 			{
10078 				genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BL;
10079 				genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10080 				loseAnim = BOTH_V1_BL_S1;
10081 				/*
10082 				genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_H1_BR;
10083 				genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10084 				loseAnim = BOTH_H1_S1_BL;
10085 				*/
10086 			}
10087 		}
10088 		break;
10089 	case BOTH_CCWCIRCLELOCK:
10090 		if ( breakType == SABERLOCK_SUPERBREAK )
10091 		{
10092 			loseAnim = BOTH_LK_S_S_S_SB_1_L;
10093 		}
10094 		else if ( result == LOCK_DRAW )
10095 		{
10096 			genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BR;
10097 			genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10098 			loseAnim = BOTH_V1_BR_S1;
10099 		}
10100 		else
10101 		{
10102 			if ( result == LOCK_STALEMATE )
10103 			{//no-one won
10104 				loseAnim = BOTH_CWCIRCLEBREAK;
10105 			}
10106 			else
10107 			{
10108 				genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BR;
10109 				genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10110 				loseAnim = BOTH_V1_BR_S1;
10111 				/*
10112 				genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_H1_BL;
10113 				genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN;
10114 				loseAnim = BOTH_H1_S1_BR;
10115 				*/
10116 			}
10117 		}
10118 		break;
10119 	}
10120 	if ( loseAnim != -1 )
10121 	{
10122 		NPC_SetAnim( genemy, SETANIM_BOTH, loseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10123 		genemy->client->ps.weaponTime = genemy->client->ps.torsoAnimTimer;// + 250;
10124 		genemy->client->ps.saberBlocked = BLOCKED_NONE;
10125 		genemy->client->ps.weaponstate = WEAPON_READY;
10126 	}
10127 	return loseAnim;
10128 }
10129 
PM_SaberLockResultAnim(gentity_t * duelist,int lockOrBreakOrSuperBreak,int winOrLose)10130 int PM_SaberLockResultAnim( gentity_t *duelist, int lockOrBreakOrSuperBreak, int winOrLose )
10131 {
10132 	int baseAnim = duelist->client->ps.torsoAnim;
10133 	switch ( baseAnim )
10134 	{
10135 	case BOTH_LK_S_S_S_L_2:		//lock if I'm using single vs. a single and other intitiated
10136 		baseAnim = BOTH_LK_S_S_S_L_1;
10137 		break;
10138 	case BOTH_LK_S_S_T_L_2:		//lock if I'm using single vs. a single and other initiated
10139 		baseAnim = BOTH_LK_S_S_T_L_1;
10140 		break;
10141 	case BOTH_LK_DL_DL_S_L_2:	//lock if I'm using dual vs. dual and other initiated
10142 		baseAnim = BOTH_LK_DL_DL_S_L_1;
10143 		break;
10144 	case BOTH_LK_DL_DL_T_L_2:	//lock if I'm using dual vs. dual and other initiated
10145 		baseAnim = BOTH_LK_DL_DL_T_L_1;
10146 		break;
10147 	case BOTH_LK_ST_ST_S_L_2:	//lock if I'm using staff vs. a staff and other initiated
10148 		baseAnim = BOTH_LK_ST_ST_S_L_1;
10149 		break;
10150 	case BOTH_LK_ST_ST_T_L_2:	//lock if I'm using staff vs. a staff and other initiated
10151 		baseAnim = BOTH_LK_ST_ST_T_L_1;
10152 		break;
10153 	}
10154 	//what kind of break?
10155 	if ( lockOrBreakOrSuperBreak == SABERLOCK_BREAK )
10156 	{
10157 		baseAnim -= 2;
10158 	}
10159 	else if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK )
10160 	{
10161 		baseAnim += 1;
10162 	}
10163 	else
10164 	{//WTF?  Not a valid result
10165 		return -1;
10166 	}
10167 	//win or lose?
10168 	if ( winOrLose == SABERLOCK_WIN )
10169 	{
10170 		baseAnim += 1;
10171 	}
10172 	//play the anim and hold it
10173 	NPC_SetAnim( duelist, SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10174 
10175 	if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK
10176 		&& winOrLose == SABERLOCK_LOSE )
10177 	{//if you lose a superbreak, you're defenseless
10178 		//make saberent not block
10179 		gentity_t *saberent = &g_entities[duelist->client->ps.saberEntityNum];
10180 		if ( saberent )
10181 		{
10182 			VectorClear(saberent->mins);
10183 			VectorClear(saberent->maxs);
10184 			G_SetOrigin(saberent, duelist->currentOrigin);
10185 		}
10186 		//set sabermove to none
10187 		duelist->client->ps.saberMove = LS_NONE;
10188 		//Hold the anim a little longer than it is
10189 		duelist->client->ps.torsoAnimTimer += 250;;
10190 	}
10191 
10192 	//no attacking during this anim
10193 	duelist->client->ps.weaponTime = duelist->client->ps.torsoAnimTimer;
10194 	duelist->client->ps.saberBlocked = BLOCKED_NONE;
10195 	if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK
10196 		&& winOrLose == SABERLOCK_WIN
10197 		&& baseAnim != BOTH_LK_ST_DL_T_SB_1_W )
10198 	{//going to attack with saber, do a saber trail
10199 		duelist->client->ps.SaberActivateTrail( 200 );
10200 	}
10201 	return baseAnim;
10202 }
10203 
PM_SaberLockBreak(gentity_t * gent,gentity_t * genemy,saberLockResult_t result,int victoryStrength)10204 void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t result, int victoryStrength )
10205 {
10206 	int	winAnim = -1, loseAnim = -1;
10207 	int breakType = SABERLOCK_BREAK;
10208 	qboolean singleVsSingle = qtrue;
10209 
10210 	if ( result == LOCK_VICTORY
10211 		&& Q_irand(0,7) < victoryStrength )
10212 	{
10213 		if ( genemy
10214 			&& genemy->NPC
10215 			&& ((genemy->NPC->aiFlags&NPCAI_BOSS_CHARACTER)
10216 				||(genemy->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER)
10217 				||(genemy->client&&genemy->client->NPC_class == CLASS_SHADOWTROOPER))
10218 			&& Q_irand(0, 4)
10219 			)
10220 		{//less of a chance of getting a superbreak against a boss
10221 			breakType = SABERLOCK_BREAK;
10222 		}
10223 		else
10224 		{
10225 			breakType = SABERLOCK_SUPERBREAK;
10226 		}
10227 	}
10228 	else
10229 	{
10230 		breakType = SABERLOCK_BREAK;
10231 	}
10232 	winAnim = PM_SaberLockWinAnim( result, breakType );
10233 	if ( winAnim != -1 )
10234 	{//a single vs. single break
10235 		if ( genemy && genemy->client )
10236 		{
10237 			loseAnim = PM_SaberLockLoseAnim( genemy, result, breakType );
10238 		}
10239 	}
10240 	else
10241 	{//must be a saberlock that's not between single and single...
10242 		singleVsSingle = qfalse;
10243 		winAnim = PM_SaberLockResultAnim( gent, breakType, SABERLOCK_WIN );
10244 		pm->ps->weaponstate = WEAPON_FIRING;
10245 		if ( genemy && genemy->client )
10246 		{
10247 			loseAnim = PM_SaberLockResultAnim( genemy, breakType, SABERLOCK_LOSE );
10248 			genemy->client->ps.weaponstate = WEAPON_READY;
10249 		}
10250 	}
10251 
10252 	if ( d_saberCombat->integer )
10253 	{
10254 		Com_Printf( "%s won saber lock, anim = %s!\n", gent->NPC_type, animTable[winAnim].name );
10255 		Com_Printf( "%s lost saber lock, anim = %s!\n", genemy->NPC_type, animTable[loseAnim].name );
10256 	}
10257 
10258 	pm->ps->saberLockTime = genemy->client->ps.saberLockTime = 0;
10259 	pm->ps->saberLockEnemy = genemy->client->ps.saberLockEnemy = ENTITYNUM_NONE;
10260 	pm->ps->saberMoveNext = LS_NONE;
10261 	if ( genemy && genemy->client )
10262 	{
10263 		genemy->client->ps.saberMoveNext = LS_NONE;
10264 	}
10265 
10266 	PM_AddEvent( EV_JUMP );
10267 	if ( result == LOCK_STALEMATE )
10268 	{//no-one won
10269 		G_AddEvent( genemy, EV_JUMP, 0 );
10270 	}
10271 	else
10272 	{
10273 		if ( pm->ps->clientNum )
10274 		{//an NPC
10275 			pm->ps->saberEventFlags |= SEF_LOCK_WON;//tell the winner to press the advantage
10276 		}
10277 		//painDebounceTime will stop them from doing anything
10278 		genemy->painDebounceTime = level.time + genemy->client->ps.torsoAnimTimer + 500;
10279 		if ( Q_irand( 0, 1 ) )
10280 		{
10281 			G_AddEvent( genemy, EV_PAIN, Q_irand( 0, 75 ) );
10282 		}
10283 		else
10284 		{
10285 			if ( genemy->NPC )
10286 			{
10287 				genemy->NPC->blockedSpeechDebounceTime = 0;
10288 			}
10289 			G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 );
10290 		}
10291 		if ( result == LOCK_VICTORY )
10292 		{//one person won
10293 			if ( Q_irand( FORCE_LEVEL_1, FORCE_LEVEL_2 ) < pm->ps->forcePowerLevel[FP_SABER_OFFENSE] )
10294 			{
10295 				vec3_t throwDir = {0,0,350};
10296 				int		winMove = pm->ps->saberMove;
10297 				if ( !singleVsSingle )
10298 				{//all others have their own super breaks
10299 					//so it doesn't try to set some other anim below
10300 					winAnim = -1;
10301 				}
10302 				else if ( winAnim == BOTH_LK_S_S_S_SB_1_W
10303 					|| winAnim == BOTH_LK_S_S_T_SB_1_W )
10304 				{//doing a superbreak on single-vs-single, don't do the old superbreaks this time
10305 					//so it doesn't try to set some other anim below
10306 					winAnim = -1;
10307 				}
10308 				else
10309 				{//JK2-style
10310 					switch ( winAnim )
10311 					{
10312 					case BOTH_A3_T__B_:
10313 						winAnim = BOTH_D1_TL___;
10314 						winMove = LS_D1_TL;
10315 						//FIXME: mod throwDir?
10316 						break;
10317 					case BOTH_K1_S1_T_:
10318 						//FIXME: mod throwDir?
10319 						break;
10320 					case BOTH_CWCIRCLEBREAK:
10321 						//FIXME: mod throwDir?
10322 						break;
10323 					case BOTH_CCWCIRCLEBREAK:
10324 						winAnim = BOTH_A1_BR_TL;
10325 						winMove = LS_A_BR2TL;
10326 						//FIXME: mod throwDir?
10327 						break;
10328 					}
10329 					if ( winAnim != BOTH_CCWCIRCLEBREAK )
10330 					{
10331 						if ( (!genemy->s.number&&genemy->health<=25)//player low on health
10332 							||(genemy->s.number&&genemy->client->NPC_class!=CLASS_KYLE&&genemy->client->NPC_class!=CLASS_LUKE&&genemy->client->NPC_class!=CLASS_TAVION&&genemy->client->NPC_class!=CLASS_ALORA&&genemy->client->NPC_class!=CLASS_DESANN)//any NPC that's not a boss character
10333 							||(genemy->s.number&&genemy->health<=50) )//boss character with less than 50 health left
10334 						{//possibly knock saber out of hand OR cut hand off!
10335 							if ( Q_irand( 0, 25 ) < victoryStrength
10336 								&& ((!genemy->s.number&&genemy->health<=10)||genemy->s.number) )
10337 							{
10338 								NPC_SetAnim( genemy, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//force this
10339 								genemy->client->dismembered = false;
10340 								G_DoDismemberment( genemy, genemy->client->renderInfo.handRPoint, MOD_SABER, 1000, HL_HAND_RT, qtrue );
10341 								G_Damage( genemy, gent, gent, throwDir, genemy->client->renderInfo.handRPoint, genemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_SABER, HL_NONE );
10342 
10343 								PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10344 								pm->ps->weaponTime = pm->ps->torsoAnimTimer + 500;
10345 								pm->ps->saberMove = winMove;
10346 								pm->ps->saberBlocked = BLOCKED_NONE;
10347 								pm->ps->weaponstate = WEAPON_FIRING;
10348 								return;
10349 							}
10350 						}
10351 					}
10352 				}
10353 				//else see if we can knock the saber out of their hand
10354 				//FIXME: for now, always disarm the right-hand saber
10355 				if ( !(genemy->client->ps.saber[0].saberFlags&SFL_NOT_DISARMABLE) )
10356 				{
10357 					//add disarmBonus into this check
10358 					victoryStrength += pm->ps->SaberDisarmBonus( 0 )*2;
10359 					if ( (genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED)
10360 						|| (genemy->client->ps.dualSabers && genemy->client->ps.saber[1].Active()) )
10361 					{//defender gets a bonus for using a 2-handed saber or 2 sabers
10362 						victoryStrength -= 2;
10363 					}
10364 					if ( pm->ps->forcePowersActive&(1<<FP_RAGE) )
10365 					{
10366 						victoryStrength += gent->client->ps.forcePowerLevel[FP_RAGE];
10367 					}
10368 					else if ( pm->ps->forceRageRecoveryTime > pm->cmd.serverTime )
10369 					{
10370 						victoryStrength--;
10371 					}
10372 					if ( genemy->client->ps.forceRageRecoveryTime > pm->cmd.serverTime )
10373 					{
10374 						victoryStrength++;
10375 					}
10376 					if ( Q_irand( 0, 10 ) < victoryStrength )
10377 					{
10378 						if ( !(genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED)
10379 							|| !Q_irand( 0, 1 ) )
10380 						{//if it's a two-handed saber, it has a 50% chance of resisting a disarming
10381 							WP_SaberLose( genemy, throwDir );
10382 							if ( winAnim != -1 )
10383 							{
10384 								PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
10385 								pm->ps->weaponTime = pm->ps->torsoAnimTimer;
10386 								pm->ps->saberMove = winMove;
10387 								pm->ps->saberBlocked = BLOCKED_NONE;
10388 								pm->ps->weaponstate = WEAPON_FIRING;
10389 							}
10390 						}
10391 					}
10392 				}
10393 			}
10394 		}
10395 	}
10396 }
10397 
G_SaberLockStrength(gentity_t * gent)10398 int G_SaberLockStrength( gentity_t *gent )
10399 {
10400 	int strength = gent->client->ps.saber[0].lockBonus;
10401 	if ( (gent->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) )
10402 	{
10403 		strength += 1;
10404 	}
10405 	if ( gent->client->ps.dualSabers && gent->client->ps.saber[1].Active() )
10406 	{
10407 		strength += 1 + gent->client->ps.saber[1].lockBonus;
10408 	}
10409 	if ( gent->client->ps.forcePowersActive&(1<<FP_RAGE) )
10410 	{
10411 		strength += gent->client->ps.forcePowerLevel[FP_RAGE];
10412 	}
10413 	else if ( gent->client->ps.forceRageRecoveryTime > pm->cmd.serverTime )
10414 	{
10415 		strength--;
10416 	}
10417 	if ( gent->s.number >= MAX_CLIENTS )
10418 	{
10419 		if ( gent->client->NPC_class == CLASS_DESANN || gent->client->NPC_class == CLASS_LUKE )
10420 		{
10421 			strength += 5+Q_irand(0,g_spskill->integer);
10422 		}
10423 		else
10424 		{
10425 			strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer);
10426 			if ( gent->NPC )
10427 			{
10428 				if ( (gent->NPC->aiFlags&NPCAI_BOSS_CHARACTER)
10429 					|| (gent->NPC->aiFlags&NPCAI_ROSH)
10430 					|| gent->client->NPC_class == CLASS_SHADOWTROOPER )
10431 				{
10432 					strength += Q_irand(0,2);
10433 				}
10434 				else if ( (gent->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER) )
10435 				{
10436 					strength += Q_irand(-1,1);
10437 				}
10438 			}
10439 		}
10440 	}
10441 	else
10442 	{//player
10443 		strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer)+Q_irand(0,1);
10444 	}
10445 	return strength;
10446 }
10447 
PM_InSaberLockOld(int anim)10448 qboolean PM_InSaberLockOld( int anim )
10449 {
10450 	switch ( anim )
10451 	{
10452 	case BOTH_BF2LOCK:
10453 	case BOTH_BF1LOCK:
10454 	case BOTH_CWCIRCLELOCK:
10455 	case BOTH_CCWCIRCLELOCK:
10456 		return qtrue;
10457 	}
10458 	return qfalse;
10459 }
10460 
PM_InSaberLock(int anim)10461 qboolean PM_InSaberLock( int anim )
10462 {
10463 	switch ( anim )
10464 	{
10465 	case BOTH_LK_S_DL_S_L_1:		//lock if I'm using single vs. a dual
10466 	case BOTH_LK_S_DL_T_L_1:		//lock if I'm using single vs. a dual
10467 	case BOTH_LK_S_ST_S_L_1:		//lock if I'm using single vs. a staff
10468 	case BOTH_LK_S_ST_T_L_1:		//lock if I'm using single vs. a staff
10469 	case BOTH_LK_S_S_S_L_1:		//lock if I'm using single vs. a single and I initiated
10470 	case BOTH_LK_S_S_T_L_1:		//lock if I'm using single vs. a single and I initiated
10471 	case BOTH_LK_DL_DL_S_L_1:	//lock if I'm using dual vs. dual and I initiated
10472 	case BOTH_LK_DL_DL_T_L_1:	//lock if I'm using dual vs. dual and I initiated
10473 	case BOTH_LK_DL_ST_S_L_1:	//lock if I'm using dual vs. a staff
10474 	case BOTH_LK_DL_ST_T_L_1:	//lock if I'm using dual vs. a staff
10475 	case BOTH_LK_DL_S_S_L_1:		//lock if I'm using dual vs. a single
10476 	case BOTH_LK_DL_S_T_L_1:		//lock if I'm using dual vs. a single
10477 	case BOTH_LK_ST_DL_S_L_1:	//lock if I'm using staff vs. dual
10478 	case BOTH_LK_ST_DL_T_L_1:	//lock if I'm using staff vs. dual
10479 	case BOTH_LK_ST_ST_S_L_1:	//lock if I'm using staff vs. a staff and I initiated
10480 	case BOTH_LK_ST_ST_T_L_1:	//lock if I'm using staff vs. a staff and I initiated
10481 	case BOTH_LK_ST_S_S_L_1:		//lock if I'm using staff vs. a single
10482 	case BOTH_LK_ST_S_T_L_1:		//lock if I'm using staff vs. a single
10483 	case BOTH_LK_S_S_S_L_2:
10484 	case BOTH_LK_S_S_T_L_2:
10485 	case BOTH_LK_DL_DL_S_L_2:
10486 	case BOTH_LK_DL_DL_T_L_2:
10487 	case BOTH_LK_ST_ST_S_L_2:
10488 	case BOTH_LK_ST_ST_T_L_2:
10489 		return qtrue;
10490 		break;
10491 	default:
10492 		return PM_InSaberLockOld( anim );
10493 		break;
10494 	}
10495 	//return qfalse;
10496 }
10497 
10498 extern qboolean ValidAnimFileIndex ( int index );
10499 extern qboolean G_CheckIncrementLockAnim( int anim, int winOrLose );
PM_SaberLocked(void)10500 qboolean PM_SaberLocked( void )
10501 {
10502 	//FIXME: maybe kick out of saberlock?
10503 	if ( pm->ps->saberLockEnemy == ENTITYNUM_NONE )
10504 	{
10505 		if ( PM_InSaberLock( pm->ps->torsoAnim ) )
10506 		{//wtf?  Maybe enemy died?
10507 			PM_SaberLockWinAnim( LOCK_STALEMATE, SABERLOCK_BREAK );
10508 		}
10509 		return qfalse;
10510 	}
10511 	gentity_t *gent = pm->gent;
10512 	if ( !gent )
10513 	{
10514 		return qfalse;
10515 	}
10516 	gentity_t *genemy = &g_entities[pm->ps->saberLockEnemy];
10517 	if ( !genemy )
10518 	{
10519 		return qfalse;
10520 	}
10521 	if ( PM_InSaberLock( pm->ps->torsoAnim ) && PM_InSaberLock( genemy->client->ps.torsoAnim ) )
10522 	{
10523 		if ( pm->ps->saberLockTime <= level.time + 500
10524 			&& pm->ps->saberLockEnemy != ENTITYNUM_NONE )
10525 		{//lock just ended
10526 			int strength = G_SaberLockStrength( gent );
10527 			int eStrength = G_SaberLockStrength( genemy );
10528 			if ( strength > 1 && eStrength > 1 && !Q_irand( 0, abs(strength-eStrength)+1 ) )
10529 			{//both knock each other down!
10530 				PM_SaberLockBreak( gent, genemy, LOCK_DRAW, 0 );
10531 			}
10532 			else
10533 			{//both "win"
10534 				PM_SaberLockBreak( gent, genemy, LOCK_STALEMATE, 0 );
10535 			}
10536 			return qtrue;
10537 		}
10538 		else if ( pm->ps->saberLockTime < level.time )
10539 		{//done... tie breaker above should have handled this, but...?
10540 			if ( PM_InSaberLock( pm->ps->torsoAnim ) && pm->ps->torsoAnimTimer > 0 )
10541 			{
10542 				pm->ps->torsoAnimTimer = 0;
10543 			}
10544 			if ( PM_InSaberLock( pm->ps->legsAnim ) && pm->ps->legsAnimTimer > 0 )
10545 			{
10546 				pm->ps->legsAnimTimer = 0;
10547 			}
10548 			return qfalse;
10549 		}
10550 		else if ( pm->cmd.buttons & BUTTON_ATTACK )
10551 		{//holding attack
10552 			if ( !(pm->ps->pm_flags&PMF_ATTACK_HELD) )
10553 			{//tapping
10554 				int	remaining = 0;
10555 				if( ValidAnimFileIndex( gent->client->clientInfo.animFileIndex ) )
10556 				{
10557 					animation_t *anim;
10558 					float		currentFrame, junk2;
10559 					int			curFrame, junk;
10560 					int			strength = 1;
10561 					anim = &level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations[pm->ps->torsoAnim];
10562 
10563 #ifdef _DEBUG
10564 					qboolean ret =
10565 #endif
10566 						gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone,
10567 						(cg.time?cg.time:level.time), &currentFrame, &junk, &junk, &junk, &junk2, NULL );
10568 #ifdef _DEBUG
10569 					assert( ret ); // this would be pretty bad, the below code seems to assume the call succeeds. -gil
10570 #endif
10571 					strength = G_SaberLockStrength( gent );
10572 					if ( PM_InSaberLockOld( pm->ps->torsoAnim ) )
10573 					{//old locks
10574 						if ( pm->ps->torsoAnim == BOTH_CCWCIRCLELOCK ||
10575 							pm->ps->torsoAnim == BOTH_BF2LOCK )
10576 						{
10577 							curFrame = floor( currentFrame )-strength;
10578 							//drop my frame one
10579 							if ( curFrame <= anim->firstFrame )
10580 							{//I won!  Break out
10581 								PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10582 								return qtrue;
10583 							}
10584 							else
10585 							{
10586 								PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10587 								remaining = curFrame-anim->firstFrame;
10588 								if ( d_saberCombat->integer )
10589 								{
10590 									Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10591 								}
10592 							}
10593 						}
10594 						else
10595 						{
10596 							curFrame = ceil( currentFrame )+strength;
10597 							//advance my frame one
10598 							if ( curFrame >= anim->firstFrame+anim->numFrames )
10599 							{//I won!  Break out
10600 								PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10601 								return qtrue;
10602 							}
10603 							else
10604 							{
10605 								PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10606 								remaining = anim->firstFrame+anim->numFrames-curFrame;
10607 								if ( d_saberCombat->integer )
10608 								{
10609 									Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10610 								}
10611 							}
10612 						}
10613 					}
10614 					else
10615 					{//new locks
10616 						if ( G_CheckIncrementLockAnim( pm->ps->torsoAnim, SABERLOCK_WIN ) )
10617 						{
10618 							curFrame = ceil( currentFrame )+strength;
10619 							//advance my frame one
10620 							if ( curFrame >= anim->firstFrame+anim->numFrames )
10621 							{//I won!  Break out
10622 								PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10623 								return qtrue;
10624 							}
10625 							else
10626 							{
10627 								PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10628 								remaining = anim->firstFrame+anim->numFrames-curFrame;
10629 								if ( d_saberCombat->integer )
10630 								{
10631 									Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10632 								}
10633 							}
10634 						}
10635 						else
10636 						{
10637 							curFrame = floor( currentFrame )-strength;
10638 							//drop my frame one
10639 							if ( curFrame <= anim->firstFrame )
10640 							{//I won!  Break out
10641 								PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength );
10642 								return qtrue;
10643 							}
10644 							else
10645 							{
10646 								PM_SetAnimFrame( gent, curFrame, qtrue, qtrue );
10647 								remaining = curFrame-anim->firstFrame;
10648 								if ( d_saberCombat->integer )
10649 								{
10650 									Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining );
10651 								}
10652 							}
10653 						}
10654 					}
10655 					if ( !Q_irand( 0, 2 ) )
10656 					{
10657 						if ( pm->ps->clientNum < MAX_CLIENTS )
10658 						{
10659 							if ( !Q_irand( 0, 3 ) )
10660 							{
10661 								PM_AddEvent( EV_JUMP );
10662 							}
10663 							else
10664 							{
10665 								PM_AddEvent( Q_irand( EV_PUSHED1, EV_PUSHED3 ) );
10666 							}
10667 						}
10668 						else
10669 						{
10670 							if ( gent->NPC && gent->NPC->blockedSpeechDebounceTime < level.time )
10671 							{
10672 								switch ( Q_irand( 0, 3 ) )
10673 								{
10674 								case 0:
10675 									PM_AddEvent( EV_JUMP );
10676 									break;
10677 								case 1:
10678 									PM_AddEvent( Q_irand( EV_ANGER1, EV_ANGER3 ) );
10679 									gent->NPC->blockedSpeechDebounceTime = level.time + 3000;
10680 									break;
10681 								case 2:
10682 									PM_AddEvent( Q_irand( EV_TAUNT1, EV_TAUNT3 ) );
10683 									gent->NPC->blockedSpeechDebounceTime = level.time + 3000;
10684 									break;
10685 								case 3:
10686 									PM_AddEvent( Q_irand( EV_GLOAT1, EV_GLOAT3 ) );
10687 									gent->NPC->blockedSpeechDebounceTime = level.time + 3000;
10688 									break;
10689 								}
10690 							}
10691 						}
10692 					}
10693 				}
10694 				else
10695 				{
10696 					return qfalse;
10697 				}
10698 
10699 				if( ValidAnimFileIndex( genemy->client->clientInfo.animFileIndex ) )
10700 				{
10701 					animation_t *anim;
10702 					anim = &level.knownAnimFileSets[genemy->client->clientInfo.animFileIndex].animations[genemy->client->ps.torsoAnim];
10703 					/*
10704 					float		currentFrame, junk2;
10705 					int			junk;
10706 
10707 					gi.G2API_GetBoneAnimIndex( &genemy->ghoul2[genemy->playerModel], genemy->lowerLumbarBone, (cg.time?cg.time:level.time), &currentFrame, &junk, &junk, &junk, &junk2, NULL );
10708 					*/
10709 
10710 					if ( !Q_irand( 0, 2 ) )
10711 					{
10712 						switch ( Q_irand( 0, 3 ) )
10713 						{
10714 						case 0:
10715 							G_AddEvent( genemy, EV_PAIN, floor((float)genemy->health/genemy->max_health*100.0f) );
10716 							break;
10717 						case 1:
10718 							G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 );
10719 							break;
10720 						case 2:
10721 							G_AddVoiceEvent( genemy, Q_irand( EV_CHOKE1, EV_CHOKE3 ), 500 );
10722 							break;
10723 						case 3:
10724 							G_AddVoiceEvent( genemy, EV_PUSHFAIL, 2000 );
10725 							break;
10726 						}
10727 					}
10728 
10729 					if ( PM_InSaberLockOld( genemy->client->ps.torsoAnim ) )
10730 					{
10731 						if ( genemy->client->ps.torsoAnim == BOTH_CCWCIRCLELOCK ||
10732 							genemy->client->ps.torsoAnim == BOTH_BF2LOCK )
10733 						{
10734 							PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue );
10735 						}
10736 						else
10737 						{
10738 							PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue );
10739 						}
10740 					}
10741 					else
10742 					{//new locks
10743 						//???
10744 						if ( G_CheckIncrementLockAnim( genemy->client->ps.torsoAnim, SABERLOCK_LOSE ) )
10745 						{
10746 							PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue );
10747 						}
10748 						else
10749 						{
10750 							PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue );
10751 						}
10752 					}
10753 				}
10754 			}
10755 		}
10756 		else
10757 		{//FIXME: other ways out of a saberlock?
10758 			//force-push?  (requires more force power?)
10759 			//kick?  (requires anim ... hit jump key?)
10760 			//roll?
10761 			//backflip?
10762 		}
10763 	}
10764 	else
10765 	{//something broke us out of it
10766 		if ( gent->painDebounceTime > level.time && genemy->painDebounceTime > level.time )
10767 		{
10768 			PM_SaberLockBreak( gent, genemy, LOCK_DRAW, 0 );
10769 		}
10770 		else if ( gent->painDebounceTime > level.time )
10771 		{
10772 			PM_SaberLockBreak( genemy, gent, LOCK_VICTORY, 0 );
10773 		}
10774 		else if ( genemy->painDebounceTime > level.time )
10775 		{
10776 			PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, 0 );
10777 		}
10778 		else
10779 		{
10780 			PM_SaberLockBreak( gent, genemy, LOCK_STALEMATE, 0 );
10781 		}
10782 	}
10783 	return qtrue;
10784 }
10785 
G_EnemyInKickRange(gentity_t * self,gentity_t * enemy)10786 qboolean G_EnemyInKickRange( gentity_t *self, gentity_t *enemy )
10787 {
10788 	if ( !self || !enemy )
10789 	{
10790 		return qfalse;
10791 	}
10792 	if ( fabs(self->currentOrigin[2]-enemy->currentOrigin[2]) < 32 )
10793 	{//generally at same height
10794 		if ( DistanceHorizontal( self->currentOrigin, enemy->currentOrigin ) <= (STAFF_KICK_RANGE+8.0f+(self->maxs[0]*1.5f)+(enemy->maxs[0]*1.5f)) )
10795 		{//within kicking range!
10796 			return qtrue;
10797 		}
10798 	}
10799 	return qfalse;
10800 }
10801 
G_CanKickEntity(gentity_t * self,gentity_t * target)10802 qboolean G_CanKickEntity( gentity_t *self, gentity_t *target )
10803 {
10804 	if ( target && target->client
10805 		&& !PM_InKnockDown( &target->client->ps )
10806 		&& G_EnemyInKickRange( self, target ) )
10807 	{
10808 		return qtrue;
10809 	}
10810 	return qfalse;
10811 }
10812 
PM_GroundDistance(void)10813 float PM_GroundDistance(void)
10814 {
10815 	trace_t tr;
10816 	vec3_t down;
10817 
10818 	VectorCopy(pm->ps->origin, down);
10819 
10820 	down[2] -= 4096;
10821 
10822 	pm->trace(&tr, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0);
10823 
10824 	VectorSubtract(pm->ps->origin, tr.endpos, down);
10825 
10826 	return VectorLength(down);
10827 }
10828 
G_GroundDistance(gentity_t * self)10829 float G_GroundDistance(gentity_t *self)
10830 {
10831 	if ( !self )
10832 	{//wtf?!!
10833 		return Q3_INFINITE;
10834 	}
10835 	trace_t tr;
10836 	vec3_t down;
10837 
10838 	VectorCopy(self->currentOrigin, down);
10839 
10840 	down[2] -= 4096;
10841 
10842 	gi.trace(&tr, self->currentOrigin, self->mins, self->maxs, down, self->s.number, self->clipmask, (EG2_Collision)0, 0);
10843 
10844 	VectorSubtract(self->currentOrigin, tr.endpos, down);
10845 
10846 	return VectorLength(down);
10847 }
10848 
G_PickAutoKick(gentity_t * self,gentity_t * enemy,qboolean storeMove)10849 saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean storeMove )
10850 {
10851 	saberMoveName_t kickMove = LS_NONE;
10852 	if ( !self || !self->client )
10853 	{
10854 		return LS_NONE;
10855 	}
10856 	if ( !enemy )
10857 	{
10858 		return LS_NONE;
10859 	}
10860 	vec3_t	v_fwd, v_rt, enemyDir, fwdAngs = {0,self->client->ps.viewangles[YAW],0};
10861 	VectorSubtract( enemy->currentOrigin, self->currentOrigin, enemyDir );
10862 	VectorNormalize( enemyDir );//not necessary, I guess, but doesn't happen often
10863 	AngleVectors( fwdAngs, v_fwd, v_rt, NULL );
10864 	float fDot = DotProduct( enemyDir, v_fwd );
10865 	float rDot = DotProduct( enemyDir, v_rt );
10866 	if ( fabs( rDot ) > 0.5f && fabs( fDot ) < 0.5f )
10867 	{//generally to one side
10868 		if ( rDot > 0 )
10869 		{//kick right
10870 			kickMove = LS_KICK_R;
10871 		}
10872 		else
10873 		{//kick left
10874 			kickMove = LS_KICK_L;
10875 		}
10876 	}
10877 	else if ( fabs( fDot ) > 0.5f && fabs( rDot ) < 0.5f )
10878 	{//generally in front or behind us
10879 		if ( fDot > 0 )
10880 		{//kick fwd
10881 			kickMove = LS_KICK_F;
10882 		}
10883 		else
10884 		{//kick back
10885 			kickMove = LS_KICK_B;
10886 		}
10887 	}
10888 	else
10889 	{//diagonal to us, kick would miss
10890 	}
10891 	if ( kickMove != LS_NONE )
10892 	{//have a valid one to do
10893 		if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE )
10894 		{//if in air, convert kick to an in-air kick
10895 			float gDist = G_GroundDistance( self );
10896 			//let's only allow air kicks if a certain distance from the ground
10897 			//it's silly to be able to do them right as you land.
10898 			//also looks wrong to transition from a non-complete flip anim...
10899 			if ((!PM_FlippingAnim( self->client->ps.legsAnim ) || self->client->ps.legsAnimTimer <= 0) &&
10900 				gDist > 64.0f && //strict minimum
10901 				gDist > (-self->client->ps.velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
10902 				)
10903 			{
10904 				switch ( kickMove )
10905 				{
10906 				case LS_KICK_F:
10907 					kickMove = LS_KICK_F_AIR;
10908 					break;
10909 				case LS_KICK_B:
10910 					kickMove = LS_KICK_B_AIR;
10911 					break;
10912 				case LS_KICK_R:
10913 					kickMove = LS_KICK_R_AIR;
10914 					break;
10915 				case LS_KICK_L:
10916 					kickMove = LS_KICK_L_AIR;
10917 					break;
10918 				default: //oh well, can't do any other kick move while in-air
10919 					kickMove = LS_NONE;
10920 					break;
10921 				}
10922 			}
10923 			else
10924 			{//leave it as a normal kick unless we're too high up
10925 				if ( gDist > 128.0f || self->client->ps.velocity[2] >= 0 )
10926 				{ //off ground, but too close to ground
10927 					kickMove = LS_NONE;
10928 				}
10929 			}
10930 		}
10931 		if ( storeMove )
10932 		{
10933 			self->client->ps.saberMoveNext = kickMove;
10934 		}
10935 	}
10936 	return kickMove;
10937 }
10938 
PM_PickAutoKick(gentity_t * enemy)10939 saberMoveName_t PM_PickAutoKick( gentity_t *enemy )
10940 {
10941 	return G_PickAutoKick( pm->gent, enemy, qfalse );
10942 }
10943 
G_PickAutoMultiKick(gentity_t * self,qboolean allowSingles,qboolean storeMove)10944 saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qboolean storeMove )
10945 {
10946 	gentity_t	*ent;
10947 	gentity_t	*entityList[MAX_GENTITIES];
10948 	vec3_t		mins, maxs;
10949 	int			i, e;
10950 	int			radius = ((self->maxs[0]*1.5f)+(self->maxs[0]*1.5f)+STAFF_KICK_RANGE+24.0f);//a little wide on purpose
10951 	vec3_t		center;
10952 	saberMoveName_t kickMove, bestKick = LS_NONE;
10953 	float		distToEnt, bestDistToEnt = Q3_INFINITE;
10954 	gentity_t	*bestEnt = NULL;
10955 	int			enemiesFront = 0;
10956 	int			enemiesBack = 0;
10957 	int			enemiesRight = 0;
10958 	int			enemiesLeft = 0;
10959 	int			enemiesSpin = 0;
10960 
10961 	if ( !self || !self->client )
10962 	{
10963 		return LS_NONE;
10964 	}
10965 
10966 	VectorCopy( self->currentOrigin, center );
10967 
10968 	for ( i = 0 ; i < 3 ; i++ )
10969 	{
10970 		mins[i] = center[i] - radius;
10971 		maxs[i] = center[i] + radius;
10972 	}
10973 
10974 	int numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
10975 
10976 	for ( e = 0 ; e < numListedEntities ; e++ )
10977 	{
10978 		ent = entityList[ e ];
10979 
10980 		if (ent == self)
10981 			continue;
10982 		if (ent->owner == self)
10983 			continue;
10984 		if ( !(ent->inuse) )
10985 			continue;
10986 		//not a client?
10987 		if ( !ent->client )
10988 			continue;
10989 		//ally?
10990 		if ( ent->client->playerTeam == self->client->playerTeam )
10991 			continue;
10992 		//dead?
10993 		if ( ent->health <= 0 )
10994 			continue;
10995 		//too far?
10996 		distToEnt = DistanceSquared( ent->currentOrigin, center );
10997 		if ( distToEnt > (radius*radius) )
10998 			continue;
10999 		kickMove = G_PickAutoKick( self, ent, qfalse );
11000 		if ( kickMove == LS_KICK_F_AIR
11001 			&& kickMove == LS_KICK_B_AIR
11002 			&& kickMove == LS_KICK_R_AIR
11003 			&& kickMove == LS_KICK_L_AIR )
11004 		{//in air?  Can't do multikicks
11005 		}
11006 		else
11007 		{
11008 			switch ( kickMove )
11009 			{
11010 			case LS_KICK_F:
11011 				enemiesFront++;
11012 				break;
11013 			case LS_KICK_B:
11014 				enemiesBack++;
11015 				break;
11016 			case LS_KICK_R:
11017 				enemiesRight++;
11018 				break;
11019 			case LS_KICK_L:
11020 				enemiesLeft++;
11021 				break;
11022 			default:
11023 				enemiesSpin++;
11024 				break;
11025 			}
11026 		}
11027 		if ( allowSingles )
11028 		{
11029 			if ( kickMove != LS_NONE
11030 				&& distToEnt < bestDistToEnt )
11031 			{
11032 				bestKick = kickMove;
11033 				bestEnt = ent;
11034 			}
11035 		}
11036 	}
11037 	kickMove = LS_NONE;
11038 	if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE )
11039 	{//can't do the multikicks in air
11040 		if ( enemiesFront && enemiesBack
11041 			&& (enemiesFront+enemiesBack)-(enemiesRight+enemiesLeft)>1 )
11042 		{//more enemies in front/back than left/right
11043 			kickMove = LS_KICK_BF;
11044 		}
11045 		else if ( enemiesRight && enemiesLeft
11046 			&& (enemiesRight+enemiesLeft)-(enemiesFront+enemiesBack)>1 )
11047 		{//more enemies on left & right than front/back
11048 			kickMove = LS_KICK_RL;
11049 		}
11050 		else if ( (enemiesFront || enemiesBack) && (enemiesRight || enemiesLeft) )
11051 		{//at least 2 enemies around us, not aligned
11052 			kickMove = LS_KICK_S;
11053 		}
11054 		else if ( enemiesSpin > 1 )
11055 		{//at least 2 enemies around us, not aligned
11056 			kickMove = LS_KICK_S;
11057 		}
11058 	}
11059 	if ( kickMove == LS_NONE
11060 		&& bestKick != LS_NONE )
11061 	{//no good multi-kick move, but we do have a nice single-kick we found
11062 		kickMove = bestKick;
11063 		//get mad at him so he knows he's being targetted
11064 		if ( (self->s.number < MAX_CLIENTS||G_ControlledByPlayer(self))
11065 			&& bestEnt != NULL )
11066 		{//player
11067 			G_SetEnemy( self, bestEnt );
11068 		}
11069 	}
11070 	if ( kickMove != LS_NONE )
11071 	{
11072 		if ( storeMove )
11073 		{
11074 			self->client->ps.saberMoveNext = kickMove;
11075 		}
11076 	}
11077 	return kickMove;
11078 }
11079 
PM_PickAutoMultiKick(qboolean allowSingles)11080 qboolean PM_PickAutoMultiKick( qboolean allowSingles )
11081 {
11082 	saberMoveName_t kickMove = G_PickAutoMultiKick( pm->gent, allowSingles, qfalse );
11083 	if ( kickMove != LS_NONE )
11084 	{
11085 		PM_SetSaberMove( kickMove );
11086 		return qtrue;
11087 	}
11088 	return qfalse;
11089 }
11090 
PM_SaberThrowable(void)11091 qboolean PM_SaberThrowable( void )
11092 {
11093 	//ugh, hard-coding this is bad...
11094 	if ( pm->ps->saberAnimLevel == SS_STAFF )
11095 	{
11096 		return qfalse;
11097 	}
11098 
11099 	if ( !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE) )
11100 	{//yes, this saber is always throwable
11101 		return qtrue;
11102 	}
11103 
11104 	//saber is not normally throwable
11105 	if ( (pm->ps->saber[0].saberFlags&SFL_SINGLE_BLADE_THROWABLE) )
11106 	{//it is throwable if only one blade is on
11107 		if ( pm->ps->saber[0].numBlades > 1 )
11108 		{//it has more than one blade
11109 			int numBladesActive = 0;
11110 			for ( int i = 0; i < pm->ps->saber[0].numBlades; i++ )
11111 			{
11112 				if ( pm->ps->saber[0].blade[i].active )
11113 				{
11114 					numBladesActive++;
11115 				}
11116 			}
11117 			if ( numBladesActive == 1 )
11118 			{//only 1 blade is on
11119 				return qtrue;
11120 			}
11121 		}
11122 	}
11123 	//nope, can't throw it
11124 	return qfalse;
11125 }
11126 
PM_CheckAltKickAttack(void)11127 qboolean PM_CheckAltKickAttack( void )
11128 {
11129 	if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)
11130 		&& (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) ||PM_SaberInReturn(pm->ps->saberMove))
11131 		&& (!PM_FlippingAnim(pm->ps->legsAnim)||pm->ps->legsAnimTimer<=250)
11132 		&& (!PM_SaberThrowable())
11133 		&& pm->ps->SaberActive()
11134 		&& !(pm->ps->saber[0].saberFlags&SFL_NO_KICKS)//okay to do kicks with this saber
11135 		&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_KICKS) )//okay to do kicks with this saber
11136 		)
11137 	{
11138 		return qtrue;
11139 	}
11140 	return qfalse;
11141 }
11142 
PM_CheckUpsideDownAttack(void)11143 qboolean PM_CheckUpsideDownAttack( void )
11144 {
11145 	if ( pm->ps->saberMove != LS_READY )
11146 	{
11147 		return qfalse;
11148 	}
11149 	if ( !(pm->cmd.buttons&BUTTON_ATTACK) )
11150 	{
11151 		return qfalse;
11152 	}
11153 	if ( pm->ps->saberAnimLevel < SS_FAST
11154 		|| pm->ps->saberAnimLevel > SS_STRONG )
11155 	{
11156 		return qfalse;
11157 	}
11158 	if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
11159 	{//FIXME: check ranks?
11160 		return qfalse;
11161 	}
11162 	//FIXME: enemy below
11163 	//FIXME: more than 64 off ground
11164 	if ( !g_debugMelee->integer )
11165 	{//hmm, can't get this to work quite the way we wanted... secret move, then!
11166 		return qfalse;
11167 	}
11168 
11169 	switch( pm->ps->legsAnim )
11170 	{
11171 	case BOTH_WALL_RUN_RIGHT_FLIP:
11172 	case BOTH_WALL_RUN_LEFT_FLIP:
11173 	case BOTH_WALL_FLIP_RIGHT:
11174 	case BOTH_WALL_FLIP_LEFT:
11175 	case BOTH_FLIP_BACK1:
11176 	case BOTH_FLIP_BACK2:
11177 	case BOTH_FLIP_BACK3:
11178 	case BOTH_WALL_FLIP_BACK1:
11179 	case BOTH_ALORA_FLIP_B:
11180 	//JKA
11181 	case BOTH_FORCEWALLRUNFLIP_END:
11182 		{
11183 			float animLength = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)pm->ps->legsAnim );
11184 			float elapsedTime = (float)(animLength-pm->ps->legsAnimTimer);
11185 			float midPoint = animLength/2.0f;
11186 			if ( elapsedTime < midPoint-100.0f
11187 				|| elapsedTime > midPoint+100.0f )
11188 			{//only a 200ms window (in middle of anim) of opportunity to do this move in these anims
11189 				return qfalse;
11190 			}
11191 		}
11192 		//NOTE: falls through on purpose
11193 	case BOTH_FLIP_HOLD7:
11194 		pm->ps->pm_flags |= PMF_SLOW_MO_FALL;
11195 		PM_SetSaberMove( LS_UPSIDE_DOWN_ATTACK );
11196 		return qtrue;
11197 		break;
11198 	}
11199 	return qfalse;
11200 }
11201 
PM_SaberMoveOkayForKata(void)11202 qboolean PM_SaberMoveOkayForKata( void )
11203 {
11204 	if ( g_saberNewControlScheme->integer )
11205 	{
11206 		if ( pm->ps->saberMove == LS_READY //not doing anything
11207 			|| PM_SaberInReflect( pm->ps->saberMove ) )//interrupt a projectile blocking move
11208 		{
11209 			return qtrue;
11210 		}
11211 		else
11212 		{
11213 			return qfalse;
11214 		}
11215 	}
11216 	else
11217 	{//old control scheme, allow it to interrupt a start or ready
11218 		if ( pm->ps->saberMove == LS_READY
11219 			|| PM_SaberInReflect( pm->ps->saberMove )//interrupt a projectile blocking move
11220 			|| PM_SaberInStart( pm->ps->saberMove ) )
11221 		{
11222 			return qtrue;
11223 		}
11224 		else
11225 		{
11226 			return qfalse;
11227 		}
11228 	}
11229 }
11230 
PM_CanDoKata(void)11231 qboolean PM_CanDoKata( void )
11232 {
11233 	if ( PM_InSecondaryStyle() )
11234 	{
11235 		return qfalse;
11236 	}
11237 	if ( !pm->ps->saberInFlight//not throwing saber
11238 		&& PM_SaberMoveOkayForKata()
11239 		/*
11240 		&& pm->ps->saberAnimLevel >= SS_FAST//fast, med or strong style
11241 		&& pm->ps->saberAnimLevel <= SS_STRONG//FIXME: Tavion, too?
11242 		*/
11243 		&& pm->ps->groundEntityNum != ENTITYNUM_NONE//not in the air
11244 		&& (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack
11245 		&& pm->cmd.forwardmove >=0 //not moving back (used to be !pm->cmd.forwardmove)
11246 		&& !pm->cmd.rightmove//not moving r/l
11247 		&& pm->cmd.upmove <= 0//not jumping...?
11248 		&& G_TryingKataAttack(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*///holding focus
11249 		&& G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER*/ )//SINGLE_SPECIAL_POWER )// have enough power
11250 	{//FIXME: check rage, etc...
11251 		return qtrue;
11252 	}
11253 	return qfalse;
11254 }
11255 
PM_SaberDroidWeapon(void)11256 void PM_SaberDroidWeapon( void )
11257 {
11258 	// make weapon function
11259 	if ( pm->ps->weaponTime > 0 ) {
11260 		pm->ps->weaponTime -= pml.msec;
11261 		if ( pm->ps->weaponTime <= 0 )
11262 		{
11263 			pm->ps->weaponTime = 0;
11264 		}
11265 	}
11266 
11267 	// Now we react to a block action by the player's lightsaber.
11268 	if ( pm->ps->saberBlocked )
11269 	{
11270 		switch ( pm->ps->saberBlocked )
11271 		{
11272 			case BLOCKED_PARRY_BROKEN:
11273 				PM_SetAnim( pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1,BOTH_PAIN3), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11274 				pm->ps->weaponTime = pm->ps->legsAnimTimer;
11275 				break;
11276 			case BLOCKED_ATK_BOUNCE:
11277 				PM_SetAnim( pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1,BOTH_PAIN3), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11278 				pm->ps->weaponTime = pm->ps->legsAnimTimer;
11279 				break;
11280 			case BLOCKED_UPPER_RIGHT:
11281 			case BLOCKED_UPPER_RIGHT_PROJ:
11282 			case BLOCKED_LOWER_RIGHT:
11283 			case BLOCKED_LOWER_RIGHT_PROJ:
11284 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_TR, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11285 				pm->ps->legsAnimTimer += Q_irand( 200, 1000 );
11286 				pm->ps->weaponTime = pm->ps->legsAnimTimer;
11287 				break;
11288 			case BLOCKED_UPPER_LEFT:
11289 			case BLOCKED_UPPER_LEFT_PROJ:
11290 			case BLOCKED_LOWER_LEFT:
11291 			case BLOCKED_LOWER_LEFT_PROJ:
11292 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_TL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11293 				pm->ps->legsAnimTimer += Q_irand( 200, 1000 );
11294 				pm->ps->weaponTime = pm->ps->legsAnimTimer;
11295 				break;
11296 			case BLOCKED_TOP:
11297 			case BLOCKED_TOP_PROJ:
11298 				PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_T_, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11299 				pm->ps->legsAnimTimer += Q_irand( 200, 1000 );
11300 				pm->ps->weaponTime = pm->ps->legsAnimTimer;
11301 				break;
11302 			default:
11303 				pm->ps->saberBlocked = BLOCKED_NONE;
11304 				break;
11305 		}
11306 
11307 		pm->ps->saberBlocked = BLOCKED_NONE;
11308 		pm->ps->saberBounceMove = LS_NONE;
11309 		pm->ps->weaponstate = WEAPON_READY;
11310 
11311 		// Done with block, so stop these active weapon branches.
11312 		return;
11313 	}
11314 }
11315 
PM_TryGrab(void)11316 void PM_TryGrab( void )
11317 {
11318 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE
11319 		//&& !pm->ps->saberInFlight
11320 		&& pm->ps->weaponTime <= 0 )//< 200 )
11321 	{
11322 		PM_SetAnim( pm, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
11323 		pm->ps->torsoAnimTimer += 200;
11324 		pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11325 		pm->ps->saberMove = pm->ps->saberMoveNext = LS_READY;
11326 		VectorClear( pm->ps->velocity );
11327 		VectorClear( pm->ps->moveDir );
11328 		pm->cmd.rightmove = pm->cmd.forwardmove = pm->cmd.upmove = 0;
11329 		if ( pm->gent )
11330 		{
11331 			pm->gent->painDebounceTime = level.time + pm->ps->torsoAnimTimer;
11332 		}
11333 		pm->ps->SaberDeactivate();
11334 	}
11335 }
11336 
PM_TryAirKick(saberMoveName_t kickMove)11337 void PM_TryAirKick( saberMoveName_t kickMove )
11338 {
11339 	if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
11340 	{//just do it
11341 		PM_SetSaberMove( kickMove );
11342 	}
11343 	else
11344 	{
11345 		float gDist = PM_GroundDistance();
11346 		//let's only allow air kicks if a certain distance from the ground
11347 		//it's silly to be able to do them right as you land.
11348 		//also looks wrong to transition from a non-complete flip anim...
11349 		if ((!PM_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsAnimTimer <= 0) &&
11350 			gDist > 64.0f && //strict minimum
11351 			gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
11352 			)
11353 		{
11354 			PM_SetSaberMove( kickMove );
11355 		}
11356 		else
11357 		{//leave it as a normal kick unless we're too high up
11358 			if ( gDist > 128.0f || pm->ps->velocity[2] >= 0 )
11359 			{ //off ground, but too close to ground
11360 			}
11361 			else
11362 			{//high close enough to ground to do a normal kick, convert it
11363 				switch ( kickMove )
11364 				{
11365 				case LS_KICK_F_AIR:
11366 					PM_SetSaberMove( LS_KICK_F );
11367 					break;
11368 				case LS_KICK_B_AIR:
11369 					PM_SetSaberMove( LS_KICK_B );
11370 					break;
11371 				case LS_KICK_R_AIR:
11372 					PM_SetSaberMove( LS_KICK_R );
11373 					break;
11374 				case LS_KICK_L_AIR:
11375 					PM_SetSaberMove( LS_KICK_L );
11376 					break;
11377 				default:
11378 					break;
11379 				}
11380 			}
11381 		}
11382 	}
11383 }
11384 
PM_CheckKick(void)11385 void PM_CheckKick( void )
11386 {
11387 	if ( !PM_KickMove( pm->ps->saberMove )//not already in a kick
11388 		&& !(pm->ps->pm_flags&PMF_DUCKED)//not ducked
11389 		&& (pm->cmd.upmove >= 0 ) )//not trying to duck
11390 	{//player kicks
11391 		//FIXME: only if FP_SABER_OFFENSE >= 3
11392 		if ( pm->cmd.rightmove )
11393 		{//kick to side
11394 			if ( pm->cmd.rightmove > 0 )
11395 			{//kick right
11396 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11397 					|| pm->cmd.upmove > 0 )
11398 				{
11399 					PM_TryAirKick( LS_KICK_R_AIR );
11400 				}
11401 				else
11402 				{
11403 					PM_SetSaberMove( LS_KICK_R );
11404 				}
11405 			}
11406 			else
11407 			{//kick left
11408 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11409 					|| pm->cmd.upmove > 0 )
11410 				{
11411 					PM_TryAirKick( LS_KICK_L_AIR );
11412 				}
11413 				else
11414 				{
11415 					PM_SetSaberMove( LS_KICK_L );
11416 				}
11417 			}
11418 			pm->cmd.rightmove = 0;
11419 		}
11420 		else if ( pm->cmd.forwardmove )
11421 		{//kick front/back
11422 			if ( pm->cmd.forwardmove > 0 )
11423 			{//kick fwd
11424 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11425 					|| pm->cmd.upmove > 0 )
11426 				{
11427 					PM_TryAirKick( LS_KICK_F_AIR );
11428 				}
11429 				/*
11430 				else if ( pm->ps->weapon == WP_SABER
11431 					&& pm->ps->saberAnimLevel == SS_STAFF
11432 					&& pm->gent
11433 					&& G_CheckEnemyPresence( pm->gent, DIR_FRONT, 64, 0.8f ) )
11434 				{//FIXME: don't jump while doing this move and don't do this move if in air
11435 					PM_SetSaberMove( LS_HILT_BASH );
11436 				}
11437 				*/
11438 				else
11439 				{
11440 					PM_SetSaberMove( LS_KICK_F );
11441 				}
11442 			}
11443 			else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11444 					|| pm->cmd.upmove > 0 )
11445 			{
11446 				PM_TryAirKick( LS_KICK_B_AIR );
11447 			}
11448 			else
11449 			{//kick back
11450 				PM_SetSaberMove( LS_KICK_B );
11451 			}
11452 			pm->cmd.forwardmove = 0;
11453 		}
11454 		else if ( pm->gent
11455 			&& pm->gent->enemy
11456 			&& G_CanKickEntity( pm->gent, pm->gent->enemy ) )
11457 		{//auto-pick?
11458 			if ( /*(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD)
11459 				&& (pm->cmd.buttons&BUTTON_ATTACK)
11460 				&&*/ PM_PickAutoMultiKick( qfalse ) )
11461 			{//kicked!
11462 				if ( pm->ps->saberMove == LS_KICK_RL )
11463 				{//just pull back
11464 					if ( d_slowmodeath->integer > 3 )
11465 					{
11466 						G_StartMatrixEffect( pm->gent, MEF_NO_SPIN, pm->ps->legsAnimTimer+500 );
11467 					}
11468 				}
11469 				else
11470 				{//normal spin
11471 					if ( d_slowmodeath->integer > 3 )
11472 					{
11473 	                    G_StartMatrixEffect( pm->gent, 0, pm->ps->legsAnimTimer+500 );
11474 					}
11475 				}
11476 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11477 					&&( pm->ps->saberMove == LS_KICK_S
11478 						||pm->ps->saberMove == LS_KICK_BF
11479 						||pm->ps->saberMove == LS_KICK_RL ) )
11480 				{//in the air and doing a jump-kick, which is a ground anim, so....
11481 					//cut z velocity...?
11482 					pm->ps->velocity[2] = 0;
11483 				}
11484 				pm->cmd.upmove = 0;
11485 			}
11486 			else
11487 			{
11488 				saberMoveName_t kickMove = PM_PickAutoKick( pm->gent->enemy );
11489 				if ( kickMove != LS_NONE )
11490 				{//Matrix?
11491 					PM_SetSaberMove( kickMove );
11492 					int meFlags = 0;
11493 					switch ( kickMove )
11494 					{
11495 					case LS_KICK_B://just pull back
11496 					case LS_KICK_B_AIR://just pull back
11497 						meFlags = MEF_NO_SPIN;
11498 						break;
11499 					case LS_KICK_L://spin to the left
11500 					case LS_KICK_L_AIR://spin to the left
11501 						meFlags = MEF_REVERSE_SPIN;
11502 						break;
11503 					default:
11504 						break;
11505 					}
11506 					if ( d_slowmodeath->integer > 3 )
11507 					{
11508 						G_StartMatrixEffect( pm->gent, meFlags, pm->ps->legsAnimTimer+500 );
11509 					}
11510 				}
11511 			}
11512 		}
11513 		else
11514 		{
11515 			if ( PM_PickAutoMultiKick( qtrue ) )
11516 			{
11517 				int meFlags = 0;
11518 				switch ( pm->ps->saberMove )
11519 				{
11520 				case LS_KICK_RL://just pull back
11521 				case LS_KICK_B://just pull back
11522 				case LS_KICK_B_AIR://just pull back
11523 					meFlags = MEF_NO_SPIN;
11524 					break;
11525 				case LS_KICK_L://spin to the left
11526 				case LS_KICK_L_AIR://spin to the left
11527 					meFlags = MEF_REVERSE_SPIN;
11528 					break;
11529 				default:
11530 					break;
11531 				}
11532 				if ( d_slowmodeath->integer > 3 )
11533 				{
11534 					G_StartMatrixEffect( pm->gent, meFlags, pm->ps->legsAnimTimer+500 );
11535 				}
11536 				if ( pm->ps->groundEntityNum == ENTITYNUM_NONE
11537 					&&( pm->ps->saberMove == LS_KICK_S
11538 						||pm->ps->saberMove == LS_KICK_BF
11539 						||pm->ps->saberMove == LS_KICK_RL ) )
11540 				{//in the air and doing a jump-kick, which is a ground anim, so....
11541 					//cut z velocity...?
11542 					pm->ps->velocity[2] = 0;
11543 				}
11544 				pm->cmd.upmove = 0;
11545 			}
11546 		}
11547 	}
11548 }
11549 
PM_CheckClearSaberBlock(void)11550 void PM_CheckClearSaberBlock( void )
11551 {
11552 	if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() )
11553 	{//player
11554 		if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ )
11555 		{//blocking a projectile
11556 			if ( pm->ps->forcePowerDebounce[FP_SABER_DEFENSE] < level.time )
11557 			{//block is done or breaking out of it with an attack
11558 				pm->ps->weaponTime = 0;
11559 				pm->ps->saberBlocked = BLOCKED_NONE;
11560 			}
11561 			else if ( (pm->cmd.buttons&BUTTON_ATTACK) )
11562 			{//block is done or breaking out of it with an attack
11563 				pm->ps->weaponTime = 0;
11564 				pm->ps->saberBlocked = BLOCKED_NONE;
11565 			}
11566 		}
11567 		else if ( pm->ps->saberBlocked == BLOCKED_UPPER_LEFT
11568 			&& pm->ps->powerups[PW_SHOCKED] > level.time )
11569 		{//probably blocking lightning
11570 			if ( (pm->cmd.buttons&BUTTON_ATTACK) )
11571 			{//trying to attack
11572 				//allow the attack
11573 				pm->ps->weaponTime = 0;
11574 				pm->ps->saberBlocked = BLOCKED_NONE;
11575 			}
11576 		}
11577 	}
11578 }
11579 
PM_SaberBlocking(void)11580 qboolean PM_SaberBlocking( void )
11581 {
11582 	// Now we react to a block action by the player's lightsaber.
11583 	if ( pm->ps->saberBlocked )
11584 	{
11585 		if ( pm->ps->saberMove > LS_PUTAWAY && pm->ps->saberMove <= LS_A_BL2TR && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN &&
11586 			(pm->ps->saberBlocked < BLOCKED_UPPER_RIGHT_PROJ || pm->ps->saberBlocked > BLOCKED_TOP_PROJ))//&& Q_irand( 0, 2 )
11587 		{//we parried another lightsaber while attacking, so treat it as a bounce
11588 			pm->ps->saberBlocked = BLOCKED_ATK_BOUNCE;
11589 		}
11590 		else if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )//player
11591 		{
11592 			if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ
11593 				&& pm->ps->saberBlocked <= BLOCKED_TOP_PROJ )
11594 			{//blocking a projectile
11595 				if ( (pm->cmd.buttons&BUTTON_ATTACK) )
11596 				{//trying to attack
11597 					if ( pm->ps->saberMove == LS_READY
11598 						|| PM_SaberInReflect( pm->ps->saberMove ) )
11599 					{//not already busy or already in projectile deflection
11600 						//trying to attack during projectile blocking, so do the attack instead
11601 						pm->ps->saberBlocked = BLOCKED_NONE;
11602 						pm->ps->saberBounceMove = LS_NONE;
11603 						pm->ps->weaponstate = WEAPON_READY;
11604 						if ( PM_SaberInReflect( pm->ps->saberMove ) && pm->ps->weaponTime > 0 )
11605 						{//interrupt the current deflection move
11606 							pm->ps->weaponTime = 0;
11607 						}
11608 						return qfalse;
11609 					}
11610 				}
11611 			}
11612 		}
11613 		/*
11614 		else if ( (pm->cmd.buttons&BUTTON_ATTACK)
11615 			&& pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT
11616 			&& pm->ps->saberBlocked <= BLOCKED_TOP
11617 			&& !PM_SaberInKnockaway( pm->ps->saberBounceMove ) )
11618 		{//if hitting attack during a parry (not already a knockaway)
11619 			if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_2 )
11620 			{//have high saber defense, turn the parry into a knockaway?
11621 				//FIXME: this could actually be bad for us...?  Leaves us open
11622 				pm->ps->saberBounceMove = PM_KnockawayForParry( pm->ps->saberBlocked );
11623 			}
11624 		}
11625 		*/
11626 
11627 		if ( pm->ps->saberBlocked != BLOCKED_ATK_BOUNCE )
11628 		{//can't attack for twice whatever your skill level's parry debounce time is
11629 			if ( pm->ps->clientNum == 0 || PM_ControlledByPlayer() )
11630 			{//player
11631 				if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1 )
11632 				{
11633 					pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]];
11634 				}
11635 			}
11636 			else
11637 			{//NPC
11638 				//pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]] * 2;
11639 				if ( pm->gent )
11640 				{
11641 					pm->ps->weaponTime = Jedi_ReCalcParryTime( pm->gent, EVASION_PARRY );
11642 				}
11643 				else
11644 				{//WTF???
11645 					pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]] * 2;
11646 				}
11647 			}
11648 		}
11649 		switch ( pm->ps->saberBlocked )
11650 		{
11651 			case BLOCKED_PARRY_BROKEN:
11652 				//whatever parry we were is in now broken, play the appropriate knocked-away anim
11653 				{
11654 					saberMoveName_t nextMove;
11655 					if ( PM_SaberInBrokenParry( pm->ps->saberBounceMove ) )
11656 					{//already have one...?
11657 						nextMove = (saberMoveName_t)pm->ps->saberBounceMove;
11658 					}
11659 					else
11660 					{
11661 						nextMove = PM_BrokenParryForParry( (saberMoveName_t)pm->ps->saberMove );
11662 					}
11663 					if ( nextMove != LS_NONE )
11664 					{
11665 						PM_SetSaberMove( nextMove );
11666 						pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11667 					}
11668 					else
11669 					{//Maybe in a knockaway?
11670 					}
11671 					//pm->ps->saberBounceMove = LS_NONE;
11672 				}
11673 				break;
11674 			case BLOCKED_ATK_BOUNCE:
11675 				// If there is absolutely no blocked move in the chart, don't even mess with the animation.
11676 				// OR if we are already in a block or parry.
11677 				if ( pm->ps->saberMove >= LS_T1_BR__R/*LS_BOUNCE_TOP*/ )//|| saberMoveData[pm->ps->saberMove].bounceMove == LS_NONE )
11678 				{//an actual bounce?  Other bounces before this are actually transitions?
11679 					pm->ps->saberBlocked = BLOCKED_NONE;
11680 				}
11681 				else if ( PM_SaberInBounce( pm->ps->saberMove ) || !PM_SaberInAttack( pm->ps->saberMove ) )
11682 				{//already in the bounce, go into an attack or transition to ready.. should never get here since can't be blocked in a bounce!
11683 					int nextMove;
11684 
11685 					if ( pm->cmd.buttons & BUTTON_ATTACK )
11686 					{//transition to a new attack
11687 						if ( pm->ps->clientNum && !PM_ControlledByPlayer() )
11688 						{//NPC
11689 							nextMove = saberMoveData[pm->ps->saberMove].chain_attack;
11690 						}
11691 						else
11692 						{//player
11693 							int newQuad = PM_SaberMoveQuadrantForMovement( &pm->cmd );
11694 							while ( newQuad == saberMoveData[pm->ps->saberMove].startQuad )
11695 							{//player is still in same attack quad, don't repeat that attack because it looks bad,
11696 								//FIXME: try to pick one that might look cool?
11697 								newQuad = Q_irand( Q_BR, Q_BL );
11698 								//FIXME: sanity check, just in case?
11699 							}//else player is switching up anyway, take the new attack dir
11700 							nextMove = transitionMove[saberMoveData[pm->ps->saberMove].startQuad][newQuad];
11701 						}
11702 					}
11703 					else
11704 					{//return to ready
11705 						if ( pm->ps->clientNum && !PM_ControlledByPlayer() )
11706 						{//NPC
11707 							nextMove = saberMoveData[pm->ps->saberMove].chain_idle;
11708 						}
11709 						else
11710 						{//player
11711 							if ( saberMoveData[pm->ps->saberMove].startQuad == Q_T )
11712 							{
11713 								nextMove = LS_R_BL2TR;
11714 							}
11715 							else if ( saberMoveData[pm->ps->saberMove].startQuad < Q_T )
11716 							{
11717 								nextMove = LS_R_TL2BR+(saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad-Q_BR);
11718 							}
11719 							else// if ( saberMoveData[pm->ps->saberMove].startQuad > Q_T )
11720 							{
11721 								nextMove = LS_R_BR2TL+(saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad-Q_TL);
11722 							}
11723 						}
11724 					}
11725 					PM_SetSaberMove( (saberMoveName_t)nextMove );
11726 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11727 				}
11728 				else
11729 				{//start the bounce move
11730 					saberMoveName_t bounceMove;
11731 					if ( pm->ps->saberBounceMove != LS_NONE )
11732 					{
11733 						bounceMove = (saberMoveName_t)pm->ps->saberBounceMove;
11734 					}
11735 					else
11736 					{
11737 						bounceMove = PM_SaberBounceForAttack( (saberMoveName_t)pm->ps->saberMove );
11738 					}
11739 					PM_SetSaberMove( bounceMove );
11740 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11741 				}
11742 				//clear the saberBounceMove
11743 				//pm->ps->saberBounceMove = LS_NONE;
11744 
11745 				if (cg_debugSaber.integer>=2)
11746 				{
11747 					Com_Printf("Saber Block: Bounce\n");
11748 				}
11749 				break;
11750 			case BLOCKED_UPPER_RIGHT:
11751 				if ( pm->ps->saberBounceMove != LS_NONE )
11752 				{
11753 					PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11754 					//pm->ps->saberBounceMove = LS_NONE;
11755 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11756 				}
11757 				else
11758 				{
11759 					PM_SetSaberMove( LS_PARRY_UR );
11760 				}
11761 
11762 				if ( cg_debugSaber.integer >= 2 )
11763 				{
11764 					Com_Printf( "Saber Block: Parry UR\n" );
11765 				}
11766 				break;
11767 			case BLOCKED_UPPER_RIGHT_PROJ:
11768 				PM_SetSaberMove( LS_REFLECT_UR );
11769 
11770 				if ( cg_debugSaber.integer >= 2 )
11771 				{
11772 					Com_Printf("Saber Block: Deflect UR\n");
11773 				}
11774 				break;
11775 			case BLOCKED_UPPER_LEFT:
11776 				if ( pm->ps->saberBounceMove != LS_NONE )
11777 				{
11778 					PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11779 					//pm->ps->saberBounceMove = LS_NONE;
11780 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11781 				}
11782 				else
11783 				{
11784 					PM_SetSaberMove( LS_PARRY_UL );
11785 				}
11786 
11787 				if ( cg_debugSaber.integer >= 2 )
11788 				{
11789 					Com_Printf( "Saber Block: Parry UL\n" );
11790 				}
11791 				break;
11792 			case BLOCKED_UPPER_LEFT_PROJ:
11793 				PM_SetSaberMove( LS_REFLECT_UL );
11794 
11795 				if ( cg_debugSaber.integer >= 2 )
11796 				{
11797 					Com_Printf("Saber Block: Deflect UL\n");
11798 				}
11799 				break;
11800 			case BLOCKED_LOWER_RIGHT:
11801 				if ( pm->ps->saberBounceMove != LS_NONE )
11802 				{
11803 					PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11804 					//pm->ps->saberBounceMove = LS_NONE;
11805 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11806 				}
11807 				else
11808 				{
11809 					PM_SetSaberMove( LS_PARRY_LR );
11810 				}
11811 
11812 				if ( cg_debugSaber.integer >= 2 )
11813 				{
11814 					Com_Printf("Saber Block: Parry LR\n");
11815 				}
11816 				break;
11817 			case BLOCKED_LOWER_RIGHT_PROJ:
11818 				PM_SetSaberMove( LS_REFLECT_LR );
11819 
11820 				if ( cg_debugSaber.integer >= 2 )
11821 				{
11822 					Com_Printf("Saber Block: Deflect LR\n");
11823 				}
11824 				break;
11825 			case BLOCKED_LOWER_LEFT:
11826 				if ( pm->ps->saberBounceMove != LS_NONE )
11827 				{
11828 					PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11829 					//pm->ps->saberBounceMove = LS_NONE;
11830 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11831 				}
11832 				else
11833 				{
11834 					PM_SetSaberMove( LS_PARRY_LL );
11835 				}
11836 
11837 				if ( cg_debugSaber.integer >= 2 )
11838 				{
11839 					Com_Printf("Saber Block: Parry LL\n");
11840 				}
11841 				break;
11842 			case BLOCKED_LOWER_LEFT_PROJ:
11843 				PM_SetSaberMove( LS_REFLECT_LL);
11844 
11845 				if ( cg_debugSaber.integer >= 2 )
11846 				{
11847 					Com_Printf("Saber Block: Deflect LL\n");
11848 				}
11849 				break;
11850 			case BLOCKED_TOP:
11851 				if ( pm->ps->saberBounceMove != LS_NONE )
11852 				{
11853 					PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove );
11854 					//pm->ps->saberBounceMove = LS_NONE;
11855 					pm->ps->weaponTime = pm->ps->torsoAnimTimer;
11856 				}
11857 				else
11858 				{
11859 					PM_SetSaberMove( LS_PARRY_UP );
11860 				}
11861 
11862 				if ( cg_debugSaber.integer >= 2 )
11863 				{
11864 					Com_Printf("Saber Block: Parry Top\n");
11865 				}
11866 				break;
11867 			case BLOCKED_TOP_PROJ:
11868 				PM_SetSaberMove( LS_REFLECT_UP );
11869 
11870 				if ( cg_debugSaber.integer >= 2 )
11871 				{
11872 					Com_Printf("Saber Block: Deflect Top\n");
11873 				}
11874 				break;
11875 			default:
11876 				pm->ps->saberBlocked = BLOCKED_NONE;
11877 				break;
11878 		}
11879 
11880 		// Charging is like a lead-up before attacking again.  This is an appropriate use, or we can create a new weaponstate for blocking
11881 		pm->ps->saberBounceMove = LS_NONE;
11882 		pm->ps->weaponstate = WEAPON_READY;
11883 
11884 		// Done with block, so stop these active weapon branches.
11885 		return qtrue;
11886 	}
11887 	return qfalse;
11888 }
11889 
PM_NPCCheckAttackRoll(void)11890 qboolean PM_NPCCheckAttackRoll( void )
11891 {
11892 	if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()//NPC
11893 		&& pm->gent
11894 		&& pm->gent->NPC
11895 		//&& Q_irand(-3,pm->gent->NPC->rank)>RANK_CREWMAN)
11896 		&& ( pm->gent->NPC->rank > RANK_CREWMAN && !Q_irand(0,3-g_spskill->integer) )
11897 		&& pm->gent->enemy
11898 		&& fabs(pm->gent->enemy->currentOrigin[2]-pm->ps->origin[2])<32
11899 		&& DistanceHorizontalSquared(pm->gent->enemy->currentOrigin, pm->ps->origin ) < (128.0f*128.0f)
11900 		&& InFOV( pm->gent->enemy->currentOrigin, pm->ps->origin, pm->ps->viewangles, 30, 90 ) )
11901 	{//stab!
11902 		return qtrue;
11903 	}
11904 	return qfalse;
11905 }
11906 /*
11907 =================
11908 PM_WeaponLightsaber
11909 
11910 Consults a chart to choose what to do with the lightsaber.
11911 While this is a little different than the Quake 3 code, there is no clean way of using the Q3 code for this kind of thing.
11912 =================
11913 */
11914 // Ultimate goal is to set the sabermove to the proper next location
11915 // Note that if the resultant animation is NONE, then the animation is essentially "idle", and is set in WP_TorsoAnim
PM_WeaponLightsaber(void)11916 void PM_WeaponLightsaber(void)
11917 {
11918 	int			addTime;
11919 	qboolean	delayed_fire = qfalse, animLevelOverridden = qfalse;
11920 	int			anim=-1;
11921 	int			curmove, newmove=LS_NONE;
11922 
11923 	if ( pm->gent
11924 		&& pm->gent->client
11925 		&& pm->gent->client->NPC_class == CLASS_SABER_DROID )
11926 	{//Saber droid does it's own attack logic
11927 		PM_SaberDroidWeapon();
11928 		return;
11929 	}
11930 
11931 	// don't allow attack until all buttons are up
11932 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
11933 		return;
11934 	}
11935 
11936 	// check for dead player
11937 	if ( pm->ps->stats[STAT_HEALTH] <= 0 )
11938 	{
11939 		if ( pm->gent )
11940 		{
11941 			pm->gent->s.loopSound = 0;
11942 		}
11943 		return;
11944 	}
11945 
11946 	// make weapon function
11947 	if ( pm->ps->weaponTime > 0 ) {
11948 		pm->ps->weaponTime -= pml.msec;
11949 		if ( pm->ps->weaponTime <= 0 )
11950 		{
11951 			pm->ps->weaponTime = 0;
11952 		}
11953 	}
11954 
11955 	if ( pm->ps->stats[STAT_WEAPONS]&(1<<WP_SCEPTER)
11956 		&& !pm->ps->dualSabers
11957 		&& pm->gent
11958 		&& pm->gent->weaponModel[1] )
11959 	{//holding scepter in left hand, use dual style
11960 		pm->ps->saberAnimLevel = SS_DUAL;
11961 		animLevelOverridden = qtrue;
11962 	}
11963 	else if ( pm->ps->saber[0].singleBladeStyle != SS_NONE//SaberStaff()
11964 		&& !pm->ps->dualSabers
11965 		&& pm->ps->saber[0].blade[0].active
11966 		&& !pm->ps->saber[0].blade[1].active )
11967 	{//using a staff, but only with first blade turned on, so use is as a normal saber...?
11968 		//override so that single-blade staff must be used with specified style
11969 		pm->ps->saberAnimLevel = pm->ps->saber[0].singleBladeStyle;//SS_STRONG;
11970 		animLevelOverridden = qtrue;
11971 	}
11972 	else if ( pm->gent
11973 		&& cg.saberAnimLevelPending != pm->ps->saberAnimLevel
11974 		&& WP_SaberStyleValidForSaber( pm->gent, cg.saberAnimLevelPending ) )
11975 	{//go ahead and use the cg.saberAnimLevelPending below
11976 		animLevelOverridden = qfalse;
11977 	}
11978 	else if ( pm->gent
11979 		&& ( WP_SaberStyleValidForSaber( pm->gent, pm->ps->saberAnimLevel )
11980 			|| WP_UseFirstValidSaberStyle( pm->gent, &pm->ps->saberAnimLevel ) ) )
11981 	{//style we are using is not valid, switched us to a valid one
11982 		animLevelOverridden = qtrue;
11983 	}
11984 	/*
11985 	else if ( pm->ps->saber[0].Active()
11986 		&& pm->ps->saber[0].stylesAllowed )
11987 	{//one of the sabers I'm using forces me to use one of a set of styles
11988 		if ( !(pm->ps->saber[0].stylesAllowed&(1<<pm->ps->saberAnimLevel)) )
11989 		{//I'm not currently using a valid one
11990 			for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ )
11991 			{//loop through and use the first valid one
11992 				if ( (pm->ps->saber[0].stylesAllowed&(1<<styleNum)) )
11993 				{//found one we can use
11994 					pm->ps->saberAnimLevel = styleNum;
11995 					animLevelOverridden = qtrue;
11996 				}
11997 			}
11998 		}
11999 	}
12000 	*/
12001 	else if ( pm->ps->dualSabers )
12002 	{
12003 		/*
12004 		if ( pm->ps->saber[1].Active()
12005 			&& pm->ps->saber[1].stylesAllowed )
12006 		{//one of the sabers I'm using forces me to use one of a set of styles
12007 			if ( !(pm->ps->saber[1].stylesAllowed&(1<<pm->ps->saberAnimLevel)) )
12008 			{//I'm not currently using a valid one
12009 				for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ )
12010 				{//loop through and use the first valid one
12011 					if ( (pm->ps->saber[1].stylesAllowed&(1<<styleNum)) )
12012 					{//found one we can use
12013 						pm->ps->saberAnimLevel = styleNum;
12014 						animLevelOverridden = qtrue;
12015 					}
12016 				}
12017 			}
12018 		}
12019 		else*/ if ( pm->ps->saber[1].Active() )
12020 		{//if second saber is on, must use dual style
12021 			pm->ps->saberAnimLevel = SS_DUAL;
12022 			animLevelOverridden = qtrue;
12023 		}
12024 		else if ( pm->ps->saber[0].Active() )
12025 		{//with only one saber on, use fast style
12026 			pm->ps->saberAnimLevel = SS_FAST;
12027 			animLevelOverridden = qtrue;
12028 		}
12029 	}
12030 	if ( !animLevelOverridden )
12031 	{
12032 		if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
12033  			&& cg.saberAnimLevelPending > SS_NONE
12034 			&& cg.saberAnimLevelPending != pm->ps->saberAnimLevel )
12035 		{
12036 			if ( !PM_SaberInStart( pm->ps->saberMove )
12037 				&& !PM_SaberInTransition( pm->ps->saberMove )
12038 				&& !PM_SaberInAttack( pm->ps->saberMove ) )
12039 			{//don't allow changes when in the middle of an attack set...(or delay the change until it's done)
12040 				pm->ps->saberAnimLevel = cg.saberAnimLevelPending;
12041 			}
12042 		}
12043 	}
12044 	else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
12045 	{//if overrid the player's saberAnimLevel, let the cgame know
12046 		cg.saberAnimLevelPending = pm->ps->saberAnimLevel;
12047 	}
12048 	/*
12049 	if ( PM_InForceGetUp( pm->ps ) )
12050 	{//if mostly up, can start attack
12051 		if ( pm->ps->torsoAnimTimer > 800 )
12052 		{//not up enough yet
12053 			// make weapon function
12054 			if ( pm->ps->weaponTime > 0 ) {
12055 				pm->ps->weaponTime -= pml.msec;
12056 				if ( pm->ps->weaponTime <= 0 )
12057 				{
12058 					pm->ps->weaponTime = 0;
12059 				}
12060 			}
12061 			return;
12062 		}
12063 		else
12064 		{
12065 			if ( pm->cmd.buttons & BUTTON_ATTACK )
12066 			{//let an attack interrupt the torso part of this force getup
12067 				pm->ps->weaponTime = 0;
12068 			}
12069 		}
12070 	}
12071 	else
12072 	*/
12073 	if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ))
12074 	{//in knockdown
12075 		if ( pm->ps->legsAnim == BOTH_ROLL_F
12076 			&& pm->ps->legsAnimTimer <= 250 )
12077 		{
12078 			if ( (pm->cmd.buttons&BUTTON_ATTACK)
12079 				|| PM_NPCCheckAttackRoll() )
12080 			{
12081 				if ( G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )
12082 				{
12083 					if ( !(pm->ps->saber[0].saberFlags&SFL_NO_ROLL_STAB)
12084 						&& (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_ROLL_STAB)) )
12085 					{//okay to do roll-stab
12086 						PM_SetSaberMove( LS_ROLL_STAB );
12087 						if ( pm->gent )
12088 						{
12089 							G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB );
12090 						}
12091 					}
12092 				}
12093 			}
12094 		}
12095 		return;
12096 	}
12097 
12098  	if ( PM_SaberLocked() )
12099 	{
12100 		pm->ps->saberMove = LS_NONE;
12101 		return;
12102 	}
12103 
12104 	if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND
12105 		|| (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START && !(pm->cmd.buttons&BUTTON_ATTACK)) )
12106 	{//if you're in the long-jump and you're not attacking (or are landing), you're not doing anything
12107 		if ( pm->ps->torsoAnimTimer )
12108 		{
12109 			return;
12110 		}
12111 	}
12112 
12113 	if ( pm->ps->legsAnim == BOTH_FLIP_HOLD7
12114 		&& !(pm->cmd.buttons&BUTTON_ATTACK) )
12115 	{//if you're in the upside-down attack hold, don't do anything unless you're attacking
12116 		return;
12117 	}
12118 
12119 	if ( PM_KickingAnim( pm->ps->legsAnim ) )
12120 	{
12121 		if ( pm->ps->legsAnimTimer )
12122 		{//you're kicking, no interruptions
12123 			return;
12124 		}
12125 		//done?  be immeditately ready to do an attack
12126 		pm->ps->saberMove = LS_READY;
12127 		pm->ps->weaponTime = 0;
12128 	}
12129 
12130 	if ( pm->ps->saberMoveNext != LS_NONE
12131 		&& (pm->ps->saberMove == LS_READY||pm->ps->saberMove == LS_NONE))//ready for another one
12132 	{//something is forcing us to set a specific next saberMove
12133 		//FIXME: if this is a NPC kick, re-verify it before executing it!
12134 		PM_SetSaberMove( (saberMoveName_t)pm->ps->saberMoveNext );
12135 		pm->ps->saberMoveNext = LS_NONE;//clear it now that we played it
12136 		return;
12137 	}
12138 
12139 	if ( pm->ps->saberEventFlags&SEF_INWATER )//saber in water
12140 	{
12141 		pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS);
12142 	}
12143 
12144 	qboolean saberInAir = qtrue;
12145 	if ( !PM_SaberInBrokenParry( pm->ps->saberMove ) && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && !PM_DodgeAnim( pm->ps->torsoAnim ) &&
12146 		pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING)
12147 	{//we're not stuck in a broken parry
12148 		if ( pm->ps->saberInFlight )
12149 		{//guiding saber
12150 			if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
12151 			{//
12152 				if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
12153 				{//fell to the ground and we're not trying to pull it back
12154 					saberInAir = qfalse;
12155 				}
12156 			}
12157 			if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING )
12158 			{
12159 				if ( pm->ps->weapon != pm->cmd.weapon )
12160 				{
12161 					PM_BeginWeaponChange( pm->cmd.weapon );
12162 				}
12163 			}
12164 			else if ( pm->ps->weapon == WP_SABER
12165 				&& (!pm->ps->dualSabers || !pm->ps->saber[1].Active()) )
12166 			{//guiding saber
12167 				if ( saberInAir )
12168 				{
12169 					if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 )
12170 					{//don't interrupt a force power anim
12171 						if ( pm->ps->torsoAnim != BOTH_LOSE_SABER
12172 							|| !pm->ps->torsoAnimTimer )
12173 						{
12174 							PM_SetAnim( pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12175 						}
12176 					}
12177 				}
12178 				return;
12179 			}
12180 		}
12181 	}
12182 
12183 	if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
12184 	{//FIXME: this is going to fire off one frame before you expect, actually
12185 		pm->gent->client->fireDelay -= pml.msec;
12186 		if ( pm->gent->client->fireDelay <= 0 )
12187 		{//just finished delay timer
12188 			pm->gent->client->fireDelay = 0;
12189 			delayed_fire = qtrue;
12190 		}
12191 	}
12192 
12193 	PM_CheckClearSaberBlock();
12194 
12195 	if ( PM_LockedAnim( pm->ps->torsoAnim )
12196 		&& pm->ps->torsoAnimTimer )
12197 	{//can't interrupt these anims ever
12198 		return;
12199 	}
12200 	if ( PM_SuperBreakLoseAnim( pm->ps->torsoAnim )
12201 		&& pm->ps->torsoAnimTimer )
12202 	{//don't interrupt these anims
12203 		return;
12204 	}
12205 	if ( PM_SuperBreakWinAnim( pm->ps->torsoAnim )
12206 		&& pm->ps->torsoAnimTimer )
12207 	{//don't interrupt these anims
12208 		return;
12209 	}
12210 
12211 	if ( PM_SaberBlocking() )
12212 	{//busy blocking, don't do attacks
12213 		return;
12214 	}
12215 
12216 	// check for weapon change
12217 	// can't change if weapon is firing, but can change again if lowering or raising
12218 	if ( (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) {
12219 		if ( pm->ps->weapon != pm->cmd.weapon ) {
12220 			PM_BeginWeaponChange( pm->cmd.weapon );
12221 		}
12222 	}
12223 
12224 	if ( pm->ps->weaponTime > 0 )
12225 	{
12226 		//FIXME: allow some window of opportunity to change your attack
12227 		//		if it just started and your directional input is different
12228 		//		than it was before... but only 100 milliseconds at most?
12229 		//OR:	Make it so that attacks don't start until 100ms after you
12230 		//		press the attack button...???
12231 		if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) //player
12232 			&& PM_SaberInReturn( pm->ps->saberMove )//in a saber return move - FIXME: what about transitions?
12233 			//&& pm->ps->torsoAnimTimer<=250//towards the end of a saber return anim
12234 			&& pm->ps->saberBlocked == BLOCKED_NONE//not interacting with any other saber
12235 			&& !(pm->cmd.buttons&BUTTON_ATTACK)//not trying to swing the saber
12236 			&& (pm->cmd.forwardmove||pm->cmd.rightmove) )//trying to kick in a specific direction
12237 		{
12238 			if ( PM_CheckAltKickAttack() )//trying to do a kick
12239 			{//allow them to do the kick now!
12240 				pm->ps->weaponTime = 0;
12241 				PM_CheckKick();
12242 				return;
12243 			}
12244 		}
12245 		else
12246 		{
12247 			if ( !pm->cmd.rightmove
12248 				&&!pm->cmd.forwardmove
12249 				&&(pm->cmd.buttons&BUTTON_ATTACK) )
12250 			{
12251 				/*
12252 				if ( PM_CheckDualSpinProtect() )
12253 				{//check to see if we're going to do the special dual push protect move
12254 					PM_SetSaberMove( LS_DUAL_SPIN_PROTECT );
12255 					pm->ps->weaponstate = WEAPON_FIRING;
12256 					return;
12257 				}
12258 				else
12259 				*/
12260 				if ( !g_saberNewControlScheme->integer )
12261 				{
12262 					saberMoveName_t pullAtk = PM_CheckPullAttack();
12263 					if ( pullAtk != LS_NONE )
12264 					{
12265 						PM_SetSaberMove( pullAtk );
12266 						pm->ps->weaponstate = WEAPON_FIRING;
12267 						return;
12268 					}
12269 				}
12270 			}
12271 			else
12272 			{
12273 				return;
12274 			}
12275 		}
12276 	}
12277 
12278 	// *********************************************************
12279 	// WEAPON_DROPPING
12280 	// *********************************************************
12281 
12282 	// change weapon if time
12283 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
12284 		PM_FinishWeaponChange();
12285 		return;
12286 	}
12287 
12288 	// *********************************************************
12289 	// WEAPON_RAISING
12290 	// *********************************************************
12291 
12292 	if ( pm->ps->weaponstate == WEAPON_RAISING )
12293 	{//Just selected the weapon
12294 		pm->ps->weaponstate = WEAPON_IDLE;
12295 		if(pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)))
12296 		{
12297 			switch ( pm->ps->legsAnim )
12298 			{
12299 			case BOTH_WALK1:
12300 			case BOTH_WALK2:
12301 			case BOTH_WALK_STAFF:
12302 			case BOTH_WALK_DUAL:
12303 			case BOTH_WALKBACK1:
12304 			case BOTH_WALKBACK2:
12305 			case BOTH_WALKBACK_STAFF:
12306 			case BOTH_WALKBACK_DUAL:
12307 			case BOTH_RUN1:
12308 			case BOTH_RUN2:
12309 			case BOTH_RUN_STAFF:
12310 			case BOTH_RUN_DUAL:
12311 			case BOTH_RUNBACK1:
12312 			case BOTH_RUNBACK2:
12313 			case BOTH_RUNBACK_STAFF:
12314 				PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL);
12315 				break;
12316 			default:
12317 				int anim = PM_ReadyPoseForSaberAnimLevel();
12318 				if (anim!=-1)
12319 				{
12320 					PM_SetAnim(pm,SETANIM_TORSO,anim,SETANIM_FLAG_NORMAL);
12321 				}
12322 				break;
12323 			}
12324 		}
12325 		else
12326 		{
12327 			qboolean saberInAir = qtrue;
12328 			if ( pm->ps->saberInFlight )
12329 			{//guiding saber
12330 				if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) )
12331 				{//we're stuck in a broken parry
12332 					saberInAir = qfalse;
12333 				}
12334 				if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
12335 				{//
12336 					if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
12337 					{//fell to the ground and we're not trying to pull it back
12338 						saberInAir = qfalse;
12339 					}
12340 				}
12341 			}
12342 			if ( pm->ps->weapon == WP_SABER
12343 				&& pm->ps->saberInFlight
12344 				&& saberInAir
12345 				&& (!pm->ps->dualSabers || !pm->ps->saber[1].Active()))
12346 			{//guiding saber
12347 				if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 )
12348 				{//don't interrupt a force power anim
12349 					if ( pm->ps->torsoAnim != BOTH_LOSE_SABER
12350 						|| !pm->ps->torsoAnimTimer )
12351 					{
12352 						PM_SetAnim( pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12353 					}
12354 				}
12355 			}
12356 			else
12357 			{
12358 				// PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_NORMAL);
12359 				// Select the proper idle Lightsaber attack move from the chart.
12360 				PM_SetSaberMove(LS_READY);
12361 			}
12362 		}
12363 		return;
12364 	}
12365 
12366 	// *********************************************************
12367 	// Check for WEAPON ATTACK
12368 	// *********************************************************
12369 
12370 	if ( PM_CanDoKata() )
12371 	{
12372 		saberMoveName_t overrideMove = LS_INVALID;
12373 		//see if we have an overridden (or cancelled) kata move
12374 		if ( pm->ps->saber[0].kataMove != LS_INVALID )
12375 		{
12376 			if ( pm->ps->saber[0].kataMove != LS_NONE )
12377 			{
12378 				overrideMove = (saberMoveName_t)pm->ps->saber[0].kataMove;
12379 			}
12380 		}
12381 		if ( overrideMove == LS_INVALID )
12382 		{//not overridden by first saber, check second
12383 			if ( pm->ps->dualSabers )
12384 			{
12385 				if ( pm->ps->saber[1].kataMove != LS_INVALID )
12386 				{
12387 					if ( pm->ps->saber[1].kataMove != LS_NONE )
12388 					{
12389 						overrideMove = (saberMoveName_t)pm->ps->saber[1].kataMove;
12390 					}
12391 				}
12392 			}
12393 		}
12394 		//no overrides, cancelled?
12395 		if ( overrideMove == LS_INVALID )
12396 		{
12397 			if ( pm->ps->saber[0].kataMove == LS_NONE )
12398 			{
12399 				overrideMove = LS_NONE;
12400 			}
12401 			else if ( pm->ps->dualSabers )
12402 			{
12403 				if ( pm->ps->saber[1].kataMove == LS_NONE )
12404 				{
12405 					overrideMove = LS_NONE;
12406 				}
12407 			}
12408 		}
12409 		if ( overrideMove == LS_INVALID )
12410 		{//not overridden
12411 			//FIXME: make sure to turn on saber(s)!
12412 			switch ( pm->ps->saberAnimLevel )
12413 			{
12414 			case SS_FAST:
12415 			case SS_TAVION:
12416 				PM_SetSaberMove( LS_A1_SPECIAL );
12417 				break;
12418 			case SS_MEDIUM:
12419 				PM_SetSaberMove( LS_A2_SPECIAL );
12420 				break;
12421 			case SS_STRONG:
12422 			case SS_DESANN:
12423 				PM_SetSaberMove( LS_A3_SPECIAL );
12424 				break;
12425 			case SS_DUAL:
12426 				PM_SetSaberMove( LS_DUAL_SPIN_PROTECT );//PM_CheckDualSpinProtect();
12427 				break;
12428 			case SS_STAFF:
12429 				PM_SetSaberMove( LS_STAFF_SOULCAL );
12430 				break;
12431 			}
12432 			pm->ps->weaponstate = WEAPON_FIRING;
12433 			if ( pm->gent )
12434 			{
12435 				G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue );//FP_SPEED, SINGLE_SPECIAL_POWER );
12436 				//G_StartMatrixEffect( pm->gent, MEF_REVERSE_SPIN, pm->ps->torsoAnimTimer );
12437 			}
12438 		}
12439 		else if ( overrideMove != LS_NONE )
12440 		{
12441 			PM_SetSaberMove( overrideMove );
12442 			pm->ps->weaponstate = WEAPON_FIRING;
12443 			if ( pm->gent )
12444 			{
12445 				G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue );//FP_SPEED, SINGLE_SPECIAL_POWER );
12446 				//G_StartMatrixEffect( pm->gent, MEF_REVERSE_SPIN, pm->ps->torsoAnimTimer );
12447 			}
12448 		}
12449 		if ( overrideMove != LS_NONE )
12450 		{//not cancelled
12451 			return;
12452 		}
12453 	}
12454 
12455 	if ( PM_CheckAltKickAttack() )
12456 	{//trying to do a kick
12457 		//FIXME: in-air kicks?
12458 		if ( pm->ps->saberAnimLevel == SS_STAFF
12459 			&& (pm->ps->clientNum >= MAX_CLIENTS||PM_ControlledByPlayer()) )
12460 		{//NPCs spin the staff
12461 			//NOTE: only NPCs can do it the easy way... they kick directly, not through ucmds...
12462 			PM_SetSaberMove( LS_SPINATTACK );
12463 			return;
12464 		}
12465 		else
12466 		{
12467 			PM_CheckKick();
12468 		}
12469 		return;
12470 	}
12471 	//this is never a valid regular saber attack button
12472 	//pm->cmd.buttons &= ~BUTTON_FORCE_FOCUS;
12473 
12474 	if ( PM_CheckUpsideDownAttack() )
12475 	{
12476 		return;
12477 	}
12478 
12479 	if(!delayed_fire)
12480 	{
12481 		// Start with the current move, and cross index it with the current control states.
12482 		if ( pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX )
12483 		{
12484 			curmove = (saberMoveName_t)pm->ps->saberMove;
12485 		}
12486 		else
12487 		{
12488 			curmove = LS_READY;
12489 		}
12490 		if ( curmove == LS_A_JUMP_T__B_ || pm->ps->torsoAnim == BOTH_FORCELEAP2_T__B_ )
12491 		{//must transition back to ready from this anim
12492 			newmove = LS_R_T2B;
12493 		}
12494 		// check for fire
12495 		else if ( !(pm->cmd.buttons & BUTTON_ATTACK) )//(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS)) )
12496 		{//not attacking
12497 			pm->ps->weaponTime = 0;
12498 
12499 			if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
12500 			{//Still firing
12501 				pm->ps->weaponstate = WEAPON_FIRING;
12502 			}
12503 			else if ( pm->ps->weaponstate != WEAPON_READY )
12504 			{
12505 				pm->ps->weaponstate = WEAPON_IDLE;
12506 			}
12507 			//Check for finishing an anim if necc.
12508 			if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B )
12509 			{//started a swing, must continue from here
12510 				newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR);
12511 			}
12512 			else if ( curmove >= LS_A_TL2BR && curmove <= LS_A_T2B )
12513 			{//finished an attack, must continue from here
12514 				newmove = LS_R_TL2BR + (curmove-LS_A_TL2BR);
12515 			}
12516 			else if ( PM_SaberInTransition( curmove ) )
12517 			{//in a transition, must play sequential attack
12518 				newmove = saberMoveData[curmove].chain_attack;
12519 			}
12520 			else if ( PM_SaberInBounce( curmove ) )
12521 			{//in a bounce
12522 				if ( pm->ps->clientNum && !PM_ControlledByPlayer() )
12523 				{//NPCs must play sequential attack
12524 					//going into another attack...
12525 					//allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3
12526 					if ( PM_SaberKataDone( LS_NONE, LS_NONE ) )
12527 					{//done with this kata, must return to ready before attack again
12528 						newmove = saberMoveData[curmove].chain_idle;
12529 					}
12530 					else
12531 					{//okay to chain to another attack
12532 						newmove = saberMoveData[curmove].chain_attack;//we assume they're attacking, even if they're not
12533 						pm->ps->saberAttackChainCount++;
12534 					}
12535 				}
12536 				else
12537 				{//player gets his by directional control
12538 					newmove = saberMoveData[curmove].chain_idle;//oops, not attacking, so don't chain
12539 				}
12540 			}
12541 			else
12542 			{//FIXME: what about returning from a parry?
12543 				//PM_SetSaberMove( LS_READY );
12544 				if ( pm->ps->saberBlockingTime > cg.time )
12545 				{
12546 					PM_SetSaberMove( LS_READY );
12547 				}
12548 				return;
12549 			}
12550 		}
12551 
12552 		// ***************************************************
12553 		// Pressing attack, so we must look up the proper attack move.
12554 		qboolean saberInAir = qtrue;
12555 		if ( pm->ps->saberInFlight )
12556 		{//guiding saber
12557 			if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) )
12558 			{//we're stuck in a broken parry
12559 				saberInAir = qfalse;
12560 			}
12561 			if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0
12562 			{//
12563 				if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY )
12564 				{//fell to the ground and we're not trying to pull it back
12565 					saberInAir = qfalse;
12566 				}
12567 			}
12568 		}
12569 
12570 		if ( pm->ps->weapon == WP_SABER
12571 			&& pm->ps->saberInFlight
12572 			&& saberInAir
12573 			&& (!pm->ps->dualSabers || !pm->ps->saber[1].Active()))
12574 		{//guiding saber
12575 			if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 )
12576 			{//don't interrupt a force power anim
12577 				if ( pm->ps->torsoAnim != BOTH_LOSE_SABER
12578 					|| !pm->ps->torsoAnimTimer )
12579 				{
12580 					PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12581 				}
12582 			}
12583 		}
12584 		else if ( pm->ps->weaponTime > 0 )
12585 		{	// Last attack is not yet complete.
12586 			pm->ps->weaponstate = WEAPON_FIRING;
12587 			return;
12588 		}
12589 		else
12590 		{
12591 			int	both = qfalse;
12592 			if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_ATTACK
12593 				|| pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND )
12594 			{//can't attack in these anims
12595 				return;
12596 			}
12597 			else if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START )
12598 			{//only 1 attack you can do from this anim
12599 				if ( pm->ps->torsoAnimTimer >= 200 )
12600 				{//hit it early enough to do the attack
12601 					PM_SetSaberMove( LS_LEAP_ATTACK );
12602 				}
12603 				return;
12604 			}
12605 			if ( curmove >= LS_PARRY_UP && curmove <= LS_REFLECT_LL )
12606 			{//from a parry or reflection, can go directly into an attack
12607 				if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() )
12608 				{//NPCs
12609 					newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad );
12610 				}
12611 				else
12612 				{
12613 					newmove = PM_SaberAttackForMovement( pm->cmd.forwardmove, pm->cmd.rightmove, curmove );
12614 				}
12615 			}
12616 
12617 			if ( newmove != LS_NONE )
12618 			{//have a valid, final LS_ move picked, so skip findingt he transition move and just get the anim
12619 				if (PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse))
12620 				{
12621 					anim = saberMoveData[newmove].animToUse;
12622 				}
12623 			}
12624 
12625 			//FIXME: diagonal dirs use the figure-eight attacks from ready pose?
12626 			if ( anim == -1 )
12627 			{
12628 				//FIXME: take FP_SABER_OFFENSE into account here somehow?
12629 				if ( PM_SaberInTransition( curmove ) )
12630 				{//in a transition, must play sequential attack
12631 					newmove = saberMoveData[curmove].chain_attack;
12632 				}
12633 				else if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B )
12634 				{//started a swing, must continue from here
12635 					newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR);
12636 				}
12637 				else if ( PM_SaberInBrokenParry( curmove ) )
12638 				{//broken parries must always return to ready
12639 					newmove = LS_READY;
12640 				}
12641 				else//if ( pm->cmd.buttons&BUTTON_ATTACK && !(pm->ps->pm_flags&PMF_ATTACK_HELD) )//only do this if just pressed attack button?
12642 				{//get attack move from movement command
12643 					/*
12644 					if ( PM_SaberKataDone() )
12645 					{//we came from a bounce and cannot chain to another attack because our kata is done
12646 						newmove = saberMoveData[curmove].chain_idle;
12647 					}
12648 					else */
12649 					if ( pm->ps->clientNum >= MAX_CLIENTS
12650 						&& !PM_ControlledByPlayer()
12651 						&& (Q_irand( 0, pm->ps->forcePowerLevel[FP_SABER_OFFENSE]-1 )
12652 							|| (pm->gent&&pm->gent->enemy&&pm->gent->enemy->client&&PM_InKnockDownOnGround(&pm->gent->enemy->client->ps))//enemy knocked down, use some logic
12653 							|| ( pm->ps->saberAnimLevel == SS_FAST && pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, 1 ) ) ) )//minor change to make fast-attack users use the special attacks more
12654 					{//NPCs use more randomized attacks the more skilled they are
12655 						newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad );
12656 					}
12657 					else
12658 					{
12659 						newmove = PM_SaberAttackForMovement( pm->cmd.forwardmove, pm->cmd.rightmove, curmove );
12660 						if ( (PM_SaberInBounce( curmove )||PM_SaberInBrokenParry( curmove ))
12661 							&& saberMoveData[newmove].startQuad == saberMoveData[curmove].endQuad )
12662 						{//this attack would be a repeat of the last (which was blocked), so don't actually use it, use the default chain attack for this bounce
12663 							newmove = saberMoveData[curmove].chain_attack;
12664 						}
12665 					}
12666 					if ( PM_SaberKataDone( curmove, newmove ) )
12667 					{//cannot chain this time
12668 						newmove = saberMoveData[curmove].chain_idle;
12669 					}
12670 				}
12671 				/*
12672 				if ( newmove == LS_NONE )
12673 				{//FIXME: should we allow this?  Are there some anims that you should never be able to chain into an attack?
12674 					//only curmove that might get in here is LS_NONE, LS_DRAW, LS_PUTAWAY and the LS_R_ returns... all of which are in Q_R
12675 					newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad );
12676 				}
12677 				*/
12678 				if ( newmove != LS_NONE )
12679 				{
12680 					if ( !PM_InCartwheel( pm->ps->legsAnim ) )
12681 					{//don't do transitions when cartwheeling - could make you spin!
12682 						//Now get the proper transition move
12683 						newmove = PM_SaberAnimTransitionMove( (saberMoveName_t)curmove, (saberMoveName_t)newmove );
12684 						if ( PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse ) )
12685 						{
12686 							anim = saberMoveData[newmove].animToUse;
12687 						}
12688 					}
12689 				}
12690 			}
12691 
12692 			if (anim == -1)
12693 			{//not side-stepping, pick neutral anim
12694 				if ( !G_TryingSpecial(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )
12695 				{//but only if not trying one of the special attacks!
12696 					if ( PM_InCartwheel( pm->ps->legsAnim )
12697 						&& pm->ps->legsAnimTimer > 100 )
12698 					{//if in the middle of a cartwheel, the chain attack is just a normal attack
12699 						//NOTE: this should match the switch in PM_InCartwheel!
12700 						switch( pm->ps->legsAnim )
12701 						{
12702 						case BOTH_ARIAL_LEFT://swing from l to r
12703 						case BOTH_CARTWHEEL_LEFT:
12704 							newmove = LS_A_L2R;
12705 							break;
12706 						case BOTH_ARIAL_RIGHT://swing from r to l
12707 						case BOTH_CARTWHEEL_RIGHT:
12708 							newmove = LS_A_R2L;
12709 							break;
12710 						case BOTH_ARIAL_F1://random l/r attack
12711 							if ( Q_irand( 0, 1 ) )
12712 							{
12713 								newmove = LS_A_L2R;
12714 							}
12715 							else
12716 							{
12717 								newmove = LS_A_R2L;
12718 							}
12719 							break;
12720 						}
12721 					}
12722 					else
12723 					{
12724 						// Add randomness for prototype?
12725 						newmove = saberMoveData[curmove].chain_attack;
12726 					}
12727 					if ( newmove != LS_NONE )
12728 					{
12729 						if (PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse))
12730 						{
12731 							anim= saberMoveData[newmove].animToUse;
12732 						}
12733 					}
12734 				}
12735 
12736 				if ( !pm->cmd.forwardmove && !pm->cmd.rightmove && pm->cmd.upmove >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
12737 				{//not moving at all, so set the anim on entire body
12738 					both = qtrue;
12739 				}
12740 
12741 			}
12742 
12743 			if ( anim == -1)
12744 			{
12745 				switch ( pm->ps->legsAnim )
12746 				{
12747 				case BOTH_WALK1:
12748 				case BOTH_WALK2:
12749 				case BOTH_WALK_STAFF:
12750 				case BOTH_WALK_DUAL:
12751 				case BOTH_WALKBACK1:
12752 				case BOTH_WALKBACK2:
12753 				case BOTH_WALKBACK_STAFF:
12754 				case BOTH_WALKBACK_DUAL:
12755 				case BOTH_RUN1:
12756 				case BOTH_RUN2:
12757 				case BOTH_RUN_STAFF:
12758 				case BOTH_RUN_DUAL:
12759 				case BOTH_RUNBACK1:
12760 				case BOTH_RUNBACK2:
12761 				case BOTH_RUNBACK_STAFF:
12762 					anim = pm->ps->legsAnim;
12763 					break;
12764 				default:
12765 					anim = PM_ReadyPoseForSaberAnimLevel();
12766 					break;
12767 				}
12768 				newmove = LS_READY;
12769 			}
12770 
12771 			if ( !pm->ps->SaberActive() )
12772 			{//turn on the saber if it's not on
12773 				pm->ps->SaberActivate();
12774 			}
12775 
12776 			PM_SetSaberMove( (saberMoveName_t)newmove );
12777 
12778 			if ( both && pm->ps->legsAnim != pm->ps->torsoAnim )
12779 			{
12780 				PM_SetAnim( pm,SETANIM_LEGS,pm->ps->torsoAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
12781 			}
12782 			if ( pm->gent && pm->gent->client )
12783 			{
12784 //				pm->gent->client->saberTrail.inAction = qtrue;
12785 //				pm->gent->client->saberTrail.duration = 75; // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter
12786 			}
12787 			if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
12788 			{//can still attack during a cartwheel/arial
12789 				//don't fire again until anim is done
12790 				pm->ps->weaponTime = pm->ps->torsoAnimTimer;
12791 			}
12792 			/*
12793 			//FIXME: this may be making it so sometimes you can't swing again right away...
12794 			if ( newmove == LS_READY )
12795 			{
12796 				pm->ps->weaponTime = 500;
12797 			}
12798 			*/
12799 		}
12800 	}
12801 
12802 	// *********************************************************
12803 	// WEAPON_FIRING
12804 	// *********************************************************
12805 
12806 	pm->ps->weaponstate = WEAPON_FIRING;
12807 
12808 	if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
12809 	{//FIXME: this is going to fire off one frame before you expect, actually
12810 		// Clear these out since we're not actually firing yet
12811 		pm->ps->eFlags &= ~EF_FIRING;
12812 		pm->ps->eFlags &= ~EF_ALT_FIRING;
12813 		return;
12814 	}
12815 
12816 	addTime = pm->ps->weaponTime;
12817 	/*if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) 	{
12818 		PM_AddEvent( EV_ALT_FIRE );
12819 		if ( !addTime )
12820 		{
12821 			addTime = weaponData[pm->ps->weapon].altFireTime;
12822 			if ( g_timescale != NULL )
12823 			{
12824 				if ( g_timescale->value < 1.0f )
12825 				{
12826 					if ( !MatrixMode )
12827 					{//Special test for Matrix Mode (tm)
12828 						if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12829 						{//player always fires at normal speed
12830 							addTime *= g_timescale->value;
12831 						}
12832 						else if ( g_entities[pm->ps->clientNum].client && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12833 						{
12834 							addTime *= g_timescale->value;
12835 						}
12836 					}
12837 				}
12838 			}
12839 		}
12840 	}
12841 	else */{
12842 		PM_AddEvent( EV_FIRE_WEAPON );
12843 		if ( !addTime )
12844 		{
12845 			addTime = weaponData[pm->ps->weapon].fireTime;
12846 			if ( g_timescale != NULL )
12847 			{
12848 				if ( g_timescale->value < 1.0f )
12849 				{
12850 					if ( !MatrixMode )
12851 					{//Special test for Matrix Mode (tm)
12852 						if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12853 						{//player always fires at normal speed
12854 							addTime *= g_timescale->value;
12855 						}
12856 						else if ( g_entities[pm->ps->clientNum].client
12857 							&& (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
12858 						{
12859 							addTime *= g_timescale->value;
12860 						}
12861 					}
12862 				}
12863 			}
12864 		}
12865 	}
12866 
12867 	if (pm->ps->forcePowersActive & (1 << FP_RAGE))
12868 	{
12869 		addTime *= 0.75;
12870 	}
12871 	else if (pm->ps->forceRageRecoveryTime > pm->cmd.serverTime)
12872 	{
12873 		addTime *= 1.5;
12874 	}
12875 
12876 	//If the phaser has been fired, delay the next recharge time
12877 	if ( !PM_ControlledByPlayer() )
12878 	{
12879 		if( pm->gent && pm->gent->NPC != NULL )
12880 		{//NPCs have their own refire logic
12881 			//FIXME: this really should be universal...
12882 			return;
12883 		}
12884 	}
12885 
12886 	if ( !PM_InCartwheel( pm->ps->torsoAnim ) )
12887 	{//can still attack during a cartwheel/arial
12888 		pm->ps->weaponTime = addTime;
12889 	}
12890 }
12891 
12892 //---------------------------------------
PM_DoChargedWeapons(void)12893 static bool PM_DoChargedWeapons( void )
12894 //---------------------------------------
12895 {
12896 	qboolean	charging = qfalse,
12897 				altFire = qfalse;
12898 
12899 	//FIXME: make jedi aware they're being aimed at with a charged-up weapon (strafe and be evasive?)
12900 	// If you want your weapon to be a charging weapon, just set this bit up
12901 	switch( pm->ps->weapon )
12902 	{
12903 	//------------------
12904 	case WP_BRYAR_PISTOL:
12905 	case WP_BLASTER_PISTOL:
12906 
12907 		// alt-fire charges the weapon
12908 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12909 		{
12910 			charging = qtrue;
12911 			altFire = qtrue;
12912 		}
12913 		break;
12914 
12915 	//------------------
12916 	case WP_DISRUPTOR:
12917 
12918 		// alt-fire charges the weapon...but due to zooming being controlled by the alt-button, the main button actually charges...but only when zoomed.
12919 		//	lovely, eh?
12920 		if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
12921 		{
12922 			if ( cg.zoomMode == 2 )
12923 			{
12924 				if ( pm->cmd.buttons & BUTTON_ATTACK )
12925 				{
12926 					charging = qtrue;
12927 					altFire = qtrue; // believe it or not, it really is an alt-fire in this case!
12928 				}
12929 			}
12930 		}
12931 		else if ( pm->gent && pm->gent->NPC )
12932 		{
12933 			if ( (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE) )
12934 			{
12935 				if ( pm->gent->fly_sound_debounce_time > level.time )
12936 				{
12937 					charging = qtrue;
12938 					altFire = qtrue;
12939 				}
12940 			}
12941 		}
12942 		break;
12943 
12944 	//------------------
12945 	case WP_BOWCASTER:
12946 
12947 		// main-fire charges the weapon
12948 		if ( pm->cmd.buttons & BUTTON_ATTACK )
12949 		{
12950 			charging = qtrue;
12951 		}
12952 		break;
12953 
12954 	//------------------
12955 	case WP_DEMP2:
12956 
12957 		// alt-fire charges the weapon
12958 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12959 		{
12960 			charging = qtrue;
12961 			altFire = qtrue;
12962 		}
12963 		break;
12964 
12965 	//------------------
12966 	case WP_ROCKET_LAUNCHER:
12967 
12968 		// Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
12969 		//	implement our alt-fire locking stuff
12970 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12971 		{
12972 			charging = qtrue;
12973 			altFire = qtrue;
12974 		}
12975 		break;
12976 
12977 	//------------------
12978 	case WP_THERMAL:
12979 		//			FIXME: Really should have a wind-up anim for player
12980 		//			as he holds down the fire button to throw, then play
12981 		//			the actual throw when he lets go...
12982 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
12983 		{
12984 			altFire = qtrue; // override default of not being an alt-fire
12985 			charging = qtrue;
12986 		}
12987 		else if ( pm->cmd.buttons & BUTTON_ATTACK )
12988 		{
12989 			charging = qtrue;
12990 		}
12991 		break;
12992 
12993 	} // end switch
12994 
12995 	// set up the appropriate weapon state based on the button that's down.
12996 	//	Note that we ALWAYS return if charging is set ( meaning the buttons are still down )
12997 	if ( charging )
12998 	{
12999 
13000 
13001 		if ( altFire )
13002 		{
13003 			if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_DROPPING )
13004 			{
13005 				if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0)
13006 				{
13007 					PM_AddEvent( EV_NOAMMO );
13008 					pm->ps->weaponTime += 500;
13009 					return true;
13010 				}
13011 
13012 				// charge isn't started, so do it now
13013 				pm->ps->weaponstate = WEAPON_CHARGING_ALT;
13014 				pm->ps->weaponChargeTime = level.time;
13015 
13016 				if ( cg_weapons[pm->ps->weapon].altChargeSound )
13017 				{
13018 					G_SoundOnEnt( pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].altChargeSnd );
13019 				}
13020 			}
13021 		}
13022 		else
13023 		{
13024 
13025 			if ( pm->ps->weaponstate != WEAPON_CHARGING && pm->ps->weaponstate != WEAPON_DROPPING )
13026 			{
13027 				if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0)
13028 				{
13029 					PM_AddEvent( EV_NOAMMO );
13030 					pm->ps->weaponTime += 500;
13031 					return true;
13032 				}
13033 
13034 				// charge isn't started, so do it now
13035 				pm->ps->weaponstate = WEAPON_CHARGING;
13036 				pm->ps->weaponChargeTime = level.time;
13037 
13038 				if ( cg_weapons[pm->ps->weapon].chargeSound && pm->gent && !pm->gent->NPC ) // HACK: !NPC mostly for bowcaster and weequay
13039 				{
13040 					G_SoundOnEnt( pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].chargeSnd );
13041 				}
13042 			}
13043 		}
13044 
13045 		return true; // short-circuit rest of weapon code
13046 	}
13047 
13048 	// Only charging weapons should be able to set these states...so....
13049 	//	let's see which fire mode we need to set up now that the buttons are up
13050 	if ( pm->ps->weaponstate == WEAPON_CHARGING )
13051 	{
13052 		// weapon has a charge, so let us do an attack
13053 		// dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
13054 		pm->cmd.buttons |= BUTTON_ATTACK;
13055 		pm->ps->eFlags |= EF_FIRING;
13056 	}
13057 	else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
13058 	{
13059 		// weapon has a charge, so let us do an alt-attack
13060 		// dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
13061 		pm->cmd.buttons |= BUTTON_ALT_ATTACK;
13062 		pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING);
13063 	}
13064 
13065 	return false; // continue with the rest of the weapon code
13066 }
13067 
13068 
13069 #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
13070 #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
13071 #define DEMP2_CHARGE_UNIT		500.0f	// ditto
13072 #define DISRUPTOR_CHARGE_UNIT	150.0f	// ditto
13073 
13074 // Specific weapons can opt to modify the ammo usage based on charges, otherwise if no special case code
13075 //	is handled below, regular ammo usage will happen
13076 //---------------------------------------
PM_DoChargingAmmoUsage(int * amount)13077 static int PM_DoChargingAmmoUsage( int *amount )
13078 //---------------------------------------
13079 {
13080 	int count = 0;
13081 
13082 	if ( pm->ps->weapon == WP_BOWCASTER && !( pm->cmd.buttons & BUTTON_ALT_ATTACK ))
13083 	{
13084 		// this code is duplicated ( I know, I know ) in G_weapon.cpp for the bowcaster alt-fire
13085 		count = ( level.time - pm->ps->weaponChargeTime ) / BOWCASTER_CHARGE_UNIT;
13086 
13087 		if ( count < 1 )
13088 		{
13089 			count = 1;
13090 		}
13091 		else if ( count > 5 )
13092 		{
13093 			count = 5;
13094 		}
13095 
13096 		if ( !(count & 1 ))
13097 		{
13098 			// if we aren't odd, knock us down a level
13099 			count--;
13100 		}
13101 
13102 		// Only bother with these checks if we don't have infinite ammo
13103 		if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13104 		{
13105 			int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13106 
13107 			// If we have enough ammo to do the full charged shot, we are ok
13108 			if ( dif < 0 )
13109 			{
13110 				// we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13111 				count += floor(dif / (float)*amount);
13112 
13113 				if ( count < 1 )
13114 				{
13115 					count = 1;
13116 				}
13117 
13118 				// now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13119 				pm->ps->weaponChargeTime = level.time - ( count * BOWCASTER_CHARGE_UNIT );
13120 			}
13121 		}
13122 
13123 		// now that count is cool, get the real ammo usage
13124 		*amount *= count;
13125 	}
13126 	else if(  ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK )
13127 			  || ( pm->ps->weapon == WP_BLASTER_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK ) )
13128 	{
13129 		// this code is duplicated ( I know, I know ) in G_weapon.cpp for the bryar alt-fire
13130 		count = ( level.time - pm->ps->weaponChargeTime ) / BRYAR_CHARGE_UNIT;
13131 
13132 		if ( count < 1 )
13133 		{
13134 			count = 1;
13135 		}
13136 		else if ( count > 5 )
13137 		{
13138 			count = 5;
13139 		}
13140 
13141 		// Only bother with these checks if we don't have infinite ammo
13142 		if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13143 		{
13144 			int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13145 
13146 			// If we have enough ammo to do the full charged shot, we are ok
13147 			if ( dif < 0 )
13148 			{
13149 				// we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13150 				count += floor(dif / (float)*amount);
13151 
13152 				if ( count < 1 )
13153 				{
13154 					count = 1;
13155 				}
13156 
13157 				// now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13158 				pm->ps->weaponChargeTime = level.time - ( count * BRYAR_CHARGE_UNIT );
13159 			}
13160 		}
13161 
13162 		// now that count is cool, get the real ammo usage
13163 		*amount *= count;
13164 	}
13165 	else if ( pm->ps->weapon == WP_DEMP2 && pm->cmd.buttons & BUTTON_ALT_ATTACK )
13166 	{
13167 		// this code is duplicated ( I know, I know ) in G_weapon.cpp for the demp2 alt-fire
13168 		count = ( level.time - pm->ps->weaponChargeTime ) / DEMP2_CHARGE_UNIT;
13169 
13170 		if ( count < 1 )
13171 		{
13172 			count = 1;
13173 		}
13174 		else if ( count > 3 )
13175 		{
13176 			count = 3;
13177 		}
13178 
13179 		// Only bother with these checks if we don't have infinite ammo
13180 		if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13181 		{
13182 			int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13183 
13184 			// If we have enough ammo to do the full charged shot, we are ok
13185 			if ( dif < 0 )
13186 			{
13187 				// we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13188 				count += floor(dif / (float)*amount);
13189 
13190 				if ( count < 1 )
13191 				{
13192 					count = 1;
13193 				}
13194 
13195 				// now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13196 				pm->ps->weaponChargeTime = level.time - ( count * DEMP2_CHARGE_UNIT );
13197 			}
13198 		}
13199 
13200 		// now that count is cool, get the real ammo usage
13201 		*amount *= count;
13202 
13203 		// this is an after-thought.  should probably re-write the function to do this naturally.
13204 		if ( *amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] )
13205 		{
13206 			*amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex];
13207 		}
13208 	}
13209 	else if ( pm->ps->weapon == WP_DISRUPTOR && pm->cmd.buttons & BUTTON_ALT_ATTACK ) // BUTTON_ATTACK will have been mapped to BUTTON_ALT_ATTACK if we are zoomed
13210 	{
13211 		// this code is duplicated ( I know, I know ) in G_weapon.cpp for the disruptor alt-fire
13212 		count = ( level.time - pm->ps->weaponChargeTime ) / DISRUPTOR_CHARGE_UNIT;
13213 
13214 		if ( count < 1 )
13215 		{
13216 			count = 1;
13217 		}
13218 		else if ( count > 10 )
13219 		{
13220 			count = 10;
13221 		}
13222 
13223 		// Only bother with these checks if we don't have infinite ammo
13224 		if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13225 		{
13226 			int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count;
13227 
13228 			// If we have enough ammo to do the full charged shot, we are ok
13229 			if ( dif < 0 )
13230 			{
13231 				// we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative
13232 				count += floor(dif / (float)*amount);
13233 
13234 				if ( count < 1 )
13235 				{
13236 					count = 1;
13237 				}
13238 
13239 				// now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked
13240 				pm->ps->weaponChargeTime = level.time - ( count * DISRUPTOR_CHARGE_UNIT );
13241 			}
13242 		}
13243 
13244 		// now that count is cool, get the real ammo usage
13245 		*amount *= count;
13246 
13247 		// this is an after-thought.  should probably re-write the function to do this naturally.
13248 		if ( *amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] )
13249 		{
13250 			*amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex];
13251 		}
13252 	}
13253 
13254 	return count;
13255 }
13256 
PM_DroidMelee(int npc_class)13257 qboolean PM_DroidMelee( int npc_class )
13258 {
13259 	if ( npc_class == CLASS_PROBE
13260 		|| npc_class == CLASS_SEEKER
13261 		|| npc_class == CLASS_INTERROGATOR
13262 		|| npc_class == CLASS_SENTRY
13263 		|| npc_class == CLASS_REMOTE )
13264 	{
13265 		return qtrue;
13266 	}
13267 	return qfalse;
13268 }
13269 
PM_WeaponWampa(void)13270 void PM_WeaponWampa( void )
13271 {
13272 	// make weapon function
13273 	if ( pm->ps->weaponTime > 0 ) {
13274 		pm->ps->weaponTime -= pml.msec;
13275 		if ( pm->ps->weaponTime <= 0 )
13276 		{
13277 			pm->ps->weaponTime = 0;
13278 		}
13279 	}
13280 
13281 	// check for weapon change
13282 	// can't change if weapon is firing, but can change again if lowering or raising
13283 	if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
13284 		if ( pm->ps->weapon != pm->cmd.weapon ) {
13285 			PM_BeginWeaponChange( pm->cmd.weapon );
13286 		}
13287 	}
13288 
13289 	if ( pm->ps->weaponTime > 0 )
13290 	{
13291 		return;
13292 	}
13293 
13294 	// change weapon if time
13295  	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
13296 		PM_FinishWeaponChange();
13297 		return;
13298 	}
13299 
13300 	if ( pm->ps->weapon == WP_SABER
13301 		&& (pm->cmd.buttons&BUTTON_ATTACK)
13302 		&& pm->ps->torsoAnim == BOTH_HANG_IDLE )
13303 	{
13304 		pm->ps->SaberActivate();
13305 		pm->ps->SaberActivateTrail( 150 );
13306 		PM_SetAnim( pm, SETANIM_BOTH, BOTH_HANG_ATTACK, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13307 		pm->ps->weaponstate = WEAPON_FIRING;
13308 		pm->ps->saberBlocked = BLOCKED_NONE;
13309 		pm->ps->saberMove = LS_READY;
13310 		pm->ps->saberMoveNext = LS_NONE;
13311 	}
13312 	else if ( pm->ps->torsoAnim == BOTH_HANG_IDLE )
13313 	{
13314 		pm->ps->SaberDeactivateTrail( 0 );
13315 		pm->ps->weaponstate = WEAPON_READY;
13316 		pm->ps->saberMove = LS_READY;
13317 		pm->ps->saberMoveNext = LS_NONE;
13318 	}
13319 }
13320 /*
13321 ==============
13322 PM_Weapon
13323 
13324 Generates weapon events and modifes the weapon counter
13325 ==============
13326 */
PM_Weapon(void)13327 static void PM_Weapon( void )
13328 {
13329 	int			addTime, amount, trueCount = 1;
13330 	qboolean	delayed_fire = qfalse;
13331 
13332 	if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) )
13333 	{
13334 		PM_WeaponWampa();
13335 		return;
13336 	}
13337 	if ( pm->ps->eFlags&EF_FORCE_DRAINED )
13338 	{//being drained
13339 		return;
13340 	}
13341 	if ( (pm->ps->forcePowersActive&(1<<FP_DRAIN))
13342 		&& pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD )
13343 	{//draining
13344 		return;
13345 	}
13346 	if (pm->ps->weapon == WP_SABER && (cg.zoomMode==3||!cg.zoomMode||pm->ps->clientNum) )		// WP_LIGHTSABER
13347 	{	// Separate logic for lightsaber, but not for player when zoomed
13348 		PM_WeaponLightsaber();
13349 		if ( pm->gent && pm->gent->client && pm->ps->saber[0].Active() && pm->ps->saberInFlight )
13350 		{//FIXME: put saberTrail in playerState
13351 			if ( pm->gent->client->ps.saberEntityState == SES_RETURNING )
13352 			{//turn off the saber trail
13353 				pm->gent->client->ps.SaberDeactivateTrail( 75 );
13354 			}
13355 			else
13356 			{//turn on the saber trail
13357 				pm->gent->client->ps.SaberActivateTrail( 150 );
13358 			}
13359 		}
13360 		return;
13361 	}
13362 
13363 	if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ))
13364 	{//in knockdown
13365 		if ( pm->ps->weaponTime > 0 ) {
13366 			pm->ps->weaponTime -= pml.msec;
13367 			if ( pm->ps->weaponTime <= 0 )
13368 			{
13369 				pm->ps->weaponTime = 0;
13370 			}
13371 		}
13372 		return;
13373 	}
13374 
13375 	if( pm->gent && pm->gent->client )
13376 	{
13377 		if ( pm->gent->client->fireDelay > 0 )
13378 		{//FIXME: this is going to fire off one frame before you expect, actually
13379 			pm->gent->client->fireDelay -= pml.msec;
13380 			if(pm->gent->client->fireDelay <= 0)
13381 			{//just finished delay timer
13382 				if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER )
13383 				{
13384 					G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav" );
13385 					pm->cmd.buttons |= BUTTON_ALT_ATTACK;
13386 				}
13387 				pm->gent->client->fireDelay = 0;
13388 				delayed_fire = qtrue;
13389 				if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
13390 					&& pm->ps->weapon == WP_THERMAL
13391 					&& pm->gent->alt_fire )
13392 				{
13393 					pm->cmd.buttons |= BUTTON_ALT_ATTACK;
13394 				}
13395 			}
13396 			else
13397 			{
13398 				if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand( 0, 1 ) )
13399 				{
13400 					G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav" );
13401 				}
13402 			}
13403 		}
13404 	}
13405 
13406    // don't allow attack until all buttons are up
13407 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
13408 		return;
13409 	}
13410 
13411 	// check for dead player
13412 	if ( pm->ps->stats[STAT_HEALTH] <= 0 )
13413 	{
13414 		if ( pm->gent && pm->gent->client )
13415 		{
13416 			pm->ps->weapon = WP_NONE;
13417 		}
13418 
13419 		if ( pm->gent )
13420 		{
13421 			pm->gent->s.loopSound = 0;
13422 		}
13423 		return;
13424 	}
13425 
13426 	// make weapon function
13427 	if ( pm->ps->weaponTime > 0 ) {
13428 		pm->ps->weaponTime -= pml.msec;
13429 		if ( pm->ps->weaponTime <= 0 )
13430 		{
13431 			pm->ps->weaponTime = 0;
13432 		}
13433 	}
13434 
13435 	// check for weapon change
13436 	// can't change if weapon is firing, but can change again if lowering or raising
13437 	if ( (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING)  && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) {
13438 		if ( pm->ps->weapon != pm->cmd.weapon && (!pm->ps->viewEntity || pm->ps->viewEntity >= ENTITYNUM_WORLD) && !PM_DoChargedWeapons()) {
13439 			PM_BeginWeaponChange( pm->cmd.weapon );
13440 		}
13441 	}
13442 
13443 	if ( pm->ps->weaponTime > 0 )
13444 	{
13445 		return;
13446 	}
13447 
13448 	// change weapon if time
13449  	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
13450 		PM_FinishWeaponChange();
13451 		return;
13452 	}
13453 
13454 	if ( pm->ps->weapon == WP_NONE )
13455 	{
13456 		return;
13457 	}
13458 
13459 	if ( pm->ps->weaponstate == WEAPON_RAISING )
13460 	{
13461 		//Just selected the weapon
13462 		pm->ps->weaponstate = WEAPON_IDLE;
13463 
13464 		if(pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)))
13465 		{
13466 			PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
13467 		}
13468 		else
13469 		{
13470 			switch(pm->ps->weapon)
13471 			{
13472 			case WP_BRYAR_PISTOL:
13473 			case WP_BLASTER_PISTOL:
13474 				if ( pm->gent
13475 					&& pm->gent->weaponModel[1] > 0 )
13476 				{//dual pistols
13477 					//FIXME: should be a better way of detecting a dual-pistols user so it's not hardcoded to the saboteurcommando...
13478 					PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
13479 				}
13480 				else
13481 				{//single pistol
13482 					PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL);
13483 				}
13484 				break;
13485 			default:
13486 				PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL);
13487 				break;
13488 			}
13489 		}
13490 		return;
13491 	}
13492 
13493 	if ( pm->gent )
13494 	{//ready to throw thermal again, add it
13495 		if ( pm->ps->weapon == WP_THERMAL
13496 			&& pm->gent->weaponModel[0] == -1 )
13497 		{//add the thermal model back in our hand
13498 			// remove anything if we have anything already
13499 			G_RemoveWeaponModels( pm->gent );
13500 			if (weaponData[pm->ps->weapon].weaponMdl[0]) {	//might be NONE, so check if it has a model
13501 				G_CreateG2AttachedWeaponModel( pm->gent, weaponData[pm->ps->weapon].weaponMdl, pm->gent->handRBolt, 0 );
13502 				//make it sound like we took another one out from... uh.. somewhere...
13503 				if ( cg.time > 0 )
13504 				{//this way we don't get that annoying change weapon sound every time a map starts
13505 					PM_AddEvent( EV_CHANGE_WEAPON );
13506 				}
13507 			}
13508 		}
13509 	}
13510 
13511 	if ( !delayed_fire )
13512 	{//didn't just finish a fire delay
13513 		if ( PM_DoChargedWeapons())
13514 		{
13515 			// In some cases the charged weapon code may want us to short circuit the rest of the firing code
13516 			return;
13517 		}
13518 		else
13519 		{
13520 			if ( !pm->gent->client->fireDelay//not already waiting to fire
13521 				&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//player
13522 				&& pm->ps->weapon == WP_THERMAL//holding thermal
13523 				&& pm->gent//gent
13524 				&& pm->gent->client//client
13525 				&& (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )//holding fire
13526 			{//delay the actual firing of the missile until the anim has played some
13527 				if ( PM_StandingAnim( pm->ps->legsAnim )
13528 					|| pm->ps->legsAnim == BOTH_THERMAL_READY )
13529 				{
13530 					PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13531 				}
13532 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13533 				pm->gent->client->fireDelay = 300;
13534 				pm->ps->weaponstate = WEAPON_FIRING;
13535 				pm->gent->alt_fire = (qboolean)(pm->cmd.buttons&BUTTON_ALT_ATTACK);
13536 				return;
13537 			}
13538 		}
13539 	}
13540 
13541  	if(!delayed_fire)
13542 	{
13543 		if ( pm->ps->weapon == WP_MELEE	&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
13544 		{//melee
13545 			if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) != (BUTTON_ATTACK|BUTTON_ALT_ATTACK) )
13546 			{//not holding both buttons
13547 				if ( (pm->cmd.buttons&BUTTON_ATTACK)&&(pm->ps->pm_flags&PMF_ATTACK_HELD) )
13548 				{//held button
13549 					//clear it
13550 					pm->cmd.buttons &= ~BUTTON_ATTACK;
13551 				}
13552 				if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)&&(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) )
13553 				{//held button
13554 					//clear it
13555 					pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
13556 				}
13557 			}
13558 		}
13559 		// check for fire
13560 		if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
13561 		{
13562 			pm->ps->weaponTime = 0;
13563 
13564 			if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
13565 			{//Still firing
13566 				pm->ps->weaponstate = WEAPON_FIRING;
13567 			}
13568 			else if ( pm->ps->weaponstate != WEAPON_READY )
13569 			{
13570 				if ( !pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time )
13571 				{
13572 					pm->ps->weaponstate = WEAPON_IDLE;
13573 				}
13574 			}
13575 
13576 			if ( pm->ps->weapon == WP_MELEE
13577 				&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())
13578 				&& PM_KickMove( pm->ps->saberMove ) )
13579 			{//melee, not attacking, clear move
13580 				pm->ps->saberMove = LS_NONE;
13581 			}
13582 			return;
13583 		}
13584 		if (pm->gent->s.m_iVehicleNum!=0)
13585 		{
13586 			// No Anims if on Veh
13587 		}
13588 
13589 		// start the animation even if out of ammo
13590 		else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER )
13591 		{
13592 			if ( pm->gent->client->moveType == MT_FLYSWIM )
13593 			{
13594 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13595 			}
13596 			else
13597 			{
13598 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13599 			}
13600 		}
13601 #ifndef BASE_SAVE_COMPAT
13602 		else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HAZARD_TROOPER )
13603 		{
13604 			// Kneel attack
13605 			//--------------
13606 			if( pm->cmd.upmove == -127 )
13607 			{
13608 				PM_SetAnim(pm,SETANIM_TORSO, BOTH_KNEELATTACK, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13609 			}
13610 			else
13611 			{
13612 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13613 			}
13614 
13615 			// Standing attack
13616 			//-----------------
13617 		}
13618 #endif
13619 		else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ASSASSIN_DROID )
13620 		{
13621 			// Crouched Attack
13622  			if (PM_CrouchAnim(pm->gent->client->ps.legsAnim))
13623 			{
13624 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS);
13625 			}
13626 
13627 			// Standing Attack
13628 			//-----------------
13629 			else
13630 			{
13631 			//	if (PM_StandingAnim(pm->gent->client->ps.legsAnim))
13632 			//	{
13633 			//		PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS);
13634 			//	}
13635 			//	else
13636 				{
13637 					PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS);
13638 				}
13639 			}
13640 		}
13641 		else
13642 		{
13643 			switch(pm->ps->weapon)
13644 			{
13645 				/*
13646 			case WP_SABER://1 - handed
13647 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13648 				break;
13649 	*/
13650 			case WP_BRYAR_PISTOL://1-handed
13651 			case WP_BLASTER_PISTOL://1-handed
13652 				if ( pm->gent && pm->gent->weaponModel[1] > 0 )
13653 				{//dual pistols
13654 					PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13655 				}
13656 				else
13657 				{//single pistol
13658 					PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13659 				}
13660 				break;
13661 
13662 			case WP_MELEE:
13663 
13664 				// since there's no RACE_BOTS, I listed all the droids that have might have melee attacks - dmv
13665 				if ( pm->gent && pm->gent->client )
13666 				{
13667 					if ( PM_DroidMelee( pm->gent->client->NPC_class ) )
13668 					{
13669 						if ( rand() & 1 )
13670 							PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
13671 						else
13672 							PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
13673 					}
13674 					else
13675 					{
13676 						int anim = -1;
13677 						if ( (pm->ps->clientNum < MAX_CLIENTS ||PM_ControlledByPlayer())
13678 							&& g_debugMelee->integer )
13679 						{
13680 							if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK) )
13681 							{
13682 								if ( (pm->cmd.buttons&BUTTON_ATTACK) )
13683 								{
13684 									PM_TryGrab();
13685 								}
13686 								else if ( !(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) )
13687 								{
13688 									PM_CheckKick();
13689 								}
13690 							}
13691 							else if ( !(pm->ps->pm_flags&PMF_ATTACK_HELD) )
13692 							{
13693 								anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE2 );
13694 							}
13695 						}
13696 						else
13697 						{
13698 							anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE2 );
13699 						}
13700 						if ( anim != -1 )
13701 						{
13702 							if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13703 							{
13704 								PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13705 							}
13706 							else
13707 							{
13708 								PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13709 							}
13710 						}
13711 					}
13712 				}
13713 				break;
13714 
13715 			case WP_TUSKEN_RIFLE:
13716 				if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13717 				{//shoot
13718 					//in alt-fire, sniper mode
13719 					PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13720 				}
13721 				else
13722 				{//melee
13723 					int anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 );	// Rifle
13724 					if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13725 					{
13726 						PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13727 					}
13728 					else
13729 					{
13730 						PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13731 					}
13732 				}
13733 				break;
13734 
13735 			case WP_TUSKEN_STAFF:
13736 
13737 				if ( pm->gent && pm->gent->client )
13738 				{
13739 					int anim;
13740 					int flags = (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
13741 					if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) )
13742 					{//player
13743 						if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13744 						{
13745 							if ( pm->cmd.buttons & BUTTON_ATTACK )
13746 							{
13747 								anim = BOTH_TUSKENATTACK3;
13748 							}
13749 							else
13750 							{
13751 								anim = BOTH_TUSKENATTACK2;
13752 							}
13753 						}
13754 						else
13755 						{
13756 							anim = BOTH_TUSKENATTACK1;
13757 						}
13758 					}
13759 					else
13760 					{// npc
13761 						if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13762 						{
13763 							anim = BOTH_TUSKENLUNGE1;
13764 							if (pm->ps->torsoAnimTimer>0)
13765 							{
13766 								flags &= ~SETANIM_FLAG_RESTART;
13767 							}
13768 						}
13769 						else
13770 						{
13771 							anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 );
13772 						}
13773 					}
13774 					if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13775 					{
13776 						PM_SetAnim( pm, SETANIM_BOTH, anim,  flags, 0);
13777 					}
13778 					else
13779 					{
13780 						PM_SetAnim( pm, SETANIM_TORSO, anim, flags, 0);
13781 					}
13782 				}
13783 				break;
13784 
13785 			case WP_NOGHRI_STICK:
13786 
13787 				if ( pm->gent && pm->gent->client )
13788 				{
13789 					int anim;
13790 					if ( pm->cmd.buttons & BUTTON_ATTACK )
13791 					{
13792 						anim = BOTH_ATTACK3;
13793 					}
13794 					else
13795 					{
13796 						anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 );
13797 					}
13798 					if ( anim != BOTH_ATTACK3 && VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 )
13799 					{
13800 						PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13801 					}
13802 					else
13803 					{
13804 						PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
13805 					}
13806 				}
13807 				break;
13808 
13809 			case WP_BLASTER:
13810 				PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
13811 				break;
13812 
13813 			case WP_DISRUPTOR:
13814 				if ( ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())&& pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE)) ||
13815 					((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomMode == 2 ) )
13816 				{//NPC or player in alt-fire, sniper mode
13817 					PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13818 				}
13819 				else
13820 				{//in primary fire mode
13821 					PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART);
13822 				}
13823 				break;
13824 
13825 			case WP_BOT_LASER:
13826 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13827 				break;
13828 
13829 			case WP_THERMAL:
13830 				if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
13831 				{
13832 					if ( PM_StandingAnim( pm->ps->legsAnim ) )
13833 					{
13834 						PM_SetAnim( pm, SETANIM_LEGS, BOTH_ATTACK10, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13835 					}
13836 					PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK10,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13837 				}
13838 				else
13839 				{
13840 					if ( cg.renderingThirdPerson )
13841 					{
13842 						if ( PM_StandingAnim( pm->ps->legsAnim )
13843 							|| pm->ps->legsAnim == BOTH_THERMAL_READY )
13844 						{
13845 							PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
13846 						}
13847 						PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//|SETANIM_FLAG_RESTART
13848 					}
13849 					else
13850 					{
13851 						PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13852 					}
13853 				}
13854 				break;
13855 
13856 			case WP_EMPLACED_GUN:
13857 				// Guess we don't play an attack animation?  Maybe we should have a custom one??
13858 				break;
13859 
13860 			case WP_NONE:
13861 				// no anim
13862 				break;
13863 
13864 			case WP_REPEATER:
13865 				if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH )
13866 				{//
13867 					if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13868 					{
13869 						PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13870 					}
13871 					else
13872 					{
13873 						PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13874 					}
13875 				}
13876 				else
13877 				{
13878 					PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13879 				}
13880 				break;
13881 
13882 			case WP_TRIP_MINE:
13883 			case WP_DET_PACK:
13884 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK11,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13885 				break;
13886 
13887 			default://2-handed heavy weapon
13888 				PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
13889 				break;
13890 			}
13891 		}
13892 	}
13893 
13894 	if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13895 	{
13896 		amount = weaponData[pm->ps->weapon].altEnergyPerShot;
13897 	}
13898 	else
13899 	{
13900 		amount = weaponData[pm->ps->weapon].energyPerShot;
13901 	}
13902 
13903 	if ( (pm->ps->weaponstate == WEAPON_CHARGING) || (pm->ps->weaponstate == WEAPON_CHARGING_ALT) )
13904 	{
13905 		// charging weapons may want to do their own ammo logic.
13906 		trueCount = PM_DoChargingAmmoUsage( &amount );
13907 	}
13908 
13909 	pm->ps->weaponstate = WEAPON_FIRING;
13910 
13911 	// take an ammo away if not infinite
13912 	if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
13913 	{
13914 		// enough energy to fire this weapon?
13915 		if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
13916 		{
13917 			pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
13918 		}
13919 		else	// Not enough energy
13920 		{
13921 			if ( !( pm->ps->eFlags & EF_LOCKED_TO_WEAPON ))
13922 			{
13923 				// Switch weapons
13924 				PM_AddEvent( EV_NOAMMO );
13925 				pm->ps->weaponTime += 500;
13926 			}
13927 			return;
13928 		}
13929 	}
13930 
13931 	if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
13932 	{//FIXME: this is going to fire off one frame before you expect, actually
13933 		// Clear these out since we're not actually firing yet
13934 		pm->ps->eFlags &= ~EF_FIRING;
13935 		pm->ps->eFlags &= ~EF_ALT_FIRING;
13936 		return;
13937 	}
13938 
13939 	if ( pm->ps->weapon == WP_EMPLACED_GUN )
13940 	{
13941 		if ( pm->gent
13942 			&& pm->gent->owner
13943 			&& pm->gent->owner->e_UseFunc == useF_eweb_use )
13944 		{//eweb always shoots alt-fire, for proper effects and sounds
13945 			PM_AddEvent( EV_ALT_FIRE );
13946 			addTime = weaponData[pm->ps->weapon].altFireTime;
13947 		}
13948 		else
13949 		{//emplaced gun always shoots normal fire
13950 			PM_AddEvent( EV_FIRE_WEAPON );
13951 			addTime = weaponData[pm->ps->weapon].fireTime;
13952 		}
13953 	}
13954 	else if ( (pm->ps->weapon == WP_MELEE && (pm->ps->clientNum>=MAX_CLIENTS||!g_debugMelee->integer) )
13955 		|| pm->ps->weapon == WP_TUSKEN_STAFF
13956 		|| (pm->ps->weapon == WP_TUSKEN_RIFLE&&!(pm->cmd.buttons&BUTTON_ALT_ATTACK))  )
13957 	{
13958 		PM_AddEvent( EV_FIRE_WEAPON );
13959 		addTime = pm->ps->torsoAnimTimer;
13960 	}
13961 	else if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
13962 	{
13963 		PM_AddEvent( EV_ALT_FIRE );
13964 		addTime = weaponData[pm->ps->weapon].altFireTime;
13965 		if ( pm->ps->weapon == WP_THERMAL )
13966 		{//threw our thermal
13967 			if ( pm->gent )
13968 			{// remove the thermal model if we had it.
13969 				G_RemoveWeaponModels( pm->gent );
13970 				if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
13971 				{//NPCs need to know when to put the thermal back in their hand
13972 					pm->ps->weaponTime = pm->ps->torsoAnimTimer-500;
13973 				}
13974 			}
13975 		}
13976 	}
13977 	else
13978 	{
13979 		if ( pm->ps->clientNum //NPC
13980 			&& !PM_ControlledByPlayer() //not under player control
13981 			&& pm->ps->weapon == WP_THERMAL //using thermals
13982 			&& pm->ps->torsoAnim != BOTH_ATTACK10 )//not in the throw anim
13983 		{//oops, got knocked out of the anim, don't throw the thermal
13984 			return;
13985 		}
13986 		PM_AddEvent( EV_FIRE_WEAPON );
13987 		addTime = weaponData[pm->ps->weapon].fireTime;
13988 
13989 		switch( pm->ps->weapon)
13990 		{
13991 		case WP_REPEATER:
13992 			// repeater is supposed to do smoke after sustained bursts
13993 			pm->ps->weaponShotCount++;
13994 			break;
13995 		case WP_BOWCASTER:
13996 			addTime *= (( trueCount < 3 ) ? 0.35f : 1.0f );// if you only did a small charge shot with the bowcaster, use less time between shots
13997 			break;
13998 		case WP_THERMAL:
13999 			if ( pm->gent )
14000 			{// remove the thermal model if we had it.
14001 				G_RemoveWeaponModels( pm->gent );
14002 				if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) )
14003 				{//NPCs need to know when to put the thermal back in their hand
14004 					pm->ps->weaponTime = pm->ps->torsoAnimTimer-500;
14005 				}
14006 			}
14007 			break;
14008 		}
14009 	}
14010 
14011 
14012 	if(!PM_ControlledByPlayer())
14013 	{
14014 		if(pm->gent && pm->gent->NPC != NULL )
14015 		{//NPCs have their own refire logic
14016 			return;
14017 		}
14018 	}
14019 
14020 	if ( g_timescale != NULL )
14021 	{
14022 		if ( g_timescale->value < 1.0f )
14023 		{
14024 			if ( !MatrixMode )
14025 			{//Special test for Matrix Mode (tm)
14026 				if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14027 				{//player always fires at normal speed
14028 					addTime *= g_timescale->value;
14029 				}
14030 				else if ( g_entities[pm->ps->clientNum].client
14031 					&& (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14032 				{
14033 					addTime *= g_timescale->value;
14034 				}
14035 			}
14036 		}
14037 	}
14038 
14039 	pm->ps->weaponTime += addTime;
14040 	pm->ps->lastShotTime = level.time;//so we know when the last time we fired our gun is
14041 
14042 	// HACK!!!!!
14043 	if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0 )
14044 	{
14045 		if ( pm->ps->weapon == WP_THERMAL || pm->ps->weapon == WP_TRIP_MINE )
14046 		{
14047 			// because these weapons have the ammo attached to the hand, we should switch weapons when the last one is thrown, otherwise it will look silly
14048 			//	NOTE: could also switch to an empty had version, but was told we aren't getting any new models at this point
14049 			CG_OutOfAmmoChange();
14050 			PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1 + 2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); // hack weapon down!
14051 			pm->ps->weaponTime = 50;
14052 		}
14053 	}
14054 }
14055 
14056 /*
14057 ==============
14058 PM_VehicleWeapon
14059 
14060 Generates weapon events and modifes the weapon counter
14061 ==============
14062 */
PM_VehicleWeapon(void)14063 static void PM_VehicleWeapon( void )
14064 {
14065 	int			addTime = 0;
14066 	qboolean	delayed_fire = qfalse;
14067 
14068 	if ( pm->ps->weapon == WP_NONE )
14069 	{
14070 		return;
14071 	}
14072 
14073 	if(pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0)
14074 	{//FIXME: this is going to fire off one frame before you expect, actually
14075 		pm->gent->client->fireDelay -= pml.msec;
14076 		if(pm->gent->client->fireDelay <= 0)
14077 		{//just finished delay timer
14078 			if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER )
14079 			{
14080 				G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav" );
14081 				pm->cmd.buttons |= BUTTON_ALT_ATTACK;
14082 			}
14083 			pm->gent->client->fireDelay = 0;
14084 			delayed_fire = qtrue;
14085 		}
14086 		else
14087 		{
14088 			if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand( 0, 1 ) )
14089 			{
14090 				G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav" );
14091 			}
14092 		}
14093 	}
14094 
14095    // don't allow attack until all buttons are up
14096 	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
14097 		return;
14098 	}
14099 
14100 	// check for dead player
14101 	if ( pm->ps->stats[STAT_HEALTH] <= 0 )
14102 	{
14103 		if ( pm->gent )
14104 		{
14105 			pm->gent->s.loopSound = 0;
14106 		}
14107 		return;
14108 	}
14109 
14110 	// make weapon function
14111 	if ( pm->ps->weaponTime > 0 ) {
14112 		pm->ps->weaponTime -= pml.msec;
14113 		if ( pm->ps->weaponTime <= 0 )
14114 		{
14115 			pm->ps->weaponTime = 0;
14116 		}
14117 	}
14118 
14119 	if ( pm->ps->weaponTime > 0 )
14120 	{
14121 		return;
14122 	}
14123 
14124 	// change weapon if time
14125 	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
14126 		PM_FinishWeaponChange();
14127 		return;
14128 	}
14129 
14130 	if ( PM_DoChargedWeapons())
14131 	{
14132 		// In some cases the charged weapon code may want us to short circuit the rest of the firing code
14133 		return;
14134 	}
14135 
14136 	if(!delayed_fire)
14137 	{
14138 		// check for fire
14139 		if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
14140 		{
14141 			pm->ps->weaponTime = 0;
14142 
14143 			if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
14144 			{//Still firing
14145 				pm->ps->weaponstate = WEAPON_FIRING;
14146 			}
14147 			else if ( pm->ps->weaponstate != WEAPON_READY )
14148 			{
14149 				if ( !pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time )
14150 				{
14151 					pm->ps->weaponstate = WEAPON_IDLE;
14152 				}
14153 			}
14154 
14155 			return;
14156 		}
14157 	}
14158 
14159 	pm->ps->weaponstate = WEAPON_FIRING;
14160 
14161 	if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
14162 	{//FIXME: this is going to fire off one frame before you expect, actually
14163 		// Clear these out since we're not actually firing yet
14164 		pm->ps->eFlags &= ~EF_FIRING;
14165 		pm->ps->eFlags &= ~EF_ALT_FIRING;
14166 		return;
14167 	}
14168 
14169 	if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
14170 	{
14171 		PM_AddEvent( EV_ALT_FIRE );
14172 		//addTime = weaponData[pm->ps->weapon].altFireTime;
14173 	}
14174 	else
14175 	{
14176 		PM_AddEvent( EV_FIRE_WEAPON );
14177 		// TODO: Use the real weapon fire time from the vehicle cfg file.
14178 		//addTime = weaponData[pm->ps->weapon].fireTime;
14179 	}
14180 
14181 /*	if(pm->gent && pm->gent->NPC != NULL )
14182 	{//NPCs have their own refire logic
14183 		return;
14184 	}*/
14185 
14186 	if ( g_timescale != NULL )
14187 	{
14188 		if ( g_timescale->value < 1.0f )
14189 		{
14190 			if ( !MatrixMode )
14191 			{//Special test for Matrix Mode (tm)
14192 				if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14193 				{//player always fires at normal speed
14194 					addTime *= g_timescale->value;
14195 				}
14196 				else if ( g_entities[pm->ps->clientNum].client
14197 					&& (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14198 				{
14199 					addTime *= g_timescale->value;
14200 				}
14201 			}
14202 		}
14203 	}
14204 
14205 	pm->ps->weaponTime += addTime;
14206 	pm->ps->lastShotTime = level.time;//so we know when the last time we fired our gun is
14207 }
14208 
14209 
14210 extern void ForceHeal( gentity_t *self );
14211 extern void ForceTelepathy( gentity_t *self );
14212 extern void ForceRage( gentity_t *self );
14213 extern void ForceProtect( gentity_t *self );
14214 extern void ForceAbsorb( gentity_t *self );
14215 extern void ForceSeeing( gentity_t *self );
PM_CheckForceUseButton(gentity_t * ent,usercmd_t * ucmd)14216 void PM_CheckForceUseButton( gentity_t *ent, usercmd_t *ucmd  )
14217 {
14218 	if ( !ent )
14219 	{
14220 		return;
14221 	}
14222 	if ( ucmd->buttons & BUTTON_USE_FORCE )
14223 	{
14224 		if (!(ent->client->ps.pm_flags & PMF_USEFORCE_HELD))
14225 		{
14226 			//impulse one shot
14227 			switch ( showPowers[cg.forcepowerSelect] )
14228 			{
14229 			case FP_HEAL:
14230 				ForceHeal( ent );
14231 				break;
14232 			case FP_SPEED:
14233 				ForceSpeed( ent );
14234 				break;
14235 			case FP_PUSH:
14236 				ForceThrow( ent, qfalse );
14237 				break;
14238 			case FP_PULL:
14239 				ForceThrow( ent, qtrue );
14240 				break;
14241 			case FP_TELEPATHY:
14242 				ForceTelepathy( ent );
14243 				break;
14244 				// Added 01/20/03 by AReis.
14245 				// New Jedi Academy powers.
14246 			case FP_RAGE:		//duration - speed, invincibility and extra damage for short period, drains your health and leaves you weak and slow afterwards.
14247 				ForceRage( ent );
14248 				break;
14249 			case FP_PROTECT:	//duration - protect against physical/energy (level 1 stops blaster/energy bolts, level 2 stops projectiles, level 3 protects against explosions)
14250 				ForceProtect( ent );
14251 				break;
14252 			case FP_ABSORB:		//duration - protect against dark force powers (grip, lightning, drain - maybe push/pull, too?)
14253 				ForceAbsorb( ent );
14254 				break;
14255 			case FP_SEE:		//duration - detect/see hidden enemies
14256 				ForceSeeing( ent );
14257 				break;
14258 			}
14259 		}
14260 		//these stay are okay to call every frame button is down
14261 		switch ( showPowers[cg.forcepowerSelect] )
14262 		{
14263 		case FP_LEVITATION:
14264 			ucmd->upmove = 127;
14265 			break;
14266 		case FP_GRIP:
14267 			ucmd->buttons |= BUTTON_FORCEGRIP;
14268 			break;
14269 		case FP_LIGHTNING:
14270 			ucmd->buttons |= BUTTON_FORCE_LIGHTNING;
14271 			break;
14272 		case FP_DRAIN:
14273 			// FIXME! Failing at WP_ForcePowerUsable(). -AReis
14274 			ucmd->buttons |= BUTTON_FORCE_DRAIN;
14275 			break;
14276 //		default:
14277 //			Com_Printf( "Use Force: Unhandled force: %d\n", showPowers[cg.forcepowerSelect]);
14278 //			break;
14279 		}
14280 		ent->client->ps.pm_flags |= PMF_USEFORCE_HELD;
14281 	}
14282 	else//not pressing USE_FORCE
14283 	{
14284 		ent->client->ps.pm_flags &= ~PMF_USEFORCE_HELD;
14285 	}
14286 }
14287 
14288 /*
14289 ================
14290 PM_ForcePower
14291 ================
14292 sends event to client for client side fx, not used
14293 */
14294 
14295 /*
14296 static void PM_ForcePower(void)
14297 {
14298 	// check for item using
14299 	if ( pm->cmd.buttons & BUTTON_USE_FORCE )
14300 	{
14301 		if ( ! ( pm->ps->pm_flags & PMF_USE_FORCE ) )
14302 		{
14303 			pm->ps->pm_flags |= PMF_USE_FORCE;
14304 			PM_AddEvent( EV_USE_FORCE);
14305 			return;
14306 		}
14307 	}
14308 	else
14309 	{
14310 		pm->ps->pm_flags &= ~PMF_USE_FORCE;
14311 	}
14312 }
14313 */
14314 
14315 /*
14316 ================
14317 PM_DropTimers
14318 ================
14319 */
PM_DropTimers(void)14320 static void PM_DropTimers( void )
14321 {
14322 	// drop misc timing counter
14323 	if ( pm->ps->pm_time )
14324 	{
14325 		if ( pml.msec >= pm->ps->pm_time )
14326 		{
14327 			pm->ps->pm_flags &= ~PMF_ALL_TIMES;
14328 			pm->ps->pm_time = 0;
14329 		}
14330 		else
14331 		{
14332 			pm->ps->pm_time -= pml.msec;
14333 		}
14334 	}
14335 
14336 	// drop legs animation counter
14337 	if ( pm->ps->legsAnimTimer > 0 )
14338 	{
14339 		int newTime = pm->ps->legsAnimTimer - pml.msec;
14340 
14341 		if ( newTime < 0 )
14342 		{
14343 			newTime = 0;
14344 		}
14345 
14346 		PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, newTime );
14347 	}
14348 
14349 	// drop torso animation counter
14350 	if ( pm->ps->torsoAnimTimer > 0 )
14351 	{
14352 		int newTime = pm->ps->torsoAnimTimer - pml.msec;
14353 
14354 		if ( newTime < 0 )
14355 		{
14356 			newTime = 0;
14357 		}
14358 
14359 		PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, newTime );
14360 	}
14361 }
14362 
PM_SetSpecialMoveValues(void)14363 void PM_SetSpecialMoveValues (void )
14364 {
14365 	Flying = 0;
14366 	if ( pm->gent )
14367 	{
14368 		if ( pm->gent->client && pm->gent->client->moveType == MT_FLYSWIM )
14369 		{
14370 			Flying = FLY_NORMAL;
14371 		}
14372 		else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
14373 		{
14374 			if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
14375 			{
14376 				Flying = FLY_VEHICLE;
14377 			}
14378 			else if ( pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
14379 			{//FIXME: or just check for hoverHeight?
14380 				Flying = FLY_HOVER;
14381 			}
14382 		}
14383 	}
14384 
14385 	if ( g_timescale != NULL )
14386 	{
14387 		if ( g_timescale->value < 1.0f )
14388 		{
14389 			if ( !MatrixMode )
14390 			{
14391 				if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14392 				{
14393 					pml.frametime *= (1.0f/g_timescale->value);
14394 				}
14395 				else if ( g_entities[pm->ps->clientNum].client
14396 					&& (pm->ps->forcePowersActive&(1<<FP_SPEED)||pm->ps->forcePowersActive&(1<<FP_RAGE)) )
14397 				{
14398 					pml.frametime *= (1.0f/g_timescale->value);
14399 				}
14400 			}
14401 		}
14402 	}
14403 }
14404 
14405 extern float cg_zoomFov;	//from cg_view.cpp
14406 
14407 //-------------------------------------------
PM_AdjustAttackStates(pmove_t * pm)14408 void PM_AdjustAttackStates( pmove_t *pm )
14409 //-------------------------------------------
14410 {
14411 	int amount;
14412 
14413 	if ( !g_saberAutoBlocking->integer
14414 		&& !g_saberNewControlScheme->integer
14415 		&& (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )
14416 	{
14417 		pm->ps->saberBlockingTime = pm->cmd.serverTime + 100;
14418 		pm->cmd.buttons &= ~BUTTON_ATTACK;
14419 		pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14420 	}
14421 	// get ammo usage
14422 	if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
14423 	{
14424 		amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot;
14425 	}
14426 	else
14427 	{
14428 		amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot;
14429 	}
14430 
14431 	if ( pm->ps->weapon == WP_SABER && (!cg.zoomMode||pm->ps->clientNum) )
14432 	{//don't let the alt-attack be interpreted as an actual attack command
14433 		if ( pm->ps->saberInFlight )
14434 		{
14435 			pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14436 			//FIXME: what about alt-attack modifier button?
14437 			if ( (!pm->ps->dualSabers || !pm->ps->saber[1].Active()) )
14438 			{//saber not in hand, can't swing it
14439 				pm->cmd.buttons &= ~BUTTON_ATTACK;
14440 			}
14441 		}
14442 		//saber staff alt-attack does a special attack anim, non-throwable sabers do kicks
14443 		if ( pm->ps->saberAnimLevel != SS_STAFF
14444 			&& !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE) )
14445 		{//using a throwable saber, so remove the saber throw button
14446 			if ( !g_saberNewControlScheme->integer
14447 				&& PM_CanDoKata() )
14448 			{//old control scheme - alt-attack + attack does kata
14449 			}
14450 			else
14451 			{//new control scheme - alt-attack doesn't have anything to do with katas, safe to clear it here
14452 				pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14453 			}
14454 		}
14455 	}
14456 
14457 	// disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player?
14458 	if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)) && pm->ps->weaponstate != WEAPON_DROPPING )
14459 	{
14460 		// we are not alt-firing yet, but the alt-attack button was just pressed and
14461 		//	we either are ducking ( in which case we don't care if they are moving )...or they are not ducking...and also not moving right/forward.
14462 		if ( !(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK)
14463 				&& ( pm->cmd.upmove < 0 || ( !pm->cmd.forwardmove && !pm->cmd.rightmove )))
14464 		{
14465 			// We just pressed the alt-fire key
14466 			if ( cg.zoomMode == 0 || cg.zoomMode == 3 )
14467 			{
14468 				G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomstart.wav" );
14469 				// not already zooming, so do it now
14470 				cg.zoomMode = 2;
14471 				cg.zoomLocked = qfalse;
14472 				cg_zoomFov = 80.0f;//(cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value;
14473 			}
14474 			else if ( cg.zoomMode == 2 )
14475 			{
14476 				G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomend.wav" );
14477 				// already zooming, so must be wanting to turn it off
14478 				cg.zoomMode = 0;
14479 				cg.zoomTime = cg.time;
14480 				cg.zoomLocked = qfalse;
14481 			}
14482 		}
14483 		else if ( !(pm->cmd.buttons & BUTTON_ALT_ATTACK ))
14484 		{
14485 			// Not pressing zoom any more
14486 			if ( cg.zoomMode == 2 )
14487 			{
14488 				// were zooming in, so now lock the zoom
14489 				cg.zoomLocked = qtrue;
14490 			}
14491 		}
14492 
14493 		if ( pm->cmd.buttons & BUTTON_ATTACK )
14494 		{
14495 			// If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll
14496 			//	just use whatever ammo was selected from above
14497 			if ( cg.zoomMode == 2 )
14498 			{
14499 				amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] -
14500 							weaponData[pm->ps->weapon].altEnergyPerShot;
14501 			}
14502 		}
14503 		else
14504 		{
14505 			// alt-fire button pressing doesn't use any ammo
14506 			amount = 0;
14507 		}
14508 
14509 	}
14510 
14511 	// Check for binocular specific mode
14512 	if ( cg.zoomMode == 1 && pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)) ) //
14513 	{
14514 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->ps->batteryCharge )
14515 		{
14516 			// zooming out
14517 			cg.zoomLocked = qfalse;
14518 			cg.zoomDir = 1;
14519 		}
14520 		else if ( pm->cmd.buttons & BUTTON_ATTACK && pm->ps->batteryCharge )
14521 		{
14522 			// zooming in
14523 			cg.zoomLocked = qfalse;
14524 			cg.zoomDir = -1;
14525 		}
14526 		else
14527 		{
14528 			// if no buttons are down, we should be in a locked state
14529 			cg.zoomLocked = qtrue;
14530 		}
14531 
14532 		// kill buttons and associated firing flags so we can't fire
14533 		pm->ps->eFlags &= ~EF_FIRING;
14534 		pm->ps->eFlags &= ~EF_ALT_FIRING;
14535 		pm->cmd.buttons &= ~(BUTTON_ALT_ATTACK|BUTTON_ATTACK);
14536 	}
14537 
14538 	// set the firing flag for continuous beam weapons, phaser will fire even if out of ammo
14539 	if ( (( pm->cmd.buttons & BUTTON_ATTACK || pm->cmd.buttons & BUTTON_ALT_ATTACK ) && ( amount >= 0 || pm->ps->weapon == WP_SABER )) )
14540 	{
14541 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
14542 		{
14543 			pm->ps->eFlags |= EF_ALT_FIRING;
14544 			if ( pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST) )
14545 			{//switch ATST barrels
14546 				pm->gent->alt_fire = qtrue;
14547 			}
14548 		}
14549 		else
14550 		{
14551 			pm->ps->eFlags &= ~EF_ALT_FIRING;
14552 			if ( pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST) )
14553 			{//switch ATST barrels
14554 				pm->gent->alt_fire = qfalse;
14555 			}
14556 		}
14557 
14558 		// This flag should always get set, even when alt-firing
14559 		pm->ps->eFlags |= EF_FIRING;
14560 	}
14561 	else
14562 	{
14563 //		int iFlags = pm->ps->eFlags;
14564 
14565 		// Clear 'em out
14566 		pm->ps->eFlags &= ~EF_FIRING;
14567 		pm->ps->eFlags &= ~EF_ALT_FIRING;
14568 
14569 		// if I don't check the flags before stopping FX then it switches them off too often, which tones down
14570 		//	the stronger FFFX so you can hardly feel them. However, if you only do iton these flags then the
14571 		//	repeat-fire weapons like tetrion and dreadnought don't switch off quick enough. So...
14572 		//
14573 /* // Might need this for beam type weapons
14574 		if ( pm->ps->weapon == WP_DREADNOUGHT || (iFlags & (EF_FIRING|EF_ALT_FIRING) )
14575 		{
14576 			cgi_FF_StopAllFX();
14577 		}
14578 		*/
14579 	}
14580 
14581 	// disruptor should convert a main fire to an alt-fire if the gun is currently zoomed
14582 	if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.number<MAX_CLIENTS||G_ControlledByPlayer(pm->gent)) )
14583 	{
14584 		if ( pm->cmd.buttons & BUTTON_ATTACK && cg.zoomMode == 2 )
14585 		{
14586 			// converting the main fire to an alt-fire
14587 			pm->cmd.buttons |= BUTTON_ALT_ATTACK;
14588 			pm->ps->eFlags |= EF_ALT_FIRING;
14589 		}
14590 		else
14591 		{
14592 			// don't let an alt-fire through
14593 			pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
14594 		}
14595 	}
14596 }
14597 
PM_WeaponOkOnVehicle(int weapon)14598 qboolean PM_WeaponOkOnVehicle( int weapon )
14599 {
14600 	//FIXME: check g_vehicleInfo for our vehicle?
14601 	switch ( weapon )
14602 	{
14603 	case WP_NONE:
14604 	case WP_SABER:
14605 	case WP_BLASTER:
14606 	case WP_THERMAL:
14607 		return qtrue;
14608 		break;
14609 	}
14610 	return qfalse;
14611 }
14612 
PM_CheckInVehicleSaberAttackAnim(void)14613 void PM_CheckInVehicleSaberAttackAnim( void )
14614 {//A bit of a hack, but makes the vehicle saber attacks act like any other saber attack...
14615 	// make weapon function
14616 	if ( pm->ps->weaponTime > 0 ) {
14617 		pm->ps->weaponTime -= pml.msec;
14618 		if ( pm->ps->weaponTime <= 0 )
14619 		{
14620 			pm->ps->weaponTime = 0;
14621 		}
14622 	}
14623 	PM_CheckClearSaberBlock();
14624 
14625 /*	if ( PM_SaberBlocking() )
14626 	{//busy blocking, don't do attacks
14627 		return;
14628 	}
14629 */
14630 	saberMoveName_t saberMove = LS_INVALID;
14631 	switch ( pm->ps->torsoAnim )
14632 	{
14633 	case BOTH_VS_ATR_S:
14634 		saberMove = LS_SWOOP_ATTACK_RIGHT;
14635 		break;
14636 	case BOTH_VS_ATL_S:
14637 		saberMove = LS_SWOOP_ATTACK_LEFT;
14638 		break;
14639 	case BOTH_VT_ATR_S:
14640 		saberMove = LS_TAUNTAUN_ATTACK_RIGHT;
14641 		break;
14642 	case BOTH_VT_ATL_S:
14643 		saberMove = LS_TAUNTAUN_ATTACK_LEFT;
14644 		break;
14645 	}
14646 	if ( saberMove != LS_INVALID )
14647 	{
14648  		if ( pm->ps->saberMove == saberMove )
14649 		{//already playing it
14650 			if ( !pm->ps->torsoAnimTimer )
14651 			{//anim was done, set it back to ready
14652  				PM_SetSaberMove( LS_READY );
14653 				pm->ps->saberMove = LS_READY;
14654 				pm->ps->weaponstate = WEAPON_IDLE;
14655 				if (pm->cmd.buttons&BUTTON_ATTACK)
14656 				{
14657 					if ( !pm->ps->weaponTime )
14658 					{
14659 	 					PM_SetSaberMove( saberMove );
14660 						pm->ps->weaponstate = WEAPON_FIRING;
14661 						pm->ps->weaponTime = pm->ps->torsoAnimTimer;
14662 					}
14663 				}
14664 			}
14665 		}
14666 		else if ( pm->ps->torsoAnimTimer
14667 			&& !pm->ps->weaponTime )
14668 		{
14669  			PM_SetSaberMove( LS_READY );
14670 			pm->ps->saberMove = LS_READY;
14671 			pm->ps->weaponstate = WEAPON_IDLE;
14672 			PM_SetSaberMove( saberMove );
14673 			pm->ps->weaponstate = WEAPON_FIRING;
14674 			pm->ps->weaponTime = pm->ps->torsoAnimTimer;
14675 		}
14676 	}
14677 	pm->ps->saberBlocking = saberMoveData[pm->ps->saberMove].blocking;
14678 }
14679 
14680 //force the vehicle to turn and travel to its forced destination point
PM_VehForcedTurning(gentity_t * veh)14681 void PM_VehForcedTurning( gentity_t *veh )
14682 {
14683 	gentity_t *dst = &g_entities[pm->ps->vehTurnaroundIndex];
14684 	float pitchD, yawD;
14685 	vec3_t dir;
14686 
14687 	if (!veh || !veh->m_pVehicle)
14688 	{
14689 		return;
14690 	}
14691 
14692 	if (!dst)
14693 	{ //can't find dest ent?
14694 		return;
14695 	}
14696 
14697 	pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
14698 	pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
14699 	pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
14700 
14701 	VectorSubtract(dst->s.origin, veh->currentOrigin, dir);
14702 	vectoangles(dir, dir);
14703 
14704 	yawD = AngleSubtract(pm->ps->viewangles[YAW], dir[YAW]);
14705 	pitchD = AngleSubtract(pm->ps->viewangles[PITCH], dir[PITCH]);
14706 
14707 	yawD *= 0.2f*pml.frametime;
14708 	pitchD *= 0.6f*pml.frametime;
14709 
14710 	pm->ps->viewangles[YAW] = AngleSubtract(pm->ps->viewangles[YAW], yawD);
14711 	pm->ps->viewangles[PITCH] = AngleSubtract(pm->ps->viewangles[PITCH], pitchD);
14712 
14713 	//PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
14714 	SetClientViewAngle(pm->gent, pm->ps->viewangles);
14715 }
14716 /*
14717 ================
14718 Pmove
14719 
14720 Can be called by either the server or the client
14721 ================
14722 */
Pmove(pmove_t * pmove)14723 void Pmove( pmove_t *pmove )
14724 {
14725 	Vehicle_t *pVeh = NULL;
14726 
14727 	pm = pmove;
14728 
14729 	// this counter lets us debug movement problems with a journal by setting a conditional breakpoint fot the previous frame
14730 	c_pmove++;
14731 
14732 	// clear results
14733 	pm->numtouch = 0;
14734 	pm->watertype = 0;
14735 	pm->waterlevel = 0;
14736 
14737 	// Clear the blocked flag
14738 	//pm->ps->pm_flags &= ~PMF_BLOCKED;
14739 	pm->ps->pm_flags &= ~PMF_BUMPED;
14740 
14741 	// In certain situations, we may want to control which attack buttons are pressed and what kind of functionality
14742 	//	is attached to them
14743 	PM_AdjustAttackStates( pm );
14744 
14745 	// clear the respawned flag if attack and use are cleared
14746 	if ( pm->ps->stats[STAT_HEALTH] > 0 &&
14747 		!( pm->cmd.buttons & BUTTON_ATTACK ) )
14748 	{
14749 		pm->ps->pm_flags &= ~PMF_RESPAWNED;
14750 	}
14751 
14752 	// clear all pmove local vars
14753 	memset (&pml, 0, sizeof(pml));
14754 
14755 	// determine the time
14756 	pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
14757 	if ( pml.msec < 1 ) {
14758 		pml.msec = 1;
14759 	} else if ( pml.msec > 200 ) {
14760 		pml.msec = 200;
14761 	}
14762 
14763 	pm->ps->commandTime = pmove->cmd.serverTime;
14764 
14765 	// save old org in case we get stuck
14766 	VectorCopy (pm->ps->origin, pml.previous_origin);
14767 
14768 	// save old velocity for crashlanding
14769 	VectorCopy (pm->ps->velocity, pml.previous_velocity);
14770 
14771 	pml.frametime = pml.msec * 0.001;
14772 
14773 	if ( pm->ps->clientNum >= MAX_CLIENTS &&
14774 		pm->gent &&
14775 		pm->gent->client &&
14776 		pm->gent->client->NPC_class == CLASS_VEHICLE )
14777 	{ //we are a vehicle
14778 		pVeh = pm->gent->m_pVehicle;
14779 		assert( pVeh );
14780 		if ( pVeh )
14781 		{
14782 			pVeh->m_fTimeModifier = (pml.frametime*60.0f);//at 16.67ms (60fps), should be 1.0f
14783 		}
14784 	}
14785 	else if ( pm->gent && PM_RidingVehicle() )
14786 	{
14787 		if ( pm->ps->vehTurnaroundIndex
14788 			&& pm->ps->vehTurnaroundTime > pm->cmd.serverTime )
14789 		{ //riding this vehicle, turn my view too
14790 			PM_VehForcedTurning( &g_entities[pm->gent->s.m_iVehicleNum] );
14791 		}
14792 	}
14793 
14794 	PM_SetSpecialMoveValues();
14795 
14796 	// update the viewangles
14797 	PM_UpdateViewAngles( pm->ps, &pm->cmd, pm->gent);
14798 
14799 	AngleVectors ( pm->ps->viewangles, pml.forward, pml.right, pml.up );
14800 
14801 	if ( pm->cmd.upmove < 10 ) {
14802 		// not holding jump
14803 		pm->ps->pm_flags &= ~PMF_JUMP_HELD;
14804 	}
14805 
14806 	// decide if backpedaling animations should be used
14807 	if ( pm->cmd.forwardmove < 0 ) {
14808 		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
14809 	} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
14810 		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
14811 	}
14812 
14813 	if ( pm->ps->pm_type >= PM_DEAD ) {
14814 		pm->cmd.forwardmove = 0;
14815 		pm->cmd.rightmove = 0;
14816 		pm->cmd.upmove = 0;
14817 		if ( pm->ps->viewheight > -12 )
14818 		{//slowly sink view to ground
14819 			pm->ps->viewheight -= 1;
14820 		}
14821 	}
14822 
14823 	if ( pm->ps->pm_type == PM_SPECTATOR ) {
14824 		PM_CheckDuck ();
14825 		PM_FlyMove ();
14826 		PM_DropTimers ();
14827 		return;
14828 	}
14829 
14830 	if ( pm->ps->pm_type == PM_NOCLIP ) {
14831 		PM_NoclipMove ();
14832 		PM_DropTimers ();
14833 		return;
14834 	}
14835 
14836 	if (pm->ps->pm_type == PM_FREEZE) {
14837 		return;		// no movement at all
14838 	}
14839 
14840 	if ( pm->ps->pm_type == PM_INTERMISSION ) {
14841 		return;		// no movement at all
14842 	}
14843 
14844 	if ( pm->ps->pm_flags & PMF_SLOW_MO_FALL )
14845 	{//half grav
14846 		pm->ps->gravity *= 0.5;
14847 	}
14848 
14849 	// set watertype, and waterlevel
14850 	PM_SetWaterLevelAtPoint( pm->ps->origin, &pm->waterlevel, &pm->watertype );
14851 
14852 	PM_SetWaterHeight();
14853 
14854 	if ( !(pm->watertype & CONTENTS_LADDER) )
14855 	{//Don't want to remember this for ladders, is only for waterlevel change events (sounds)
14856 		pml.previous_waterlevel = pmove->waterlevel;
14857 	}
14858 
14859 
14860 	waterForceJump = qfalse;
14861 	if ( pmove->waterlevel && pm->ps->clientNum )
14862 	{
14863 		if ( pm->ps->forceJumpZStart//force jumping
14864 			||(pm->gent&&pm->gent->NPC && level.time<pm->gent->NPC->jumpTime)) //TIMER_Done(pm->gent, "forceJumpChasing" )) )//force-jumping
14865 		{
14866 			waterForceJump = qtrue;
14867 		}
14868 	}
14869 
14870 	// set mins, maxs, and viewheight
14871 	PM_SetBounds();
14872 
14873 	if ( !Flying && !(pm->watertype & CONTENTS_LADDER) && pm->ps->pm_type != PM_DEAD )
14874 	{//NOTE: noclippers shouldn't jump or duck either, no?
14875 		PM_CheckDuck();
14876 	}
14877 
14878 	// set groundentity
14879 	PM_GroundTrace();
14880 	if ( Flying == FLY_HOVER )
14881 	{//never stick to the ground
14882 		PM_HoverTrace();
14883 	}
14884 
14885 	if ( pm->ps->pm_type == PM_DEAD ) {
14886 		PM_DeadMove ();
14887 	}
14888 
14889 	PM_DropTimers();
14890 
14891 	/*
14892 	if ( PM_RidingVehicle() )
14893 	{
14894 		PM_NoclipMove();
14895 	}
14896 	else */if ( pm->ps && ( (pm->ps->eFlags&EF_LOCKED_TO_WEAPON)
14897 							|| (pm->ps->eFlags&EF_HELD_BY_RANCOR)
14898 							|| (pm->ps->eFlags&EF_HELD_BY_WAMPA)
14899 							|| (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE) ) )
14900 	{//in an emplaced gun
14901 		PM_NoclipMove();
14902 	}
14903 	else if ( Flying == FLY_NORMAL )//|| pm->ps->gravity <= 0 )
14904 	{
14905 		// flight powerup doesn't allow jump and has different friction
14906 		PM_FlyMove();
14907 	}
14908 	else if ( Flying == FLY_VEHICLE )
14909 	{
14910 		PM_FlyVehicleMove();
14911 	}
14912 	else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP )
14913 	{
14914 		PM_WaterJumpMove();
14915 	}
14916 	else if ( pm->waterlevel > 1 //in water
14917 			 &&((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || !waterForceJump) )//player or NPC not force jumping
14918 	{//force-jumping NPCs should
14919 		// swimming or in ladder
14920 		PM_WaterMove();
14921 	}
14922 	else if (pm->gent && pm->gent->NPC && pm->gent->NPC->jumpTime!=0)
14923 	{
14924 		ucmd.forwardmove = 0;
14925 		ucmd.rightmove = 0;
14926 		ucmd.upmove = 0;
14927 		pm->ps->speed = 0;
14928 		VectorClear(pm->ps->moveDir);
14929 
14930 		PM_AirMove();
14931 	}
14932 	else if ( pml.walking )
14933 	{// walking on ground
14934 		vec3_t	oldOrg;
14935 
14936 		VectorCopy( pm->ps->origin, oldOrg );
14937 
14938 		PM_WalkMove();
14939 
14940 
14941 		float threshHold = 0.001f, movedDist = DistanceSquared( oldOrg, pm->ps->origin );
14942 		if ( PM_StandingAnim( pm->ps->legsAnim ) || pm->ps->legsAnim == BOTH_CROUCH1 )
14943 		{
14944 			threshHold = 0.005f;
14945 		}
14946 
14947 		if ( movedDist < threshHold )
14948 		{//didn't move, play no legs anim
14949 		//	pm->cmd.forwardmove = pm->cmd.rightmove = 0;
14950 		}
14951 	}
14952 	else
14953 	{
14954 		if ( pm->ps->gravity <= 0 )
14955 		{
14956 			PM_FlyMove();
14957 		}
14958 		else
14959 		{
14960 			// airborne
14961 			PM_AirMove();
14962 		}
14963 	}
14964 
14965 	//PM_Animate();
14966 
14967 	// If we didn't move at all, then why bother doing this again -MW.
14968 	if(!(VectorCompare(pm->ps->origin,pml.previous_origin)))
14969 	{
14970 		PM_GroundTrace();
14971 		if ( Flying == FLY_HOVER )
14972 		{//never stick to the ground
14973 			PM_HoverTrace();
14974 		}
14975 	}
14976 
14977 	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
14978 	{//on ground
14979 		pm->ps->forceJumpZStart = 0;
14980 		pm->ps->jumpZStart = 0;
14981 		pm->ps->pm_flags &= ~PMF_JUMPING;
14982 		pm->ps->pm_flags &= ~PMF_TRIGGER_PUSHED;
14983 		pm->ps->pm_flags &= ~PMF_SLOW_MO_FALL;
14984 	}
14985 
14986 	// If we didn't move at all, then why bother doing this again -MW.
14987 	// Note: ok, so long as we don't have water levels that change.
14988 	if(!(VectorCompare(pm->ps->origin,pml.previous_origin)))
14989 	{
14990 		PM_SetWaterLevelAtPoint( pm->ps->origin, &pm->waterlevel, &pm->watertype );
14991 		PM_SetWaterHeight();
14992 	}
14993 
14994 //	PM_ForcePower(); sends event to client for client side fx, not used
14995 
14996 	// If we're a vehicle, do our special weapon function.
14997 	if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE )
14998 	{
14999 		pVeh = pm->gent->m_pVehicle;
15000 
15001 		// Using vehicle weapon...
15002 		//if ( pm->cmd.weapon == WP_NONE )
15003 		{
15004 			//PM_Weapon();
15005 			//PM_AddEvent( EV_FIRE_WEAPON );
15006 			PM_VehicleWeapon();
15007 		}
15008 	}
15009 	// If we are riding a vehicle...
15010 	else if ( PM_RidingVehicle() )
15011 	{
15012 		if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
15013 		{// alt attack always does other stuff when riding a vehicle (turbo)
15014 		}
15015 		else if ( (pm->ps->eFlags&EF_NODRAW) )
15016 		{//inside a vehicle?  don't do any weapon stuff
15017 		}
15018 		else if ( pm->ps->weapon == WP_BLASTER//using blaster
15019 		 || pm->ps->weapon == WP_THERMAL//using thermal
15020 		 || pm->ps->weaponstate == WEAPON_DROPPING//changing weapon - dropping
15021 		 || pm->ps->weaponstate == WEAPON_RAISING//changing weapon - raising
15022 		 || (pm->cmd.weapon != pm->ps->weapon && PM_WeaponOkOnVehicle( pm->cmd.weapon )) )//FIXME: make this a vehicle call to see if this new weapon is valid for this vehicle
15023 		{//either weilding a weapon we can fire with normal weapon logic, or trying to change to a valid weapon
15024 			// call normal weapons code... should we override the normal fire anims with vehicle fire anims in here or in a subsequent call to VehicleWeapons or something?
15025 			//Maybe break PM_Weapon into PM_Weapon and PM_WeaponAnimate (then call our own PM_VehicleWeaponAnimate)?
15026 			PM_Weapon();
15027 		}
15028 		//BUT: now call Vehicle's weapon code, to handle lightsaber and (maybe) overriding weapon ready/firing anims?
15029 	}
15030 	// otherwise do the normal weapon function.
15031 	else
15032 	{
15033 		// weapons
15034 		PM_Weapon();
15035 	}
15036 	if ( pm->cmd.buttons & BUTTON_ATTACK )
15037 	{
15038 		pm->ps->pm_flags |= PMF_ATTACK_HELD;
15039 	}
15040 	else
15041 	{
15042 		pm->ps->pm_flags &= ~PMF_ATTACK_HELD;
15043 	}
15044 	if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
15045 	{
15046 		pm->ps->pm_flags |= PMF_ALT_ATTACK_HELD;
15047 	}
15048 	else
15049 	{
15050 		pm->ps->pm_flags &= ~PMF_ALT_ATTACK_HELD;
15051 	}
15052 	if ( pm->cmd.buttons & BUTTON_FORCE_FOCUS )
15053 	{
15054 		pm->ps->pm_flags |= PMF_FORCE_FOCUS_HELD;
15055 	}
15056 	else
15057 	{
15058 		pm->ps->pm_flags &= ~PMF_FORCE_FOCUS_HELD;
15059 	}
15060 
15061 	if ( pm->gent )//&& pm->gent->s.number == 0 )//player only?
15062 	{
15063 		// Use
15064 		PM_Use();
15065 	}
15066 
15067 	// Calculate the resulting speed of the last pmove
15068 	//-------------------------------------------------
15069 	if ( pm->gent )
15070 	{
15071 		pm->gent->resultspeed = ((Distance(pm->ps->origin, pm->gent->currentOrigin) / pml.msec) * 1000);
15072 		if (pm->gent->resultspeed>5.0f)
15073 		{
15074 			pm->gent->lastMoveTime = level.time;
15075 		}
15076 
15077 		// If Have Not Been Moving For A While, Stop
15078 		//-------------------------------------------
15079 		if (pml.walking && (level.time - pm->gent->lastMoveTime)>1000)
15080 		{
15081 			pm->cmd.forwardmove = pm->cmd.rightmove = 0;
15082 		}
15083 	}
15084 
15085 
15086 	// ANIMATION
15087 	//================================
15088 
15089 	// TEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMP
15090 	if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON )
15091 	{
15092 		if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but...
15093 		{//full body
15094 			PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL
15095 		}
15096 		else
15097 		{//stand (or could be overridden by strafe anims)
15098 			PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
15099 		}
15100 	}
15101 	else if ( pm->gent && pm->ps && (pm->ps->eFlags&EF_HELD_BY_RANCOR) )
15102 	{
15103 		PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL
15104 	}
15105 	// If we are a vehicle, animate...
15106 	else if ( pVeh )
15107 	{
15108 		pVeh->m_pVehicleInfo->Animate( pVeh );
15109 	}
15110 	// If we're riding a vehicle, don't do anything!.
15111 	else if ( ( pVeh = PM_RidingVehicle() ) != 0 )
15112 	{
15113 		PM_CheckInVehicleSaberAttackAnim();
15114 	}
15115 	else // TEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMP
15116 	{
15117 		// footstep events / legs animations
15118 		PM_Footsteps();
15119 	}
15120 	// torso animation
15121 	if ( !pVeh )
15122 	{//not riding a vehicle
15123 		PM_TorsoAnimation();
15124 	}
15125 
15126 	// entering / leaving water splashes
15127 	PM_WaterEvents();
15128 
15129 	// snap some parts of playerstate to save network bandwidth
15130 	// SnapVector( pm->ps->velocity );
15131 
15132 	if ( !pm->cmd.rightmove && !pm->cmd.forwardmove && pm->cmd.upmove <= 0 )
15133 	{
15134 		if ( VectorCompare( pm->ps->velocity, vec3_origin ) )
15135 		{
15136 			pm->ps->lastStationary = level.time;
15137 		}
15138 	}
15139 
15140 	if ( pm->ps->pm_flags & PMF_SLOW_MO_FALL )
15141 	{//half grav
15142 		pm->ps->gravity *= 2;
15143 	}
15144 }
15145