

// bg_pmove.c -- both games player movement code
// takes a playerstate and a usercmd as input and returns a modifed playerstate

#include "q_shared.h"
#include "bg_public.h"
#include "bg_local.h"

// Rafael gameskill
int bg_pmove_gameskill_integer;
// done

// JPW NERVE -- stuck this here so it can be seen client & server side
float Com_GetFlamethrowerRange(void) {
	if (pm->gametype != GT_SINGLE_PLAYER)
		return 2500; // multiplayer range is longer for balance
	else
		return 850; // single player range remains unchanged
}
// jpw

pmove_t		*pm;
pml_t		pml;

// movement parameters
float	pm_stopspeed = 100;
//float	pm_duckScale = 0.25;

//----(SA)	modified
float	pm_waterSwimScale	= 0.5;
float	pm_waterWadeScale	= 0.70;
float	pm_slagSwimScale	= 0.30;
float	pm_slagWadeScale	= 0.70;

float	pm_accelerate		= 10;
float	pm_airaccelerate	= 1;
float	pm_wateraccelerate	= 4;
float	pm_slagaccelerate	= 2;
float	pm_flyaccelerate	= 8;

float	pm_friction			= 6;
float	pm_waterfriction	= 1;
float	pm_slagfriction		= 1;
float	pm_flightfriction	= 3;
float	pm_ladderfriction	= 14;
float	pm_spectatorfriction = 5.0f;

//----(SA)	end

int		c_pmove = 0;

/*
===============
PM_AddEvent

===============
*/
void PM_AddEvent( int newEvent ) {
	BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
}

/*
===============
PM_AddTouchEnt
===============
*/
void PM_AddTouchEnt( int entityNum ) {
	int		i;

	if ( entityNum == ENTITYNUM_WORLD ) {
		return;
	}
	if ( pm->numtouch == MAXTOUCH ) {
		return;
	}

	// see if it is already added
	for ( i = 0 ; i < pm->numtouch ; i++ ) {
		if ( pm->touchents[ i ] == entityNum ) {
			return;
		}
	}

	// add it
	pm->touchents[pm->numtouch] = entityNum;
	pm->numtouch++;
}

/*
==============
PM_StartWeaponAnim
==============
*/
static void PM_StartWeaponAnim( int anim ) {
	if ( pm->ps->pm_type >= PM_DEAD )
		return;
	
	if ( pm->ps->weapAnimTimer > 0 )
		return;

	if(pm->cmd.weapon == WP_NONE)
		return;

	pm->ps->weapAnim = ( ( pm->ps->weapAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;	
}

static void PM_ContinueWeaponAnim( int anim ) {
	if(pm->cmd.weapon == WP_NONE)
		return;

	if ( ( pm->ps->weapAnim & ~ANIM_TOGGLEBIT ) == anim ) {
		return;
	}
	if ( pm->ps->weapAnimTimer > 0 ) {
		return;		// a high priority animation is running
	}
	PM_StartWeaponAnim( anim );
}

/*
==================
PM_ClipVelocity

Slide off of the impacting surface
==================
*/
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
	float	backoff;
	float	change;
	int		i;
	
	backoff = DotProduct (in, normal);
	
	if ( backoff < 0 ) {
		backoff *= overbounce;
	} else {
		backoff /= overbounce;
	}

	for ( i=0 ; i<3 ; i++ ) {
		change = normal[i]*backoff;
		out[i] = in[i] - change;
	}
}

/*
========================
PM_ExertSound

plays random exertion sound when sprint key is press
========================
*/
static void PM_ExertSound (void)
{
	int rval;
	static int	oldexerttime = 0;
	static int	oldexertcnt = 0;

	if (pm->cmd.serverTime > oldexerttime + 500)
		oldexerttime = pm->cmd.serverTime;
	else
		return;
	
	rval = rand()%3;

	if (oldexertcnt != rval)
		 oldexertcnt = rval;
	else
		oldexertcnt++;
	
	if (oldexertcnt > 2)
		oldexertcnt = 0;
				
	if (oldexertcnt == 1)
		PM_AddEvent (EV_EXERT2);
	else if (oldexertcnt == 2)
		PM_AddEvent (EV_EXERT3);
	else
		PM_AddEvent (EV_EXERT1);
}


/*
==================
PM_Friction

Handles both ground friction and water friction
==================
*/
static void PM_Friction( void ) {
	vec3_t	vec;
	float	*vel;
	float	speed, newspeed, control;
	float	drop;
	
	vel = pm->ps->velocity;
	
	VectorCopy( vel, vec );
	if ( pml.walking ) {
		vec[2] = 0;	// ignore slope movement
	}

	speed = VectorLength(vec);
	if (speed < 1) {
		vel[0] = 0;
		vel[1] = 0;		// allow sinking underwater
		// FIXME: still have z friction underwater?
		return;
	}

	drop = 0;

	// apply ground friction
	if ( pm->waterlevel <= 1 ) {
		if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
			// if getting knocked back, no friction
			if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
				control = speed < pm_stopspeed ? pm_stopspeed : speed;
				drop += control*pm_friction*pml.frametime;
			}
		}
	}

	// apply water friction even if just wading
	if ( pm->waterlevel ) {	
		if ( pm->watertype == CONTENTS_SLIME )	//----(SA)	slag
			drop += speed*pm_slagfriction*pm->waterlevel*pml.frametime;
		else
			drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
	}

	// apply flying friction
	if ( pm->ps->powerups[PW_FLIGHT]) {
		drop += speed*pm_flightfriction*pml.frametime;
	}

	if ( pm->ps->pm_type == PM_SPECTATOR) {
		drop += speed*pm_spectatorfriction*pml.frametime;
	}

	// apply ladder strafe friction
	if ( pml.ladder ) {
		drop += speed*pm_ladderfriction*pml.frametime;
	}

	// scale the velocity
	newspeed = speed - drop;
	if (newspeed < 0) {
		newspeed = 0;
	}
	newspeed /= speed;

	vel[0] = vel[0] * newspeed;
	vel[1] = vel[1] * newspeed;
	vel[2] = vel[2] * newspeed;
}


/*
==============
PM_Accelerate

Handles user intended acceleration
==============
*/
static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {
#if 1
	// q2 style
	int			i;
	float		addspeed, accelspeed, currentspeed;

	currentspeed = DotProduct (pm->ps->velocity, wishdir);
	addspeed = wishspeed - currentspeed;
	if (addspeed <= 0) {
		return;
	}
	accelspeed = accel*pml.frametime*wishspeed;
	if (accelspeed > addspeed) {
		accelspeed = addspeed;
	}
	
	// Ridah, variable friction for AI's
	if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {
		accelspeed *= (1.0 / pm->ps->friction);
	}
	if (accelspeed > addspeed) {
		accelspeed = addspeed;
	}

	for (i=0 ; i<3 ; i++) {
		pm->ps->velocity[i] += accelspeed*wishdir[i];	
	}
#else
	// proper way (avoids strafe jump maxspeed bug), but feels bad
	vec3_t		wishVelocity;
	vec3_t		pushDir;
	float		pushLen;
	float		canPush;

	VectorScale( wishdir, wishspeed, wishVelocity );
	VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
	pushLen = VectorNormalize( pushDir );

	canPush = accel*pml.frametime*wishspeed;
	if (canPush > pushLen) {
		canPush = pushLen;
	}

	VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
#endif
}



/*
============
PM_CmdScale

Returns the scale factor to apply to cmd movements
This allows the clients to use axial -127 to 127 values for all directions
without getting a sqrt(2) distortion in speed.
============
*/
static float PM_CmdScale( usercmd_t *cmd ) {
	int		max;
	float	total;
	float	scale;

	if (pm->ps->aiChar) {
		// restrict AI character movements (don't strafe or run backwards as fast as they can run forwards)
		if (cmd->forwardmove < -64.0) {
			cmd->forwardmove = -64.0;
		}
		if (cmd->rightmove > 64.0) {
			cmd->rightmove = 64.0;
		} else if (cmd->rightmove < -64.0) {
			cmd->rightmove = -64.0;
		}
	}

	max = abs( cmd->forwardmove );
	if ( abs( cmd->rightmove ) > max ) {
		max = abs( cmd->rightmove );
	}
	if ( abs( cmd->upmove ) > max ) {
		max = abs( cmd->upmove );
	}
	if ( !max ) {
		return 0;
	}

	total = sqrt( cmd->forwardmove * cmd->forwardmove
		+ cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
	scale = (float)pm->ps->speed * max / ( 127.0 * total );

	if (pm->cmd.buttons & BUTTON_SPRINT && pm->ps->sprintTime > 50)
	{
		scale *= pm->ps->sprintSpeedScale;
	}
	else
		scale *= pm->ps->runSpeedScale;
	
	if (pm->ps->pm_type == PM_NOCLIP)
		scale *= 3;

// JPW NERVE -- half move speed if heavy weapon is carried
// this is the counterstrike way of doing it -- ie you can switch to a non-heavy weapon and move at
// full speed.  not completely realistic (well, sure, you can run faster with the weapon strapped to your
// back than in carry position) but more fun to play.  If it doesn't play well this way we'll bog down the
// player if the own the weapon at all.
//
// added #ifdef for game/cgame to project so we can get correct g_gametype variable and only do this in
// multiplayer if necessary
	if (pm->gametype != GT_SINGLE_PLAYER)
	{
		if ((pm->ps->weapon == WP_VENOM) || (pm->ps->weapon == WP_VENOM_FULL) || (pm->ps->weapon == WP_PANZERFAUST))
			scale *= 0.5;
		if (pm->ps->weapon == WP_FLAMETHROWER) // trying some different balance for the FT
			scale *= 0.7; 
	}
// jpw

	return scale;
}


/*
================
PM_SetMovementDir

Determine the rotation of the legs reletive
to the facing dir
================
*/
static void PM_SetMovementDir( void ) {
// Ridah, changed this for more realistic angles (at the cost of more network traffic?)
#if 1
	float	speed;
	vec3_t	moved;
	int moveyaw;

	VectorSubtract (pm->ps->origin, pml.previous_origin, moved);

	if (	(pm->cmd.forwardmove || pm->cmd.rightmove)
		&&	(pm->ps->groundEntityNum != ENTITYNUM_NONE)
		&&	(speed = VectorLength( moved ))
		&&	(speed > pml.frametime*5))		// if moving slower than 20 units per second, just face head angles
	{
		vec3_t	dir;

		VectorNormalize2( moved, dir );
		vectoangles( dir, dir );

		moveyaw = (int)AngleDelta( dir[YAW], pm->ps->viewangles[YAW] );

		if (pm->cmd.forwardmove < 0)
			moveyaw = (int)AngleNormalize180(moveyaw + 180);

		if (abs(moveyaw) > 75)
		{
			if (moveyaw > 0)
			{
				moveyaw = 75;
			}
			else
			{
				moveyaw = -75;
			}
		}

		pm->ps->movementDir = (signed char)moveyaw;
	}
	else
	{
		pm->ps->movementDir = 0;
	}
#else
	if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
		if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
			pm->ps->movementDir = 0;
		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
			pm->ps->movementDir = 1;
		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
			pm->ps->movementDir = 2;
		} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
			pm->ps->movementDir = 3;
		} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
			pm->ps->movementDir = 4;
		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
			pm->ps->movementDir = 5;
		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
			pm->ps->movementDir = 6;
		} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
			pm->ps->movementDir = 7;
		}
	} else {
		// if they aren't actively going directly sideways,
		// change the animation to the diagonal so they
		// don't stop too crooked
		if ( pm->ps->movementDir == 2 ) {
			pm->ps->movementDir = 1;
		} else if ( pm->ps->movementDir == 6 ) {
			pm->ps->movementDir = 7;
		} 
	}
#endif
}


/*
=============
PM_CheckJump
=============
*/
static qboolean PM_CheckJump( void ) {
	// JPW NERVE -- jumping in multiplayer uses and requires sprint juice (to prevent turbo skating, sprint + jumps)
	if (pm->gametype != GT_SINGLE_PLAYER) {
		// don't allow jump accel
		if (pm->cmd.serverTime - pm->ps->jumpTime < 850)
			return qfalse;

		// don't allow if player tired 
//		if (pm->ps->sprintTime < 2500) // JPW pulled this per id request; made airborne jumpers wildly inaccurate with gunfire to compensate
//			return qfalse;
	}
	// jpw

	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
		return qfalse;		// don't allow jump until all buttons are up
	}

	if ( pm->cmd.upmove < 10 ) {
		// not holding jump
		return qfalse;
	}

	// must wait for jump to be released
	if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
		// clear upmove so cmdscale doesn't lower running speed
		pm->cmd.upmove = 0;
		return qfalse;
	}

	pml.groundPlane = qfalse;		// jumping away
	pml.walking = qfalse;
	pm->ps->pm_flags |= PMF_JUMP_HELD;

	pm->ps->groundEntityNum = ENTITYNUM_NONE;
	pm->ps->velocity[2] = JUMP_VELOCITY;
	PM_AddEvent( EV_JUMP );
	
	if ( pm->cmd.forwardmove >= 0 ) {
		BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qtrue );
		pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
	} else {
		BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qtrue );
		pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
	}

	return qtrue;
}

/*
=============
PM_CheckWaterJump
=============
*/
static qboolean	PM_CheckWaterJump( void ) {
	vec3_t	spot;
	int		cont;
	vec3_t	flatforward;

	if (pm->ps->pm_time) {
		return qfalse;
	}

	// check for water jump
	if ( pm->waterlevel != 2 ) {
		return qfalse;
	}

	flatforward[0] = pml.forward[0];
	flatforward[1] = pml.forward[1];
	flatforward[2] = 0;
	VectorNormalize (flatforward);

	VectorMA (pm->ps->origin, 30, flatforward, spot);
	spot[2] += 4;
	cont = pm->pointcontents (spot, pm->ps->clientNum );
	if ( !(cont & CONTENTS_SOLID) ) {
		return qfalse;
	}

	spot[2] += 16;
	cont = pm->pointcontents (spot, pm->ps->clientNum );
	if ( cont ) {
		return qfalse;
	}

	// jump out of water
	VectorScale (pml.forward, 200, pm->ps->velocity);
	pm->ps->velocity[2] = 350;

	pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
	pm->ps->pm_time = 2000;

	return qtrue;
}

//============================================================================


/*
===================
PM_WaterJumpMove

Flying out of the water
===================
*/
static void PM_WaterJumpMove( void ) {
	// waterjump has no control, but falls

	PM_StepSlideMove( qtrue );

	pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
	if (pm->ps->velocity[2] < 0) {
		// cancel as soon as we are falling down again
		pm->ps->pm_flags &= ~PMF_ALL_TIMES;
		pm->ps->pm_time = 0;
	}
}

/*
===================
PM_WaterMove

===================
*/
static void PM_WaterMove( void ) {
	int		i;
	vec3_t	wishvel;
	float	wishspeed;
	vec3_t	wishdir;
	float	scale;
	float	vel;

	if ( PM_CheckWaterJump() ) {
		PM_WaterJumpMove();
		return;
	}
#if 0
	// jump = head for surface
	if ( pm->cmd.upmove >= 10 ) {
		if (pm->ps->velocity[2] > -300) {
			if ( pm->watertype == CONTENTS_WATER ) {
				pm->ps->velocity[2] = 100;
			} else if (pm->watertype == CONTENTS_SLIME) {
				pm->ps->velocity[2] = 80;
			} else {
				pm->ps->velocity[2] = 50;
			}
		}
	}
#endif
	PM_Friction ();

	scale = PM_CmdScale( &pm->cmd );
	//
	// user intentions
	//
	if ( !scale ) {
		wishvel[0] = 0;
		wishvel[1] = 0;
		wishvel[2] = -60;		// sink towards bottom
//		wishvel[2] = -10;	//----(SA)	mod for DM
	} else {
		for (i=0 ; i<3 ; i++)
			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;

		wishvel[2] += scale * pm->cmd.upmove;
	}

	VectorCopy (wishvel, wishdir);
	wishspeed = VectorNormalize(wishdir);

	if ( pm->watertype == CONTENTS_SLIME ) {	//----(SA)	slag
		if ( wishspeed > pm->ps->speed * pm_slagSwimScale ) {
			wishspeed = pm->ps->speed * pm_slagSwimScale;
		}

		PM_Accelerate (wishdir, wishspeed, pm_slagaccelerate);
	}
	else {
		if ( wishspeed > pm->ps->speed * pm_waterSwimScale ) {
			wishspeed = pm->ps->speed * pm_waterSwimScale;
		}

		PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
	}


	// make sure we can go up slopes easily under water
	if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
		vel = VectorLength(pm->ps->velocity);
		// slide along the ground plane
		PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
			pm->ps->velocity, OVERCLIP );

		VectorNormalize(pm->ps->velocity);
		VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
	}

	PM_SlideMove( qfalse );
}

// TTimo gcc: defined but not used
#if 0
/*
===================
PM_InvulnerabilityMove

Only with the invulnerability powerup
===================
*/
static void PM_InvulnerabilityMove( void ) {
	pm->cmd.forwardmove = 0;
	pm->cmd.rightmove = 0;
	pm->cmd.upmove = 0;
	VectorClear(pm->ps->velocity);
}
#endif

/*
===================
PM_FlyMove

Only with the flight powerup
===================
*/
static void PM_FlyMove( void ) {
	int		i;
	vec3_t	wishvel;
	float	wishspeed;
	vec3_t	wishdir;
	float	scale;

	// normal slowdown
	PM_Friction ();

	if (pm->ps->aiChar == AICHAR_NONE || pml.ladder) {
		scale = PM_CmdScale( &pm->cmd );
	} else {
		// AI is allowed to fly freely
		scale = 1.0;
	}
	//
	// user intentions
	//
	if ( !scale ) {
		wishvel[0] = 0;
		wishvel[1] = 0;
		wishvel[2] = 0;
	} else {
		for (i=0 ; i<3 ; i++) {
			wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
		}

		if (pm->ps->aiChar == AICHAR_FEMZOMBIE) {	// femzombie has upmove relative to angles
			for (i=0 ; i<3 ; i++) {
				wishvel[i] += scale * pml.up[i]*pm->cmd.upmove;
			}
		} else {
			wishvel[2] += scale * pm->cmd.upmove;
		}
	}

	VectorCopy (wishvel, wishdir);
	wishspeed = VectorNormalize(wishdir);

	PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);

	PM_StepSlideMove( qfalse );
}


/*
===================
PM_AirMove

===================
*/
static void PM_AirMove( void ) {
	int			i;
	vec3_t		wishvel;
	float		fmove, smove;
	vec3_t		wishdir;
	float		wishspeed;
	float		scale;
	usercmd_t	cmd;

	PM_Friction();

	fmove = pm->cmd.forwardmove;
	smove = pm->cmd.rightmove;

	cmd = pm->cmd;
	scale = PM_CmdScale( &cmd );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
//	PM_SetMovementDir();

	// project moves down to flat plane
	pml.forward[2] = 0;
	pml.right[2] = 0;
	VectorNormalize (pml.forward);
	VectorNormalize (pml.right);

	for ( i = 0 ; i < 2 ; i++ ) {
		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
	}
	wishvel[2] = 0;

	VectorCopy (wishvel, wishdir);
	wishspeed = VectorNormalize(wishdir);
	wishspeed *= scale;

	// not on ground, so little effect on velocity
	PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);

	// we may have a ground plane that is very steep, even
	// though we don't have a groundentity
	// slide along the steep plane
	if ( pml.groundPlane ) {
		PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
			pm->ps->velocity, OVERCLIP );
	}

	PM_StepSlideMove ( qtrue );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
	PM_SetMovementDir();
}

/*
===================
PM_WalkMove

===================
*/
static void PM_WalkMove( void ) {
	int			i;
	vec3_t		wishvel;
	float		fmove, smove;
	vec3_t		wishdir;
	float		wishspeed;
	float		scale;
	usercmd_t	cmd;
	float		accelerate;
	float		vel;

	if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
		// begin swimming
		PM_WaterMove();
		return;
	}


	if ( PM_CheckJump () ) {
		// jumped away
		if ( pm->waterlevel > 1 ) {
			PM_WaterMove();
		} else {
			PM_AirMove();
		}

		// JPW NERVE
		if (pm->gametype != GT_SINGLE_PLAYER) {
			pm->ps->jumpTime = pm->cmd.serverTime;
			pm->ps->sprintTime -= 2500;
			if (pm->ps->sprintTime < 0)
				pm->ps->sprintTime = 0;
		}
		// jpw

		return;
	}

	PM_Friction ();

	fmove = pm->cmd.forwardmove;
	smove = pm->cmd.rightmove;

	cmd = pm->cmd;
	scale = PM_CmdScale( &cmd );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
//	PM_SetMovementDir();

	// project moves down to flat plane
	pml.forward[2] = 0;
	pml.right[2] = 0;

	// project the forward and right directions onto the ground plane
	PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
	PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
	//
	VectorNormalize (pml.forward);
	VectorNormalize (pml.right);

	for ( i = 0 ; i < 3 ; i++ ) {
		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
	}
	// when going up or down slopes the wish velocity should Not be zero
//	wishvel[2] = 0;

	VectorCopy (wishvel, wishdir);
	wishspeed = VectorNormalize(wishdir);
	wishspeed *= scale;

	// clamp the speed lower if ducking
	if ( pm->ps->pm_flags & PMF_DUCKED ) {
		/*
		if ( wishspeed > pm->ps->speed * pm_duckScale ) {
			wishspeed = pm->ps->speed * pm_duckScale;
		}
		*/
		if ( wishspeed > pm->ps->speed * pm->ps->crouchSpeedScale ) {
			wishspeed = pm->ps->speed * pm->ps->crouchSpeedScale;
		}
	}

	// clamp the speed lower if wading or walking on the bottom
	if ( pm->waterlevel ) {
		float	waterScale;

		waterScale = pm->waterlevel / 3.0;
		if ( pm->watertype == CONTENTS_SLIME )	//----(SA)	slag
			waterScale = 1.0 - ( 1.0 - pm_slagSwimScale ) * waterScale;
		else
			waterScale = 1.0 - ( 1.0 - pm_waterSwimScale ) * waterScale;

		if ( wishspeed > pm->ps->speed * waterScale ) {
			wishspeed = pm->ps->speed * waterScale;
		}
	}

	// when a player gets hit, they temporarily lose
	// full control, which allows them to be moved a bit
	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
		accelerate = pm_airaccelerate;
	} 
	else if ( ( pm->ps->stats[STAT_HEALTH] <= 0 ) && pm->ps->aiChar && (pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK ) ) { 
		accelerate = pm_airaccelerate;
	}
	else {
		accelerate = pm_accelerate;
	}

	PM_Accelerate (wishdir, wishspeed, accelerate);

	//Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
	//Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity));

	if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
	} 
	else if ( ( pm->ps->stats[STAT_HEALTH] <= 0 ) && pm->ps->aiChar && ( pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK ) ) {
		pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
	}
	else {
		// don't reset the z velocity for slopes
		//pm->ps->velocity[2] = 0;
	}

//----(SA)	added
	// show breath when standing on 'snow' surfaces
	if(pml.groundTrace.surfaceFlags & SURF_SNOW)
		pm->ps->eFlags |= EF_BREATH;
	else
		pm->ps->eFlags &= ~EF_BREATH;
//----(SA)	end

	vel = VectorLength(pm->ps->velocity);

	// slide along the ground plane
	PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, 
		pm->ps->velocity, OVERCLIP );

	// don't do anything if standing still
	if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
		return;
	}

	// don't decrease velocity when going up or down a slope
	VectorNormalize(pm->ps->velocity);
	VectorScale(pm->ps->velocity, vel, pm->ps->velocity);

	PM_StepSlideMove( qfalse );

// Ridah, moved this down, so we use the actual movement direction
	// set the movementDir so clients can rotate the legs for strafing
	PM_SetMovementDir();
}


/*
==============
PM_DeadMove
==============
*/
static void PM_DeadMove( void ) {
	float	forward;

	if ( !pml.walking ) {
		return;
	}

	// extra friction

	forward = VectorLength (pm->ps->velocity);
	forward -= 20;
	if ( forward <= 0 ) {
		VectorClear (pm->ps->velocity);
	} else {
		VectorNormalize (pm->ps->velocity);
		VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
	}
}


/*
===============
PM_NoclipMove
===============
*/
static void PM_NoclipMove( void ) {
	float	speed, drop, friction, control, newspeed;
	int			i;
	vec3_t		wishvel;
	float		fmove, smove;
	vec3_t		wishdir;
	float		wishspeed;
	float		scale;

	pm->ps->viewheight = DEFAULT_VIEWHEIGHT;

	// friction

	speed = VectorLength (pm->ps->velocity);
	if (speed < 1)
	{
		VectorCopy (vec3_origin, pm->ps->velocity);
	}
	else
	{
		drop = 0;

		friction = pm_friction*1.5;	// extra friction
		control = speed < pm_stopspeed ? pm_stopspeed : speed;
		drop += control*friction*pml.frametime;

		// scale the velocity
		newspeed = speed - drop;
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;

		VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
	}

	// accelerate
	scale = PM_CmdScale( &pm->cmd );

	fmove = pm->cmd.forwardmove;
	smove = pm->cmd.rightmove;
	
	for (i=0 ; i<3 ; i++)
		wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
	wishvel[2] += pm->cmd.upmove;

	VectorCopy (wishvel, wishdir);
	wishspeed = VectorNormalize(wishdir);
	wishspeed *= scale;

	PM_Accelerate( wishdir, wishspeed, pm_accelerate );

	// move
	VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
}

//============================================================================

/*
================
PM_FootstepForSurface

Returns an event number apropriate for the groundsurface
================
*/
static int PM_FootstepForSurface( void ) {
	if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS ) {
		return 0;
	}
	// JOSEPH 9-16-99
	if ( pml.groundTrace.surfaceFlags & SURF_METAL ) {
		return EV_FOOTSTEP_METAL;
	}

	if ( pml.groundTrace.surfaceFlags & SURF_WOOD ) {
		return EV_FOOTSTEP_WOOD;
	}

	if ( pml.groundTrace.surfaceFlags & SURF_GRASS ) {
		return EV_FOOTSTEP_GRASS;
	}

	if ( pml.groundTrace.surfaceFlags & SURF_GRAVEL ) {
		return EV_FOOTSTEP_GRAVEL;
	}
	// END JOSEPH

	if ( pml.groundTrace.surfaceFlags & SURF_ROOF ) {
		return EV_FOOTSTEP_ROOF;
	}
	
	if ( pml.groundTrace.surfaceFlags & SURF_SNOW ) {
		return EV_FOOTSTEP_SNOW;
	}

//----(SA)	added
	if ( pml.groundTrace.surfaceFlags & SURF_CARPET ) {
		return EV_FOOTSTEP_CARPET;
	}
//----(SA)	end
	return EV_FOOTSTEP;
}


/*
=================
PM_CrashLand

Check for hard landings that generate sound events
=================
*/
static void PM_CrashLand( void ) {
	float		delta;
	float		dist;
	float		vel, acc;
	float		t;
	float		a, b, c, den;

	// Ridah, only play this if coming down hard
	if (!pm->ps->legsTimer)	{
		if (pml.previous_velocity[2] < -220)
		{
			BG_AnimScriptEvent( pm->ps, ANIM_ET_LAND, qfalse, qtrue );
		}
	}

	// calculate the exact velocity on landing
	dist = pm->ps->origin[2] - pml.previous_origin[2];
	vel = pml.previous_velocity[2];
	acc = -pm->ps->gravity;

	a = acc / 2;
	b = vel;
	c = -dist;

	den =  b * b - 4 * a * c;
	if ( den < 0 ) {
		return;
	}
	t = (-b - sqrt( den ) ) / ( 2 * a );

	delta = vel + t * acc;
	delta = delta*delta * 0.0001;

	// never take falling damage if completely underwater
	if ( pm->waterlevel == 3 ) {
		return;
	}

	// reduce falling damage if there is standing water
	if ( pm->waterlevel == 2 ) {
		delta *= 0.25;
	}
	if ( pm->waterlevel == 1 ) {
		delta *= 0.5;
	}

	if ( delta < 1 ) {
		return;
	}

	// create a local entity event to play the sound

	// SURF_NODAMAGE is used for bounce pads where you don't ever
	// want to take damage or play a crunch sound
	if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) )  
	{
		if ( pm->debugLevel )
			Com_Printf ("delta: %5.2f\n", delta);

/* JPW NERVE removed from MP, breaks too many levels and skill as no-fall-damage indicator isn't obvious
		// Rafael gameskill
		if (bg_pmove_gameskill_integer == 1)
		{
			if (delta > 7)
				delta = 8;
		}
		// done
*/

		if (delta > 77)
		{
			PM_AddEvent(EV_FALL_NDIE);
		}
		//else if (delta > 67)
		//{
		//	PM_AddEvent(EV_FALL_DMG_75);
		//}
		else if (delta > 67)
		{
			PM_AddEvent(EV_FALL_DMG_50);
		}
		//else if (delta > 48)
		//{
		//	PM_AddEvent(EV_FALL_DMG_30);
		//}
		else if (delta > 58)
		{
			// this is a pain grunt, so don't play it if dead
			if ( pm->ps->stats[STAT_HEALTH] > 0 ) 
			{
				PM_AddEvent(EV_FALL_DMG_25);
			}
		}
		else if (delta > 48)
		{
			// this is a pain grunt, so don't play it if dead
			if ( pm->ps->stats[STAT_HEALTH] > 0 ) 
			{
				PM_AddEvent(EV_FALL_DMG_15);
			}
		}
		else if (delta > 38.75)
		{
			// this is a pain grunt, so don't play it if dead
			if ( pm->ps->stats[STAT_HEALTH] > 0 ) 
			{
				PM_AddEvent(EV_FALL_DMG_10);
			}
		}
		else if ( delta > 7 ) 
		{
			PM_AddEvent( EV_FALL_SHORT );
		} 
		else 
		{
			PM_AddEvent( PM_FootstepForSurface() );
		}
	}

	// start footstep cycle over
	pm->ps->bobCycle = 0;
}



/*
=============
PM_CorrectAllSolid
=============
*/
static int PM_CorrectAllSolid( trace_t *trace ) {
	int			i, j, k;
	vec3_t		point;

	if ( pm->debugLevel ) {
		Com_Printf("%i:allsolid\n", c_pmove);
	}

	// jitter around
	for (i = -1; i <= 1; i++) {
		for (j = -1; j <= 1; j++) {
			for (k = -1; k <= 1; k++) {
				VectorCopy(pm->ps->origin, point);
				point[0] += (float) i;
				point[1] += (float) j;
				point[2] += (float) k;
				pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
				if ( !trace->allsolid ) {
					point[0] = pm->ps->origin[0];
					point[1] = pm->ps->origin[1];
					point[2] = pm->ps->origin[2] - 0.25;

					pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
					pml.groundTrace = *trace;
					return qtrue;
				}
			}
		}
	}

	pm->ps->groundEntityNum = ENTITYNUM_NONE;
	pml.groundPlane = qfalse;
	pml.walking = qfalse;

	return qfalse;
}


/*
=============
PM_GroundTraceMissed

The ground trace didn't hit a surface, so we are in freefall
=============
*/
static void PM_GroundTraceMissed( void ) {
	trace_t		trace;
	vec3_t		point;

	if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {
		// we just transitioned into freefall
		if ( pm->debugLevel ) {
			Com_Printf("%i:lift\n", c_pmove);
		}

		// if they aren't in a jumping animation and the ground is a ways away, force into it
		// if we didn't do the trace, the player would be backflipping down staircases
		VectorCopy( pm->ps->origin, point );
		point[2] -= 64;

		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
		if ( trace.fraction == 1.0 ) {
			if ( pm->cmd.forwardmove >= 0 ) {
				BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qtrue );
				pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
			} else {
				BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qtrue );
				pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
			}
		}
	}

	pm->ps->groundEntityNum = ENTITYNUM_NONE;
	pml.groundPlane = qfalse;
	pml.walking = qfalse;
}


/*
=============
PM_GroundTrace
=============
*/
static void PM_GroundTrace( void ) {
	vec3_t		point;
	trace_t		trace;

	point[0] = pm->ps->origin[0];
	point[1] = pm->ps->origin[1];
	// DHM - Nerve
	if ( pm->ps->eFlags & EF_MG42_ACTIVE )
		point[2] = pm->ps->origin[2] - 1.f;
	else
		point[2] = pm->ps->origin[2] - 0.25;

	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
	pml.groundTrace = trace;

	// do something corrective if the trace starts in a solid...
	if ( trace.allsolid ) {
		if ( !PM_CorrectAllSolid(&trace) )
			return;
	}

	// if the trace didn't hit anything, we are in free fall
	if ( trace.fraction == 1.0 ) {
		PM_GroundTraceMissed();
		pml.groundPlane = qfalse;
		pml.walking = qfalse;
		return;
	}

	// check if getting thrown off the ground
	if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
		if ( pm->debugLevel ) {
			Com_Printf("%i:kickoff\n", c_pmove);
		}
		// go into jump animation
		if ( pm->cmd.forwardmove >= 0 ) {
			BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMP, qfalse, qfalse );
			pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
		} else {
		BG_AnimScriptEvent( pm->ps, ANIM_ET_JUMPBK, qfalse, qfalse );
			pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
		}

		pm->ps->groundEntityNum = ENTITYNUM_NONE;
		pml.groundPlane = qfalse;
		pml.walking = qfalse;
		return;
	}
	
	// slopes that are too steep will not be considered onground
	if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) {
		if ( pm->debugLevel ) {
			Com_Printf("%i:steep\n", c_pmove);
		}
		// FIXME: if they can't slide down the slope, let them
		// walk (sharp crevices)
		pm->ps->groundEntityNum = ENTITYNUM_NONE;
		pml.groundPlane = qtrue;
		pml.walking = qfalse;
		return;
	}

	pml.groundPlane = qtrue;
	pml.walking = qtrue;

	// hitting solid ground will end a waterjump
	if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
	{
		pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
		pm->ps->pm_time = 0;
	}

	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
		// just hit the ground
		if ( pm->debugLevel ) {
			Com_Printf("%i:Land\n", c_pmove);
		}
		
		PM_CrashLand();

		// don't do landing time if we were just going down a slope
		if ( pml.previous_velocity[2] < -200 ) {
			// don't allow another jump for a little while
			pm->ps->pm_flags |= PMF_TIME_LAND;
			pm->ps->pm_time = 250;
		}
	}

	pm->ps->groundEntityNum = trace.entityNum;

	// don't reset the z velocity for slopes
//	pm->ps->velocity[2] = 0;

	PM_AddTouchEnt( trace.entityNum );
}


/*
=============
PM_SetWaterLevel	FIXME: avoid this twice?  certainly if not moving
=============
*/
static void PM_SetWaterLevel( void ) {
	vec3_t		point;
	int			cont;
	int			sample1;
	int			sample2;

	//
	// get waterlevel, accounting for ducking
	//
	pm->waterlevel = 0;
	pm->watertype = 0;

	// Ridah, modified this
	point[0] = pm->ps->origin[0];
	point[1] = pm->ps->origin[1];
	point[2] = pm->ps->origin[2] + pm->ps->mins[2] + 1;	
	cont = pm->pointcontents( point, pm->ps->clientNum );

	if ( cont & MASK_WATER ) {
		sample2 = pm->ps->viewheight - pm->ps->mins[2];
		sample1 = sample2 / 2;

		pm->watertype = cont;
		pm->waterlevel = 1;
		point[2] = pm->ps->origin[2] + pm->ps->mins[2] + sample1;
		cont = pm->pointcontents (point, pm->ps->clientNum );
		if ( cont & MASK_WATER ) {
			pm->waterlevel = 2;
			point[2] = pm->ps->origin[2] + pm->ps->mins[2] + sample2;
			cont = pm->pointcontents (point, pm->ps->clientNum );
			if ( cont & MASK_WATER ){
				pm->waterlevel = 3;
			}
		}
	}
	// done.

	// UNDERWATER
	BG_UpdateConditionValue( pm->ps->clientNum, ANIM_COND_UNDERWATER, (pm->waterlevel > 2), qtrue );

}



/*
==============
PM_CheckDuck

Sets mins, maxs, and pm->ps->viewheight
==============
*/
static void PM_CheckDuck (void)
{
	trace_t	trace;

	// Ridah, modified this for configurable bounding boxes
	pm->mins[0] = pm->ps->mins[0];
	pm->mins[1] = pm->ps->mins[1];

	pm->maxs[0] = pm->ps->maxs[0];
	pm->maxs[1] = pm->ps->maxs[1];

	pm->mins[2] = pm->ps->mins[2];

	if (pm->ps->pm_type == PM_DEAD)
	{
		pm->maxs[2] = pm->ps->maxs[2];			// NOTE: must set death bounding box in game code
		pm->ps->viewheight = pm->ps->deadViewHeight;
		return;
	}

	if (pm->cmd.upmove < 0)
	{	// duck
		pm->ps->pm_flags |= PMF_DUCKED;
	}
	else
	{	// stand up if possible
		if (pm->ps->pm_flags & PMF_DUCKED)
		{
			// try to stand up
			pm->maxs[2] = pm->ps->maxs[2];
			pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
			if (!trace.allsolid)
				pm->ps->pm_flags &= ~PMF_DUCKED;
		}
	}

	if (pm->ps->pm_flags & PMF_DUCKED)
	{
		pm->maxs[2] = pm->ps->crouchMaxZ;
		pm->ps->viewheight = pm->ps->crouchViewHeight;
	}
	else
	{
		pm->maxs[2] = pm->ps->maxs[2];
		pm->ps->viewheight = pm->ps->standViewHeight;
	}
	// done.
}



//===================================================================


/*
===============
PM_Footsteps
===============
*/
static void PM_Footsteps( void ) {
	float		bobmove;
	int			old;
	qboolean	footstep;
	qboolean	iswalking;
	int			animResult=-1;

	if (pm->ps->eFlags & EF_DEAD) {

		// DHM - Nerve :: before going to limbo, play a wounded/fallen animation
		if ( !pm->ps->pm_time && !(pm->ps->pm_flags & PMF_LIMBO) )
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_FALLEN, qtrue );

		return;
	}

	iswalking = qfalse;

	//
	// calculate speed and cycle to be used for
	// all cyclic walking effects
	//
	pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
		+  pm->ps->velocity[1] * pm->ps->velocity[1] );

	// mg42, always idle
	if ( pm->ps->persistant[PERS_HWEAPON_USE] ) {
		animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLE, qtrue );
		//
		return;
	}

	// swimming
	if ( pm->waterlevel > 2 ) {

		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_SWIMBK, qtrue );
		} else {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_SWIM, qtrue );
		}

		return;
	}

	// in the air
	if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
		if(pm->ps->pm_flags & PMF_LADDER)				// on ladder
		{
			if (pm->ps->velocity[2] >= 0)
			{
				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_CLIMBUP, qtrue );
				//BG_PlayAnimName( pm->ps, "BOTH_CLIMB", ANIM_BP_BOTH, qfalse, qtrue, qfalse );
			}
			else if (pm->ps->velocity[2] < 0)
			{
				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_CLIMBDOWN, qtrue );
				//BG_PlayAnimName( pm->ps, "BOTH_CLIMB_DOWN", ANIM_BP_BOTH, qfalse, qtrue, qfalse );
			}
		}

		return;
	}

	// if not trying to move
	if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
		if (  pm->xyspeed < 5 ) {
			pm->ps->bobCycle = 0;	// start at beginning of cycle again
		}
		if (pm->xyspeed > 120) {
			return;	// continue what they were doing last frame, until we stop
		}
		if ( pm->ps->pm_flags & PMF_DUCKED ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLECR, qtrue );
		}
		if (animResult < 0) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLE, qtrue );
		}
		//
		return;
	}


	footstep = qfalse;

	if ( pm->ps->pm_flags & PMF_DUCKED ) {
		bobmove = 0.5;	// ducked characters bob much faster
		if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKCRBK, qtrue );
		} else {
			animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKCR, qtrue );
		}
		// ducked characters never play footsteps
	} else 	if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
			bobmove = 0.4;	// faster speeds bob faster
			footstep = qtrue;
			// check for strafing
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if (animResult < 0) {	// if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_RUNBK, qtrue );
			}
		} else {
			bobmove = 0.3;
			// check for strafing
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if (animResult < 0) {	// if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALKBK, qtrue );
			}
		}

	} else {

		if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
			bobmove = 0.4;	// faster speeds bob faster
			footstep = qtrue;
			// check for strafing
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if (animResult < 0) {	// if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_RUN, qtrue );
			}
		} else {
			bobmove = 0.3;	// walking bobs slow
			if (pm->ps->aiChar != AICHAR_NONE)
			{
				footstep = qtrue;
				iswalking = qtrue;
			}
			if ( pm->cmd.rightmove && !pm->cmd.forwardmove ) {
				if ( pm->cmd.rightmove > 0 ) {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFERIGHT, qtrue );
				} else {
					animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_STRAFELEFT, qtrue );
				}
			}
			if (animResult < 0) {	// if we havent found an anim yet, play the run
				animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_WALK, qtrue );
			}
		}
	}

	// if no anim found yet, then just use the idle as default
	if (animResult < 0) {
		animResult = BG_AnimScriptAnimation( pm->ps, pm->ps->aiState, ANIM_MT_IDLE, qtrue );
	}

	// check for footstep / splash sounds
	old = pm->ps->bobCycle;
	pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;

	// if we just crossed a cycle boundary, play an apropriate footstep event
	if (iswalking)
	{
		// sounds much more natural this way
		if (old > pm->ps->bobCycle) {
		
			if ( pm->waterlevel == 0 ) {
				if ( footstep && !pm->noFootsteps ) {
					PM_AddEvent( PM_FootstepForSurface() );
				}
			} else if ( pm->waterlevel == 1 ) {
				// splashing
				PM_AddEvent( EV_FOOTSPLASH );
			} else if ( pm->waterlevel == 2 ) {
				// wading / swimming at surface
				PM_AddEvent( EV_SWIM );
			} else if ( pm->waterlevel == 3 ) {
				// no sound when completely underwater
			}

		}
	}
	else if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {
		
		if (pm->ps->sprintExertTime && pm->waterlevel <= 2)
			PM_ExertSound ();

		if ( pm->waterlevel == 0 ) {
			// on ground will only play sounds if running
			if ( footstep && !pm->noFootsteps ) {
				PM_AddEvent( PM_FootstepForSurface() );
			}
		} else if ( pm->waterlevel == 1 ) {
			// splashing
			PM_AddEvent( EV_FOOTSPLASH );
		} else if ( pm->waterlevel == 2 ) {
			// wading / swimming at surface
			PM_AddEvent( EV_SWIM );
		} else if ( pm->waterlevel == 3 ) {
			// no sound when completely underwater

		}
	}
}

/*
==============
PM_WaterEvents

Generate sound events for entering and leaving water
==============
*/
static void PM_WaterEvents( void ) {		// FIXME?
	//
	// if just entered a water volume, play a sound
	//
	if (!pml.previous_waterlevel && pm->waterlevel) {
		PM_AddEvent( EV_WATER_TOUCH );
	}

	//
	// if just completely exited a water volume, play a sound
	//
	if (pml.previous_waterlevel && !pm->waterlevel) {
		PM_AddEvent( EV_WATER_LEAVE );
	}

	//
	// check for head just going under water
	//
	if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
		PM_AddEvent( EV_WATER_UNDER );
	}

	//
	// check for head just coming out of water
	//
	if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
		PM_AddEvent( EV_WATER_CLEAR );
	}
}


/*
==============
PM_BeginWeaponReload
==============
*/
static void PM_BeginWeaponReload( int weapon ) {
	// only allow reload if the weapon isn't already occupied (firing is okay)
	if(pm->ps->weaponstate != WEAPON_READY && pm->ps->weaponstate != WEAPON_FIRING )
		return;

	if(weapon < WP_BEGINGERMAN || weapon > WP_DYNAMITE2)
		return;

	// no reload when you've got a chair in your hands
	if(pm->ps->eFlags & EF_MELEE_ACTIVE)
		return;

	// no reload when leaning (this includes manual and auto reloads)
	if(pm->ps->leanf)
		return;

	// (SA) easier check now that the animation system handles the specifics
	switch(weapon) {
		case WP_DYNAMITE:
		case WP_DYNAMITE2:
		case WP_GRENADE_LAUNCHER:
		case WP_GRENADE_PINEAPPLE:
			break;

		default:
			// DHM - Nerve :: override current animation (so reloading after firing will work)
			BG_AnimScriptEvent( pm->ps, ANIM_ET_RELOAD, qfalse, qtrue );
			break;
	}


	PM_ContinueWeaponAnim(WEAP_RELOAD1);


	// okay to reload while overheating without tacking the reload time onto the end of the
	// current weaponTime (the reload time is partially absorbed into the overheat time)
	if(pm->ps->weaponstate == WEAPON_READY )
		pm->ps->weaponTime += ammoTable[weapon].reloadTime;
	else if( pm->ps->weaponTime < ammoTable[weapon].reloadTime)
		pm->ps->weaponTime += (ammoTable[weapon].reloadTime - pm->ps->weaponTime);

	pm->ps->weaponstate = WEAPON_RELOADING;
	PM_AddEvent( EV_FILL_CLIP );	// play reload sound
}




/*
===============
PM_BeginWeaponChange
===============
*/
static void PM_BeginWeaponChange( int oldweapon, int newweapon) {	//----(SA)	modified to play 1st person alt-mode transition animations.
	int switchtime;

	if ( newweapon <= WP_NONE || newweapon >= WP_NUM_WEAPONS ) {
		return;
	}

	if ( !( COM_BitCheck( pm->ps->weapons, newweapon ) ) ) {
		return;
	}
	
	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
		return;
	}

	// don't allow switch if you're holding a hot potato or dynamite
	if(pm->ps->grenadeTimeLeft > 0) {
		return;
	}

	switch( newweapon ) {

		case WP_GAUNTLET:
		case WP_MONSTER_ATTACK1:
		case WP_MONSTER_ATTACK2:
		case WP_MONSTER_ATTACK3:
			break;

		case WP_DYNAMITE:
		case WP_DYNAMITE2:
		case WP_GRENADE_LAUNCHER:
		case WP_GRENADE_PINEAPPLE:
			// initialize the timer on the potato you're switching to
			pm->ps->grenadeTimeLeft = 0;
		
		default:
			//----(SA)	only play the weapon switch sound for the player
			if(!(pm->ps->aiChar))
				PM_AddEvent( EV_CHANGE_WEAPON );

			// it's an alt mode, play different anim
			if(newweapon == weapAlts[oldweapon])
				PM_StartWeaponAnim(WEAP_ALTSWITCHFROM);
			else
				PM_StartWeaponAnim(WEAP_DROP);	// PM_ContinueWeaponAnim(WEAP_DROP);
	}

	BG_AnimScriptEvent( pm->ps, ANIM_ET_DROPWEAPON, qfalse, qfalse );

	pm->ps->weaponstate = WEAPON_DROPPING;

	switchtime = 250;	// dropping/raising usually takes 1/4 sec.
	// sometimes different switch times for alt weapons
	switch(oldweapon) {
		case WP_LUGER:
			if(newweapon == weapAlts[oldweapon])
				switchtime = 50;
			break;
		case WP_SILENCER:
			if(newweapon == weapAlts[oldweapon])
				switchtime = 1200;
			break;
		case WP_FG42:
		case WP_FG42SCOPE:
			if(newweapon == weapAlts[oldweapon])
				switchtime = 50;	// fast
			break;
	}

	pm->ps->weaponTime += switchtime;
}


/*
===============
PM_FinishWeaponChange
===============
*/
static void PM_FinishWeaponChange( void ) {
	int oldweapon, newweapon, switchtime;

	newweapon = pm->cmd.weapon;
	if ( newweapon < WP_NONE || newweapon >= WP_NUM_WEAPONS ) {
		newweapon = WP_NONE;
	}

	if ( !( COM_BitCheck( pm->ps->weapons, newweapon ) ) ) {
		newweapon = WP_NONE;
	}

	oldweapon = pm->ps->weapon;

	pm->ps->weapon = newweapon;
	pm->ps->weaponstate = WEAPON_RAISING;

	switch(newweapon)
	{
		// don't really care about anim since these weapons don't show in view.  
		// However, need to set the animspreadscale so they are initally at worst accuracy
		case WP_SNOOPERSCOPE:
		case WP_SNIPERRIFLE:
			pm->ps->aimSpreadScale = 255;			// initially at lowest accuracy
			pm->ps->aimSpreadScaleFloat = 255.0f;	// initially at lowest accuracy

		default:
			break;
	}

	// doesn't happen too often (player switched weapons away then back very quickly)
	if(oldweapon == newweapon)
		return;

	// dropping/raising usually takes 1/4 sec.
	switchtime = 250;

	// sometimes different switch times for alt weapons
	switch(newweapon) {
		case WP_LUGER:
			if(newweapon == weapAlts[oldweapon])
				switchtime = 50;
			break;
		case WP_SILENCER:
			if(newweapon == weapAlts[oldweapon])
				switchtime = 1190;
			break;
		case WP_FG42:
		case WP_FG42SCOPE:
			if(newweapon == weapAlts[oldweapon])
				switchtime = 50;	// fast
			break;
	}

	pm->ps->weaponTime += switchtime;

	BG_UpdateConditionValue( pm->ps->clientNum, ANIM_COND_WEAPON, newweapon, qtrue );

	// play an animation
	BG_AnimScriptEvent( pm->ps, ANIM_ET_RAISEWEAPON, qfalse, qfalse );

	// alt weapon switch was played when switching away, just go into idle
	if(weapAlts[oldweapon] == newweapon)
		PM_StartWeaponAnim(WEAP_ALTSWITCHTO);
	else
		PM_StartWeaponAnim(WEAP_RAISE);

}


/*
==============
PM_ReloadClip
==============
*/
static void PM_ReloadClip(int weapon) {
	int ammoreserve, ammoclip, ammomove;

	ammoreserve = pm->ps->ammo[ BG_FindAmmoForWeapon(weapon)];
	ammoclip	= pm->ps->ammoclip[BG_FindClipForWeapon( weapon )];

	ammomove = ammoTable[weapon].maxclip - ammoclip;

	if(ammoreserve < ammomove)
		ammomove = ammoreserve;

	if(ammomove) {
		pm->ps->ammo[ BG_FindAmmoForWeapon(weapon)] -= ammomove;
		pm->ps->ammoclip[BG_FindClipForWeapon(weapon)] += ammomove;
	}

	if(weapon == WP_AKIMBO)	// reload colt too
		PM_ReloadClip(WP_COLT);
}

/*
==============
PM_FinishWeaponReload
==============
*/

static void PM_FinishWeaponReload(void) {
	PM_ReloadClip(pm->ps->weapon);			// move ammo into clip
	pm->ps->weaponstate = WEAPON_READY;		// ready to fire
}


/*
==============
PM_CheckforReload
==============
*/
void PM_CheckForReload(int weapon) {
	qboolean autoreload;
	qboolean reloadRequested;

	if(pm->noWeapClips)	// no need to reload
		return;

	// user is forcing a reload (manual reload)
	reloadRequested = (qboolean)(pm->cmd.wbuttons & WBUTTON_RELOAD);

	switch(pm->ps->weaponstate) {
		case WEAPON_RAISING:
		case WEAPON_DROPPING:
		case WEAPON_READYING:
		case WEAPON_RELAXING:
		case WEAPON_RELOADING:
			return;
			break;
		default:
			break;
	}

	autoreload = pm->pmext->bAutoReload || !IS_AUTORELOAD_WEAPON(weapon);
	
	// in auto reload mode, clip is empty, but you have reserves.
	if (	autoreload && !(pm->ps->ammoclip[BG_FindClipForWeapon(weapon)]) &&	// clip is empty...
			pm->ps->ammo[BG_FindAmmoForWeapon(weapon)] )			// and you have reserves
	{
		PM_BeginWeaponReload(weapon);
	}
	else if (reloadRequested)
	{
		// don't allow a force reload if it won't have any effect (no more ammo reserves or full clip)
		if ( pm->ps->ammo[BG_FindAmmoForWeapon(weapon)] && pm->ps->ammoclip[BG_FindClipForWeapon(weapon)] < ammoTable[weapon].maxclip ) {
			PM_BeginWeaponReload(weapon);
		}
	} else if(weapon == WP_AKIMBO) {	// also check colt for reload
		PM_CheckForReload(WP_COLT);
	}
}

/*
==============
PM_SwitchIfEmpty
==============
*/
static void PM_SwitchIfEmpty(void)
{
	// weapon from here down will be a thrown explosive
	if(	pm->ps->weapon != WP_GRENADE_LAUNCHER &&
		pm->ps->weapon != WP_GRENADE_PINEAPPLE &&
		pm->ps->weapon != WP_DYNAMITE &&
		pm->ps->weapon != WP_DYNAMITE2)
		return;

	if(pm->ps->ammoclip[ BG_FindClipForWeapon(pm->ps->weapon)])	// still got ammo in clip
		return;

	if(pm->ps->ammo[ BG_FindAmmoForWeapon(pm->ps->weapon)])	// still got ammo in reserve
		return;

	// If this was the last one, remove the weapon and switch away before the player tries to fire next

	// NOTE: giving grenade ammo to a player will re-give him the weapon (if you do it through add_ammo())
	switch(pm->ps->weapon) {
		case WP_GRENADE_LAUNCHER:
		case WP_GRENADE_PINEAPPLE:
		case WP_DYNAMITE:
		case WP_DYNAMITE2:
			COM_BitClear( pm->ps->weapons, pm->ps->weapon);
			break;
		default:
			break;
	}

	PM_AddEvent( EV_NOAMMO );
}


/*
==============
PM_WeaponUseAmmo
	accounts for clips being used/not used
==============
*/
void PM_WeaponUseAmmo( int wp, int amount ) {
	int takeweapon;

	if(pm->noWeapClips)
		pm->ps->ammo[ BG_FindAmmoForWeapon(wp)] -= amount;
	else {
		takeweapon = BG_FindClipForWeapon(wp);
		if(wp == WP_AKIMBO) {
			if(!BG_AkimboFireSequence(pm->ps))
				takeweapon = WP_COLT;
		}

		pm->ps->ammoclip[takeweapon] -= amount;
	}
}


/*
==============
PM_WeaponAmmoAvailable
	accounts for clips being used/not used
==============
*/
int PM_WeaponAmmoAvailable( int wp ) {
	if(pm->noWeapClips)
		return pm->ps->ammo[ BG_FindAmmoForWeapon(wp)];
	else
		return pm->ps->ammoclip[BG_FindClipForWeapon( wp )];
}

/*
==============
PM_WeaponClipEmpty
	accounts for clips being used/not used
==============
*/
int PM_WeaponClipEmpty( int wp ) {
	if(pm->noWeapClips) {
		if(!(pm->ps->ammo[ BG_FindAmmoForWeapon(wp)]))
			return 1;
	}else{
		if(!(pm->ps->ammoclip[BG_FindClipForWeapon(wp)]))
			return 1;
	}

	return 0;
}


/*
==============
PM_CoolWeapons
==============
*/
void PM_CoolWeapons( void ) {
	int wp;

	for(wp=0;wp<WP_NUM_WEAPONS;wp++) {

		// if you have the weapon
		if( COM_BitCheck( pm->ps->weapons, wp) ) {
			// and it's hot
			if(pm->ps->weapHeat[wp]) {
				pm->ps->weapHeat[wp] -= ((float)ammoTable[wp].coolRate * pml.frametime);

				if(pm->ps->weapHeat[wp] < 0)
					pm->ps->weapHeat[wp] = 0;

			}
		}
	}

	// a weapon is currently selected, convert current heat value to 0-255 range for client transmission
	if(pm->ps->weapon) {
		pm->ps->curWeapHeat = ( ( (float)pm->ps->weapHeat[pm->ps->weapon] / (float)ammoTable[pm->ps->weapon].maxHeat) ) * 255.0f;

//		if(pm->ps->weapHeat[pm->ps->weapon])
//			Com_Printf("pm heat: %d, %d\n", pm->ps->weapHeat[pm->ps->weapon], pm->ps->curWeapHeat);
	}

}

/*
==============
PM_AdjustAimSpreadScale
==============
*/
//#define	AIMSPREAD_DECREASE_RATE		300.0f
#define	AIMSPREAD_DECREASE_RATE		200.0f		// (SA) when I made the increase/decrease floats (so slower weapon recover could happen for scoped weaps) the average rate increased significantly
#define	AIMSPREAD_INCREASE_RATE		800.0f
#define	AIMSPREAD_VIEWRATE_MIN		30.0f		// degrees per second
#define	AIMSPREAD_VIEWRATE_RANGE	120.0f		// degrees per second

void PM_AdjustAimSpreadScale( void ) {
//	int		increase, decrease, i;
	int		i;
	float	increase, decrease;		// (SA) was losing lots of precision on slower weapons (scoped)
	float	viewchange, cmdTime, wpnScale;

	// all weapons are very inaccurate in zoomed mode
	if(pm->ps->eFlags & EF_ZOOMING) {

		pm->ps->aimSpreadScale = 255;
		pm->ps->aimSpreadScaleFloat = 255;
		return;
	}

	cmdTime = (float)(pm->cmd.serverTime - pm->oldcmd.serverTime) / 1000.0;

	wpnScale = 0.0f;
	switch(pm->ps->weapon) {
	case WP_LUGER:
	case WP_SILENCER:
		wpnScale = 0.5f;
		break;
	case WP_AKIMBO:	//----(SA)	added
		wpnScale = 0.5;
		break;
	case WP_COLT:
		wpnScale = 0.4f;		// doesn't fire as fast, but easier to handle than luger
		break;
	case WP_VENOM:
		wpnScale = 0.9f;		// very heavy
		break;
	case WP_VENOM_FULL:
		wpnScale = 1.5f;
		break;
	case WP_SNIPERRIFLE:	// (SA) looong time to recover
		wpnScale = 10.0f;
		break;
	case WP_SNOOPERSCOPE:	// (SA) looong time to recover
		wpnScale = 8.0f;
		break;
	case WP_MAUSER:
		wpnScale = 0.5f;
		break;
	case WP_GARAND:
		wpnScale = 0.5f;
		break;
	case WP_MP40:
		wpnScale = 0.6f;		// 2 handed, but not as long as mauser, so harder to keep aim
		break;
//----(SA)	added
	case WP_BAR:
	case WP_BAR2:
		wpnScale = 1.0f;
		break;
//----(SA)	end
	case WP_FG42:
	case WP_FG42SCOPE:
		wpnScale = 0.6f;
		break;
	case WP_THOMPSON:
		wpnScale = 0.6f;
		break;
	case WP_STEN:
		wpnScale = 0.6f;
		break;
	//case WP_PANZERFAUST:
	//case WP_ROCKET_LAUNCHER:
	//	wpnScale = 0.5;
	//	break;
	}

	if (wpnScale) {

// JPW NERVE crouched players recover faster (mostly useful for snipers)
		if (pm->ps->eFlags & EF_CROUCHING)
			wpnScale *= 0.5;
// jpw

		decrease = (cmdTime * AIMSPREAD_DECREASE_RATE) / wpnScale;

		viewchange = 0;
		// take player movement into account (even if only for the scoped weapons)
		// TODO: also check for jump/crouch and adjust accordingly
		if(pm->ps->weapon == WP_SNIPERRIFLE || pm->ps->weapon == WP_SNOOPERSCOPE) {
			for (i=0; i<2; i++)
				viewchange += fabs(pm->ps->velocity[i]);
		}

		else {
			// take player view rotation into account
			for (i=0; i<2; i++)
				viewchange += fabs( SHORT2ANGLE(pm->cmd.angles[i]) - SHORT2ANGLE(pm->oldcmd.angles[i]) );
		}

		viewchange = (float)viewchange / cmdTime;	// convert into this movement for a second
		viewchange -= AIMSPREAD_VIEWRATE_MIN / wpnScale;
		if (viewchange <= 0) viewchange = 0;
		else if (viewchange > (AIMSPREAD_VIEWRATE_RANGE / wpnScale)) viewchange = AIMSPREAD_VIEWRATE_RANGE / wpnScale;

		// now give us a scale from 0.0 to 1.0 to apply the spread increase
		viewchange = viewchange / (float)(AIMSPREAD_VIEWRATE_RANGE/wpnScale);

		increase = (int)(cmdTime * viewchange * AIMSPREAD_INCREASE_RATE);
	} else {
		increase = 0;
		decrease = AIMSPREAD_DECREASE_RATE;
	}

	// update the aimSpreadScale
	pm->ps->aimSpreadScaleFloat += (increase - decrease);
	if (pm->ps->aimSpreadScaleFloat < 0) pm->ps->aimSpreadScaleFloat = 0;
	if (pm->ps->aimSpreadScaleFloat > 255) pm->ps->aimSpreadScaleFloat = 255;

	pm->ps->aimSpreadScale = (int)pm->ps->aimSpreadScaleFloat;	// update the int for the client
}

#define weaponstateFiring (pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_FIRINGALT)

#define GRENADE_DELAY	250

/*
==============
PM_Weapon

Generates weapon events and modifes the weapon counter
==============
*/

#define VENOM_LOW_IDLE	WEAP_IDLE1
#define VENOM_HI_IDLE	WEAP_IDLE2
#define VENOM_RAISE		WEAP_ATTACK1
#define VENOM_ATTACK	WEAP_ATTACK2
#define VENOM_LOWER		WEAP_ATTACK_LASTSHOT

//#define DO_WEAPON_DBG 1

static void PM_Weapon( void ) {
	int			addTime = 0; // TTimo: init
	int			ammoNeeded;
	qboolean	delayedFire;	//----(SA)  true if the delay time has just expired and this is the frame to send the fire event
	int			aimSpreadScaleAdd;
	int			weapattackanim;
	int			pfausttimeout;
#ifdef DO_WEAPON_DBG
  static int weaponstate_last = -1;
#endif

	// don't allow attack until all buttons are up
	if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
		return;
	}

	// ignore if spectator
	if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
		return;
	}

	// check for dead player
	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
		//pm->ps->weapon = WP_NONE;
		return;
	}

	// special mounted mg42 handling
	if (pm->ps->persistant[PERS_HWEAPON_USE]) {
		if( pm->cmd.buttons & BUTTON_ATTACK ) {
			if( pm->ps->weaponTime > 0 ) {
				pm->ps->weaponTime -= pml.msec;
				if (pm->ps->weaponTime < 0)
					pm->ps->weaponTime = 0;
				return;
			}

			PM_AddEvent( EV_FIRE_WEAPON_MG42 );

			pm->ps->weaponTime += MG42_RATE_OF_FIRE;

			BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			pm->ps->viewlocked = 2;		// this enable screen jitter when firing
		}
		return;
	}

	pm->watertype = 0;

  // TTimo
  // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=416
#ifdef DO_WEAPON_DBG
  if (pm->ps->weaponstate != weaponstate_last)
  {
    #ifdef CGAMEDLL
    Com_Printf("CGAME DLL\n");
    #else
    Com_Printf("GAME  DLL\n");
    #endif
		switch(pm->ps->weaponstate) {
			case WEAPON_READY:
				Com_Printf(" -- WEAPON_READY\n");
				break;
			case WEAPON_RAISING:
				Com_Printf(" -- WEAPON_RAISING\n");
				break;
			case WEAPON_DROPPING:
				Com_Printf(" -- WEAPON_DROPPING\n");
				break;
			case WEAPON_READYING:
				Com_Printf(" -- WEAPON_READYING\n");
				break;
			case WEAPON_RELAXING:
				Com_Printf(" -- WEAPON_RELAXING\n");
				break;
			case WEAPON_VENOM_REST:
				Com_Printf(" -- WEAPON_VENOM_REST\n");
				break;
			case WEAPON_FIRING:
				Com_Printf(" -- WEAPON_FIRING\n");
				break;
			case WEAPON_FIRINGALT:
				Com_Printf(" -- WEAPON_FIRINGALT\n");
				break;
			case WEAPON_RELOADING:
				Com_Printf(" -- WEAPON_RELOADING\n");
				break;
		}
		Com_Printf("weap: %d\n", pm->ps->weapon);
    weaponstate_last = pm->ps->weaponstate;
  }
#endif

	// dec venom timer
	if ( pm->ps->venomTime > 0 ) {
		pm->ps->venomTime -= pml.msec;
	}

	// weapon cool down
	PM_CoolWeapons();

	// check for item using
	if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
		if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) )
		{
			gitem_t *item;

			pm->ps->pm_flags |= PMF_USE_ITEM_HELD;

			if(pm->cmd.holdable) {
				item = BG_FindItemForHoldable(pm->cmd.holdable);

				if(item && (pm->ps->holdable[pm->cmd.holdable] >= item->quantity))	// ->quantity being how much 'ammo' is taken per use
				{
					PM_AddEvent( EV_USE_ITEM0 + pm->cmd.holdable);
					// don't take books away when used
					if(pm->cmd.holdable < HI_BOOK1 || pm->cmd.holdable > HI_BOOK3)
						pm->ps->holdable[ pm->cmd.holdable ]-= item->quantity;

					if(pm->ps->holdable[pm->cmd.holdable] <= 0) {	// empty
						PM_AddEvent(EV_NOITEM);
					}
				}
			}
			else
				PM_AddEvent( EV_USE_ITEM0);		// send "using nothing"
			return;
		}
	}
	else
	{
		pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
	}


	delayedFire = qfalse;

	if(pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE || pm->ps->weapon == WP_DYNAMITE2)
	{
		// (SA) AI's don't set grenadeTimeLeft on +attack, so I don't check for (pm->ps->aiChar) here
		if(pm->ps->grenadeTimeLeft > 0)
		{
			if(pm->ps->weapon == WP_DYNAMITE || pm->ps->weapon == WP_DYNAMITE2) {
				pm->ps->grenadeTimeLeft += pml.msec;

				// JPW NERVE -- in multiplayer, dynamite becomes strategic, so start timer @ 30 seconds
				if (pm->gametype != GT_SINGLE_PLAYER) {
					if (pm->ps->grenadeTimeLeft < 5000)
						pm->ps->grenadeTimeLeft = 5000;
				}
				// jpw

//				Com_Printf("Dynamite Timer: %d\n", pm->ps->grenadeTimeLeft);
			}
			else {
				pm->ps->grenadeTimeLeft -= pml.msec;
//				Com_Printf("Grenade Timer: %d\n", pm->ps->grenadeTimeLeft);

				if(pm->ps->grenadeTimeLeft <= 100)	// give two frames advance notice so there's time to launch and detonate
				{
					pm->ps->grenadeTimeLeft = 100;
					PM_AddEvent( EV_FIRE_WEAPON );
					pm->ps->weaponTime = 1600;
					return;
				}
			}

			if(!(pm->cmd.buttons & BUTTON_ATTACK))	//----(SA)	modified
			{
				if (pm->ps->weaponDelay == ammoTable[pm->ps->weapon].fireDelayTime) {
					// released fire button.  Fire!!!
					BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
				}
			}
			else
				return;
		}
	}

	if ( pm->ps->weaponDelay > 0 )
	{
		pm->ps->weaponDelay -= pml.msec;
		if ( pm->ps->weaponDelay <= 0 )
		{
			pm->ps->weaponDelay = 0;
			delayedFire = qtrue;		// weapon delay has expired.  Fire this frame

			// double check the player is still holding the fire button down for these weapons
			// so you don't get a delayed "non-fire" (fire hit and released, then shot fires)
			switch(pm->ps->weapon) {
				case WP_VENOM:
				case WP_VENOM_FULL:
					if(pm->ps->weaponstate == WEAPON_FIRING)
						delayedFire = qfalse;
					break;
				default:
					break;
			}
		}
	}


	if (pm->ps->weaponstate == WEAPON_RELAXING)
	{
		pm->ps->weaponstate = WEAPON_READY;
		return;
	}

	// make weapon function
	if ( pm->ps->weaponTime > 0 ) {
		pm->ps->weaponTime -= pml.msec;
		if (pm->ps->weaponTime < 0)
			pm->ps->weaponTime = 0;

		// JPW NERVE -- added back for multiplayer pistol balancing
		if (pm->gametype != GT_SINGLE_PLAYER) {
			if ( pm->ps->weapon == WP_LUGER ) {
				if ( pm->ps->releasedFire ) {
					if ( (pm->cmd.buttons & BUTTON_ATTACK) && pm->ps->weaponTime <= 150 ) {
						pm->ps->weaponTime = 0;
					}
				} else if (!(pm->cmd.buttons & BUTTON_ATTACK)) {
					pm->ps->releasedFire = qtrue;
				}
			} else if ( pm->ps->weapon == WP_COLT ) {
				if ( pm->ps->releasedFire ) {
					if ( (pm->cmd.buttons & BUTTON_ATTACK) && pm->ps->weaponTime <= 150 ) {
						pm->ps->weaponTime = 0;
					}
				} else if (!(pm->cmd.buttons & BUTTON_ATTACK)) {
					pm->ps->releasedFire = qtrue;
				}
			}
		}
// jpw
	
	}



	// check for weapon change
	// can't change if weapon is firing, but can change
	// again if lowering or raising

  // TTimo gcc: suggest parentheses around && within ||
	if ( pm->ps->weaponTime <= 0 || ( !weaponstateFiring && pm->ps->weaponDelay <= 0 ) ) {
		if ( pm->ps->weapon != pm->cmd.weapon ) {
			PM_BeginWeaponChange( pm->ps->weapon, pm->cmd.weapon );	//----(SA)	modified
		}
	}

	// check for clip change
	PM_CheckForReload(pm->ps->weapon);

	if ( pm->ps->weaponTime > 0 || pm->ps->weaponDelay > 0) {
		return;
	}

	if ( pm->ps->weaponstate == WEAPON_RELOADING ) {
		PM_FinishWeaponReload();
	}

	// change weapon if time
	if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
		PM_FinishWeaponChange();
		return;
	}

	if ( pm->ps->weaponstate == WEAPON_RAISING )
	{
		pm->ps->weaponstate = WEAPON_READY;
		PM_StartWeaponAnim(WEAP_IDLE1);
		return;
	}


	if(pm->ps->weapon == WP_NONE)	// this is possible since the player starts with nothing
		return;


	// JPW NERVE -- in multiplayer, don't allow panzerfaust or dynamite to fire if charge bar isn't full
	if (pm->gametype >= GT_WOLF) {
		if (pm->ps->weapon == WP_PANZERFAUST) {
			if (pm->ps->stats[ STAT_PLAYER_CLASS ] == PC_LT)
				pfausttimeout = pm->ltChargeTime;
			else
				pfausttimeout = pm->soldierChargeTime;
			if (pm->cmd.serverTime - pm->ps->classWeaponTime < pfausttimeout) 
				return;
		}
		if (pm->ps->weapon == WP_DYNAMITE) {
			if (pm->cmd.serverTime - pm->ps->classWeaponTime < pm->engineerChargeTime)
				return;
		}
		if (pm->ps->weapon == WP_MEDKIT) {
			if (pm->cmd.serverTime - pm->ps->classWeaponTime < (pm->medicChargeTime*0.25f))
				return;
		}
		if (pm->ps->weapon == WP_AMMO) {
			if (pm->cmd.serverTime - pm->ps->classWeaponTime < (pm->ltChargeTime*0.25f))
				return;
		}
		if (pm->ps->weapon == WP_SMOKE_GRENADE) {
			if (pm->cmd.serverTime - pm->ps->classWeaponTime < (pm->ltChargeTime*0.5f))
				return;
		}
	}
	// jpw

	// check for fire
	// if not on fire button and there's not a delayed shot this frame...
	if ( !(pm->cmd.buttons & (BUTTON_ATTACK | WBUTTON_ATTACK2)) && !delayedFire) {
		pm->ps->weaponTime	= 0;
		pm->ps->weaponDelay	= 0;
	
		if(weaponstateFiring) {	// you were just firing, time to relax
			PM_ContinueWeaponAnim(WEAP_IDLE1);
		}

		pm->ps->weaponstate = WEAPON_READY;
		return;
	}

	// jpw
	// player is leaning - no fire
	if(pm->ps->leanf != 0 && pm->ps->weapon != WP_GRENADE_LAUNCHER && pm->ps->weapon != WP_GRENADE_PINEAPPLE ) {
		return;
	}

	// player is zooming - no fire
	// JPW NERVE in MP, LT needs to zoom to call artillery
	if(pm->ps->eFlags & EF_ZOOMING) {
#ifdef GAMEDLL
		if (pm->gametype != GT_SINGLE_PLAYER) {
			pm->ps->weaponTime += 500;
			PM_AddEvent( EV_FIRE_WEAPON );
		}
#endif
		return;
	}

	// player is underwater - no fire
	if(pm->waterlevel == 3) {
		if(	pm->ps->weapon != WP_KNIFE &&
			pm->ps->weapon != WP_KNIFE2 &&
			pm->ps->weapon != WP_GRENADE_LAUNCHER&&
			pm->ps->weapon != WP_GRENADE_PINEAPPLE &&
			pm->ps->weapon != WP_DYNAMITE &&
			pm->ps->weapon != WP_DYNAMITE2) {
				PM_AddEvent(EV_NOFIRE_UNDERWATER);	// event for underwater 'click' for nofire
				pm->ps->weaponTime	= 500;
				return;
		}
	}

	// start the animation even if out of ammo
	switch(pm->ps->weapon)
	{
		default:
			if(!weaponstateFiring) {
				// delay so the weapon can get up into position before firing (and showing the flash)
				pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
			} else {
				BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			}
			break;
		// machineguns should continue the anim, rather than start each fire
		case WP_MP40:
		case WP_THOMPSON:
		case WP_STEN:
		case WP_VENOM:
		case WP_BAR:	//----(SA)	added
		case WP_BAR2:	//----(SA)	added
		case WP_FG42:
		case WP_FG42SCOPE:
		case WP_MEDKIT:					// NERVE - SMF
		case WP_PLIERS:					// NERVE - SMF
		case WP_SMOKE_GRENADE:			// NERVE - SMF
			if(!weaponstateFiring) {
				if(pm->ps->aiChar == AICHAR_PROTOSOLDIER && pm->ps->weapon == WP_VENOM) {
					// proto gets fast spin-up
					pm->ps->weaponDelay = 150;
				} else {
					// delay so the weapon can get up into position before firing (and showing the flash)
					pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
				}
			} else {
				BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qtrue, qtrue );
			}
			break;
		case WP_PANZERFAUST:
		case WP_ROCKET_LAUNCHER:
		case WP_CROSS:
		case WP_SILENCER:
		case WP_LUGER:
		case WP_COLT:
		case WP_AKIMBO:	//----(SA)	added
		case WP_SNIPERRIFLE:
		case WP_SNOOPERSCOPE:
		case WP_MAUSER:
		case WP_GARAND:
		case WP_VENOM_FULL:
			if(!weaponstateFiring) {
				// JPW NERVE -- pfaust has spinup time in MP
				if (pm->gametype != GT_SINGLE_PLAYER) {
					if (pm->ps->weapon == WP_PANZERFAUST)
						PM_AddEvent( EV_SPINUP );
				}
				// jpw

				pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;
			} else {
				BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
			}
			break;

		// melee
		case WP_KNIFE:
		case WP_KNIFE2:
			if(!delayedFire)
			{
				BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qfalse );
			}
			break;
		case WP_GAUNTLET:
			if(!delayedFire)
			{
				BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qfalse, qfalse );
			}
			break;

		// throw
		case WP_DYNAMITE:
		case WP_DYNAMITE2:
		case WP_GRENADE_LAUNCHER:
		case WP_GRENADE_PINEAPPLE:
			if(!delayedFire) {
				if(pm->ps->aiChar) {	// ai characters go into their regular animation setup
					BG_AnimScriptEvent( pm->ps, ANIM_ET_FIREWEAPON, qtrue, qtrue );
				}
				else {				// the player pulls the fuse and holds the hot potato
					if(PM_WeaponAmmoAvailable(pm->ps->weapon)) {
						if(pm->ps->weapon == WP_DYNAMITE || pm->ps->weapon == WP_DYNAMITE2)
							pm->ps->grenadeTimeLeft = 50;
						else
							pm->ps->grenadeTimeLeft = 4000; // start at four seconds and count down

						PM_StartWeaponAnim(WEAP_ATTACK1);
					}
				}

				pm->ps->weaponDelay = ammoTable[pm->ps->weapon].fireDelayTime;

			}
			break;
	}

	pm->ps->weaponstate = WEAPON_FIRING;

	

	// check for out of ammo

	ammoNeeded = ammoTable[pm->ps->weapon].uses;

	if(pm->ps->weapon ) {
		int			ammoAvailable;
		qboolean	reloading, playswitchsound = qtrue;

		ammoAvailable = PM_WeaponAmmoAvailable(pm->ps->weapon);

		if(ammoNeeded > ammoAvailable)
		{
			// you have ammo for this, just not in the clip
			reloading = (qboolean)(ammoNeeded <= pm->ps->ammo[ BG_FindAmmoForWeapon(pm->ps->weapon)]);
      
      // if not in auto-reload mode, and reload was not explicitely requested, just play the 'out of ammo' sound
      if (!pm->pmext->bAutoReload && IS_AUTORELOAD_WEAPON(pm->ps->weapon) && !(pm->cmd.wbuttons & WBUTTON_RELOAD))
      {        
        reloading = qfalse;
      }

			if(pm->ps->eFlags & EF_MELEE_ACTIVE)	// not going to be allowed to reload if holding a chair
				reloading = qfalse;

			if(pm->ps->weapon == WP_SNOOPERSCOPE) {
				reloading = qfalse;
			}

			switch(pm->ps->weapon) {
				// Ridah, only play if using a triggered weapon
				case WP_GAUNTLET:
				case WP_MONSTER_ATTACK1:
				case WP_DYNAMITE:
				case WP_DYNAMITE2:
				case WP_GRENADE_LAUNCHER:
				case WP_GRENADE_PINEAPPLE:
					playswitchsound = qfalse;
					break;

				// some weapons not allowed to reload.  must switch back to primary first
				case WP_SNOOPERSCOPE:
				case WP_SNIPERRIFLE:
				case WP_FG42SCOPE:
					reloading = qfalse;
					break;
			}

			if(playswitchsound) {
				if(reloading)
					PM_AddEvent( EV_EMPTYCLIP );
				else
					PM_AddEvent( EV_NOAMMO );
			}

			if(reloading) {
				PM_ContinueWeaponAnim(WEAP_RELOAD1);	//----(SA)	
			} else {
				PM_ContinueWeaponAnim(WEAP_IDLE1);
				pm->ps->weaponTime += 500;
			}

			return;
		}
	}

	if(pm->ps->weaponDelay > 0)
		// if it hits here, the 'fire' has just been hit and the weapon dictated a delay.
		// animations have been started, weaponstate has been set, but no weapon events yet. (except possibly EV_NOAMMO)
		// checks for delayed weapons that have already been fired are return'ed above.
		return;


	// take an ammo away if not infinite
	if(PM_WeaponAmmoAvailable(pm->ps->weapon) != -1 ) {
		// Rafael - check for being mounted on mg42
		if (!(pm->ps->persistant[PERS_HWEAPON_USE]))
			PM_WeaponUseAmmo(pm->ps->weapon, ammoNeeded);
	}


	// fire weapon

	// add weapon heat
	if(ammoTable[pm->ps->weapon].maxHeat)
		pm->ps->weapHeat[pm->ps->weapon] += ammoTable[pm->ps->weapon].nextShotTime;

	// first person weapon animations

	// if this was the last round in the clip, play the 'lastshot' animation
	// this animation has the weapon in a "ready to reload" state
	if(pm->ps->weapon == WP_AKIMBO) {
		if(BG_AkimboFireSequence(pm->ps))
			weapattackanim = WEAP_ATTACK2;
		else
			weapattackanim = WEAP_ATTACK1;
	} else {
		if(PM_WeaponClipEmpty(pm->ps->weapon))
			weapattackanim = WEAP_ATTACK_LASTSHOT;
		else
			weapattackanim = WEAP_ATTACK1;
	}

	switch(pm->ps->weapon) {
		case WP_MAUSER:
		case WP_GRENADE_LAUNCHER:
		case WP_GRENADE_PINEAPPLE:
		case WP_DYNAMITE:
		case WP_DYNAMITE2:
			PM_StartWeaponAnim(weapattackanim);
			break;

		case WP_VENOM:
		case WP_MP40:
		case WP_THOMPSON:
		case WP_STEN:
		case WP_MEDKIT:
		case WP_PLIERS:
		case WP_SMOKE_GRENADE:
			PM_ContinueWeaponAnim(weapattackanim);
			break;

		default:
			// RF, testing
//			PM_ContinueWeaponAnim(weapattackanim);
			PM_StartWeaponAnim(weapattackanim);
			break;
	}

	// JPW NERVE -- in multiplayer, pfaust fires once then switches to pistol since it's useless for a while
	if (pm->gametype != GT_SINGLE_PLAYER) {
		if ( (pm->ps->weapon == WP_PANZERFAUST) || (pm->ps->weapon == WP_SMOKE_GRENADE ) || (pm->ps->weapon == WP_DYNAMITE) )
			PM_AddEvent( EV_NOAMMO );
	}
	// jpw

	if(PM_WeaponClipEmpty(pm->ps->weapon)) {
		PM_AddEvent( EV_FIRE_WEAPON_LASTSHOT );
	} else {
		PM_AddEvent( EV_FIRE_WEAPON );
	}

	// RF
	pm->ps->releasedFire = qfalse;
	pm->ps->lastFireTime = pm->cmd.serverTime;


	aimSpreadScaleAdd = 0;

	switch( pm->ps->weapon ) {
	case WP_KNIFE:
	case WP_KNIFE2:
	case WP_SPEARGUN_CO2:
	case WP_SPEARGUN:
	case WP_PANZERFAUST:
	case WP_ROCKET_LAUNCHER:
	case WP_DYNAMITE:
	case WP_DYNAMITE2:
	case WP_GRENADE_LAUNCHER:
	case WP_GRENADE_PINEAPPLE:
	case WP_PROX:
	case WP_FLAMETHROWER:
	case WP_CROSS:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		break;

	case WP_LUGER:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 35;
		break;

	case WP_COLT:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 20;
		break;

//----(SA)	added
	case WP_AKIMBO:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 20;
		break;
//----(SA)	end

	case WP_MAUSER:
	case WP_GARAND:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 50;
		break;

	case WP_SNIPERRIFLE:	// (SA) not so much added per shot.  these weapons mostly uses player movement to get out of whack
		addTime = ammoTable[pm->ps->weapon].nextShotTime;

		// JPW NERVE crippling the rifle a bit in multiplayer; it's way too 
		// strong so make it go completely out every time you fire
		if (pm->gametype != GT_SINGLE_PLAYER) {
			// avoid exploiting centerview to go around the spread
			pm->pmext->blockCenterViewTime = pm->cmd.serverTime + 1000;
			aimSpreadScaleAdd = 100;
		}
		else
			aimSpreadScaleAdd = 20;
		// jpw

		break;
	case WP_SNOOPERSCOPE:
		// JPW NERVE crippling the rifle a bit in multiplayer; it's way too strong so 
		// make it go completely out every time you fire snooper doesn't do one-shot body 
		// kills, so give it a little less bounce
		addTime = ammoTable[pm->ps->weapon].nextShotTime;

		if (pm->gametype != GT_SINGLE_PLAYER) {
			aimSpreadScaleAdd = 50;
//			addTime *= 2;
		}
		else
			aimSpreadScaleAdd = 10;
		// jpw

		break;

	case WP_BAR:	//----(SA)	added
	case WP_BAR2:	//----(SA)	added

	case WP_FG42:
	case WP_FG42SCOPE:

	case WP_MP40:
	case WP_THOMPSON:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 15+rand()%10;	// (SA) new values for DM
		break;

	case WP_STEN:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 15+rand()%10;	// (SA) new values for DM
		break;

	case WP_SILENCER:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 35;
		break;

	case WP_VENOM:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 10;
		break;

	case WP_VENOM_FULL:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		aimSpreadScaleAdd = 40;
		break;
// JPW NERVE
	case WP_ARTY:
	case WP_MEDIC_SYRINGE:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		break;
	case WP_AMMO:
		addTime = ammoTable[pm->ps->weapon].nextShotTime;
		break;
// jpw
	// JPW: engineers disarm bomb "on the fly" (high sample rate) but medics & LTs throw out health pack/smoke grenades slow
	// NERVE - SMF
	case WP_PLIERS:
		addTime = 50;
		break;
	case WP_MEDKIT:
		addTime = 1000;
		break;
	case WP_SMOKE_GRENADE:
		addTime = 1000;
		break;
	// -NERVE - SMF
	case WP_MONSTER_ATTACK1:
		switch (pm->ps->aiChar) {
		case AICHAR_ZOMBIE:
			// Zombie spitting blood
			addTime = 1000;
			break;
		default:
			break;
		}

	default:
	case WP_GAUNTLET:
		switch (pm->ps->aiChar)
		{
			case AICHAR_LOPER:	// delay 'til next attack
			case AICHAR_SEALOPER:
				addTime = 1000;
				break;
			default:
				addTime = 250;
				break;
		}
		break;
	}


	// check for overheat

	// the weapon can overheat, and it's hot
	if(	ammoTable[pm->ps->weapon].maxHeat && pm->ps->weapHeat[pm->ps->weapon]) {
		// it is overheating
		if(pm->ps->weapHeat[pm->ps->weapon] >= ammoTable[pm->ps->weapon].maxHeat) {
			pm->ps->weapHeat[pm->ps->weapon] = ammoTable[pm->ps->weapon].maxHeat;	// cap heat to max
			PM_AddEvent( EV_WEAP_OVERHEAT);
//			PM_StartWeaponAnim(WEAP_IDLE1);	// removed.  client handles anim in overheat event
			addTime = 2000;		// force "heat recovery minimum" to 2 sec right now
		}
	}

	if ( pm->ps->powerups[PW_HASTE] ) {
		addTime /= 1.3;
	}

	// add the recoil amount to the aimSpreadScale
//	pm->ps->aimSpreadScale += 3.0*aimSpreadScaleAdd;
//	if (pm->ps->aimSpreadScale > 255) pm->ps->aimSpreadScale = 255;
	pm->ps->aimSpreadScaleFloat += 3.0*aimSpreadScaleAdd;
	if (pm->ps->aimSpreadScaleFloat > 255)
		pm->ps->aimSpreadScaleFloat = 255;
	pm->ps->aimSpreadScale = (int)(pm->ps->aimSpreadScaleFloat);

	pm->ps->weaponTime += addTime;

	PM_SwitchIfEmpty();
}


/*
================
PM_Animate
================
*/
#define MYTIMER_SALUTE	 1133	// 17 frames, 15 fps
#define MYTIMER_DISMOUNT 667	// 10 frames, 15 fps

static void PM_Animate( void ) {
/*
	if ( pm->cmd.buttons & BUTTON_GESTURE ) {
 		if ( pm->ps->torsoTimer == 0) {
			PM_StartTorsoAnim( BOTH_SALUTE );
			PM_StartLegsAnim( BOTH_SALUTE );

			pm->ps->torsoTimer = MYTIMER_SALUTE;
			pm->ps->legsTimer = MYTIMER_SALUTE;

			if (!pm->ps->aiChar)	// Ridah, we'll play a custom sound upon calling the Taunt
				PM_AddEvent( EV_TAUNT );	// for playing the sound
		}
	}
*/
}


/*
================
PM_DropTimers
================
*/
static void PM_DropTimers( void ) {
	// drop misc timing counter
	if ( pm->ps->pm_time ) {
		if ( pml.msec >= pm->ps->pm_time ) {
			pm->ps->pm_flags &= ~PMF_ALL_TIMES;
			pm->ps->pm_time = 0;
		} else {
			pm->ps->pm_time -= pml.msec;
		}
	}

	// drop animation counter
	if ( pm->ps->legsTimer > 0 ) {
		pm->ps->legsTimer -= pml.msec;
		if ( pm->ps->legsTimer < 0 ) {
			pm->ps->legsTimer = 0;
		}
	}

	if ( pm->ps->torsoTimer > 0 ) {
		pm->ps->torsoTimer -= pml.msec;
		if ( pm->ps->torsoTimer < 0 ) {
			pm->ps->torsoTimer = 0;
		}
	}

	// first person weapon counter
	if ( pm->ps->weapAnimTimer > 0 ) {
		pm->ps->weapAnimTimer -= pml.msec;
		if ( pm->ps->weapAnimTimer < 0 ) {
			pm->ps->weapAnimTimer = 0;
		}
	}
}



#define LEAN_MAX	28.0f
#define LEAN_TIME_TO	200.0f	// time to get to/from full lean
#define LEAN_TIME_FR	300.0f	// time to get to/from full lean

/*
==============
PM_CalcLean

==============
*/
void PM_UpdateLean(playerState_t *ps, usercmd_t *cmd, pmove_t *tpm) {
	vec3_t		start, end, tmins, tmaxs, right;
	int			leaning = 0;	// -1 left, 1 right
	float		leanofs = 0;
	vec3_t		viewangles;
	trace_t		trace;

	if(ps->aiChar)
		return;

	if( (cmd->wbuttons & (WBUTTON_LEANLEFT|WBUTTON_LEANRIGHT))  && !cmd->forwardmove && cmd->upmove <= 0 ) {
		// if both are pressed, result is no lean
		if(cmd->wbuttons & WBUTTON_LEANLEFT)
			leaning -= 1;
		if(cmd->wbuttons & WBUTTON_LEANRIGHT)
			leaning += 1;
	}

	if(ps->eFlags & EF_MG42_ACTIVE) {
		leaning = 0;	// leaning not allowed on mg42
	}

	if(ps->eFlags & EF_FIRING)
		leaning = 0;	// not allowed to lean while firing

  // ATVI Wolfenstein Misc #479 - initial fix to #270 would crash in g_synchronousClients 1 situation
	if (ps->weaponstate == WEAPON_FIRING && (ps->weapon == WP_DYNAMITE || ps->weapon == WP_DYNAMITE2))
		leaning = 0; // not allowed while tossing dynamite

	leanofs = ps->leanf;


	if(!leaning) {	// go back to center position
		if ( leanofs > 0 ) {		// right
			//FIXME: play lean anim backwards?
			leanofs -= (((float)pml.msec/(float)LEAN_TIME_FR)*LEAN_MAX);
			if ( leanofs < 0 )
				leanofs = 0;
		}
		else if ( leanofs < 0 ) {	// left
			//FIXME: play lean anim backwards?
			leanofs += (((float)pml.msec/(float)LEAN_TIME_FR)*LEAN_MAX);
			if ( leanofs > 0 )
				leanofs = 0;
		}
	}

	if(leaning) {
		if(leaning > 0) {	// right
			if(leanofs < LEAN_MAX)
				leanofs += (((float)pml.msec/(float)LEAN_TIME_TO)*LEAN_MAX);

			if(leanofs > LEAN_MAX)
				leanofs = LEAN_MAX;

		}
		else {				// left
			if(leanofs > -LEAN_MAX)
				leanofs -= (((float)pml.msec/(float)LEAN_TIME_TO)*LEAN_MAX);

			if(leanofs < -LEAN_MAX)
				leanofs = -LEAN_MAX;

		}
	}

	ps->leanf = leanofs;

	if(leaning){
		VectorCopy( ps->origin, start );
		start[2] += ps->viewheight;

		VectorCopy( ps->viewangles, viewangles );
		viewangles[ROLL] += leanofs/2.0f;
		AngleVectors( viewangles, NULL, right, NULL );
		VectorMA( start, leanofs, right, end );		
		
		VectorSet( tmins, -8, -8, -7 ); // ATVI Wolfenstein Misc #472, bumped from -4 to cover gun clipping issue
		VectorSet( tmaxs, 8, 8, 4 );

		if(pm)
			pm->trace (&trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID);
		else
			tpm->trace (&trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID);

		ps->leanf *= trace.fraction;
	}


	if(ps->leanf)
		cmd->rightmove = 0;		// also disallowed in cl_input ~391

}



/*
================
PM_UpdateViewAngles

This can be used as another entry point when only the viewangles
are being updated isntead of a full move
================
*/
void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, void (trace)( trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask ) ) {	//----(SA)	modified
	short	temp;
	int		i;
	pmove_t tpm;
	vec3_t	oldViewAngles;

	// DHM - Nerve :: Added support for PMF_TIME_LOCKPLAYER
	if ( ps->pm_type == PM_INTERMISSION || ps->pm_flags & PMF_TIME_LOCKPLAYER ) {
		return;		// no view changes at all
	}

	if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {

		// DHM - Nerve :: Allow players to look around while 'wounded' or lock to a medic if nearby
		temp = cmd->angles[1] + ps->delta_angles[1];
		if ( ps->stats[STAT_DEAD_YAW] == 999 )
			ps->stats[STAT_DEAD_YAW] = SHORT2ANGLE(temp);
		return;		// no view changes at all
	}

	VectorCopy( ps->viewangles, oldViewAngles );

	// circularly clamp the angles with deltas
	for (i=0 ; i<3 ; i++) {
		temp = cmd->angles[i] + ps->delta_angles[i];
		if ( i == PITCH ) {
			// don't let the player look up or down more than 90 degrees
			if ( temp > 16000 ) {
				ps->delta_angles[i] = 16000 - cmd->angles[i];
				temp = 16000;
			} else if ( temp < -16000 ) {
				ps->delta_angles[i] = -16000 - cmd->angles[i];
				temp = -16000;
			}
		}
		ps->viewangles[i] = SHORT2ANGLE(temp);		
	}

	if( ps->eFlags & EF_MG42_ACTIVE ) {
		float yaw, oldYaw;
		float degsSec = MG42_YAWSPEED;
		float arcMin, arcMax, arcDiff;

		yaw = ps->viewangles[YAW];
		oldYaw = oldViewAngles[YAW];

		if ( yaw - oldYaw > 180 ) {
			yaw -= 360;
		}
		if ( yaw - oldYaw < -180 ) {
			yaw += 360;
		}

		if( yaw > oldYaw ) {
			if( yaw - oldYaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw + degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW];
			}
		} else if( oldYaw > yaw ) {
			if( oldYaw - yaw > degsSec * pml.frametime ) {
				ps->viewangles[YAW] = oldYaw - degsSec * pml.frametime;

				// Set delta_angles properly
				ps->delta_angles[YAW] = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW];
			}
		}

		// limit harc and varc
		
		// pitch (varc)
		arcMax = pm->pmext->varc;
		arcMin = pm->pmext->varc / 2;
		arcDiff = AngleNormalize180( ps->viewangles[PITCH] - pm->pmext->centerangles[PITCH] );


		if( arcDiff > arcMin ) {
			ps->viewangles[PITCH] = AngleNormalize180( pm->pmext->centerangles[PITCH] + arcMin );

			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT(ps->viewangles[PITCH]) - cmd->angles[PITCH];
		} else if( arcDiff < -arcMax ) {
			ps->viewangles[PITCH] = AngleNormalize180( pm->pmext->centerangles[PITCH] - arcMax );
			
			// Set delta_angles properly
			ps->delta_angles[PITCH] = ANGLE2SHORT(ps->viewangles[PITCH]) - cmd->angles[PITCH];
		}

		// yaw (harc)
		arcMin = arcMax = pm->pmext->harc;
		arcDiff = AngleNormalize180( ps->viewangles[YAW] - pm->pmext->centerangles[YAW] );


		if( arcDiff > arcMin ) {
			ps->viewangles[YAW] = AngleNormalize180( pm->pmext->centerangles[YAW] + arcMin );

			// Set delta_angles properly
			ps->delta_angles[YAW] = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW];
		} else if( arcDiff < -arcMax ) {
			ps->viewangles[YAW] = AngleNormalize180( pm->pmext->centerangles[YAW] - arcMax );
			
			// Set delta_angles properly
			ps->delta_angles[YAW] = ANGLE2SHORT(ps->viewangles[YAW]) - cmd->angles[YAW];
		}
	}

	tpm.trace = (void *)&trace;
//	tpm.trace (&trace, start, tmins, tmaxs, end, ps->clientNum, MASK_PLAYERSOLID);

	PM_UpdateLean(ps, cmd, &tpm);
}

/*
================
PM_CheckLadderMove

  Checks to see if we are on a ladder
================
*/
qboolean	ladderforward;
vec3_t		laddervec;

void PM_CheckLadderMove (void) {
	vec3_t	spot;
	vec3_t	flatforward;
	trace_t	trace;
	float	tracedist;
	#define	TRACE_LADDER_DIST	48.0
	qboolean wasOnLadder;
	
	if (pm->ps->pm_time)
		return;

	//if (pm->ps->pm_flags & PM_DEAD)
	//	return;

	if (pml.walking) {
		tracedist = 1.0;
	} else {
		tracedist = TRACE_LADDER_DIST;
	}

	wasOnLadder = ((pm->ps->pm_flags & PMF_LADDER) != 0);

	pml.ladder = qfalse;
	pm->ps->pm_flags &= ~PMF_LADDER;	// clear ladder bit
	ladderforward = qfalse;

	/*
	if (pm->ps->eFlags & EF_DEAD) {	// dead bodies should fall down ladders
		return;
	}

	if (pm->ps->pm_flags & PM_DEAD && pm->ps->stats[STAT_HEALTH] <= 0)
	{
		return;
	}
	*/
	if (pm->ps->stats[STAT_HEALTH] <= 0)
	{
		pm->ps->groundEntityNum = ENTITYNUM_NONE;
		pml.groundPlane = qfalse;
		pml.walking = qfalse;
		return;
	}

	// check for ladder
	flatforward[0] = pml.forward[0];
	flatforward[1] = pml.forward[1];
	flatforward[2] = 0;
	VectorNormalize (flatforward);

	VectorMA (pm->ps->origin, tracedist, flatforward, spot);
	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask);
	if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER))
	{
		pml.ladder = qtrue;
	}
/*
	if (!pml.ladder && DotProduct(pm->ps->velocity, pml.forward) < 0) {
		// trace along the negative velocity, so we grab onto a ladder if we are trying to reverse onto it from above the ladder
		flatforward[0] = -pm->ps->velocity[0];
		flatforward[1] = -pm->ps->velocity[1];
		flatforward[2] = 0;
		VectorNormalize (flatforward);

		VectorMA (pm->ps->origin, tracedist, flatforward, spot);
		pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask);
		if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER))
		{
			pml.ladder = qtrue;
		}
	}
*/
	if (pml.ladder) {
		VectorCopy( trace.plane.normal, laddervec );
	}

	if (pml.ladder && !pml.walking && (trace.fraction * tracedist > 1.0)) {
		vec3_t mins;
		// if we are only just on the ladder, don't do this yet, or it may throw us back off the ladder
		pml.ladder = qfalse;
		VectorCopy( pm->mins, mins );
		mins[2] = -1;
		VectorMA (pm->ps->origin, -tracedist, laddervec, spot);
		pm->trace (&trace, pm->ps->origin, mins, pm->maxs, spot, pm->ps->clientNum, pm->tracemask);
		if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER)) {
			ladderforward = qtrue;
			pml.ladder = qtrue;
			pm->ps->pm_flags |= PMF_LADDER;	// set ladder bit
		} else {
			pml.ladder = qfalse;
		}
	} else if (pml.ladder) {
		pm->ps->pm_flags |= PMF_LADDER;	// set ladder bit
	}

	// create some up/down velocity if touching ladder
	if ( pml.ladder )
	{
		if (pml.walking) {
			// we are currently on the ground, only go up and prevent X/Y if we are pushing forwards
			if (pm->cmd.forwardmove <= 0) {
				pml.ladder = qfalse;
			}
		}
	}

	// if we have just dismounted the ladder at the top, play dismount
	if (!pml.ladder && wasOnLadder && pm->ps->velocity[2] > 0) {
		BG_AnimScriptEvent( pm->ps, ANIM_ET_CLIMB_DISMOUNT, qfalse, qfalse );
	}
	// if we have just mounted the ladder
	if (pml.ladder && !wasOnLadder && pm->ps->velocity[2] < 0) {	// only play anim if going down ladder
		BG_AnimScriptEvent( pm->ps, ANIM_ET_CLIMB_MOUNT, qfalse, qfalse );
	}
}

/*
============
PM_LadderMove
============
*/
void PM_LadderMove (void) {
	float	wishspeed, scale;
	vec3_t	wishdir, wishvel;
	float	upscale;

	if (ladderforward) {
		// move towards the ladder
		VectorScale( laddervec, -200.0, wishvel );
		pm->ps->velocity[0] = wishvel[0];
		pm->ps->velocity[1] = wishvel[1];
	}

	upscale = (pml.forward[2] + 0.5)*2.5;
	if (upscale > 1.0)
		upscale = 1.0;
	else if (upscale < -1.0)
		upscale = -1.0;

	// forward/right should be horizontal only
	pml.forward[2] = 0;
	pml.right[2] = 0;
	VectorNormalize (pml.forward);
	VectorNormalize (pml.right);

	// move depending on the view, if view is straight forward, then go up
	// if view is down more then X degrees, start going down
	// if they are back pedalling, then go in reverse of above
	scale = PM_CmdScale( &pm->cmd );
	VectorClear( wishvel );

	if (pm->cmd.forwardmove) {
		if (pm->ps->aiChar)
			wishvel[2] = 0.5 * upscale * scale * (float)pm->cmd.forwardmove;
		else	// player speed
			wishvel[2] = 0.9 * upscale * scale * (float)pm->cmd.forwardmove;
	}
//Com_Printf("wishvel[2] = %i, fwdmove = %i\n", (int)wishvel[2], (int)pm->cmd.forwardmove );

	if (pm->cmd.rightmove) {
		// strafe, so we can jump off ladder
		vec3_t	ladder_right, ang;
		vectoangles( laddervec, ang );
		AngleVectors( ang, NULL, ladder_right, NULL );

		// if we are looking away from the ladder, reverse the right vector
		if (DotProduct( laddervec, pml.forward ) > 0)
			VectorInverse( ladder_right );

		VectorMA( wishvel, 0.5 * scale * (float)pm->cmd.rightmove, pml.right, wishvel );
	}

	// do strafe friction
	PM_Friction();

	wishspeed = VectorNormalize2( wishvel, wishdir );

	PM_Accelerate (wishdir, wishspeed, pm_accelerate);
	if (!wishvel[2])
	{
		if (pm->ps->velocity[2] > 0)
		{
			pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
			if (pm->ps->velocity[2] < 0)
				pm->ps->velocity[2]  = 0;
		}
		else
		{
			pm->ps->velocity[2] += pm->ps->gravity * pml.frametime;
			if (pm->ps->velocity[2] > 0)
				pm->ps->velocity[2]  = 0;
		}
	}

//Com_Printf("vel[2] = %i\n", (int)pm->ps->velocity[2] );

	PM_StepSlideMove (qfalse);	// no gravity while going up ladder

	// always point legs forward
	pm->ps->movementDir = 0;
}


/*
==============
PM_Sprint
==============
*/
void PM_Sprint( void ) {
	if (pm->cmd.buttons & BUTTON_SPRINT && (pm->cmd.forwardmove || pm->cmd.rightmove)
		&& !(pm->ps->pm_flags & PMF_DUCKED) )
	{
		// take time from powerup before taking it from sprintTime
		if(pm->ps->powerups[PW_NOFATIGUE]) {
			pm->ps->powerups[PW_NOFATIGUE] -= 50;

			// (SA) go ahead and continue to recharge stamina at double 
			// rate with stamina powerup even when exerting
			pm->ps->sprintTime += 10;
			if (pm->ps->sprintTime > 20000)
				pm->ps->sprintTime = 20000;

			if(pm->ps->powerups[PW_NOFATIGUE] < 0)
				 pm->ps->powerups[PW_NOFATIGUE] = 0;
		}
		// JPW NERVE -- sprint time tuned for multiplayer
		else if (pm->gametype != GT_SINGLE_PLAYER) {
			// JPW NERVE adjusted for framerate independence
			pm->ps->sprintTime -= 5000*pml.frametime;
		}
		else {
			// *not* adjusted, dunno what they want for tuning values
			pm->ps->sprintTime -= 50;
		}
		// jpw

		if (pm->ps->sprintTime < 0)
			pm->ps->sprintTime = 0;
		
		if (!pm->ps->sprintExertTime)
		{
			pm->ps->sprintExertTime = 1;
		}
	}
	else 
	{	
		// JPW NERVE -- in multiplayer, recharge faster for top 75% of sprint bar 
		// (for people that *just* use it for jumping, not sprint) this code was 
		// mucked about with to eliminate client-side framerate-dependancy in wolf single player
#ifdef GAMEDLL
		if(pm->ps->powerups[PW_NOFATIGUE])	// (SA) recharge at 2x with stamina powerup
			pm->ps->sprintTime += 10;
		else {
			if (pm->gametype != GT_SINGLE_PLAYER) {
				pm->ps->sprintTime += 500*pml.frametime;		// JPW NERVE adjusted for framerate independence
				if (pm->ps->sprintTime > 5000)
					pm->ps->sprintTime += 500*pml.frametime;	// JPW NERVE adjusted for framerate independence
			}
			else
				pm->ps->sprintTime += 5;
			// jpw
		}
#endif // GAMEDLL
		if (pm->ps->sprintTime > 20000)
			pm->ps->sprintTime = 20000;

		pm->ps->sprintExertTime = 0;
	}
}

/*
================
PmoveSingle

================
*/
void trap_SnapVector( float *v );

void PmoveSingle (pmove_t *pmove) {
	// Ridah
	qboolean	isDummy;

	isDummy = ((pmove->ps->eFlags & EF_DUMMY_PMOVE) != 0);
	// done.

	if (!isDummy) {
		// RF, update conditional values for anim system
		BG_AnimUpdatePlayerStateConditions( pmove );
	}

	pm = pmove;

	// this counter lets us debug movement problems with a journal
	// by setting a conditional breakpoint fot the previous frame
	c_pmove++;

	// clear results
	pm->numtouch = 0;
	pm->watertype = 0;
	pm->waterlevel = 0;

	if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
		pm->tracemask &= ~CONTENTS_BODY;	// corpses can fly through bodies
	}

	// make sure walking button is clear if they are running, to avoid
	// proxy no-footsteps cheats
	if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
		pm->cmd.buttons &= ~BUTTON_WALKING;
	}

	// set the talk balloon flag
	if (!isDummy) {
		if ( !pm->ps->aiChar && pm->cmd.buttons & BUTTON_TALK ) {
			pm->ps->eFlags |= EF_TALK;
		} else {
			pm->ps->eFlags &= ~EF_TALK;
		}
	}

	// set the firing flag for continuous beam weapons

	pm->ps->eFlags &= ~(EF_FIRING | EF_ZOOMING);

	if( pm->cmd.wbuttons & WBUTTON_ZOOM && pm->ps->stats[STAT_HEALTH] >= 0 ) {
		if(pm->ps->stats[STAT_KEYS] & (1<<INV_BINOCS)) {		// (SA) binoculars are an inventory item (inventory==keys)
			if(pm->ps->weapon != WP_SNIPERRIFLE && pm->ps->weapon != WP_SNOOPERSCOPE) {	// don't allow binocs if using the sniper scope
				if(!(pm->ps->eFlags & EF_MG42_ACTIVE)) {	// or if mounted on a weapon
					pm->ps->eFlags |= EF_ZOOMING;
				}
			}

			// don't allow binocs if in the middle of throwing grenade
			if( (pm->ps->weapon == WP_GRENADE_LAUNCHER || pm->ps->weapon == WP_GRENADE_PINEAPPLE || pm->ps->weapon == WP_DYNAMITE || pm->ps->weapon == WP_DYNAMITE2) && pm->ps->grenadeTimeLeft > 0) {
				pm->ps->eFlags &= ~EF_ZOOMING;
			}
		}
	}


	if ( !(pm->ps->pm_flags & PMF_RESPAWNED) &&
		  (pm->ps->pm_type != PM_INTERMISSION) ) {

		// check for ammo
		if(PM_WeaponAmmoAvailable(pm->ps->weapon)) {
			// check if zooming
			// DHM - Nerve :: Let's use the same flag we just checked above, Ok?
			if(!(pm->ps->eFlags & EF_ZOOMING)) {
				if(!pm->ps->leanf) {
					if ( pm->ps->weaponstate == WEAPON_READY || pm->ps->weaponstate == WEAPON_FIRING ) {

						// all clear, fire!
						if( pm->cmd.buttons & BUTTON_ATTACK && !(pm->cmd.buttons & BUTTON_TALK) )
							pm->ps->eFlags |= EF_FIRING;
					}
				}
			}
		}
	}


	// clear the respawned flag if attack and use are cleared
	if ( pm->ps->stats[STAT_HEALTH] > 0 && 
		!( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
		pm->ps->pm_flags &= ~PMF_RESPAWNED;
	}

	// if talk button is down, dissallow all other input
	// this is to prevent any possible intercept proxy from
	// adding fake talk balloons
	if ( pmove->cmd.buttons & BUTTON_TALK ) {
		// keep the talk button set tho for when the cmd.serverTime > 66 msec
		// and the same cmd is used multiple times in Pmove
		pmove->cmd.buttons = BUTTON_TALK;
		pmove->cmd.wbuttons = 0;
		pmove->cmd.forwardmove = 0;
		pmove->cmd.rightmove = 0;
		pmove->cmd.upmove = 0;
	}

	// clear all pmove local vars
	memset (&pml, 0, sizeof(pml));

	// determine the time
	pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
	if (!isDummy) {
		if ( pml.msec < 1 ) {
			pml.msec = 1;
		} else if ( pml.msec > 200 ) {
			pml.msec = 200;
		}
	}
	pm->ps->commandTime = pmove->cmd.serverTime;

	// save old org in case we get stuck
	VectorCopy (pm->ps->origin, pml.previous_origin);

	// save old velocity for crashlanding
	VectorCopy (pm->ps->velocity, pml.previous_velocity);

	pml.frametime = pml.msec * 0.001;

	// update the viewangles
	// Ridah
	if (!isDummy)
	// done.
		if (!(pm->ps->pm_flags & PMF_LIMBO)) // JPW NERVE
		PM_UpdateViewAngles( pm->ps, &pm->cmd, pm->trace );	//----(SA)	modified

	AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);

	if ( pm->cmd.upmove < 10 ) {
		// not holding jump
		pm->ps->pm_flags &= ~PMF_JUMP_HELD;
	}

	// decide if backpedaling animations should be used
	if ( pm->cmd.forwardmove < 0 ) {
		pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
	} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
		pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
	}

	if ( pm->ps->pm_type >= PM_DEAD || pm->ps->pm_flags & (PMF_LIMBO|PMF_TIME_LOCKPLAYER) ) {			// DHM - Nerve
		pm->cmd.forwardmove = 0;
		pm->cmd.rightmove = 0;
		pm->cmd.upmove = 0;
	}

	if ( pm->ps->pm_type == PM_SPECTATOR ) {
		PM_CheckDuck ();
		PM_FlyMove ();
		PM_DropTimers ();
		return;
	}

	if ( pm->ps->pm_type == PM_NOCLIP ) {
		PM_NoclipMove ();
		PM_DropTimers ();
		return;
	}

	if (pm->ps->pm_type == PM_FREEZE) {
		return;		// no movement at all
	}

	if ( pm->ps->pm_type == PM_INTERMISSION ) {
		return;		// no movement at all
	}

	// set watertype, and waterlevel
	PM_SetWaterLevel();
	pml.previous_waterlevel = pmove->waterlevel;

	// set mins, maxs, and viewheight
	PM_CheckDuck ();

	// set groundentity
	PM_GroundTrace();

	if ( pm->ps->pm_type == PM_DEAD ) {
		PM_DeadMove ();
	}

	// Ridah, ladders
	PM_CheckLadderMove ();

	if (!isDummy) {
		PM_DropTimers();
	}

	if (pml.ladder) {
		PM_LadderMove();
	} else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
		PM_WaterJumpMove();
	} else if ( pm->waterlevel > 1 ) {
		// swimming
		PM_WaterMove();
	} else if ( pml.walking ) {
		// walking on ground
		PM_WalkMove();
	} else {
		// airborne
		PM_AirMove();
	}

	
	PM_Sprint();

	
	// Ridah
	if (!isDummy)
	// done.
		PM_Animate();

	// set groundentity, watertype, and waterlevel
	PM_GroundTrace();
	PM_SetWaterLevel();

	// Ridah
	if (!isDummy) {
	// done.

		// weapons
		PM_Weapon();

		// footstep events / legs animations
		PM_Footsteps();

		// entering / leaving water splashes
		PM_WaterEvents();

		// snap some parts of playerstate to save network bandwidth
		trap_SnapVector( pm->ps->velocity );
//		SnapVector( pm->ps->velocity );

	// Ridah
	}
	// done.
}


/*
================
Pmove

Can be called by either the server or the client
================
*/
int Pmove (pmove_t *pmove) {
	int			finalTime;

	// Ridah
	if (pmove->ps->eFlags & EF_DUMMY_PMOVE) {
		PmoveSingle( pmove );
		return (0);
	}
	// done.

	finalTime = pmove->cmd.serverTime;

	if ( finalTime < pmove->ps->commandTime ) {
		return (0);	// should not happen
	}

	if ( finalTime > pmove->ps->commandTime + 1000 ) {
		pmove->ps->commandTime = finalTime - 1000;
	}

	pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);

	// RF
	pm = pmove;
	PM_AdjustAimSpreadScale ();

//	startedTorsoAnim = -1;
//	startedLegAnim = -1;

	// chop the move up if it is too long, to prevent framerate
	// dependent behavior
	while ( pmove->ps->commandTime != finalTime ) {
		int		msec;

		msec = finalTime - pmove->ps->commandTime;

		if ( pmove->pmove_fixed ) {
			if ( msec > pmove->pmove_msec ) {
				msec = pmove->pmove_msec;
			}
		}
		else {
			if ( msec > 66 ) {
				msec = 66;
			}
		}
		pmove->cmd.serverTime = pmove->ps->commandTime + msec;
		PmoveSingle( pmove );

		if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
			pmove->cmd.upmove = 20;
		}
	}

	//PM_CheckStuck();

	if ( (pm->ps->stats[STAT_HEALTH] <= 0 || pm->ps->pm_type == PM_DEAD ) && pml.groundTrace.surfaceFlags & SURF_MONSTERSLICK )
		return (pml.groundTrace.surfaceFlags);
	else 
		return (0);

}
