//INCIN -- duplicated bg_pmove.c exception of predict code added to below
// cg_pmove.c -- both games player movement code
// takes a playerstate and a usercmd as input and returns a modifed playerstate

#include "../game/q_shared.h"
#include "../game/bg_public.h"
#include "cg_local.h"
#include "../game/bg_local.h"

// Alliance
#include "../game/bg_battlebots.h"
#include "../game/bb_defs.h"

qboolean BG_battleWeaponsEnabled(void);
int BG_gamemode(void);
qboolean BG_grappleEnabled(pmove_t *pm);

qboolean BG_IamClient(void);
qboolean BG_IamServer(void);
qboolean BG_noClientPredictFire(void);
//qboolean BG_predictHook(void);
void BG_check_Offhand_Grapple(pmove_t *pm);
qboolean BG_ammoRestore(void);
// end alliance

pmove_t		*pm;
pml_t		pml;

// movement parameters
float	pm_stopspeed = 100.0f;
float	pm_duckScale = 0.25f;
float	pm_swimScale = 0.50f;
float	pm_wadeScale = 0.70f;

// Alliance-ladder
float   pm_ladderScale = 0.50; // Set the max movement speed to HALF of normal
float   pm_ladderAccelerate = 3000; // The acceleration to friction
// ratio is 1:1
float   pm_ladderfriction = 3000;   // Friction is high enough so you
// don't slip down

float	pm_accelerate = 10.0f;
float	pm_airaccelerate = 1.0f;
float	pm_wateraccelerate = 4.0f;
float	pm_flyaccelerate = 8.0f;

float	pm_friction = 6.0f;
float	pm_waterfriction = 1.0f;
float	pm_flightfriction = 3.0f;
float	pm_spectatorfriction = 5.0f;

int		c_pmove = 0;






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

// begin Joe
void PM_AddEvent2( int newEvent, int eventParm ) {
    BG_AddPredictableEventToPlayerstate( newEvent, eventParm, pm->ps );
}
// Joe
void BG_grappleEvents(void) {
        PM_AddEvent(EV_FIRE_OFFHAND_GRAPPLE);
    if ((pm->cmd.buttons & BUTTON_GRAPPLE)
		&& ! ( pm->ps->stats[STAT_LASTBUTTONS] & BUTTON_GRAPPLE))
	{
		//if(!cg_serverConfig.grapple_onhand)
			PM_AddEvent(EV_FIRE_GRAPPLE);
	}
    // ok except for hitting sky or player
    // or time limit hit
    /*    else if ( ! (pm->cmd.buttons & BUTTON_OFFHAND_GRAPPLE) &&
                  ( pm->ps->stats[STAT_LASTBUTTONS] & BUTTON_OFFHAND_GRAPPLE))
            PM_AddEvent( EV_RELEASE_OFFHAND_GRAPPLE);
    */
}
// end Joe

/*
===============
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_StartTorsoAnim
===================-
*/
static void PM_StartTorsoAnim( int anim ) {
    if ( pm->ps->pm_type >= PM_DEAD ) {
        return;
    }
    pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
                        | anim;
}
static void PM_StartLegsAnim( int anim ) {
    if ( pm->ps->pm_type >= PM_DEAD ) {
        return;
    }
    if ( pm->ps->legsTimer > 0 ) {
        return;		// a high priority animation is running
    }
    pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
                       | anim;
}

static void PM_ContinueLegsAnim( int anim ) {
    if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) {
        return;
    }
    if ( pm->ps->legsTimer > 0 ) {
        return;		// a high priority animation is running
    }
    PM_StartLegsAnim( anim );
}

static void PM_ContinueTorsoAnim( int anim ) {
    if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) {
        return;
    }
    if ( pm->ps->torsoTimer > 0 ) {
        return;		// a high priority animation is running
    }
    PM_StartTorsoAnim( anim );
}

static void PM_ForceLegsAnim( int anim ) {
    pm->ps->legsTimer = 0;
    PM_StartLegsAnim( anim );
}


/*
==================
PM_ClipVelocity
 
Slide off of the impacting surface
==================
*/
static 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_Friction
 
Handles both ground friction and water friction
==================
*/
static float 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 0;
    }

    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 ) {
        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;
    }

    // alliance-ladder
    if ( pml.ladder ) // If they're on a 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;

    // Alliance: Myrkul: report amount of dropped speed
    return drop;
}


/*
==============
PM_Accelerate
 
Handles user intended acceleration
==============
*/
static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) {


    // Alliance: Myrkul: server option to allow/disallow strafe jump
    // Also added comments throughout
    if (BG_strafeJump()) {
        // q2 style
        int			i;
        float		addspeed, accelspeed, currentspeed;

        currentspeed = DotProduct (pm->ps->velocity, wishdir);

        // Player is already going at a higher speed in the direction he wants
        // to accelerate than the speed he wishes to accelerate to, so return.
        // Problem: if player is trying to make a small change in direction
        // while over max speed, his movement attempt will be completely
        // ignored.  The further over max speed he is, the larger range of
        // movement attempts will be ignored.
        addspeed = wishspeed - currentspeed;
        if (addspeed <= 0) {
            return;
        }
        accelspeed = accel*pml.frametime*wishspeed;

        //Battle
        //removed to get better momentum
        //if (accelspeed > addspeed) {
        //    accelspeed = addspeed;
        //}

        for (i=0 ; i<3 ; i++) {
            pm->ps->velocity[i] += accelspeed*wishdir[i];
        }

        //Battle end
    }
    else {
        // proper way (avoids strafe jump maxspeed bug), but feels bad

        // Alliance: Myrkul: only feels bad because the maxspeed was effectively
        // higher with the old movement code, since players routinely exceeded
        // the maxspeed setting.  If you raise maxspeed to a value that reflects
        // the effective maxspeed with the old code, eg a ~10% increase, the new
        // movement code feels fine, in fact better than the old.

        vec3_t		wishVelocity;
        vec3_t		pushDir;
        float		pushLen;
        float		canPush;

        // Alliance: Myrkul: added case where player is already going fast
        // enough in the direction he wants to accelerate.  See below for
        // problems with this.
        float currentSpeed;
        float currentSpeedOnWishdir;
        float wishDirLength = VectorLength(wishdir);

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

        // the speed that the player already has in the direction he wants to
        // accelerate
        currentSpeedOnWishdir = DotProduct (pm->ps->velocity, wishdir);

        // Alliance: Myrkul: new overspeed movement rules
        // currentSpeed - wishspeed > 1 : don't bother with tiny velocity
        // differences probably caused by round-off error
        if (wishDirLength > 0 &&
                currentSpeed > 0 &&
                currentSpeed - wishspeed > 1 &&
                currentSpeedOnWishdir >= wishspeed) {
            // Player's controls indicate he wants to accelerate in a direction
            // where he already has more speed than he could acquire if he keeps
            // accelerating at the current pace.

            // Example: Player has pushed by something (knockback, jumppad,
            // hook..) and is over his maximum speed, and is now trying to
            // change direction by some amount less than 90 degrees.

            // Another example: Player is using a joystick or mouse to control
            // translational movement and his wishspeed is dropping slightly as
            // he changes direction.

            // In all previous versions of Quake the player simply wouldn't
            // accelerate in this case, with various mostly subtle consequences
            // (try changing direction slightly via air control after being
            // flung by a high speed jump pad, or after releasing from the hook).

            // If we use the new strafe-jump-preventing algorithm as-is, the
            // player will decelerate rapidly to his maxspeed even though his
            // controls indicate acceleration in the general direction of his
            // current velocity, which seems wrong.

            // Instead we want the player to be able to change direction at the
            // expense of killing some of his momentum.

            float angleDiff;
            float targetSpeed;

            // angle at which normal acceleration would take over, ie the angle
            // at which the player's speed in the wished for direction would
            // exactly equal the speed he would accelerate up to under normal
            // acceleration.
            float crossoverAngle = safe_acos(wishspeed / currentSpeed);

            // difference in angle between current velocity and acceleration
            // vector, based on a . b = |a| |b| cos theta
            angleDiff = safe_acos(currentSpeedOnWishdir / currentSpeed);

            // length of vector to the point where the acceleration vector
            // intersects with a line between the current velocity and the point
            // where a vector at the crossover angle would intersect a circle
            // with a radius of the wished-for speed (draw it to understand)
            targetSpeed = wishspeed / cos(crossoverAngle - angleDiff);

            // a velocity in the same direction as the acceleration vector, with
            // a speed between the player's current speed and the speed he would
            // acquire under normal acceleration in this direciton
            VectorScale( wishdir, targetSpeed, wishVelocity );

        } else {
            // the velocity the player wants to have, according to his ucmd
            VectorScale( wishdir, wishspeed, wishVelocity );
        }

        // difference between the target velocity and his current velocity
        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 );
    }
    /*
    //#if 1

    if (!BG_strafeJump()) {
    // 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;
    }

    for (i=0 ; i<3 ; i++) {
    pm->ps->velocity[i] += accelspeed*wishdir[i];
    }
    }
    //#else
    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;

    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 );

    return scale;
}


/*
================
PM_SetMovementDir
 
Determine the rotation of the legs reletive
to the facing dir
================
*/
static void PM_SetMovementDir( void ) {
    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;
        }
    }
}


/*
=============
PM_CheckJump
=============
*/
static qboolean PM_CheckJump( void ) {
	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;
    // Alliance: Myrkul: server-controlled ramp jump option
    if (BG_rampJump()) {
        if (pm->ps->velocity[2] < 0) {
            // allow full height jumps while running down slopes
            pm->ps->velocity[2] = JUMP_VELOCITY;
        } else {
            pm->ps->velocity[2] += JUMP_VELOCITY;
        }
    } else {
        pm->ps->velocity[2] = JUMP_VELOCITY;
    }
    PM_AddEvent( EV_JUMP );

    if ( pm->cmd.forwardmove >= 0 ) {
        PM_ForceLegsAnim( LEGS_JUMP );
        pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
    } else {
        PM_ForceLegsAnim( LEGS_JUMPB );
        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;
}

//============================================================================
/*		Moved bg_slidemove.c here	and changed them all to static				*/
//============================================================================

/*
 
input: origin, velocity, bounds, groundPlane, trace function
 
output: origin, velocity, impacts, stairup boolean
 
*/

// Alliance: Myrkul: "grafting" movement rule.  Players routinely go into
// freefall when the ground slope changes by as little as 30 degrees, and
// are then unable to jump or accelerate rapidly, leaving them helpless
// until they hit the ground a few hundred ms later.  Instead, when a player
// enters a new groundplane that is less than 45 degrees different from the
// ground plane he was on before, make his velocity coplanar with the new
// plane (and don't reduce velocity).
/*
==================
PM_Graft
==================
*/
static qboolean PM_Graft(void) {
    int maxGraft;

    maxGraft = BG_grafting();

    if ( maxGraft && pml.groundPlane) {
        // we started out on the ground

        vec3_t point;
        trace_t trace;

        // find the new groundplane if there is one
        VectorCopy(pm->ps->origin, point);
        point[2] -= maxGraft;
        pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point,
                   pm->ps->clientNum, pm->tracemask);

        if (trace.fraction != 1.0 &&
                (pml.groundTrace.plane.dist != trace.plane.dist ||
                 !VectorCompare(pml.groundTrace.plane.normal, trace.plane.normal)) &&
                DotProduct(pml.groundTrace.plane.normal, trace.plane.normal) > 0.7) {
            // we have a new groundplane, with a slope less than 45 degrees
            // different from the previous groundplane
            float vel;
            float delta;
            if (pm->debugLevel) {
                Com_Printf("%i:grafting onto new ground plane\n", c_pmove);
            }
            vel = VectorNormalize(pm->ps->velocity);
            // make our velocity parallel to the new ground plane
            PM_ClipVelocity (pm->ps->velocity, trace.plane.normal,
                             pm->ps->velocity, OVERCLIP );
            // but don't reduce it
            VectorScale(pm->ps->velocity, vel, pm->ps->velocity);

            // Snap position downward to avoid gratuitously lifting off surface
            // and ending up in freefall
            delta = pm->ps->origin[2] - trace.endpos[2];
            if (delta > maxGraft) {
                delta = maxGraft;
            }
            pm->ps->origin[2] -= delta;

            // smooth height transition (same method as used for stepping up
            // stairs)
            if ( delta > 2 ) {
                if ( delta < 7 ) {
                    PM_AddEvent( EV_DOWNSTEP_4 );
                } else if ( delta < 11 ) {
                    PM_AddEvent( EV_DOWNSTEP_8 );
                } else if ( delta < 15 ) {
                    PM_AddEvent( EV_DOWNSTEP_12 );
                } else {
                    PM_AddEvent( EV_DOWNSTEP_16 );
                }
            }
            if ( pm->debugLevel ) {
                Com_Printf("%i:downstepped: %.2f\n", c_pmove, delta);
            }
            return qtrue; // we grafted
        }
    }
    return qfalse; // we didn't graft
}



/*
==================
PM_SlideMove
 
Returns qtrue if the velocity was clipped in some way
==================
*/
#define	MAX_CLIP_PLANES	5

// Alliance: Myrkul: created level of indirection to allow the caller to avoid
// grafting, so that the caller can do grafting himself.  Needed to allow
// stepping.
//static qboolean	PM_SlideMoveWithGraft( qboolean gravity, qboolean graft );



static qboolean	PM_SlideMoveWithGraft( qboolean gravity, qboolean graft ) {
    int			bumpcount, numbumps;
    vec3_t		dir;
    float		d;
    int			numplanes;
    vec3_t		planes[MAX_CLIP_PLANES];
    vec3_t		primal_velocity;
    vec3_t		clipVelocity;
    int			i, j, k;
    trace_t	trace;
    vec3_t		end;
    float		time_left;
    float		into;
    vec3_t		endVelocity;
    vec3_t		endClipVelocity;

    numbumps = 4;

    VectorCopy (pm->ps->velocity, primal_velocity);

    if ( gravity ) {
        VectorCopy( pm->ps->velocity, endVelocity );
        endVelocity[2] -= pm->ps->gravity * pml.frametime;
        pm->ps->velocity[2] = ( pm->ps->velocity[2] + endVelocity[2] ) * 0.5;
        primal_velocity[2] = endVelocity[2];
        if ( pml.groundPlane ) {
            // slide along the ground plane
            PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
                             pm->ps->velocity, OVERCLIP );
        }
    }

    time_left = pml.frametime;

    // never turn against the ground plane
    if ( pml.groundPlane ) {
        numplanes = 1;
        VectorCopy( pml.groundTrace.plane.normal, planes[0] );
    } else {
        numplanes = 0;
    }

    // never turn against original velocity
    VectorNormalize2( pm->ps->velocity, planes[numplanes] );
    numplanes++;
    for ( bumpcount=0 ; bumpcount < numbumps ; bumpcount++ ) {

        // calculate position we are trying to move to
        VectorMA( pm->ps->origin, time_left, pm->ps->velocity, end );

        // see if we can make it there
        pm->trace ( &trace, pm->ps->origin, pm->mins, pm->maxs, end, pm->ps->clientNum, pm->tracemask);

        if (trace.allsolid) {
            // entity is completely trapped in another solid
            pm->ps->velocity[2] = 0;	// don't build up falling damage, but allow sideways acceleration
            return qtrue;
        }

        if (trace.fraction > 0) {
            // actually covered some distance
            VectorCopy (trace.endpos, pm->ps->origin);
        }

        if (trace.fraction == 1) {
            break;		// moved the entire distance
        }

        // save entity for contact
        PM_AddTouchEnt( trace.entityNum );

        time_left -= time_left * trace.fraction;

        if (numplanes >= MAX_CLIP_PLANES) {
            // this shouldn't really happen
            VectorClear( pm->ps->velocity );
            return qtrue;
        }

        //
        // if this is the same plane we hit before, nudge velocity
        // out along it, which fixes some epsilon issues with
        // non-axial planes
        //
        for ( i = 0 ; i < numplanes ; i++ ) {
            if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) {
                VectorAdd( trace.plane.normal, pm->ps->velocity, pm->ps->velocity );
                break;
            }
        }
        if ( i < numplanes ) {
            continue;
        }
        VectorCopy (trace.plane.normal, planes[numplanes]);
        numplanes++;

        //
        // modify velocity so it parallels all of the clip planes
        //

        // find a plane that it enters
        for ( i = 0 ; i < numplanes ; i++ ) {
            into = DotProduct( pm->ps->velocity, planes[i] );
            if ( into >= 0.1 ) {
                continue;		// move doesn't interact with the plane
            }

            // see how hard we are hitting things
            if ( -into > pml.impactSpeed ) {
                pml.impactSpeed = -into;
            }

            // slide along the plane
            PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP );

            // slide along the plane
            PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP );

            // see if there is a second plane that the new move enters
            for ( j = 0 ; j < numplanes ; j++ ) {
                if ( j == i ) {
                    continue;
                }
                if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) {
                    continue;		// move doesn't interact with the plane
                }

                // try clipping the move to the plane
                PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP );
                PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP );

                // see if it goes back into the first clip plane
                if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) {
                    continue;
                }
                // slide the original velocity along the crease
                CrossProduct (planes[i], planes[j], dir);
                VectorNormalize( dir );
                d = DotProduct( dir, pm->ps->velocity );
                VectorScale( dir, d, clipVelocity );
                CrossProduct (planes[i], planes[j], dir);
                VectorNormalize( dir );
                d = DotProduct( dir, endVelocity );
                VectorScale( dir, d, endClipVelocity );
                // see if there is a third plane the the new move enters
                for ( k = 0 ; k < numplanes ; k++ ) {
                    if ( k == i || k == j ) {
                        continue;
                    }
                    if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) {
                        continue;		// move doesn't interact with the plane
                    }
                    // stop dead at a tripple plane interaction
                    VectorClear( pm->ps->velocity );
                    return qtrue;
                }
            }

            // if we have fixed all interactions, try another move
            VectorCopy( clipVelocity, pm->ps->velocity );
            VectorCopy( endClipVelocity, endVelocity );
            break;
        }
    }

    if ( gravity ) {
        VectorCopy( endVelocity, pm->ps->velocity );
    }

    // don't change velocity if in a timer (FIXME: is this correct?)
    if ( pm->ps->pm_time ) {
        VectorCopy( primal_velocity, pm->ps->velocity );
    }

    // Alliance: Myrkul: stay attached to new groundplane
    if (graft)
        PM_Graft();

    return ( bumpcount != 0 );
}


static qboolean	PM_SlideMove( qboolean gravity ) {
    return PM_SlideMoveWithGraft( gravity, qtrue );
}
/*
==================
PM_TryStep
See if stepping in the given direction allows us to move
==================
*/
static int PM_TryStep(vec3_t start_o, vec3_t start_v, vec3_t stepVector, qboolean gravity) {
    trace_t		trace;
    vec3_t      stepOrigin;
    vec3_t      back;
    int         bumped;

    VectorAdd(start_o, stepVector, stepOrigin);

    /*
    Com_Printf("org: %s, step: %s, after: %s\n", vtofs(start_o),
               vtofs(stepVector), vtofs(stepOrigin));
    */

    // does moving the player stepVector put him in a solid?
    pm->trace (&trace, stepOrigin, pm->mins, pm->maxs, stepOrigin, pm->ps->clientNum, pm->tracemask);
    if ( trace.allsolid ) {
        return -1;		// can't step this way
    }

    // try slidemove from this position
    VectorCopy (stepOrigin, pm->ps->origin);
    VectorCopy (start_v, pm->ps->velocity);

    // don't graft, we'll do it ourselves
    bumped = PM_SlideMoveWithGraft(gravity, qfalse);

    // move the player back a bit in order to be flush with whatever surface was
    // just stepped around
    VectorSubtract(vec3_origin, stepVector, back);
    VectorAdd(pm->ps->origin, back, back);
    pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask);
    if ( !trace.allsolid ) {
        // successfully moved back some of the stepped distance
        VectorCopy (trace.endpos, pm->ps->origin);
    }

    // Alliance: Myrkul: make our velocity coplanar with our new groundplane (if
    // we didn't graft to it, which will already have made the velocity
    // coplanar)

    if (!PM_Graft() && trace.fraction < 1.0 ) {
        PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
    }
    //Pmove(pm);
    //trap_SnapVector( pm->ps->velocity );
    // return whether we ran into anything
    return bumped;
}

/*
==================
PM_UpdateBestMove
 
==================
*/
vec3_t bestMove_o, bestMove_v;
float bestMoveLength;
int bestBumps;

void PM_UpdateBestMove(vec3_t start_o, vec3_t move_o, vec3_t move_v, int bumps, char *movename) {
    float moveLength;

    moveLength = Distance(start_o, move_o);
    //if (moveLength >= bestMoveLength) {
    if (bumps < bestBumps) {
        if ( pm->debugLevel ) {
            Com_Printf("%i:keeping new %s move: %.4f\n",
                       c_pmove, movename, moveLength);
        }
        bestMoveLength = moveLength;
        VectorCopy(move_o, bestMove_o);
        VectorCopy(move_v, bestMove_v);
    } else {
        if ( pm->debugLevel ) {
            Com_Printf("%i:kept old move instead of %s: %.4f\n",
                       c_pmove, movename, bestMoveLength);
        }
    }

}

/*
==================
PM_StepSlideMove
 
==================
*/
static void PM_StepSlideMove( qboolean gravity ) {
    vec3_t		start_o, start_v;
    //    vec3_t		down_o, down_v;
    trace_t		trace;
    //	float		down_dist, up_dist;
    //	vec3_t		delta, delta2;
    vec3_t		up, down;
    //    float		stepSize;

    // Alliance: Myrkul
    vec3_t      stepVector;
    int         bumps;

    VectorCopy (pm->ps->origin, start_o);
    VectorCopy (pm->ps->velocity, start_v);

    /*
        if ( PM_SlideMove( gravity ) == 0 ) {
            return;		// we got exactly where we wanted to go first try
        }
    */

    if ( (bumps = PM_SlideMoveWithGraft( gravity, qfalse )) == 0 ) {
        // we got exactly where we wanted to go first try
        // graft to the new groundplane if any
        PM_Graft();
        return;
    }

    VectorCopy(start_o, down);
    down[2] -= STEPSIZE;
    pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
    VectorSet(up, 0, 0, 1);
    // never step up when you still have up velocity
    if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 ||
                                     DotProduct(trace.plane.normal, up) < 0.7)) {
        return;
    }

    // the normal step up
    VectorSet(stepVector, 0, 0, STEPSIZE);
    bumps = PM_TryStep(start_o, start_v, stepVector, gravity);

    /*
        VectorCopy (pm->ps->origin, down_o);
        VectorCopy (pm->ps->velocity, down_v);
     
        VectorCopy (start_o, up);
        up[2] += STEPSIZE;
     
        // test the player position if they were a stepheight higher
        pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask);
        if ( trace.allsolid ) {
            if ( pm->debugLevel ) {
                Com_Printf("%i:bend can't step\n", c_pmove);
            }
            return;		// can't step up
        }
     
        stepSize = trace.endpos[2] - start_o[2];
        // try slidemove from this position
        VectorCopy (trace.endpos, pm->ps->origin);
        VectorCopy (start_v, pm->ps->velocity);
     
        PM_SlideMove( gravity );
     
        // push down the final amount
        VectorCopy (pm->ps->origin, down);
        down[2] -= stepSize;
        pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask);
        if ( !trace.allsolid ) {
            VectorCopy (trace.endpos, pm->ps->origin);
        }
        if ( trace.fraction < 1.0 ) {
            PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP );
        }
    */

#if 0
    // if the down trace can trace back to the original position directly, don't step
    pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask);
    if ( trace.fraction == 1.0 ) {
        // use the original move
        VectorCopy (down_o, pm->ps->origin);
        VectorCopy (down_v, pm->ps->velocity);
        if ( pm->debugLevel ) {
            Com_Printf("%i:bend\n", c_pmove);
        }
    } else
#endif
    {
        // use the step move
        float	delta;

        delta = pm->ps->origin[2] - start_o[2];
        if ( delta > 2 ) {
            if ( delta < 7 ) {
                PM_AddEvent( EV_STEP_4 );
            } else if ( delta < 11 ) {
                PM_AddEvent( EV_STEP_8 );
            } else if ( delta < 15 ) {
                PM_AddEvent( EV_STEP_12 );
            } else {
                PM_AddEvent( EV_STEP_16 );
            }
        }
        if ( pm->debugLevel ) {
            Com_Printf("%i:stepped\n", c_pmove);
        }
    }
}

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


/*
===================
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
    }
    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 ( wishspeed > pm->ps->speed * pm_swimScale ) {
        wishspeed = pm->ps->speed * pm_swimScale;
    }

    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 );
}

#ifdef MISSIONPACK
/*
===================
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 ();

    scale = PM_CmdScale( &pm->cmd );
    //
    // 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;
        }

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

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

    PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);

    PM_StepSlideMove( qfalse );
}

/* HUGO
===================
PM_AirMoveRegular
Baseq3 PM_AirMove proc. Used in regular (non-combat) mode
===================
*/
static void PM_AirMoveRegular( 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 );

    // 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 );
    }

#if 0
    //ZOID:  If we are on the grapple, try stair-stepping
    //this allows a player to use the grapple to pull himself
    //over a ledge
    if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
        PM_StepSlideMove ( qtrue );
    else
        PM_SlideMove ( qtrue );
#endif

#if 0
    //ZOID:  If we are on the grapple, try stair-stepping
    //this allows a player to use the grapple to pull himself
    //over a ledge
    if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
        PM_StepSlideMove ( qtrue );
    else
        PM_SlideMove ( qtrue );
#endif

    PM_StepSlideMove ( qtrue );
}



/*
===================
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;

    // Alliance: Myrkul: server cvar movement options
    float       airSpeed;
    float       airMove;

    //if (!BG_battleWeaponsEnabled()) {
    if (!BG_strafeJump()) {
        PM_AirMoveRegular(); // Hugo - baseq3 airmove
        return;
    }

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

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

    // set the movementDir so clients can rotate the legs for strafing
    PM_SetMovementDir();

    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;

    // Alliance: Myrkul: server cvar controlling max airspeed
    // If g_airSpeed is less than g_speed, you can accelerate in mid-air, but
    // you can't build up full running speed.
    airSpeed = BG_airSpeed();

    // server cvar controlling air acceleration
		if(BG_airMove() != 0)
		airMove = BG_airMove()/10.0;
	else
		airMove = 1;

    // clamp wished-for speed to max airSpeed
    if (wishspeed > airSpeed)
        wishspeed = airSpeed;

    // In the acceleration function, rate of acceleration is actually
    // proportional to the rate of speed you are trying to attain (so you don't
    // eg sprint up to walking speed or sprint up to full swimming speed).  In
    // the case of airSpeed however, we want independant control of maximum
    // airspeed and air acceleration.  So when we lower the airspeed, we raise
    // the air acceleration proportionally to compensate.  In QW, id software
    // accomplished this behavior with a separate air acceleration function with
    // one differing line.
    if (airSpeed < pm->ps->speed) {
        // max air speed is set lower than max ground speed, compensate for the
        // acceleration rate drop this will cause
        airMove *= pm->ps->speed/airSpeed;
    }

    // Alliance: Myrkul: server controlled air acceleration constant
    if (!BG_strafeJump()) {
        // For an airborne object horizontal motion is independant of vertical
        // motion.  As a side-effect of the strafe jump bug this didn't matter
        // with the old player movement code

        int vertical = pm->ps->velocity[2];

        //BB testing movement
        //pm->ps->velocity[2] *= 0.75; // pretend we're not moving vertically

        PM_Accelerate (wishdir, wishspeed, airMove);
        pm->ps->velocity[2] = vertical;
    } else {
        PM_Accelerate (wishdir, wishspeed, airMove);
    }
	/*
    // Alliance: Myrkul: learn strafe jumping technique
    if (BG_strafeJumpTrainer()) {
        // don't consider vertical
        vec3_t groundVel;
        VectorCopy(pm->ps->velocity, groundVel);
        groundVel[2] = 0;

        Com_Printf("avel= (%6.1f %6.1f %6.1f), len: %5.1f, fdrop: 0.0\n",
                   groundVel[0], groundVel[1], groundVel[2],
                   VectorLength(groundVel));
    }
*/
    // 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 );
    }

#if 0
    //ZOID:  If we are on the grapple, try stair-stepping
    //this allows a player to use the grapple to pull himself
    //over a ledge
    if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
        PM_StepSlideMove ( qtrue );
    else
        PM_SlideMove ( qtrue );
#endif

    PM_StepSlideMove ( qtrue );


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

static void PM_GrappleMove( void ) {
    vec3_t vel, v;
	float len;
	VectorSubtract(pm->ps->grapplePoint, pm->ps->origin, v);
    len = VectorNormalize(v);
//	Com_Printf("%f", len);

	if(abs(len) < 32){
//		pm->ps->pm_flags &= ~PMF_OFFHAND_GRAPPLE_PULL;
//		return;
		VectorScale(pml.forward, -16, v);
		VectorAdd(pm->ps->grapplePoint, v, v);
//		VectorSubtract(pm->ps->origin, pm->mins, v);
		VectorSubtract(pm->ps->origin, pm->maxs, v);

		VectorSubtract(v, pm->ps->origin, vel);
		VectorNormalize( vel );
		
		VectorScale(vel, 0, vel);
		VectorCopy(vel, pm->ps->velocity);
		trap_SnapVector(pm->ps->velocity);
		pml.groundPlane = qfalse;
    } else {
		VectorScale(pml.forward, -16, v);
		VectorAdd(pm->ps->grapplePoint, v, v);
		VectorSubtract(v, pm->ps->origin, vel);
		VectorNormalize( vel );
		VectorScale(vel, getGrapplePullSpeed(), vel);
		VectorCopy(vel, pm->ps->velocity);
		trap_SnapVector(pm->ps->velocity);
		pml.groundPlane = qfalse;
	}
}

/*
===================
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;
    float       drop;

    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();
        }
        return;
    }

    // Alliance: Myrkul: remember speed drop due to friction
    drop = PM_Friction ();

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

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

    // 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;
        }
    }

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

        waterScale = pm->waterlevel / 3.0;
        waterScale = 1.0 - ( 1.0 - pm_swimScale ) * 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 {
        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));

    // Alliance: Myrkul: learn strafe jumping technique
    /*
	if (BG_strafeJumpTrainer()) {
        Com_Printf("gvel= (%6.1f %6.1f %6.1f), len: %5.1f, fdrop: %5.1f\n",
                   pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2],
                   VectorLength(pm->ps->velocity), drop);
    }
	*/
    if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK ) {
        pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
    } else {
        // don't reset the z velocity for slopes
        //		pm->ps->velocity[2] = 0;
    }

    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 decrease velocity when going up or down a slope
    VectorNormalize(pm->ps->velocity);
    VectorScale(pm->ps->velocity, vel, pm->ps->velocity);

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

    PM_StepSlideMove( qfalse );

    //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));

}


/*
==============
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;
    }
    if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) {
        return EV_FOOTSTEP_METAL;
    }
    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;

    // decide which landing animation to use
    if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
        PM_ForceLegsAnim( LEGS_LANDB );
    } else {
        PM_ForceLegsAnim( LEGS_LAND );
    }

    pm->ps->legsTimer = TIMER_LAND;

    // 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;

    // ducking while falling doubles damage
    if ( pm->ps->pm_flags & PMF_DUCKED ) {
        delta *= 2;
    }

    // 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 ( delta > 60 ) {
            PM_AddEvent( EV_FALL_FAR );
        } else if ( delta > 40 ) {
            // this is a pain grunt, so don't play it if dead
            if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
                PM_AddEvent( EV_FALL_MEDIUM );
            }
        } else if ( delta > 7 ) {
            PM_AddEvent( EV_FALL_SHORT );
        } else {
            PM_AddEvent( PM_FootstepForSurface() );
        }
    }

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

/*
=============
PM_CheckStuck
=============
*/
/*
void PM_CheckStuck(void) {
	trace_t trace;
 
	pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask);
	if (trace.allsolid) {
		//int shit = qtrue;
	}
}
*/

/*
=============
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 ) {
                PM_ForceLegsAnim( LEGS_JUMP );
                pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
            } else {
                PM_ForceLegsAnim( LEGS_JUMPB );
                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];
    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 ) {
            PM_ForceLegsAnim( LEGS_JUMP );
            pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
        } else {
            PM_ForceLegsAnim( LEGS_JUMPB );
            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;

    point[0] = pm->ps->origin[0];
    point[1] = pm->ps->origin[1];
    point[2] = pm->ps->origin[2] + MINS_Z + 1;
    cont = pm->pointcontents( point, pm->ps->clientNum );

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

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

}

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

#ifdef MISSIONPACK

    if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
        if ( pm->ps->pm_flags & PMF_INVULEXPAND ) {
            // invulnerability sphere has a 42 units radius
            VectorSet( pm->mins, -42, -42, -42 );
            VectorSet( pm->maxs, 42, 42, 42 );
        } else {
            VectorSet( pm->mins, -15, -15, MINS_Z );
            VectorSet( pm->maxs, 15, 15, 16 );
        }
        pm->ps->pm_flags |= PMF_DUCKED;
        pm->ps->viewheight = CROUCH_VIEWHEIGHT;
        return;
    }
#endif
    pm->ps->pm_flags &= ~PMF_INVULEXPAND;

    pm->mins[0] = -15;
    pm->mins[1] = -15;

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

    pm->mins[2] = MINS_Z;

    if (pm->ps->pm_type == PM_DEAD) {
        pm->maxs[2] = -8;
        pm->ps->viewheight = DEAD_VIEWHEIGHT;
        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] = 32;
            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] = 16;
        pm->ps->viewheight = CROUCH_VIEWHEIGHT;
    } else {
        pm->maxs[2] = 32;
        pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
    }
}



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


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

    //
    // 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] );

    if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
#ifdef MISSIONPACK
        if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
            PM_ContinueLegsAnim( LEGS_IDLECR );
        }
#endif
        // airborne leaves position in cycle intact, but doesn't advance
        if ( pm->waterlevel > 1 ) {
            PM_ContinueLegsAnim( LEGS_SWIM );
        }
        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->ps->pm_flags & PMF_DUCKED ) {
                PM_ContinueLegsAnim( LEGS_IDLECR );
            } else {
                PM_ContinueLegsAnim( LEGS_IDLE );
            }
        }
        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 ) {
            PM_ContinueLegsAnim( LEGS_BACKCR );
        } else {
            PM_ContinueLegsAnim( LEGS_WALKCR );
        }
        // 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;
        	} else {
        		bobmove = 0.3;
        	}
        	PM_ContinueLegsAnim( LEGS_BACK );
        */
    } else {
        if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
            bobmove = 0.4f;	// faster speeds bob faster
            if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
                PM_ContinueLegsAnim( LEGS_BACK );
            } else {
                PM_ContinueLegsAnim( LEGS_RUN );
            }
            footstep = qtrue;
        } else {
            bobmove = 0.3f;	// walking bobs slow
            if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
                PM_ContinueLegsAnim( LEGS_BACKWALK );
            } else {
                PM_ContinueLegsAnim( LEGS_WALK );
            }
        }
    }

    // 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 ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {
        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_BeginWeaponChange
===============
*/
static void PM_BeginWeaponChange( int weapon ) {
    if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
        return;
    }

    if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
        return;
    }

    if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
        return;
    }
	
    if ( pm->ps->ammo[ pm->ps->weapon ] <= 0 &&
            pm->ps->weapon != WP_GRAPPLING_HOOK &&
		pm->ps->weapon != WP_GAUNTLET)
    {
        //set there ammo to zero if it for some reason fell below 0
        pm->ps->ammo[ pm->ps->weapon ] = 0;
    }
    PM_AddEvent( EV_CHANGE_WEAPON );
    pm->ps->weaponstate = WEAPON_DROPPING;


    // Joe fast weapon change
    if(BG_battleWeaponsEnabled()
#ifndef MISSIONPACK
            || pm->ps->powerups[PW_RUNEHASTE]
#endif
      )
        // pm->ps->weaponTime += 1;
        pm->ps->weaponTime += 3;  // Joe lastweap
    else
        // end Joe
        pm->ps->weaponTime += 200;

    PM_StartTorsoAnim( TORSO_DROP );
}


/*
===============
PM_FinishWeaponChange
===============
*/
static void PM_FinishWeaponChange( void ) {
    int		weapon;

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

    if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
        weapon = WP_NONE;
    }

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

    // Joe fast weapon change
    if (BG_battleWeaponsEnabled()
#ifndef MISSIONPACK
            || pm->ps->powerups[PW_RUNEHASTE]
#endif
       )
        // pm->ps->weaponTime += 1;
        pm->ps->weaponTime += 0;  // Joe lastweap
    else
        // end Joe
        pm->ps->weaponTime += 250;


    // Joe hg and triple rail bug fix
    // don't fire after running out and switching back later
    // lastbuttons[pm->ps->clientNum] = 0; // Joe hand grenade
    //   railsFired[pm->ps->clientNum] = 0; // Joe triple rail
    // shotgunFired[pm->ps->clientNum] = 0; // Joe double shotgun
    pm->ps->stats[STAT_REPEAT_WEAPONS] = 0; // try to use for all 3

    // end Joe hg and tri rail bug fix

    PM_StartTorsoAnim( TORSO_RAISE );
}


/*
==============
PM_TorsoAnimation
 
==============
*/
static void PM_TorsoAnimation( void ) {
    if ( pm->ps->weaponstate == WEAPON_READY ) {
        if ( pm->ps->weapon == WP_GAUNTLET ) {
            PM_ContinueTorsoAnim( TORSO_STAND2 );
        } else {
            PM_ContinueTorsoAnim( TORSO_STAND );
        }
        return;
    }
}


/*
==============
PM_Weapon
 
Generates weapon events and modifes the weapon counter
==============
*/
static void PM_Weapon( void ) {
    int		addTime;

    addTime = 1000;



    // 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;
    }

    // alliance - Hugo
    // ignore if red spectator
    if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR_RED ) {
        return;
    }

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


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



    // check for item using
    if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
        if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
            if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
                    && pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
                // don't use medkit if at max health
            }
            else {
                pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
                PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
                pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
            }
            return;
        }
    } else {
        pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
    }

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

    if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
        if ( pm->ps->weapon != pm->cmd.weapon ) {
            PM_BeginWeaponChange( pm->cmd.weapon );
        }
    }

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

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

        // Joe lastweap
        if(BG_battleWeaponsEnabled()
#ifndef MISSIONPACK
                || pm->ps->powerups[PW_RUNEHASTE]
#endif
          )
            ;
        else
            // end Joe lastweap

            return;
    }

    if ( pm->ps->weaponstate == WEAPON_RAISING ) {
        pm->ps->weaponstate = WEAPON_READY;
        if ( pm->ps->weapon == WP_GAUNTLET ) {
            PM_StartTorsoAnim( TORSO_STAND2 );
        } else {
            PM_StartTorsoAnim( TORSO_STAND );
        }

        // Joe lastweap
        if(BG_battleWeaponsEnabled())
            ;
        else
            // end Joe lastweap

            return;
    }

    // begin Joe triple rail
    // stay for the rail shots if the attack button is down or not
    // but one of the first two rail shots have been fired
    // sometimes there's a false client side fire
    if (//BG_battleWeaponsEnabled() &&
        pm->ps->weapon == WP_RAILGUN
        && BG_tripleRailEnabled()
        //        ((railsFired[pm->ps->clientNum] == 1)
        //         || railsFired[pm->ps->clientNum] == 2))
        && ((pm->ps->stats[STAT_REPEAT_WEAPONS] == 1)
            || pm->ps->stats[STAT_REPEAT_WEAPONS] == 2)) {
        ;  // don't leave
    }
    else  { // end Joe triple rail

        // Joe double shotgun
        if (//BG_battleWeaponsEnabled() &&
            pm->ps->weapon == WP_SHOTGUN &&
            pm->ps->stats[STAT_REPEAT_WEAPONS] == 1
            && BATTLE_SHOTGUN_REPEATS == 2
            && BG_superShotgun()) {
            ;  // don't leave
        }
        else {
            // end Joe


            // begin handgrenade Joe
            // using pm->ps->stats[STAT_REPEAT_WEAPONS] to keep track
            // of the last command buttons
            if (pm->ps->weapon == WP_HANDGRENADE) {
                // Hugo
                if ( pm->ps->eFlags & EF_MOVER_STOP ) {
                    //G_Printf("boom!\n");
                    //pm->cmd.buttons &= ~BUTTON_ATTACK;
                    //pm->ps->stats[STAT_LASTBUTTONS] |= BUTTON_ATTACK;
                    // getting rid of tick/charge events
                    pm->ps->eFlags &= ~EF_BOUNCE_HALF;
                    pm->ps->eFlags &= ~EF_BOUNCE;
                    pm->ps->eFlags &= ~EF_MOVER_STOP;

                } else {
                    // just let go of attack button for hand grenade, don't return
                    if ( !(pm->cmd.buttons & BUTTON_ATTACK) &&
                            // (lastbuttons[pm->ps->clientNum] & BUTTON_ATTACK) )
                            // (pm->ps->stats[STAT_REPEAT_WEAPONS] & BUTTON_ATTACK) )
                            (pm->ps->stats[STAT_LASTBUTTONS] & BUTTON_ATTACK) ) {
                        // getting rid of tick/charge events
                        pm->ps->eFlags &= ~EF_BOUNCE_HALF;
                        pm->ps->eFlags &= ~EF_BOUNCE;
                        pm->ps->eFlags &= ~EF_MOVER_STOP;
                        // lastbuttons[pm->ps->clientNum] = pm->cmd.buttons;
                        // pm->ps->stats[STAT_REPEAT_WEAPONS] = pm->cmd.buttons;
                        // lastbuttons saved at bottom of Pmove()
                    }
                    else {
                        // Joe - just pressed attack button
                        // doesn't always play
                        // weapon not firing code
                        pm->ps->weaponTime = 0;
						pm->ps->eFlags |= EF_BOUNCE_HALF;
                        pm->ps->weaponstate = WEAPON_READY;
                        // lastbuttons[pm->ps->clientNum] = pm->cmd.buttons;
                        // pm->ps->stats[STAT_REPEAT_WEAPONS] = pm->cmd.buttons;
                        // lastbuttons saved at bottom of Pmove()
                        return;
                    }
                }
            }

            // check for fire

            else { // end handgrenade Joe


                if (!(pm->cmd.buttons & BUTTON_ATTACK) ) {
                    pm->ps->weaponTime = 0;
                    pm->ps->weaponstate = WEAPON_READY;
                    // Hugo - chaingun
                    if (!BG_battleWeaponsEnabled())
                        pm->ps->stats[STAT_REPEAT_WEAPONS] = 0;
                    //
                    return;
                }
            }
        }
    }
    // start the animation even if out of ammo
    if ( pm->ps->weapon == WP_GAUNTLET ) {
        // the guantlet only "fires" when it actually hits something
        if ( !pm->gauntletHit ) {
            pm->ps->weaponTime = 0;
            pm->ps->weaponstate = WEAPON_READY;
            return;
        }
        PM_StartTorsoAnim( TORSO_ATTACK2 );
    } else {
        PM_StartTorsoAnim( TORSO_ATTACK );
    }

    pm->ps->weaponstate = WEAPON_FIRING;

    // check for out of ammo
    //these weapons listed are repetitive weapons with -1 ammo --infinite...
    if ( pm->ps->ammo[ pm->ps->weapon ] == 0 &&
            pm->ps->weapon != WP_GRAPPLING_HOOK &&
            pm->ps->weapon != WP_GAUNTLET)
    {
        //set there ammo to zero if it for some reason fell below 0
        pm->ps->ammo[ pm->ps->weapon ] = 0;
        PM_AddEvent( EV_NOAMMO );
        // Joe combat weapons
        //if(! BG_battleWeaponsEnabled())
        //    pm->ps->weaponTime += 500;

        //return;
        //INCIN
        //dont let weapons switch and fire
        //this leaves the weapons state with -1 or more ammo
        //this is a heel of alot smoother now on weapon switching when out of ammo
    }


    // take an ammo away if not infinite
    // Hugo - Added this for instagib
    if (( pm->ps->ammo[ pm->ps->weapon ] != -1 ) &&
            ( pm->ps->ammo[ pm->ps->weapon ] != 0 )) 
	{
        // Joe triple rail, only 1 slug for 3 quick shots
        if (//BG_battleWeaponsEnabled() &&
            pm->ps->weapon == WP_RAILGUN
            && BG_tripleRailEnabled()) {
            //            if (railsFired[pm->ps->clientNum] == 2)
            if (pm->ps->stats[STAT_REPEAT_WEAPONS] == 2)
                pm->ps->ammo[ pm->ps->weapon ]--;
            else
                ;  // do nothing
        }
        else
            // end Joe triple rail

            // Joe double shotgun
            if (//BG_battleWeaponsEnabled() &&
                pm->ps->weapon == WP_SHOTGUN
                && BATTLE_SHOTGUN_REPEATS == 2
                && BG_superShotgun()) {
                // if (shotgunFired[pm->ps->clientNum] == 1)
                if (pm->ps->stats[STAT_REPEAT_WEAPONS] == 1)
                    pm->ps->ammo[ pm->ps->weapon ]--;
                else
                    ;  // do nothing
            }
            else
                // end Joe double shotgun
                pm->ps->ammo[ pm->ps->weapon ]--;

    }

    // fire weapon

    if(! BG_noClientPredictFire())   // Joe
        PM_AddEvent( EV_FIRE_WEAPON );

    switch( pm->ps->weapon ) {
    default:

    case WP_GAUNTLET:
        addTime = 300;
        break;

    case WP_LIGHTNING:

        // Joe combat weapons, 10/s with 13 damage, 130 damage/sec
        if (BG_battleWeaponsEnabled())
            addTime = BATTLE_LIGHTNING_RATE;
        else
            // end Joe

            addTime = 50;

        break;
    case WP_SHOTGUN:

        // begin Joe double shotgun
        // set the timing
        if (//BG_battleWeaponsEnabled() &&
            BATTLE_SHOTGUN_REPEATS == 2
            &&  BG_superShotgun()) {

            pm->ps->stats[STAT_REPEAT_WEAPONS]++;

            if (pm->ps->stats[STAT_REPEAT_WEAPONS] == 1)
                addTime = 200;
            else if (pm->ps->stats[STAT_REPEAT_WEAPONS] == 2) {
                addTime = 800;
                pm->ps->stats[STAT_REPEAT_WEAPONS] = 0;
            } else
                Com_Printf("pm->ps->stats[STAT_REPEAT_WEAPONS] %d times?\n",
                           pm->ps->stats[STAT_REPEAT_WEAPONS]);

        } else
            // end Joe
            addTime = 1000;
        break;
    case WP_MACHINEGUN:

        if (!BG_battleWeaponsEnabled()) {
            if ( pm->ps->stats[STAT_REPEAT_WEAPONS] < 100) {
                pm->ps->stats[STAT_REPEAT_WEAPONS] += 10;
            }
            addTime = 130 - pm->ps->stats[STAT_REPEAT_WEAPONS];
        } else {
            addTime = 100;
        }
        break;
    case WP_GRENADE_LAUNCHER:
        addTime = 800;
        break;
    case WP_ROCKET_LAUNCHER:
        addTime = 800;
        break;
    case WP_PLASMAGUN:
        addTime = 100;
        break;
    case WP_RAILGUN:

        // begin Joe triple rail
        if (//BG_battleWeaponsEnabled() &&
            BG_tripleRailEnabled()) {
            //            railsFired[pm->ps->clientNum]++;
            pm->ps->stats[STAT_REPEAT_WEAPONS]++;

            //            if (railsFired[pm->ps->clientNum] == 1 ||
            //                railsFired[pm->ps->clientNum] == 2)
            if (pm->ps->stats[STAT_REPEAT_WEAPONS] == 1 ||
                    pm->ps->stats[STAT_REPEAT_WEAPONS] == 2)
                addTime = 100;
            else if (pm->ps->stats[STAT_REPEAT_WEAPONS] == 3)
                //            else if (railsFired[pm->ps->clientNum] == 3)
            {
                addTime = 1300;
                //                railsFired[pm->ps->clientNum] = 0;
                pm->ps->stats[STAT_REPEAT_WEAPONS] = 0;
            } else
                //                Com_Printf("railsFired %d times?\n",
                //                           railsFired[pm->ps->clientNum]);
                Com_Printf("pm->ps->stats[STAT_REPEAT_WEAPONS] %d times?\n",
                           pm->ps->stats[STAT_REPEAT_WEAPONS]);
        } else // end Joe triple rail

            addTime = 1500;
        break;
    case WP_BFG:
        // Alliance: combat weapons
        // FIXME this has to wait until combat weapons are
        // communicated properly to the client
        // but it's so cool -- Joe
        // not sure where to define this, g_main.c or cg_main.c
        // cgame depends on this file to compile

        if (BG_battleWeaponsEnabled())
            addTime = 800;
        else
            addTime = 200;
        break;
        // Alliance: grapple isn't a weapon

    case WP_GRAPPLING_HOOK:
        addTime = 50;//400;
        break;

        //Battle -Hand Grenade - Banshee
    case WP_HANDGRENADE:
        addTime = 600;
        break;
        //Battle - End

#ifdef MISSIONPACK

    case WP_NAILGUN:
        addTime = 1000;
        break;
    case WP_PROX_LAUNCHER:
        addTime = 800;
        break;
    case WP_CHAINGUN:
        addTime = 30;
        break;
#endif

    }

#ifdef MISSIONPACK
    if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
        addTime /= 1.5;
    } else
        if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
            addTime /= 1.3;
        } else {
#else
    // alliance - Hugo
    if ( pm->ps->powerups[PW_RUNEHASTE] ) {
        addTime /= 1.3;
    }
#endif
            if ( pm->ps->powerups[PW_HASTE] ) {
                addTime /= 1.3;
            }
#ifdef MISSIONPACK

        }
#endif

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


/*
================
PM_Animate
================
*/

static void PM_Animate( void ) {
    if ( pm->cmd.buttons & BUTTON_GESTURE ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_GESTURE );
            pm->ps->torsoTimer = TIMER_GESTURE;
            PM_AddEvent( EV_TAUNT );
        }
#ifdef MISSIONPACK

    } else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_GETFLAG );
            pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
        }
    }
    else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_GUARDBASE );
            pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
        }
    }
    else if ( pm->cmd.buttons & BUTTON_PATROL ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_PATROL );
            pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
        }
    }
    else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_FOLLOWME );
            pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
        }
    /*}
    else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
            pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
        }
    }
    else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
        if ( pm->ps->torsoTimer == 0 ) {
            PM_StartTorsoAnim( TORSO_NEGATIVE );
            pm->ps->torsoTimer = 600;	//TIMER_GESTURE;
        }*/

#endif

    }
}


//battle - Hand Grenade Check - Banshee -- modified by INCIN
/*
================
PM_GrenadeCheck
================
*/
static void PM_GrenadeCheck( void ) {
    int grenammo;


    //Trigger player 'Weapon Change' animation during grenade throw,
    //since no anims exist for it

    if(pm->ps->weapon == WP_HANDGRENADE) {
        if (pm->ps->weaponstate == WEAPON_FIRING) {
            if ( pm->ps->torsoTimer == 0 ) {
                //The PM-Add Event below simply plays the SOUND for change weapon,
                //and does not affect animation
                //PM_AddEvent( EV_CHANGE_WEAPON);
                pm->ps->weaponTime += 25;
                pm->ps->weaponstate = WEAPON_DROPPING;
                PM_StartTorsoAnim( TORSO_DROP );
            }
        }
    }

    grenammo = (pm->ps->ammo[WP_HANDGRENADE] < pm->ps->ammo[WP_GRENADE_LAUNCHER]) ? pm->ps->ammo[WP_HANDGRENADE] : pm->ps->ammo[WP_GRENADE_LAUNCHER];

    //Sync ammo of hand grenades and grenades
    if(grenammo > 0) {
        pm->ps->ammo[WP_HANDGRENADE] = grenammo;
        pm->ps->ammo[WP_GRENADE_LAUNCHER] = grenammo;
        if (!(pm->ps->stats[STAT_WEAPONS] & ( 1 << WP_HANDGRENADE ))) {
            pm->ps->stats[STAT_WEAPONS] |= ( 1 << WP_HANDGRENADE );
        }
    } else {
        pm->ps->ammo[WP_GRENADE_LAUNCHER] = 0;
        pm->ps->ammo[WP_HANDGRENADE] = 0;
        if (pm->ps->stats[STAT_WEAPONS] & ( 1 << WP_HANDGRENADE )) {
            pm->ps->stats[STAT_WEAPONS] &= ~ ( 1 << WP_HANDGRENADE );
        }
    }
}
//battle - End

/*
================
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;
        }
    }
}

/*
================
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, const usercmd_t *cmd ) {
    short		temp;
    int		i;

    if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
        return;		// no view changes at all
    }

    if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
        return;		// no view changes at all
    }

    // 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);
    }

}

// Alliance function
// XXX just bypass events entirely
/*
================
PM_Grapple
 
Checks if the offhand grapple should be fired
================
*/

static void PM_Grapple( void ) {

    if ((!BG_grappleEnabled(pm)) ||
            // don't allow grapple until all buttons are up
            (pm->ps->pm_flags & PMF_RESPAWNED) ||
            // ignore if spectator
            ((pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR)) ||
            (pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR_RED) ||
            (pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR_BLUE) ||

            (!(pm->cmd.buttons & BUTTON_GRAPPLE)) ||

            (pm->ps->pm_flags & PMF_GRAPPLE_PULL) ||

            (pm->ps->stats[STAT_LASTBUTTONS] & BUTTON_GRAPPLE)) {
        return;
    }
    // fire grapple
    // FIXME: This is a hell of a lot of overhead in the middle of a grapple.
    // Ideally we could tell from here if the grapple is extended, but
    // that may not be possible given the available client state.
	if(!cg_serverConfig.grapple_onhand)
		PM_AddEvent(EV_FIRE_OFFHAND_GRAPPLE);
}


// Alliance-ladder: 2 new functions:

/*
===================
PM_LadderMove()
by: Calrathan [Arthur Tomlin]
 
Right now all I know is that this works for VERTICAL ladders. 
Ladders with angles on them (urban2 for AQ2) Haven't been tested
===================
*/
static void PM_LadderMove( void ) {
    int i;
    vec3_t wishvel;
    float wishspeed;
    vec3_t wishdir;
    float scale;
    float vel;

    PM_Friction ();

    scale = PM_CmdScale( &pm->cmd );

    // user intentions [what the user is attempting to do]
    if ( !scale ) {
        wishvel[0] = 0;
        wishvel[1] = 0;
        wishvel[2] = 0;
    } else {   // if they're trying to move... lets calculate it
        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 ( wishspeed > pm->ps->speed * pm_ladderScale ) {
        wishspeed = pm->ps->speed * pm_ladderScale;
    }

    PM_Accelerate (wishdir, wishspeed, pm_ladderAccelerate);

    // This SHOULD help us with sloped ladders, but it remains untested.
    if ( pml.groundPlane && DotProduct( pm->ps->velocity,
                                        pml.groundTrace.plane.normal ) < 0 ) {
        vel = VectorLength(pm->ps->velocity);
        // slide along the ground plane [the ladder section
        // under our feet]
        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 ); // move without gravity
}

/*
=============
CheckLadder [ ARTHUR TOMLIN ]
=============
*/
static void CheckLadder( void ) {
    vec3_t flatforward,spot;
    trace_t trace;
    pml.ladder = qfalse;
    // check for ladder
    flatforward[0] = pml.forward[0];
    flatforward[1] = pml.forward[1];
    flatforward[2] = 0;
    VectorNormalize (flatforward);
    VectorMA (pm->ps->origin, 1, flatforward, spot);
    pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, spot,
               pm->ps->clientNum, MASK_PLAYERSOLID);

    if ((trace.fraction < 1) && (trace.surfaceFlags & SURF_LADDER))
        pml.ladder = qtrue;

}


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


static void PmoveSingle (pmove_t *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 ( pm->cmd.buttons & BUTTON_TALK ) {
        pm->ps->eFlags |= EF_TALK;
    } else {
        pm->ps->eFlags &= ~EF_TALK;
    }

    // set the firing flag for continuous beam weapons
    if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION
            && ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
        pm->ps->eFlags |= EF_FIRING;
    } else {
        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.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 ( pml.msec < 1 ) {
        pml.msec = 1;
    } else if ( pml.msec > EVENT_VALID_MSEC ) {//EVENT_VALID_MSEC ==300
        pml.msec = EVENT_VALID_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
    PM_UpdateViewAngles( pm->ps, &pm->cmd );

    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->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 || pm->ps->pm_type == PM_SPINTERMISSION) {
        return;		// no movement at all
    }

    // Hugo
    PM_Grapple();

    // 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 ();
    }
    PM_DropTimers();
    // alliance-ladder
    CheckLadder(); // ARTHUR TOMLIN check and see if they're on a ladder
    // begin Joe
    // set PMF_GRAPPLE_PULL if grapple hits the wall
    //    if (BG_IamServer())
    //    if (BG_predictHook())
    BG_check_Offhand_Grapple(pm);
    // end Joe
#ifdef MISSIONPACK

    if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
        PM_InvulnerabilityMove();
    } else
#endif
    if ( pm->ps->powerups[PW_FLIGHT] ) {
        // flight powerup doesn't allow jump and has different friction
        PM_FlyMove();
        // Joe - try to grapple move too
            if ((pm->cmd.buttons & BUTTON_GRAPPLE) &&
                    (pm->ps->pm_flags & PMF_GRAPPLE_PULL))
            PM_GrappleMove();
        } else if ((pm->cmd.buttons & BUTTON_GRAPPLE) &&      // Joe predict grapple let go?
                   (pm->ps->pm_flags & PMF_GRAPPLE_PULL)) {

		PM_GrappleMove();
		trap_SnapVector( pm->ps->velocity );
        PM_AirMove();
        } else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL && pm->ps->weapon == WP_GRAPPLING_HOOK) {
		PM_GrappleMove();
		trap_SnapVector( pm->ps->velocity );
        PM_AirMove();
    } else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
        PM_WaterJumpMove();
    } else if ( pm->waterlevel > 1 ) {
        // swimming
        PM_WaterMove();

        // alliance-ladder
    }
    else if (pml.ladder) {
        PM_LadderMove();

    } else if ( pml.walking ) {
        // walking on ground
        PM_WalkMove();
     } else {
            // airborne
		PM_AirMove();
	}

    PM_Animate();

    //Alliance - Hand Grenade -Banshee - Where most of tha magic is born :)
    PM_GrenadeCheck();
    //Alliance -End

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

    // weapons
    PM_Weapon();

    // torso animation
    PM_TorsoAnimation();

    // 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 );
}




/*
================
Pmove
 
Can be called by either the server or the client
================
*/
static void Pmove (pmove_t *pmove) {
    static int		finalTime;
    static int		msec;
    finalTime = pmove->cmd.serverTime;

    if ( finalTime < pmove->ps->commandTime ) {
        return;	// 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);

    // chop the move up if it is too long, to prevent framerate
    // dependent behavior

    while ( pmove->ps->commandTime != finalTime) {

        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 );
        //INCIN -- moved to PmoveSingle

        if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
            pmove->cmd.upmove = 20;
        }
    }
    //PM_CheckStuck();
    // Joe - save lastbuttons for next time
    pm->ps->stats[STAT_LASTBUTTONS] = pm->cmd.buttons;
}


// Copyright (C) 1999-2000 Id Software, Inc.
//
// cg_predict.c -- this file generates cg.predictedPlayerState by either
// interpolating between snapshots from the server or locally predicting
// ahead the client's movement.
// It also handles local physics interaction, like fragments bouncing off walls

//#include "cg_local.h"

static	pmove_t		cg_pmove;

static	int			cg_numSolidEntities;
static	centity_t	*cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
static	int			cg_numTriggerEntities;
static	centity_t	*cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT];

/*
====================
CG_BuildSolidList
 
When a new cg.snap has been set, this function builds a sublist
of the entities that are actually solid, to make for more
efficient collision detection
====================
*/
void CG_BuildSolidList( void ) {
    int			i;
    centity_t	*cent;
    snapshot_t	*snap;
    entityState_t	*ent;

    cg_numSolidEntities = 0;
    cg_numTriggerEntities = 0;

    if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
        snap = cg.nextSnap;
    } else {
        snap = cg.snap;
    }

    for ( i = 0 ; i < snap->numEntities ; i++ ) {
        cent = &cg_entities[ snap->entities[ i ].number ];
        ent = &cent->currentState;

        if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) {
            cg_triggerEntities[cg_numTriggerEntities] = cent;
            cg_numTriggerEntities++;
            continue;
        }

        if ( cent->nextState.solid ) {
            cg_solidEntities[cg_numSolidEntities] = cent;
            cg_numSolidEntities++;
            continue;
        }
    }
}

/*
====================
CG_ClipMoveToEntities
 
====================
*/
static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
                                    int skipNumber, int mask, trace_t *tr ) {
    int			i, x, zd, zu;
    trace_t		trace;
    entityState_t	*ent;
    clipHandle_t 	cmodel;
    vec3_t		bmins, bmaxs;
    vec3_t		origin, angles;
    centity_t	*cent;

    for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
        cent = cg_solidEntities[ i ];
        ent = &cent->currentState;

        if ( ent->number == skipNumber ) {
            continue;
        }

        if ( ent->solid == SOLID_BMODEL ) {
            // special value for bmodel
            cmodel = trap_CM_InlineModel( ent->modelindex );
            VectorCopy( cent->lerpAngles, angles );
            BG_EvaluateTrajectory( &cent->currentState.pos, cg.physicsTime, origin );
        } else {
            // encoded bbox
            x = (ent->solid & 255);
            zd = ((ent->solid>>8) & 255);
            zu = ((ent->solid>>16) & 255) - 32;

            bmins[0] = bmins[1] = -x;
            bmaxs[0] = bmaxs[1] = x;
            bmins[2] = -zd;
            bmaxs[2] = zu;

            cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
            VectorCopy( vec3_origin, angles );
            VectorCopy( cent->lerpOrigin, origin );
        }


        trap_CM_TransformedBoxTrace ( &trace, start, end,
                                      mins, maxs, cmodel,  mask, origin, angles);

        if (trace.allsolid || trace.fraction < tr->fraction) {
            trace.entityNum = ent->number;
            *tr = trace;
        } else if (trace.startsolid) {
            tr->startsolid = qtrue;
        }
        if ( tr->allsolid ) {
            return;
        }
    }
}

/*
================
CG_Trace
================
*/
void	CG_Trace( trace_t *result, const vec3_t start, /*const*/ vec3_t mins, /*const*/ vec3_t maxs, const vec3_t end,
               int skipNumber, int mask ) {
    trace_t	t;

    trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
    t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
    // check all other solid models
    CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t);

    *result = t;
}

/*
================
CG_PointContents
================
*/
int		CG_PointContents( const vec3_t point, int passEntityNum ) {
    int			i;
    entityState_t	*ent;
    centity_t	*cent;
    clipHandle_t cmodel;
    int			contents;

    contents = trap_CM_PointContents (point, 0);

    for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
        cent = cg_solidEntities[ i ];

        ent = &cent->currentState;

        if ( ent->number == passEntityNum ) {
            continue;
        }

        if (ent->solid != SOLID_BMODEL) { // special value for bmodel
            continue;
        }

        cmodel = trap_CM_InlineModel( ent->modelindex );
        if ( !cmodel ) {
            continue;
        }

        contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
    }

    return contents;
}

/*
================
CG_UpdateSpecViewAngles
 //BB
This sets cg.specViewAngles and cg.spectatorMode for spectators
================
*/
void CG_UpdateSpecViewAngles(playerState_t *ps, const usercmd_t *cmd) {
    short		temp;
    int		i;
    int min, max;

    if (ps->stats[STAT_HEALTH] > 0) {
        return;
    }

    if (ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
        return;
    }

    max = 16000;
    min = -900;

    // circularly clamp the angles with deltas
    for (i=0 ; i<3 ; i++) {
        temp = cmd->angles[i] + cg.deltaAngles[i];
        if ( i == PITCH ) {
            // don't let the spectator look up or down more than 90 degrees
            if ( temp > max ) {
                cg.deltaAngles[i] = max - cmd->angles[i];
                temp = max;
            } else if ( temp < min ) {
                cg.deltaAngles[i] = min - cmd->angles[i];
                temp = min;
            }
        }
        cg.specViewAngles[i] = SHORT2ANGLE(temp);
    }
}


/*
========================
CG_InterpolatePlayerState
 
Generates cg.predictedPlayerState by interpolating between
cg.snap->player_state and cg.nextFrame->player_state
========================
*/
void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd );
static void CG_InterpolatePlayerState( qboolean grabAngles ) {
    float			f;
    int				i;
    playerState_t	*out;
    snapshot_t		*prev, *next;

    out = &cg.predictedPlayerState;
    prev = cg.snap;
    next = cg.nextSnap;

    *out = cg.snap->ps;

    //BB
    if (1) {
        usercmd_t	cmd;
        int			cmdNum;

        cmdNum = trap_GetCurrentCmdNumber();
        trap_GetUserCmd( cmdNum, &cmd );
        CG_UpdateSpecViewAngles(out, &cmd);
    }
    //BB end

    // if we are still allowing local input, short circuit the view angles
    if ( grabAngles ) {
        usercmd_t	cmd;
        int			cmdNum;

        cmdNum = trap_GetCurrentCmdNumber();
        trap_GetUserCmd( cmdNum, &cmd );

        PM_UpdateViewAngles( out, &cmd );
    }

    // if the next frame is a teleport, we can't lerp to it
    if ( cg.nextFrameTeleport ) {
        return;
    }

    if ( !next || next->serverTime <= prev->serverTime ) {
        return;
    }

    f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );

    i = next->ps.bobCycle;
    if ( i < prev->ps.bobCycle ) {
        i += 256;		// handle wraparound
    }
    out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );

    for ( i = 0 ; i < 3 ; i++ ) {
        out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
        if ( !grabAngles ) {
            out->viewangles[i] = LerpAngle(
                                     prev->ps.viewangles[i], next->ps.viewangles[i], f );
        }
        out->velocity[i] = prev->ps.velocity[i] +
                           f * (next->ps.velocity[i] - prev->ps.velocity[i] );
    }

}

/*
===================
CG_TouchItem
===================
*/
static void CG_TouchItem( centity_t *cent ) {
    gitem_t		*item;

    if ( !cg_predictItems.integer ) {
        return;
    }
    if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, &cent->currentState, cg.time ) ) {
        return;
    }

    // never pick an item up twice in a prediction
    if ( cent->miscTime == cg.time ) {
        return;
    }

    if ( !BG_CanItemBeGrabbed( cgs.gametype, &cent->currentState, &cg.predictedPlayerState ) ) {
        return;		// can't hold it
    }

    item = &bg_itemlist[ cent->currentState.modelindex ];

    // Special case for flags.
    // We don't predict touching our own flag
#ifdef MISSIONPACK

    if( cgs.gametype == GT_1FCTF ) {
        if( item->giTag != PW_NEUTRALFLAG ) {
            return;
        }
    }
    if( cgs.gametype == GT_CTF || cgs.gametype == GT_RTF ||cgs.gametype == GT_HARVESTER ) {
#else
    if( cgs.gametype >= GT_CTF ) {
#endif
        if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED &&
                item->giTag == PW_REDFLAG)
            return;
        if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE &&
                item->giTag == PW_BLUEFLAG)
            return;
    }

    // grab it
    BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState);

    // Alliance: if weapons stay, don't remove it from the frame
    if (!BG_weaponsStayEnabled()) {
        // remove it from the frame so it won't be drawn
        cent->currentState.eFlags |= EF_NODRAW;
    }

    // don't touch it again this prediction
    cent->miscTime = cg.time;

    // if its a weapon, give them some predicted ammo so the autoswitch will work
    if ( item->giType == IT_WEAPON ) {
        cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
        if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) {
            cg.predictedPlayerState.ammo[ item->giTag ] = 1;
        }
    }
}


/*
=========================
CG_TouchTriggerPrediction
 
Predict push triggers and items
=========================
*/
static void CG_TouchTriggerPrediction( void ) {
    int			i;
    trace_t		trace;
    entityState_t	*ent;
    clipHandle_t cmodel;
    centity_t	*cent;
    qboolean	spectator;

    // dead clients don't activate triggers
    if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
        return;
    }

    spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR );

    if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) {
        return;
    }

    for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) {
        cent = cg_triggerEntities[ i ];
        ent = &cent->currentState;

        if ( ent->eType == ET_ITEM && !spectator ) {
            CG_TouchItem( cent );
            continue;
        }

        if ( ent->solid != SOLID_BMODEL ) {
            continue;
        }

        cmodel = trap_CM_InlineModel( ent->modelindex );
        if ( !cmodel ) {
            continue;
        }

        trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin,
                          cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );

        if ( !trace.startsolid ) {
            continue;
        }

        if ( ent->eType == ET_TELEPORT_TRIGGER ) {
            cg.hyperspace = qtrue;
        } else if ( ent->eType == ET_PUSH_TRIGGER ) {
            BG_TouchJumpPad( &cg.predictedPlayerState, ent );
        }
    }

    // if we didn't touch a jump pad this pmove frame
    if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) {
        cg.predictedPlayerState.jumppad_frame = 0;
        cg.predictedPlayerState.jumppad_ent = 0;
    }
}

//unlagged - optimized prediction
#define ABS(x) ((x) < 0 ? (-(x)) : (x))

static int IsUnacceptableError( playerState_t *ps, playerState_t *pps ) {
    vec3_t delta;
    int i;

    if ( pps->pm_type != ps->pm_type ||
            pps->pm_flags != ps->pm_flags ||
            pps->pm_time != ps->pm_time ) {
        return 1;
    }

    VectorSubtract( pps->origin, ps->origin, delta );
    if ( VectorLengthSquared( delta ) > 0.1f * 0.1f ) {
        if ( cg_showmiss.integer ) {
            CG_Printf("delta: %.2f  ", VectorLength(delta) );
        }
        return 2;
    }

    VectorSubtract( pps->velocity, ps->velocity, delta );
    if ( VectorLengthSquared( delta ) > 0.1f * 0.1f ) {
        if ( cg_showmiss.integer ) {
            CG_Printf("delta: %.2f  ", VectorLength(delta) );
        }
        return 3;
    }

    if ( pps->weaponTime != ps->weaponTime ||
            pps->gravity != ps->gravity ||
            pps->speed != ps->speed ||
            pps->delta_angles[0] != ps->delta_angles[0] ||
            pps->delta_angles[1] != ps->delta_angles[1] ||
            pps->delta_angles[2] != ps->delta_angles[2] ||
            pps->groundEntityNum != ps->groundEntityNum ) {
        return 4;
    }

    if ( pps->legsTimer != ps->legsTimer ||
            pps->legsAnim != ps->legsAnim ||
            pps->torsoTimer != ps->torsoTimer ||
            pps->torsoAnim != ps->torsoAnim ||
            pps->movementDir != ps->movementDir ) {
        return 5;
    }

    VectorSubtract( pps->grapplePoint, ps->grapplePoint, delta );
    if ( VectorLengthSquared( delta ) > 0.1f * 0.1f ) {
        return 6;
    }

    if ( pps->eFlags != ps->eFlags ) {
        return 7;
    }

    if ( pps->eventSequence != ps->eventSequence ) {
        return 8;
    }

    for ( i = 0; i < MAX_PS_EVENTS; i++ ) {
        if ( pps->events[i] != ps->events[i] ||
                pps->eventParms[i] != ps->eventParms[i] ) {
            return 9;
        }
    }

    if ( pps->externalEvent != ps->externalEvent ||
            pps->externalEventParm != ps->externalEventParm ||
            pps->externalEventTime != ps->externalEventTime ) {
        return 10;
    }

    if ( pps->clientNum != ps->clientNum ||
            pps->weapon != ps->weapon ||
            pps->weaponstate != ps->weaponstate ) {
        return 11;
    }

    if ( ABS(pps->viewangles[0] - ps->viewangles[0]) > 1.0f ||
            ABS(pps->viewangles[1] - ps->viewangles[1]) > 1.0f ||
            ABS(pps->viewangles[2] - ps->viewangles[2]) > 1.0f ) {
        return 12;
    }

    if ( pps->viewheight != ps->viewheight ) {
        return 13;
    }

    if ( pps->damageEvent != ps->damageEvent ||
            pps->damageYaw != ps->damageYaw ||
            pps->damagePitch != ps->damagePitch ||
            pps->damageCount != ps->damageCount ) {
        return 14;
    }

    for ( i = 0; i < MAX_STATS; i++ ) {
        if ( pps->stats[i] != ps->stats[i] ) {
            return 15;
        }
    }

    for ( i = 0; i < MAX_PERSISTANT; i++ ) {
        if ( pps->persistant[i] != ps->persistant[i] ) {
            return 16;
        }
    }

    for ( i = 0; i < MAX_POWERUPS; i++ ) {
        if ( pps->powerups[i] != ps->powerups[i] ) {
            return 17;
        }
    }

    for ( i = 0; i < MAX_WEAPONS; i++ ) {
        if ( pps->ammo[i] != ps->ammo[i] ) {
            return 18;
        }
    }

    if ( pps->generic1 != ps->generic1 ||
            pps->loopSound != ps->loopSound ||
            pps->jumppad_ent != ps->jumppad_ent ) {
        return 19;
    }

    return 0;
}
//unlagged - optimized prediction

/*
=================
CG_PredictPlayerState
 
Generates cg.predictedPlayerState for the current cg.time
cg.predictedPlayerState is guaranteed to be valid after exiting.
 
For demo playback, this will be an interpolation between two valid
playerState_t.
 
For normal gameplay, it will be the result of predicted usercmd_t on
top of the most recent playerState_t received from the server.
 
Each new snapshot will usually have one or more new usercmd over the last,
but we simulate all unacknowledged commands each time, not just the new ones.
This means that on an internet connection, quite a few pmoves may be issued
each frame.
 
OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
differs from the predicted one.  Would require saving all intermediate
playerState_t during prediction.
 
We detect prediction errors and allow them to be decayed off over several frames
to ease the jerk.
=================
*/

vmCvar_t	cg_pmove_msec;

void CG_PredictPlayerState( void ) {
    int			cmdNum, current;
    playerState_t	oldPlayerState;
    qboolean	moved;
    usercmd_t	oldestCmd;
    usercmd_t	latestCmd;
    //unlagged - optimized prediction
    int stateIndex=0, predictCmd=0;
    int numPredicted = 0, numPlayedBack = 0; // debug code
    //unlagged - optimized prediction

    cg.hyperspace = qfalse;	// will be set if touching a trigger_teleport

    // if this is the first frame we must guarantee
    // predictedPlayerState is valid even if there is some
    // other error condition
    if ( !cg.validPPS ) {
        cg.validPPS = qtrue;
        cg.predictedPlayerState = cg.snap->ps;
    }


    // demo playback just copies the moves
    if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
            //  __ZCAM__
            /* camera jitter fix (client side) */
            || (cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR
                || cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR_RED
                || cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR_BLUE)
            ///* __ZCAM__ */
       ) {
        CG_InterpolatePlayerState( qfalse );
        return;
    }

    // non-predicting local movement will grab the latest angles
    if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
        CG_InterpolatePlayerState( qtrue );
        return;
    }

    // prepare for pmove
    cg_pmove.ps = &cg.predictedPlayerState;
    cg_pmove.trace = CG_Trace;
    cg_pmove.pointcontents = CG_PointContents;
    if ( cg_pmove.ps->pm_type == PM_DEAD ) {
        cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
    } else {
        cg_pmove.tracemask = MASK_PLAYERSOLID;
    }
    if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ||
            cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR_RED ||
            cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR_BLUE ) {
        cg_pmove.tracemask &= ~CONTENTS_BODY;	// spectators can fly through bodies
    }
    cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;

    // save the state before the pmove so we can detect transitions
    oldPlayerState = cg.predictedPlayerState;

    current = trap_GetCurrentCmdNumber();

    // if we don't have the commands right after the snapshot, we
    // can't accurately predict a current position, so just freeze at
    // the last good position we had
    cmdNum = current - CMD_BACKUP + 1;
    trap_GetUserCmd( cmdNum, &oldestCmd );
    if ( oldestCmd.serverTime > cg.snap->ps.commandTime
            && oldestCmd.serverTime < cg.time ) {	// special check for map_restart
        if ( cg_showmiss.integer ) {
            CG_Printf ("exceeded PACKET_BACKUP on commands\n");
        }
        return;
    }

    // get the latest command so we can know which commands are from previous map_restarts
    trap_GetUserCmd( current, &latestCmd );

    // get the most recent information we have, even if
    // the server time is beyond our current cg.time,
    // because predicted player positions are going to
    // be ahead of everything else anyway
    if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
        cg.predictedPlayerState = cg.nextSnap->ps;
        cg.physicsTime = cg.nextSnap->serverTime;
    } else {
        cg.predictedPlayerState = cg.snap->ps;
        cg.physicsTime = cg.snap->serverTime;
    }

    if ( pmove_msec.integer < 8 ) {
        trap_Cvar_Set("pmove_msec", "8");
        trap_Cvar_Update(&pmove_msec);
        trap_Cvar_Set("cg_pmove_msec", "8");
        trap_Cvar_Update(&cg_pmove_msec);

    } else if (pmove_msec.integer > 33) {
        trap_Cvar_Set("pmove_msec", "33");
        trap_Cvar_Update(&pmove_msec);
        trap_Cvar_Set("cg_pmove_msec", "33");
        trap_Cvar_Update(&cg_pmove_msec);
    }


    cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer;
    cg_pmove.pmove_msec = pmove_msec.integer;
    //unlagged - optimized prediction
    // Like the comments described above, a player's state is entirely
    // re-predicted from the last valid snapshot every client frame, which
    // can be really, really, really slow.  Every old command has to be
    // run again.  For every client frame that is *not* directly after a
    // snapshot, this is unnecessary, since we have no new information.
    // For those, we'll play back the predictions from the last frame and
    // predict only the newest commands.  Essentially, we'll be doing
    // an incremental predict instead of a full predict.
    //
    // If we have a new snapshot, we can compare its player state's command
    // time to the command times in the queue to find a match.  If we find
    // a matching state, and the predicted version has not deviated, we can
    // use the predicted state as a base - and also do an incremental predict.
    //
    // With this method, we get incremental predicts on every client frame
    // except a frame following a new snapshot in which there was a prediction
    // error.  This yeilds anywhere from a 15% to 40% performance increase,
    // depending on how much of a bottleneck the CPU is.

    // we check for cg_latentCmds because it'll mess up the optimization
    // FIXME: make cg_latentCmds work with cg_optimizePrediction?
    if ( cg_optimizePrediction.integer && !cg_latentCmds.integer ) {
        if ( cg.nextFrameTeleport || cg.thisFrameTeleport ) {
            // do a full predict
            cg.lastPredictedCommand = 0;
            cg.stateTail = cg.stateHead;
            predictCmd = current - CMD_BACKUP + 1;
        }
        // cg.physicsTime is the current snapshot's serverTime
        // if it's the same as the last one
        else if ( cg.physicsTime == cg.lastServerTime ) {
            // we have no new information, so do an incremental predict
            predictCmd = cg.lastPredictedCommand + 1;
        } else {
            // we have a new snapshot

            int i;
            qboolean error = qtrue;

            // loop through the saved states queue
            for ( i = cg.stateHead; i != cg.stateTail; i = (i + 1) % NUM_SAVED_STATES ) {
                // if we find a predicted state whose commandTime matches the snapshot player state's commandTime
                if ( cg.savedPmoveStates[i].commandTime == cg.predictedPlayerState.commandTime ) {
                    // make sure the state differences are acceptable
                    int errorcode = IsUnacceptableError( &cg.predictedPlayerState, &cg.savedPmoveStates[i] );

                    // too much change?
                    if ( errorcode ) {
                        if ( cg_showmiss.integer ) {
                            CG_Printf("errorcode %d at %d\n", errorcode, cg.time);
                        }
                        // yeah, so do a full predict
                        break;
                    }

                    // this one is almost exact, so we'll copy it in as the starting point
                    *cg_pmove.ps = cg.savedPmoveStates[i];
                    // advance the head
                    cg.stateHead = (i + 1) % NUM_SAVED_STATES;

                    // set the next command to predict
                    predictCmd = cg.lastPredictedCommand + 1;

                    // a saved state matched, so flag it
                    error = qfalse;
                    break;
                }
            }

            // if no saved states matched
            if ( error ) {
                // do a full predict
                cg.lastPredictedCommand = 0;
                cg.stateTail = cg.stateHead;
                predictCmd = current - CMD_BACKUP + 1;
            }
        }

        // keep track of the server time of the last snapshot so we
        // know when we're starting from a new one in future calls
        cg.lastServerTime = cg.physicsTime;
        stateIndex = cg.stateHead;
    }
/*
    if ( (latestCmd.buttons & BUTTON_GRAPPLE &&  !cg_serverConfig.grapple_onhand)) {
        cg_pmove.ps->pm_flags |= PMF_GRAPPLE_PULL;
        //cg_pmove.ps->pm_flags |= PMF_JUMP_HELD;	//dont allow multiple jumps while grapples out
	
    }
    else if ( !(latestCmd.buttons & BUTTON_GRAPPLE && !cg_serverConfig.grapple_onhand)) {
        cg_pmove.ps->pm_flags &= ~PMF_GRAPPLE_PULL;
    }
	*/
	/*
    if (latestCmd.buttons & BUTTON_ATTACK && cg.snap->ps.weapon == WP_GRAPPLING_HOOK) {
       cg_pmove.ps->pm_flags |= PMF_GRAPPLE_PULL;
    }
    else if(!(latestCmd.buttons & BUTTON_ATTACK && cg.snap->ps.weapon == WP_GRAPPLING_HOOK)) {
        cg_pmove.ps->pm_flags &= ~PMF_GRAPPLE_PULL;
    }
	*/

    /*	if (cg_pmove.ps->pm_flags & PMF_JUMP_HELD && cg_pmove.ps->pm_flags & PMF_GRAPPLE_PULL) {
		cg_pmove.ps->pm_flags &=  ~PMF_JUMP_HELD;

	}
	*/

    //unlagged - optimized prediction
    // run cmds
    moved = qfalse;
    for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) {
        // get the command
        trap_GetUserCmd( cmdNum, &cg_pmove.cmd );

        if ( cg_pmove.pmove_fixed ) {
            PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd );
        }

        //BB
        CG_UpdateSpecViewAngles( cg_pmove.ps, &cg_pmove.cmd );
        //BB end

        // don't do anything if the time is before the snapshot player time
        if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) {
            continue;
        }

        // don't do anything if the command was from a previous map_restart
        if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) {
            continue;
        }

        // check for a prediction error from last frame
        // on a lan, this will often be the exact value
        // from the snapshot, but on a wan we will have
        // to predict several commands to get to the point
        // we want to compare
        if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) {
            vec3_t	delta;
            float	len;

            if ( cg.thisFrameTeleport ) {
                // a teleport will not cause an error decay
                VectorClear( cg.predictedError );
                if ( cg_showmiss.integer ) {
                    CG_Printf( "PredictionTeleport\n" );
                }
                cg.thisFrameTeleport = qfalse;
            } else {
                vec3_t	adjusted;
                CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
                                           cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );

                if ( cg_showmiss.integer ) {
                    if (!VectorCompare( oldPlayerState.origin, adjusted )) {
                        CG_Printf("prediction error\n");
                    }
                }
                VectorSubtract( oldPlayerState.origin, adjusted, delta );
                len = VectorLength( delta );
                if ( len > 0.1 ) {
                    if ( cg_showmiss.integer ) {
                        CG_Printf("Prediction miss: %f\n", len);
                    }
                    if ( cg_errorDecay.integer ) {
                        int		t;
                        float	f;

                        t = cg.time - cg.predictedErrorTime;
                        f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
                        if ( f < 0 ) {
                            f = 0;
                        }
                        if ( f > 0 && cg_showmiss.integer ) {
                            CG_Printf("Double prediction decay: %f\n", f);
                        }
                        VectorScale( cg.predictedError, f, cg.predictedError );
                    } else {
                        VectorClear( cg.predictedError );
                    }
                    VectorAdd( delta, cg.predictedError, cg.predictedError );
                    cg.predictedErrorTime = cg.oldTime;
                }
            }
        }

        // don't predict gauntlet firing, which is only supposed to happen
        // when it actually inflicts damage
        cg_pmove.gauntletHit = qfalse;

        if ( cg_pmove.pmove_fixed ) {
            cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
        }

        //unlagged - optimized prediction
        // we check for cg_latentCmds because it'll mess up the optimization
        if ( cg_optimizePrediction.integer && !cg_latentCmds.integer ) {
            // if we need to predict this command, or we've run out of space in the saved states queue
            if ( cmdNum >= predictCmd || (stateIndex + 1) % NUM_SAVED_STATES == cg.stateHead ) {
                // run the Pmove
                Pmove (&cg_pmove);

                numPredicted++; // debug code

                // record the last predicted command
                cg.lastPredictedCommand = cmdNum;

                // if we haven't run out of space in the saved states queue
                if ( (stateIndex + 1) % NUM_SAVED_STATES != cg.stateHead ) {
                    // save the state for the false case (of cmdNum >= predictCmd)
                    // in later calls to this function
                    cg.savedPmoveStates[stateIndex] = *cg_pmove.ps;
                    stateIndex = (stateIndex + 1) % NUM_SAVED_STATES;
                    cg.stateTail = stateIndex;
                }
            } else {
                numPlayedBack++; // debug code

                if ( cg_showmiss.integer &&
                        cg.savedPmoveStates[stateIndex].commandTime != cg_pmove.cmd.serverTime) {
                    // this should ONLY happen just after changing the value of pmove_fixed
                    CG_Printf( "saved state miss\n" );
                }

                // play back the command from the saved states
                *cg_pmove.ps = cg.savedPmoveStates[stateIndex];

                // go to the next element in the saved states array
                stateIndex = (stateIndex + 1) % NUM_SAVED_STATES;
            }
        } else {
            // run the Pmove
            Pmove (&cg_pmove);

            numPredicted++; // debug code
        }
        //unlagged - optimized prediction


        moved = qtrue;

        // add push trigger movement effects
        CG_TouchTriggerPrediction();

        // check for predictable events that changed from previous predictions
        //CG_CheckChangedPredictableEvents(&cg.predictedPlayerState);
    }

    //unlagged - optimized prediction
    // do a /condump after a few seconds of this
    //CG_Printf("cg.time: %d, numPredicted: %d, numPlayedBack: %d\n", cg.time, numPredicted, numPlayedBack); // debug code
    // if everything is working right, numPredicted should be 1 more than 98%
    // of the time, meaning only ONE predicted move was done in the frame
    // you should see other values for numPredicted after IsUnacceptableError
    // returns nonzero, and that's it
    //unlagged - optimized prediction


    if ( cg_showmiss.integer > 1 ) {
        CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time );
    }

    if ( !moved ) {
        if ( cg_showmiss.integer ) {
            CG_Printf( "not moved\n" );
        }
        return;
    }

    // adjust for the movement of the groundentity
    CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
                               cg.predictedPlayerState.groundEntityNum,
                               cg.physicsTime, cg.time, cg.predictedPlayerState.origin );

    if ( cg_showmiss.integer ) {
        if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) {
            CG_Printf("WARNING: dropped event\n");
        }
    }

    // fire events and other transition triggered things
    CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState );

    if ( cg_showmiss.integer ) {
        if (cg.eventSequence > cg.predictedPlayerState.eventSequence) {
            CG_Printf("WARNING: double event\n");
            cg.eventSequence = cg.predictedPlayerState.eventSequence;
        }
    }
}


