stvoy-sp-sdk/game/bg_pmove.cpp

2626 lines
64 KiB
C++

// bg_pmove.c -- both games player movement code
// takes a playerstate and a usercmd as input and returns a modifed playerstate
// define GAME_INCLUDE so that g_public.h does not define the
// short, server-visible gclient_t and gentity_t structures,
// because we define the full size ones in this file
#define GAME_INCLUDE
#include "q_shared.h"
#include "g_shared.h"
#include "bg_local.h"
#include "g_local.h"
#include "anims.h"
#include "../cgame/cg_local.h" // yeah I know this is naughty, but we're shipping soon...
extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType );
extern void AddSoundEvent( gentity_t *owner, vec3_t position, float radius, alertEventLevel_e alertLevel );
extern void AddSightEvent( gentity_t *owner, vec3_t position, float radius, alertEventLevel_e alertLevel );
extern cvar_t *g_timescale;
static qboolean MatrixMode;
qboolean Flying;
pmove_t *pm;
pml_t pml;
// movement parameters
const float pm_stopspeed = 100.0f;
const float pm_duckScale = 0.50f;
const float pm_swimScale = 0.50f;
float pm_ladderScale = 0.7f;
const float pm_accelerate = 12.0f;
const float pm_airaccelerate = 4.0f;
const float pm_wateraccelerate = 4.0f;
const float pm_flyaccelerate = 8.0f;
const float pm_friction = 6.0f;
const float pm_waterfriction = 1.0f;
const float pm_flightfriction = 3.0f;
const float pm_frictionModifier = 3.0f; //Used for "careful" mode (when pressing use)
const float pm_airDecelRate = 1.35f; //Used for air decelleration away from current movement velocity
int c_pmove = 0;
extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
extern void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags);
extern void PM_TorsoAnimation( void );
extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame );
extern int PM_AnimLength( int index, animNumber_t anim );
extern qboolean PM_InDeathAnim ( void );
extern qboolean PM_InOnGroundAnim (gentity_t *self);
extern weaponInfo_t cg_weapons[MAX_WEAPONS];
extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim );
#define PHASER_RECHARGE_TIME 100
/*
===============
PM_AddEvent
===============
*/
void PM_AddEvent( int newEvent )
{
AddEventToPlayerstate( newEvent, 0, pm->ps );
}
/*
===============
PM_AddTouchEnt
===============
*/
void PM_AddTouchEnt( int entityNum ) {
int i;
if ( entityNum == ENTITYNUM_WORLD ) {
return;
}
if ( pm->numtouch == MAXTOUCH ) {
return;
}
// see if it is already added
for ( i = 0 ; i < pm->numtouch ; i++ ) {
if ( pm->touchents[ i ] == entityNum ) {
return;
}
}
// add it
pm->touchents[pm->numtouch] = entityNum;
pm->numtouch++;
}
/*
==================
PM_ClipVelocity
Slide off of the impacting surface
This will pull you down onto slopes if heading away from
them and push you up them as you go up them.
Also stops you when you hit walls.
==================
*/
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 void PM_Friction( void ) {
vec3_t vec;
float *vel;
float speed, newspeed, control;
float drop, friction = pm->ps->friction;
vel = pm->ps->velocity;
VectorCopy( vel, vec );
if ( pml.walking ) {
vec[2] = 0; // ignore slope movement
}
speed = VectorLength(vec);
if (speed < 1) {
vel[0] = 0;
vel[1] = 0; // allow sinking underwater
// FIXME: still have z friction underwater?
return;
}
drop = 0;
// apply ground friction, even if on ladder
if ( (pm->watertype & CONTENTS_LADDER) || pm->waterlevel <= 1 ) {
if ( (pm->watertype & CONTENTS_LADDER) || (pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK)) ) {
// if getting knocked back, no friction
if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
//If the use key is pressed. slow the player more quickly
if ( pm->cmd.buttons & BUTTON_USE )
friction *= pm_frictionModifier;
control = speed < pm_stopspeed ? pm_stopspeed : speed;
drop += control*friction*pml.frametime;
}
}
}
// apply water friction even if just wading
if ( pm->waterlevel && !(pm->watertype & CONTENTS_LADDER))
{
drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
}
// apply flying friction
if ( pm->ps->pm_type == PM_SPECTATOR )
{
drop += speed*pm_flightfriction*pml.frametime;
}
// scale the velocity
newspeed = speed - drop;
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
vel[0] = vel[0] * newspeed;
vel[1] = vel[1] * newspeed;
vel[2] = vel[2] * newspeed;
}
/*
==============
PM_Accelerate
Handles user intended acceleration
==============
*/
static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel )
{
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];
}
}
/*
============
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.0f * 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 ) {
//Don't allow jump until all buttons are up
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return qfalse;
}
//Not jumping
if ( pm->cmd.upmove < 10 ) {
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;
}
{
gentity_t *groundEnt = &g_entities[pm->ps->groundEntityNum];
if ( groundEnt && groundEnt->NPC )
{//Can't jump off of someone's head
return qfalse;
}
}
//Jumping
pml.groundPlane = qfalse;
pml.walking = qfalse;
pm->ps->pm_flags |= PMF_JUMP_HELD;
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pm->ps->velocity[2] = JUMP_VELOCITY;
if ( pm->gent )
{
if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) )
{
PM_AddEvent( EV_JUMP );
}
}
else
{
PM_AddEvent( EV_JUMP );
}
//Set the animations
if ( pm->cmd.forwardmove >= 0 ) {
if(!PM_InDeathAnim())
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE);
}
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
if(!PM_InDeathAnim())
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE);
}
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;
}
if ( pm->watertype & CONTENTS_LADDER ) {
if (pm->ps->velocity[2] <= 0)
return qfalse;
}
flatforward[0] = pml.forward[0];
flatforward[1] = pml.forward[1];
flatforward[2] = 0;
VectorNormalize( flatforward );
VectorMA( pm->ps->origin, 30, flatforward, spot );
spot[2] += 4;
cont = pm->pointcontents (spot, pm->ps->clientNum );
if ( !(cont & CONTENTS_SOLID) ) {
return qfalse;
}
spot[2] += 16;
cont = pm->pointcontents( spot, pm->ps->clientNum );
if ( cont ) {
return qfalse;
}
// jump out of water
VectorScale( pml.forward, 200, pm->ps->velocity );
pm->ps->velocity[2] = 350;
pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
pm->ps->pm_time = 2000;
return qtrue;
}
//============================================================================
/*
===================
PM_WaterJumpMove
Flying out of the water
===================
*/
static void PM_WaterJumpMove( void )
{
// waterjump has no control, but falls
PM_StepSlideMove( qtrue );
pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
if (pm->ps->velocity[2] < 0)
{
// cancel as soon as we are falling down again
pm->ps->pm_flags &= ~PMF_ALL_TIMES;
pm->ps->pm_time = 0;
}
}
/*
===================
PM_WaterMove
===================
*/
static void PM_WaterMove( void ) {
int i;
vec3_t wishvel;
float wishspeed;
vec3_t wishdir;
float scale;
float vel;
if ( PM_CheckWaterJump() ) {
PM_WaterJumpMove();
return;
}
#if 0
// jump = head for surface
if ( pm->cmd.upmove >= 10 ) {
if (pm->ps->velocity[2] > -300) {
if ( pm->watertype == CONTENTS_WATER ) {
pm->ps->velocity[2] = 100;
} else if (pm->watertype == CONTENTS_SLIME) {
pm->ps->velocity[2] = 80;
} else {
pm->ps->velocity[2] = 50;
}
}
}
#endif
PM_Friction ();
scale = PM_CmdScale( &pm->cmd );
//
// user intentions
//
if ( !scale ) {
wishvel[0] = 0;
wishvel[1] = 0;
if ( pm->watertype & CONTENTS_LADDER ) {
wishvel[2] = 0;
} else {
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 ( pm->watertype & CONTENTS_LADDER ) //ladder
{
if ( wishspeed > pm->ps->speed * pm_ladderScale ) {
wishspeed = pm->ps->speed * pm_ladderScale;
}
PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );
} else {
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 );
}
/*
===================
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 );
}
/*
===================
PM_AirMove
===================
*/
static void PM_AirMove( void ) {
int i;
vec3_t wishvel;
float fmove, smove;
vec3_t wishdir;
float wishspeed;
float scale;
usercmd_t cmd;
PM_Friction();
fmove = pm->cmd.forwardmove;
smove = pm->cmd.rightmove;
cmd = pm->cmd;
scale = PM_CmdScale( &cmd );
// 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);
/*
if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_STASIS )
{//FIXME: do a check for movetype_float
//Can move fairly well in air while falling
PM_Accelerate (wishdir, wishspeed, pm_accelerate/2.0f);
}
else
{
*/
if ( ( DotProduct (pm->ps->velocity, wishdir) ) < 0.0f )
{//Encourage deceleration away from the current velocity
wishspeed *= pm_airDecelRate;
}
// not on ground, so little effect on velocity
PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
//}
// we may have a ground plane that is very steep, even
// though we don't have a groundentity
// slide along the steep plane
if ( pml.groundPlane ) {
PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
pm->ps->velocity, OVERCLIP );
}
PM_StepSlideMove ( qtrue );
}
/*
===================
PM_WalkMove
===================
*/
static void PM_WalkMove( void ) {
int i;
vec3_t wishvel;
float fmove, smove;
vec3_t wishdir;
float wishspeed;
float scale;
usercmd_t cmd;
float accelerate;
float vel;
if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
// begin swimming
PM_WaterMove();
return;
}
if ( PM_CheckJump () ) {
// jumped away
if ( pm->waterlevel > 1 ) {
PM_WaterMove();
} else {
PM_AirMove();
}
return;
}
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));
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;
if(pm->gent && pm->gent->client)
{
pm->ps->viewheight = pm->gent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET;
if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
{
assert(0);
}
VectorCopy( pm->gent->mins, pm->mins );
VectorCopy( pm->gent->maxs, pm->maxs );
}
else
{
pm->ps->viewheight = DEFAULT_MAXS_2 + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
{
assert(0);
}
pm->mins[0] = DEFAULT_MINS_0;
pm->mins[1] = DEFAULT_MINS_1;
pm->mins[2] = DEFAULT_MINS_2;
pm->maxs[0] = DEFAULT_MAXS_0;
pm->maxs[1] = DEFAULT_MAXS_1;
pm->maxs[2] = DEFAULT_MAXS_2;
}
// 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;
// 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;
}
if(!PM_InDeathAnim())
{
if((pm->ps->legsAnim&~ANIM_TOGGLEBIT) == BOTH_FALLDEATH1||(pm->ps->legsAnim&~ANIM_TOGGLEBIT) == BOTH_FALLDEATH1INAIR)
{//FIXME: add a little bounce?
//FIXME: cut voice channel?
pm->ps->gravity = 1.0;
PM_AddEvent( EV_FALL_FAR );
int old_pm_type = pm->ps->pm_type;
pm->ps->pm_type = PM_NORMAL;
//Hack because for some reason PM_SetAnim just returns if you're dead...???
PM_SetAnim(pm, SETANIM_BOTH, BOTH_FALLDEATH1LAND, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
pm->ps->pm_type = old_pm_type;
return;
}
if ( delta > 20 || pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) //EV_FALL_SHORT or jumping back
{// decide which landing animation to use
if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP )
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_LANDBACK1,SETANIM_FLAG_OVERRIDE);
}
else
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_LAND1,SETANIM_FLAG_OVERRIDE);
}
}
}
// 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 > 50 )
{
PM_AddEvent( EV_FALL_FAR );//damage is dealt in g_active, ClientEvents
if ( pm->gent && pm->gent->s.number == 0 )
{
vec3_t bottom;
VectorCopy( pm->ps->origin, bottom );
bottom[2] += pm->mins[2];
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, bottom, 256, AEL_SUSPICIOUS );
}
}
}
else if ( delta > 25 )
{
// this is a pain grunt, so don't play it if dead
if ( pm->ps->stats[STAT_HEALTH] > 0 )
{
PM_AddEvent( EV_FALL_MEDIUM );//damage is dealt in g_active, ClientEvents
if ( pm->gent && pm->gent->s.number == 0 )
{
vec3_t bottom;
VectorCopy( pm->ps->origin, bottom );
bottom[2] += pm->mins[2];
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR );
}
}
}
}
else if ( delta > 7 )
{
PM_AddEvent( EV_FALL_SHORT );
if ( pm->gent && pm->gent->s.number == 0 )
{
vec3_t bottom;
VectorCopy( pm->ps->origin, bottom );
bottom[2] += pm->mins[2];
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, bottom, 128, AEL_MINOR );
}
}
if ( pm->gent )
{
if ( pm->gent->client && pm->gent->client->race == RACE_REAVER )
{
PM_SetAnim( pm,SETANIM_BOTH,BOTH_LAND1,SETANIM_FLAG_OVERRIDE );
}
}
}
else
{
PM_AddEvent( PM_FootstepForSurface() );
}
}
// start footstep cycle over
pm->ps->bobCycle = 0;
}
/*
=============
PM_CorrectAllSolid
=============
*/
static void PM_CorrectAllSolid( void ) {
if ( pm->debugLevel ) {
Com_Printf("%i:allsolid\n", c_pmove); //NOTENOTE: If this ever happens, I'd really like to see this print!
}
// FIXME: jitter around
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pml.groundPlane = qfalse;
pml.walking = 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 )
{//FIXME: if velocity[2] < 0 and didn't jump, use some falling anim
if ( pm->cmd.forwardmove >= 0 )
{
if(!PM_InDeathAnim())
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE);
}
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
}
else
{
if(!PM_InDeathAnim())
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE);
}
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 ) {
PM_CorrectAllSolid();
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 )
{
if(!PM_InDeathAnim())
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE);
}
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
}
else
{
if(!PM_InDeathAnim())
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE);
}
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;
}
if (!pm->cmd.forwardmove && !pm->cmd.rightmove) {
pm->ps->velocity[2] = 0; //wouldn't normally want this because of slopes, but we aren't tyring to move...
}
}
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] + DEFAULT_MINS_2 + 1;
cont = pm->pointcontents( point, pm->ps->clientNum );
if ( cont & (MASK_WATER|CONTENTS_LADDER) )
{
sample2 = pm->ps->viewheight - DEFAULT_MINS_2;
sample1 = sample2 / 2;
pm->watertype = cont;
pm->waterlevel = 1;
point[2] = pm->ps->origin[2] + DEFAULT_MINS_2 + sample1;
cont = pm->pointcontents (point, pm->ps->clientNum );
if ( cont & (MASK_WATER|CONTENTS_LADDER) )
{
if ( Q_stricmp( "biohulk", pm->gent->NPC_type ) != 0 )
{//special hack for biohulk- they're never waist-high in water
pm->waterlevel = 2;
}
point[2] = pm->ps->origin[2] + DEFAULT_MINS_2 + sample2;
cont = pm->pointcontents (point, pm->ps->clientNum );
if ( cont & (MASK_WATER|CONTENTS_LADDER) )
{
pm->waterlevel = 3;
}
}
}
}
/*
==============
PM_SetBounds
Sets mins, maxs
==============
*/
static void PM_SetBounds (void)
{
if ( pm->gent && pm->gent->client )
{
if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
{
//assert(0);
}
VectorCopy( pm->gent->mins, pm->mins );
VectorCopy( pm->gent->maxs, pm->maxs );
}
else
{
if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
{
assert(0);
}
pm->mins[0] = DEFAULT_MINS_0;
pm->mins[1] = DEFAULT_MINS_1;
pm->maxs[0] = DEFAULT_MAXS_0;
pm->maxs[1] = DEFAULT_MAXS_1;
pm->mins[2] = DEFAULT_MINS_2;
pm->maxs[2] = DEFAULT_MAXS_2;
}
}
/*
==============
PM_CheckDuck
Sets mins, maxs, and pm->ps->viewheight
==============
*/
static void PM_CheckDuck (void)
{
trace_t trace;
int standheight;
int crouchheight;
int oldHeight;
if ( pm->gent && pm->gent->client )
{
if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] )
{
//assert(0);
}
standheight = pm->gent->client->standheight;
crouchheight = pm->gent->client->crouchheight;
}
else
{
if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 )
{
assert(0);
}
standheight = DEFAULT_MAXS_2;
crouchheight = CROUCH_MAXS_2;
}
oldHeight = pm->maxs[2];
if ( pm->cmd.upmove < 0 )
{ // trying to duck
pm->maxs[2] = crouchheight;
pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT;
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
{//Not ducked already and trying to duck in mid-air
//will raise your feet, unducking whilst in air will drop feet
if ( !(pm->ps->pm_flags&PMF_DUCKED) )
{
pm->ps->eFlags ^= EF_TELEPORT_BIT;
}
pm->ps->origin[2] += oldHeight - pm->maxs[2];//diff will be zero if were already ducking
//Don't worry, we know we fit in a smaller size
}
pm->ps->pm_flags |= PMF_DUCKED;
}
else
{ // want to stop ducking, stand up if possible
if ( pm->ps->pm_flags & PMF_DUCKED )
{//Was ducking
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
{//unducking whilst in air will try to drop feet
pm->maxs[2] = standheight;
pm->ps->origin[2] += oldHeight - pm->maxs[2];
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
if ( !trace.allsolid )
{
pm->ps->eFlags ^= EF_TELEPORT_BIT;
pm->ps->pm_flags &= ~PMF_DUCKED;
}
else
{//Put us back
pm->ps->origin[2] -= oldHeight - pm->maxs[2];
}
//NOTE: this isn't the best way to check this, you may have room to unduck
//while in air, but your feet are close to landing. Probably won't be a
//noticable shortcoming
}
else
{
// try to stand up
pm->maxs[2] = standheight;
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 )
{//Still ducking
pm->maxs[2] = crouchheight;
pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT;
}
else
{//standing now
pm->maxs[2] = standheight;
//FIXME: have a crouchviewheight and standviewheight on ent?
pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT;
}
}
}
//===================================================================
/*
===============
PM_Footsteps
===============
*/
static void PM_Footsteps( void )
{
float bobmove;
int old, oldAnim;
qboolean footstep = qfalse;
qboolean careful = qfalse;
qboolean validNPC = qfalse;
if( pm->gent == NULL || pm->gent->client == NULL )
return;
if( pm->gent->NPC != NULL )
{
validNPC = qtrue;
if( pm->cmd.buttons & BUTTON_CAREFUL )
{
careful = qtrue;
}
}
pm->gent->client->renderInfo.legsFpsMod = 1.0f;
//
// 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 )
{
// airborne leaves position in cycle intact, but doesn't advance
if ( pm->waterlevel > 0 )
{
if ( pm->watertype & CONTENTS_LADDER )
{//FIXME: check for watertype, save waterlevel for whether to play
//the get off ladder transition anim!
if ( pm->ps->velocity[2] )
{//going up or down it
int anim;
if ( pm->ps->velocity[2] > 0 )
{
anim = BOTH_LADDER_UP1;
}
else
{
anim = BOTH_LADDER_DWN1;
}
PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
if ( pm->waterlevel >= 2 ) //arms on ladder
{
PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
}
if (fabs(pm->ps->velocity[2]) >5) {
bobmove = 0.005 * fabs(pm->ps->velocity[2]); // climbing bobs slow
if (bobmove > 0.3)
bobmove = 0.3F;
goto DoFootSteps;
}
}
else
{
PM_SetAnim( pm, SETANIM_LEGS, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
pm->ps->legsAnimTimer += 300;
if ( pm->waterlevel >= 2 ) //arms on ladder
{
PM_SetAnim( pm, SETANIM_TORSO, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART );
pm->ps->torsoAnimTimer += 300;
}
}
return;
}
else if ( pm->waterlevel > 1 ) //off ground and in deep water
{
// PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM1,SETANIM_FLAG_NORMAL);
PM_SetAnim(pm,SETANIM_LEGS,BOTH_FLOAT2,SETANIM_FLAG_NORMAL);
return;
}
}
else
{
if ( pm->ps->pm_flags & PMF_DUCKED )
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL);
}
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 )
{
if(!pm->gent || !PM_InOnGroundAnim(pm->gent))
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL);
}
}
else
{
if ( pm->ps->weapon == WP_IMPERIAL_BLADE || pm->ps->weapon == WP_KLINGON_BLADE )
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND3,SETANIM_FLAG_NORMAL);
}
else if( careful || (validNPC && pm->ps->weapon > WP_PHASER && pm->ps->weapon < WP_TRICORDER && pm->gent->client->race != RACE_BORG))//Being careful or carrying a 2-handed weapon
{//Squadmates use BOTH_STAND2
oldAnim = pm->ps->legsAnim&~ANIM_TOGGLEBIT;
if(oldAnim != BOTH_GUARD_LOOKAROUND1 && oldAnim != BOTH_GUARD_IDLE1 &&
oldAnim != BOTH_STAND2_RANDOM1 && oldAnim != BOTH_STAND2_RANDOM2 && oldAnim != BOTH_STAND2_RANDOM3 && oldAnim != BOTH_STAND2_RANDOM4 &&
oldAnim != BOTH_STAND2TO4 && oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4 )
{//Don't auto-override the guard idles
PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
//if(oldAnim != BOTH_STAND2 && (pm->ps->legsAnim&~ANIM_TOGGLEBIT) == BOTH_STAND2)
//{
// pm->ps->legsAnimTimer = 500;
//}
}
}
/*
else if(pm->gent && pm->gent->client->race == RACE_BORG && pm->gent->enemy)
{//Angry borg
PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
}
*/
else
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
}
}
}
else
{//not trying to move, but I am, must be sliding? Default to standing?
PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
}
return;
}
if ( pm->ps->pm_flags & PMF_DUCKED )
{
bobmove = 0.5; // ducked characters bob much faster
if(!pm->gent || !PM_InOnGroundAnim(pm->gent))
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,SETANIM_FLAG_NORMAL);
}
// ducked characters never play footsteps
}
else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
{//Moving backwards
if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
{//running backwards
bobmove = 0.4F; // faster speeds bob faster
if( careful )
{//Squadmates use LEGS_RUNBACK2
oldAnim = pm->ps->legsAnim&~ANIM_TOGGLEBIT;
PM_SetAnim(pm,SETANIM_LEGS,LEGS_RUNBACK2,SETANIM_FLAG_NORMAL);
//Now figure out an FPS bias
if(pm->gent->client->ps.speed)
{
pm->gent->client->renderInfo.legsFpsMod = (float)pm->gent->NPC->stats.runSpeed/(float)pm->gent->client->ps.speed;
}
if(oldAnim != LEGS_RUNBACK2 && (pm->ps->legsAnim&~ANIM_TOGGLEBIT) == LEGS_RUNBACK2)
{
pm->ps->legsAnimTimer = 500;
}
}
else
{
PM_SetAnim(pm,SETANIM_LEGS,LEGS_RUNBACK1,SETANIM_FLAG_NORMAL);
}
footstep = qtrue;
}
else
{//walking backwards
bobmove = 0.3F; // faster speeds bob faster
if( careful )
{//Squadmates use BOTH_WALK2
PM_SetAnim(pm,SETANIM_LEGS,LEGS_WALKBACK2,SETANIM_FLAG_NORMAL);
}
else
{
PM_SetAnim(pm,SETANIM_LEGS,LEGS_WALKBACK1,SETANIM_FLAG_NORMAL);
}
// footstep = qtrue;
}
}
else
{
if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
{
bobmove = 0.4F; // faster speeds bob faster
if( careful )
{//Squadmates use BOTH_RUN2
oldAnim = pm->ps->legsAnim&~ANIM_TOGGLEBIT;
PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,SETANIM_FLAG_NORMAL); //FIXME: Until we get a better running animation
//Now figure out an FPS bias
if(pm->gent->client->ps.speed)
{
pm->gent->client->renderInfo.legsFpsMod = (float)pm->gent->NPC->stats.runSpeed/(float)pm->gent->client->ps.speed;
}
if(oldAnim != BOTH_RUN2 && (pm->ps->legsAnim&~ANIM_TOGGLEBIT) == BOTH_RUN2)
{
pm->ps->legsAnimTimer = 500;
}
}
else
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,SETANIM_FLAG_NORMAL);
}
footstep = qtrue;
}
else
{
bobmove = 0.3F; // walking bobs slow
if( careful || (validNPC && pm->ps->weapon > WP_PHASER && pm->ps->weapon < WP_TRICORDER && pm->gent->client->race != RACE_BORG))//Being careful or carrying a 2-handed weapon
{//Squadmates use BOTH_WALK2
oldAnim = pm->ps->legsAnim&~ANIM_TOGGLEBIT;
PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,SETANIM_FLAG_NORMAL);
if(pm->gent->client->ps.speed)
{
pm->gent->client->renderInfo.legsFpsMod = (float)pm->gent->NPC->stats.walkSpeed/(float)pm->gent->client->ps.speed;
}
if(oldAnim != BOTH_WALK2 && (pm->ps->legsAnim&~ANIM_TOGGLEBIT) == BOTH_WALK2)
{
pm->ps->legsAnimTimer = 500;
}
}
/*
else if(pm->gent && pm->gent->client->race == RACE_BORG && pm->gent->enemy)
{//Angry borg
PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,SETANIM_FLAG_NORMAL);
}
*/
else
{
PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,SETANIM_FLAG_NORMAL);
}
//Enemy NPCs always make footsteps for the benefit of the player
if ( pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->playerTeam != TEAM_STARFLEET )
{
footstep = qtrue;
}
}
}
if(pm->gent != NULL)
{
if( pm->gent->client->renderInfo.legsFpsMod > 2 )
{
pm->gent->client->renderInfo.legsFpsMod = 2;
}
else if(pm->gent->client->renderInfo.legsFpsMod < 0.5)
{
pm->gent->client->renderInfo.legsFpsMod = 0.5;
}
}
//Stasis have no feet, hence, no footsteps
if ( pm->gent->client->playerTeam == TEAM_STASIS )
return;
DoFootSteps:
// 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->watertype & CONTENTS_LADDER )
{
if ( !pm->noFootsteps )
{
if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {// on ladder
PM_AddEvent( EV_FOOTSTEP_METAL );
} else {
PM_AddEvent( PM_FootstepForSurface() ); //still on ground
}
if ( pm->gent && pm->gent->s.number == 0 )
{
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR );
}
}
}
}
else if ( pm->waterlevel == 0 )
{
// on ground will only play sounds if running
if ( footstep && !pm->noFootsteps )
{
PM_AddEvent( PM_FootstepForSurface() );
if ( pm->gent && pm->gent->s.number == 0 )
{
vec3_t bottom;
VectorCopy( pm->ps->origin, bottom );
bottom[2] += pm->mins[2];
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR );
}
}
}
}
else if ( pm->waterlevel == 1 )
{
// splashing
PM_AddEvent( EV_FOOTSPLASH );
if ( pm->gent && pm->gent->s.number == 0 )
{
vec3_t bottom;
VectorCopy( pm->ps->origin, bottom );
bottom[2] += pm->mins[2];
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR );
}
}
}
else if ( pm->waterlevel == 2 )
{
// wading / swimming at surface
PM_AddEvent( EV_SWIM );
if ( pm->gent && pm->gent->s.number == 0 )
{
if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE )
{
AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR );
}
}
} // no sound when completely underwater
}
}
/*
==============
PM_WaterEvents
Generate sound events for entering and leaving water
==============
*/
static void PM_WaterEvents( void ) { // FIXME?
if ( pm->watertype & CONTENTS_LADDER ) //fake water for ladder
{
return;
}
//
// 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 );
}
}
qboolean G_WeaponChangeEffect ( gentity_t *ent )
{
if ( !ent || !ent->client )
{
return qfalse;
}
int torsoAnim = PM_TorsoAnimForFrame( ent, ent->client->renderInfo.torsoFrame );
if ( torsoAnim == TORSO_DROPWEAP1 || torsoAnim == TORSO_DROPWEAP2 || torsoAnim == TORSO_DROPWEAP3 ||
torsoAnim == TORSO_RAISEWEAP1 || torsoAnim == TORSO_RAISEWEAP2 || torsoAnim == TORSO_RAISEWEAP3 )
{//are getting the weapon from our transporter buffer
if ( ent->s.number == 0 || strstr( ent->client->renderInfo.torsoModelName , "hazard" ) != NULL ||
Q_stricmp( "alexascav", ent->NPC_type ) == 0 ||
Q_stricmp( "munroscav", ent->NPC_type ) == 0 )
{//UGH... hacky way of knowing if this is a hazard guy
//Do weapon select transporter effect
ent->fx_time = level.time;
ent->s.powerups |= ( 1 << PW_DISINT_6 );
ent->client->ps.powerups[PW_DISINT_6] = level.time + 600;
return qtrue;
}
}
return qfalse;
}
/*
===============
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 ( cg.time > 0 )
{//this way we don't get that annoying change weapon sound every time a map starts
PM_AddEvent( EV_CHANGE_WEAPON );
}
pm->ps->weaponstate = WEAPON_DROPPING;
pm->ps->weaponTime += 200;
PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1,SETANIM_FLAG_NORMAL);
if ( pm->gent && pm->gent->client )
{//predicting weapon switch effect
pm->gent->fx_time = level.time;
pm->gent->s.powerups |= ( 1 << PW_DISINT_6 );
pm->gent->client->ps.powerups[PW_DISINT_6] = level.time + PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, TORSO_DROPWEAP1) + PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, TORSO_RAISEWEAP1);
}
}
/*
===============
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;
pm->ps->weaponTime += 250;
PM_SetAnim(pm,SETANIM_TORSO,TORSO_RAISEWEAP1,SETANIM_FLAG_NORMAL);
}
/*
==============
PM_Use
Generates a use event
==============
*/
#define USE_DELAY 2000
void PM_Use( void )
{
if ( pm->ps->useTime > 0 )
pm->ps->useTime -= 100;//pm->cmd.msec;
if ( pm->ps->useTime > 0 ) {
return;
}
if ( ! (pm->cmd.buttons & BUTTON_USE ) )
{
pm->useEvent = 0;
pm->ps->useTime = 0;
return;
}
pm->useEvent = EV_USE;
pm->ps->useTime = USE_DELAY;
}
/*
==============
PM_Weapon
Generates weapon events and modifes the weapon counter
==============
*/
static void PM_Weapon( void )
{
int addTime,amount;
qboolean delayed_fire = qfalse;
weaponInfo_t *weapon;
if(pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0)
{//FIXME: this is going to fire off one frame before you expect, actually
pm->gent->client->fireDelay -= pml.msec;
if(pm->gent->client->fireDelay <= 0)
{//just finished delay timer
pm->gent->client->fireDelay = 0;
delayed_fire = qtrue;
}
}
// don't allow attack until all buttons are up
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return;
}
//Check for phaser ammo recharge
if ( pm->ps->rechargeTime <= 0 ) {
if ( pm->ps->ammo[weaponData[WP_PHASER].ammoIndex] < ammoData[AMMO_PHASER].max) {
pm->ps->rechargeTime = PHASER_RECHARGE_TIME;
pm->ps->ammo[weaponData[WP_PHASER].ammoIndex]++;
}
} else {
pm->ps->rechargeTime -= pml.msec;
}
// check for dead player
if ( pm->ps->stats[STAT_HEALTH] <= 0 )
{
if ( pm->gent && pm->gent->client )
{
// Sigh..borg shouldn't drop their weapon attachments when they die.
if ( pm->gent->client->playerTeam != TEAM_BORG )
{
pm->ps->weapon = WP_NONE;
}
}
if ( pm->gent )
{
pm->gent->s.loopSound = 0;
}
return;
}
// make weapon function
if ( pm->ps->weaponTime > 0 ) {
pm->ps->weaponTime -= pml.msec;
}
// check for weapon change
// can't change if weapon is firing, but can change again if lowering or raising
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();
return;
}
if ( pm->ps->weaponstate == WEAPON_RAISING )
{//Just selected the weapon
pm->ps->weaponstate = WEAPON_IDLE;
if(pm->gent && pm->gent->s.number == 0)
{
PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
}
else
{
switch(pm->ps->weapon)
{
case WP_PHASER:
PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL);
break;
case WP_COMPRESSION_RIFLE:
PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL);
break;
case WP_BORG_WEAPON:
case WP_BORG_TASER:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
break;
case WP_TRICORDER:
case WP_RED_HYPO:
case WP_BLUE_HYPO:
case WP_VOYAGER_HYPO:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL);
break;
default:
PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL);
break;
}
}
return;
}
weapon = &cg_weapons[pm->ps->weapon];
if(!delayed_fire)
{
// check for fire
if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
{
pm->ps->weaponTime = 0;
if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
{//Still firing
pm->ps->weaponstate = WEAPON_FIRING;
}
else if ( pm->ps->weaponstate != WEAPON_READY )
{
pm->ps->weaponstate = WEAPON_IDLE;
}
return;
}
// start the animation even if out of ammo
switch(pm->ps->weapon)
{
case WP_PHASER://1 - handed
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
case WP_COMPRESSION_RIFLE://2-handed
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
case WP_BORG_WEAPON:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_BORG_TASER:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_BORG_ASSIMILATOR:
case WP_BORG_DRILL:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_FORGE_PSYCH:
PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_FORGE_PROJ:
if ( rand() & 1 )
PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
else
PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_MELEE:
// The slash trail "formation" hasn't been started yet, so don't try and use the old points.
pm->gent->trigger_formation = qfalse;
if ( pm->gent->client->race == RACE_REAVER || pm->gent->client->race == RACE_HARVESTER || pm->gent->client->race == RACE_STASIS || pm->gent->client->race == RACE_BOT)
{
if ( rand() & 1 )
PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
else
PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
}
else if ( pm->gent->client->race == RACE_AVATAR )
{
PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
}
else
{
PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
}
break;
case WP_STASIS_ATTACK:
PM_SetAnim( pm, SETANIM_BOTH,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
break;
case WP_TETRION_DISRUPTOR:
PM_SetAnim( pm, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_SCAVENGER_RIFLE:
case WP_CHAOTICA_GUARD_GUN:
if ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) != BOTH_HEROSTANCE1 )
{//HACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACK
PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
}
break;
case WP_PARASITE:
PM_SetAnim( pm, SETANIM_BOTH, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_KLINGON_BLADE:
case WP_IMPERIAL_BLADE:
{
int anim = -1;
int parts = SETANIM_TORSO;
anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE3 );
if ( anim == -1 )
{
anim = BOTH_ATTACK1;
}
if ( !pm->cmd.forwardmove && !pm->cmd.rightmove && pm->cmd.upmove >= 0 )
{
parts = SETANIM_BOTH;
}
PM_SetAnim(pm,parts,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
}
break;
case WP_DESPERADO:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
case WP_PALADIN:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
case WP_BLUE_HYPO:
case WP_RED_HYPO:
case WP_VOYAGER_HYPO:
PM_SetAnim(pm,SETANIM_TORSO,TORSO_HYPOSPRAY1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
case WP_TRICORDER:
PM_SetAnim(pm,SETANIM_TORSO,TORSO_MEDICORDER1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
break;
case WP_BOT_WELDER:
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
default://2-handed heavy weapon
PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD);
break;
}
}
pm->ps->weaponstate = WEAPON_FIRING;
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
amount = weaponData[pm->ps->weapon].altEnergyPerShot;
else
amount = weaponData[pm->ps->weapon].energyPerShot;
// take an ammo away if not infinite
if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
{
// enough energy to fire this weapon?
if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
{
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
}
else // Not enough energy
{
if ( pm->ps->weapon == WP_PHASER )
{ // force our ammo to be zero
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] = 0;
}
else
{ // Switch weapons
PM_AddEvent( EV_NOAMMO );
pm->ps->weaponTime += 500;
return;
}
}
}
if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 )
{//FIXME: this is going to fire off one frame before you expect, actually
// Clear these out since we're not actually firing yet
pm->ps->eFlags &= ~EF_FIRING;
pm->ps->eFlags &= ~EF_ALT_FIRING;
return;
}
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) {
PM_AddEvent( EV_ALT_FIRE );
addTime = weaponData[pm->ps->weapon].altFireTime;
}
else {
PM_AddEvent( EV_FIRE_WEAPON );
addTime = weaponData[pm->ps->weapon].fireTime;
}
//If the phaser has been fired, delay the next recharge time
if ( pm->ps->weapon == WP_PHASER ) {
pm->ps->rechargeTime = PHASER_RECHARGE_TIME;
}
if(pm->gent && pm->gent->NPC != NULL )
{//NPCs have their own refire logic
return;
}
if( MatrixMode )
{//Special test for Matrix Mode (tm)
//player always fires at normal speed
addTime *= g_timescale->value;
}
pm->ps->weaponTime += addTime;
}
/*
================
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 legs animation counter
if ( pm->ps->legsAnimTimer > 0 )
{
int newTime = pm->ps->legsAnimTimer - pml.msec;
if ( newTime < 0 )
{
newTime = 0;
}
PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, newTime );
}
// drop torso animation counter
if ( pm->ps->torsoAnimTimer > 0 )
{
int newTime = pm->ps->torsoAnimTimer - pml.msec;
if ( newTime < 0 )
{
newTime = 0;
}
PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, newTime );
}
}
void PM_SetSpecialMoveValues (void )
{
Flying = qfalse;
if ( pm->gent )
{
if ( pm->gent->NPC )
{
if ( pm->gent->NPC->stats.moveType == MT_FLYSWIM )
{
Flying = qtrue;
}
}
}
MatrixMode = qfalse;
if ( g_timescale != NULL )
{
if ( g_timescale->value < 1.0f )
{
if ( pm->ps->clientNum == 0 )
{
MatrixMode = qtrue;
}
}
}
if( MatrixMode )
{
float upScale = 1.0f/g_timescale->value;
//player always moves at full speed of 20fps
pml.msec *= upScale;
if ( pm->cmd.forwardmove * upScale > 127 )
{
pm->cmd.forwardmove = 127;
}
else if ( pm->cmd.forwardmove * upScale < -127 )
{
pm->cmd.forwardmove = -127;
}
else
{
pm->cmd.forwardmove = (int)(pm->cmd.forwardmove * upScale);
}
if ( pm->cmd.rightmove * upScale > 127 )
{
pm->cmd.rightmove = 127;
}
else if ( pm->cmd.rightmove * upScale < -127 )
{
pm->cmd.rightmove = -127;
}
else
{
pm->cmd.rightmove = (int)(pm->cmd.rightmove * upScale);
}
}
}
/*
================
Pmove
Can be called by either the server or the client
================
*/
void Pmove( pmove_t *pmove )
{
pm = pmove;
int amount;
// 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;
// Clear the blocked flag
pm->ps->pm_flags &= ~PMF_BLOCKED;
pm->ps->pm_flags &= ~PMF_BUMPED;
// set the talk balloon flag
if ( pm->cmd.buttons & BUTTON_TALK )
pm->ps->eFlags |= EF_TALK;
else
pm->ps->eFlags &= ~EF_TALK;
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot;
else
amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot;
// set the firing flag for continuous beam weapons, phaser will fire even if out of ammo
if ( (( pm->cmd.buttons & BUTTON_ATTACK || pm->cmd.buttons & BUTTON_ALT_ATTACK ) && ( amount >= 0 || pm->ps->weapon == WP_PHASER )) )
{
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
{
pm->ps->eFlags |= EF_ALT_FIRING;
}
else
{
pm->ps->eFlags &= ~EF_ALT_FIRING;
}
// This flag should always get set, even when alt-firing
pm->ps->eFlags |= EF_FIRING;
}
else
{
int iFlags = pm->ps->eFlags;
// Clear 'em out
pm->ps->eFlags &= ~EF_FIRING;
pm->ps->eFlags &= ~EF_ALT_FIRING;
// if I don't check the flags before stopping FX then it switches them off too often, which tones down
// the stronger FFFX so you can hardly feel them. However, if you only do iton these flags then the
// repeat-fire weapons like tetrion and dreadnought don't switch off quick enough. So...
//
if (pm->ps->weapon == WP_TETRION_DISRUPTOR ||
pm->ps->weapon == WP_DREADNOUGHT ||
(iFlags & (EF_FIRING|EF_ALT_FIRING))
)
{
cgi_FF_StopAllFX();
}
}
// 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 ) {
pmove->cmd.buttons = 0;
pmove->cmd.forwardmove = 0;
pmove->cmd.rightmove = 0;
pmove->cmd.upmove = 0;
}
// clear all pmove local vars
memset (&pml, 0, sizeof(pml));
// determine the time
pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
if ( pml.msec < 1 ) {
pml.msec = 1;
} else if ( pml.msec > 200 ) {
pml.msec = 200;
}
PM_SetSpecialMoveValues();
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, pm->gent);
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->viewheight > -12 )
{//slowly sink view to ground
pm->ps->viewheight -= 1;
}
}
if ( pm->ps->pm_type == PM_SPECTATOR ) {
PM_CheckDuck ();
PM_FlyMove ();
PM_DropTimers ();
return;
}
if ( pm->ps->pm_type == PM_NOCLIP ) {
PM_NoclipMove ();
PM_DropTimers ();
return;
}
if (pm->ps->pm_type == PM_FREEZE) {
return; // no movement at all
}
if ( pm->ps->pm_type == PM_INTERMISSION ) {
return; // no movement at all
}
// set watertype, and waterlevel
PM_SetWaterLevel();
if ( !(pm->watertype & CONTENTS_LADDER) )
{//Don't want to remember this for ladders, is only for waterlevel change events (sounds)
pml.previous_waterlevel = pmove->waterlevel;
}
// set mins, maxs, and viewheight
PM_SetBounds();
if ( !Flying && !(pm->watertype & CONTENTS_LADDER) && pm->ps->pm_type != PM_DEAD )
{//NOTE: noclippers shouldn't jump or duck either, no?
PM_CheckDuck();
}
// set groundentity
PM_GroundTrace();
if ( pm->ps->pm_type == PM_DEAD ) {
PM_DeadMove ();
}
PM_DropTimers();
if ( Flying )
{
// flight powerup doesn't allow jump and has different friction
PM_FlyMove();
}
else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP )
{
PM_WaterJumpMove();
}
else if ( pm->waterlevel > 1 )
{
// swimming or in ladder
PM_WaterMove();
}
else if ( pml.walking )
{// walking on ground
vec3_t oldOrg;
VectorCopy( pm->ps->origin, oldOrg );
PM_WalkMove();
if ( VectorCompare( oldOrg, pm->ps->origin ) )
{//didn't move, play no legs anim
pm->cmd.forwardmove = pm->cmd.rightmove = 0;
}
}
else
{
// airborne
PM_AirMove();
}
//PM_Animate();
// set groundentity, watertype, and waterlevel
PM_GroundTrace();
PM_SetWaterLevel();
// weapons
PM_Weapon();
if(pm->gent && pm->gent->s.number == 0)
{//player only
// Use
PM_Use();
}
// footstep events / legs animations
PM_Footsteps();
// torso animation
PM_TorsoAnimation();
// entering / leaving water splashes
PM_WaterEvents();
// snap some parts of playerstate to save network bandwidth
SnapVector( pm->ps->velocity );
}