rpgxef/code/game/bg_pmove.c

3385 lines
76 KiB
C
Raw Normal View History

// Copyright (C) 1999-2000 Id Software, Inc.
//
// bg_pmove.c -- both games player movement code
// takes a playerstate and a usercmd as input and returns a modifed playerstate
#include "q_shared.h"
#include "bg_public.h"
#include "bg_local.h"
pmove_t *pm;
pml_t pml;
// movement parameters
const float pm_stopspeed = 100;
const float pm_duckScale = 0.50;
const float pm_swimScale = 0.50;
const float pm_ladderScale = 0.7f;
const float pm_accelerate = 10;
const float pm_airaccelerate = 1;
const float pm_wateraccelerate = 4;
const float pm_flyaccelerate = 8;
const float pm_friction = 6;
const float pm_waterfriction = 1;
const float pm_flightfriction = 3;
const float pm_evosuitfriction = 0.25; //RPG-X | Phenix | 8/8/2004
int c_pmove = 0;
#define PHASER_RECHARGE_TIME 100
//RPG-X | Marcin | Big hack but it appears to work | 06/12/2008
#ifdef QAGAME
extern vmCvar_t rpg_rifleDelay;
extern vmCvar_t rpg_disruptorDelay;
extern vmCvar_t rpg_photonDelay;
extern vmCvar_t rpg_altPhotonDelay;
extern vmCvar_t rpg_TR116Delay;
extern vmCvar_t rpg_altTricorderDelay;
#define RIFLE_DELAY rpg_rifleDelay.integer
#define DISRUPTOR_DELAY rpg_disruptorDelay.integer
#define PHOTON_DELAY rpg_photonDelay.integer
#define ALT_PHOTON_DELAY rpg_altPhotonDelay.integer
#define TR116_DELAY rpg_TR116Delay.integer
2013-05-16 21:54:40 +00:00
#define ALT_TRICORDER_DELAY rpg_altTricorderDelay.integer
#else
#define RIFLE_DELAY 250
#define DISRUPTOR_DELAY 700
#define PHOTON_DELAY 1600
#define ALT_PHOTON_DELAY 1600
#define TR116_DELAY 500
#define ALT_TRICORDER_DELAY 1000
#endif
//TiM - Copied from the UI module.
//My aim here is to adapt the checks code here so the function can be used both for
//ingame animation, as well as UI animation
// RPG-X UI Required Ones
#define ANIM_IDLE 0
#define ANIM_RUN 1
#define ANIM_WALK 2
#define ANIM_BACK 3
#define ANIM_JUMP 4
#define ANIM_CROUCH 5
#define ANIM_ATTACK 22
//Ingame required ones
#define ANIM_JUMPB 6
#define ANIM_RUNB 7
#define ANIM_WALKB 8
#define ANIM_CROUCHWALK 9
#define ANIM_CROUCHWALKB 10
#define ANIM_SWIM 11
#define ANIM_FLY 12
#define ANIM_TURN 13
/**
* Checks if the player holding a two handed weapon.
*/
qboolean PM_Holding2HandedWeapon ( void ) {
switch (pm->ps->weapon) {
case WP_7:
case WP_8:
case WP_9:
case WP_6:
return qtrue;
}
return qfalse;
}
/**
* Checks if the player is crouching.
*/
qboolean PM_PlayerCrouching ( int legsAnim ) {
//switch( pm->ps->legsAnim ) {
switch( ( legsAnim & ~ANIM_TOGGLEBIT) ) {
case BOTH_CROUCH1IDLE:
case BOTH_CROUCH1WALK:
case BOTH_CROUCH2IDLE:
return qtrue;
}
return qfalse;
}
/**
* Check if player is idling.
*/
qboolean PM_PlayerIdling ( int torsoAnim, int legsAnim ) {
//TiM : Cool hacky way to make sure both upper and lower anims are the same
switch( ( legsAnim & ~ANIM_TOGGLEBIT) ) //+ ( torsoAnim & ~ANIM_TOGGLEBIT)) >> 1
{
case BOTH_STAND1:
case BOTH_STAND2:
case BOTH_STAND3:
case BOTH_STAND4:
case BOTH_CROWDLOOK3:
{
switch ( ( torsoAnim & ~ANIM_TOGGLEBIT) )
{
case BOTH_STAND1:
case BOTH_STAND2:
case BOTH_STAND3:
case BOTH_STAND4:
case BOTH_CROWDLOOK3:
case TORSO_TRICORDER1:
case TORSO_HYPOSPRAY1:
case TORSO_HYPO1:
case TORSO_DROPWEAP1:
case TORSO_RAISEWEAP1:
case TORSO_PADD1:
case TORSO_COFFEE:
return qtrue;
}
}
}
return qfalse;
}
/**
* \brief Checks if player is holding a loppable weapon.
*
* A weapon that can have its
* firing animation looped,
* like the PADD or tricorder, etc
*/
qboolean PM_HoldingLoopableWeapon ( void ) {
switch (pm->ps->weapon) {
case WP_13:
case WP_2:
case WP_3:
case WP_11:
case WP_4:
return qtrue;
}
return qfalse;
}
/**
* \brief Checks if player is holding a spillable weapon.
*
* Player is holding a weapon that
* shouldn't let players do the
* 'slowing down' anim
*/
qboolean PM_HoldingSpillableWeapon( void ) {
switch ( pm->ps->weapon ) {
case WP_4:
case WP_6:
case WP_9:
case WP_8:
case WP_7:
return qtrue;
}
return qfalse;
}
/**
* Check to see if the player is moving at all
*/
qboolean PM_PlayerWalking( int anim )
{
switch( anim & ~ANIM_TOGGLEBIT )
{
case BOTH_WALK1:
case BOTH_WALK2:
case BOTH_WALK3:
case BOTH_WALK4:
case BOTH_WALK7:
case LEGS_WALKBACK1:
return qtrue;
}
return qfalse;
}
/**
* Check to see if the player is running
*/
qboolean PM_PlayerRunning( int anim )
{
switch( anim & ~ANIM_TOGGLEBIT )
{
case BOTH_RUN1:
case BOTH_RUN2:
case LEGS_RUNBACK2:
return qtrue;
}
return qfalse;
}
/**
* Check to see if the player is moving while crouching
*/
qboolean PM_PlayerCrouchWalking( int anim )
{
switch( anim & ~ANIM_TOGGLEBIT )
{
case BOTH_CROUCH1WALK:
return qtrue;
}
return qfalse;
}
/**
* TiM: An index is defined, and depending
* on which weapon is active, a specific
* animation is returned.
* I could have used pm->ps->weapon instead
* of manually defining it as an paramter,
* but I'm going to use this in the UI module,
* which is out of pm's scope.
*/
int PM_GetAnim ( int anim, int weapon, qboolean injured, qboolean upper )
{
playerState_t *ps = pm->ps;
// Called when player is in idle crouching
switch ( anim ) {
case ANIM_CROUCH:
//2 handed weapon - "heavy"
switch (weapon) {
case WP_7:
case WP_8:
case WP_9:
if ( ps->pm_flags & ANIM_ALERT2 && upper )
return TORSO_WEAPONREADY2;
else if (upper)
return BOTH_STAND2;
else
return LEGS_KNEEL1;
break;
//2 handed weapon - "light"
case WP_6:
if ( ps->pm_flags & ANIM_ALERT && upper )
return BOTH_STAND2;
else if (upper)
return TORSO_WEAPONREADY2;
else
return LEGS_KNEEL1;
break;
//1 handed weapon - "phaser"
case WP_5:
case WP_10:
if ( upper )
return TORSO_WEAPONPOSE1;
else
return BOTH_CROUCH1IDLE;
break;
case WP_4:
if (upper)
return TORSO_COFFEE;
//break;
//Generic tools - "everything else"
default:
return BOTH_CROUCH2IDLE;
break;
}
break;
//Called when player is in idle standing
case ANIM_IDLE:
//2 handed weapon - "heavy"
switch (weapon) {
//case WP_7:
case WP_8:
case WP_9:
case WP_7:
if (injured)
return BOTH_INJURED4;
else
{
if ( ps->pm_flags & ANIM_ALERT )
return BOTH_STAND2;
else if ( ps->pm_flags & ANIM_ALERT2 )
{
if ( upper )
return TORSO_WEAPONREADY2;
else
return BOTH_STAND2;
}
else
return BOTH_STAND4;
}
break;
//2 handed weapon - "light"
case WP_6:
if (injured)
return BOTH_INJURED4;
else
{
if ( ps->pm_flags & ANIM_ALERT )
return BOTH_STAND2;
else if ( ps->pm_flags & ANIM_ALERT2 )
{
if ( upper )
return TORSO_WEAPONREADY2;
else
return BOTH_STAND2;
}
else
return BOTH_STAND4;
}
break;
//1 handed weapon - "phaser"
case WP_5:
case WP_10:
if (injured)
return BOTH_INJURED4;
else {
if ( ps->pm_flags & ANIM_ALERT && upper )
return TORSO_WEAPONIDLE1;
else if ( ps->pm_flags & ANIM_ALERT2 && upper )
return TORSO_WEAPONREADY1;
else
return BOTH_STAND1;
}
break;
//Generic tools - "everything else"
case WP_4:
if (upper)
return TORSO_COFFEE;
else
return BOTH_STAND1;
break;
default:
if (injured)
return BOTH_INJURED4;
else
return BOTH_STAND1;
break;
}
break;
//Called when player fires their weapon
case ANIM_ATTACK:
//2 handed weapon - "heavy"
switch (weapon) {
//case WP_7:
case WP_8:
case WP_9:
case WP_7:
if ( ps->pm_flags & ANIM_ALERT2 )
return BOTH_ATTACK2;
else
return BOTH_ATTACK3;
break;
//2 handed weapon - "light"
case WP_6:
if ( ps->pm_flags & ANIM_ALERT2 )
return BOTH_ATTACK2;
else
{
if (upper)
return BOTH_ATTACK3;
else
return BOTH_ATTACK3;
}
break;
//1 handed weapon - "phaser"
case WP_5:
case WP_10:
case WP_15:
case WP_13:
if (upper)
return TORSO_WEAPONREADY1;
else
return BOTH_STAND1;
break;
//Other Tools "padd"
case WP_3:
if (upper)
return TORSO_PADD1;
else
return BOTH_STAND1;
break;
//Other Tools "tricorder"
case WP_2:
if (upper)
{
if ( !pm->medic )
return TORSO_TRICORDER1;
else
return TORSO_MEDICORDER1;
}
else
return BOTH_STAND1;
break;
//Other: "Medkit"
case WP_11:
if (upper)
return TORSO_ACTIVATEMEDKIT1;
else
return BOTH_STAND1;
break;
//Other: "Hypo
case WP_12:
if (upper)
return TORSO_HYPO1;
else
return BOTH_STAND1;
case WP_4:
if (upper)
return TORSO_COFFEE;
//break;
default:
if (upper)
return TORSO_WEAPONREADY1;
else
return BOTH_STAND1;
break;
}
break;
//When the player jumps
case ANIM_JUMP:
return BOTH_JUMP1;
//Wen the player jumps backwards
case ANIM_JUMPB:
return BOTH_JUMPBACK1;
//When the player runs
case ANIM_RUN:
if (injured) {
return BOTH_RUNINJURED1;
}
//2 handed weapons
switch (weapon) {
case WP_8:
case WP_9:
case WP_6:
case WP_7:
if (upper)
return BOTH_RUN2;
else
return BOTH_RUN1;
break;
case WP_4:
if (upper)
return TORSO_COFFEE;
//break;
//EVERYTHING ELSE
default:
return BOTH_RUN1;
}
break;
//When the player runs back
case ANIM_RUNB:
//2 handed weapons
switch (weapon) {
//case WP_7:
case WP_8:
case WP_9:
case WP_6:
case WP_7:
if (upper)
return BOTH_WALK2;
else
if ( injured )
return LEGS_WALKBACK1;
else
return LEGS_RUNBACK2;
break;
//EVERYTHING ELSE
case WP_4:
if (upper)
return TORSO_COFFEE;
default:
if (upper)
return BOTH_WALK1;
else
if ( injured )
return LEGS_WALKBACK1;
else
return LEGS_RUNBACK2;
break;
}
break;
//When the player walks
case ANIM_WALK:
if ( ps->legsTimer > 0 && bg_emoteList[ps->legsTimer].enumName == BOTH_STAND3 )
return BOTH_WALK3;
//2 handed weapons
switch (weapon) {
case WP_8:
case WP_9:
case WP_6:
case WP_7:
if ( ps->pm_flags & ANIM_ALERT )
return BOTH_WALK2;
else if ( ps->pm_flags & ANIM_ALERT2 )
{
if ( upper )
return TORSO_WEAPONREADY2;
else
return BOTH_WALK2;
}
else
return BOTH_WALK4;
break;
//Other Tools "everything else"
case WP_4:
if (upper)
return TORSO_COFFEE;
case WP_5:
case WP_10:
if ( ps->pm_flags & ANIM_ALERT )
{
if ( upper )
return TORSO_WEAPONIDLE1;
}
else if ( ps->pm_flags & ANIM_ALERT2 )
{
if ( upper )
return TORSO_WEAPONREADY1;
}
default:
return BOTH_WALK1;
break;
}
break;
//When the player walks baaaack
case ANIM_WALKB:
//2 handed weapons
switch (weapon) {
case WP_8:
case WP_9:
case WP_6:
case WP_7:
if ( ps->pm_flags & ANIM_ALERT )
{
if ( upper )
return BOTH_WALK2;
else
return LEGS_WALKBACK1;
}
else if ( ps->pm_flags & ANIM_ALERT2 )
{
if ( upper )
return TORSO_WEAPONREADY2;
else
return LEGS_WALKBACK1;
}
else
{
if ( upper )
return BOTH_WALK4;
else
return LEGS_WALKBACK1;
}
break;
case WP_4:
if (upper)
return TORSO_COFFEE;
//break;
case WP_5:
case WP_10:
if ( ps->pm_flags & ANIM_ALERT && upper)
return TORSO_WEAPONIDLE1;
else if ( ps->pm_flags & ANIM_ALERT2 && upper )
return TORSO_WEAPONREADY1;
//Other Tools "everything else"
default:
if ( upper )
return BOTH_WALK1;
else
return LEGS_WALKBACK1;
break;
}
break;
//When the player crouch walks
case ANIM_CROUCHWALK:
//2 handed weapons
switch (weapon) {
//case WP_7:
case WP_6:
case WP_7:
if ( upper )
return TORSO_WEAPONREADY2;
else
return BOTH_CROUCH1WALK;
break;
case WP_8:
case WP_9:
if ( ps->pm_flags & ANIM_ALERT2 && upper )
return TORSO_WEAPONREADY2;
else if ( upper )
return BOTH_WALK2;
else
return BOTH_CROUCH1WALK;
break;
case WP_4:
if (upper)
return TORSO_COFFEE;
//break;
case WP_5:
case WP_10:
if ( ps->pm_flags & ANIM_ALERT && upper )
return TORSO_WEAPONIDLE1;
else if ( ps->pm_flags & ANIM_ALERT2 && upper )
return TORSO_WEAPONREADY1;
//Other Tools "everything else"
default:
return BOTH_CROUCH1WALK;
break;
}
break;
//When the player crouch walks bak
case ANIM_CROUCHWALKB:
//2 handed weapons
switch (weapon) {
//case WP_7:
case WP_8:
case WP_9:
case WP_6:
case WP_7:
if ( ps->pm_flags & ANIM_ALERT2 )
return TORSO_WEAPONREADY2;
else if ( upper )
return BOTH_WALK2;
else
return BOTH_CROUCH1WALK;
break;
case WP_4:
if (upper)
return TORSO_COFFEE;
case WP_5:
case WP_10:
if ( ps->pm_flags & ANIM_ALERT && upper )
return TORSO_WEAPONIDLE1;
else if ( ps->pm_flags & ANIM_ALERT2 && upper )
return TORSO_WEAPONREADY1;
//Other Tools "everything else"
default:
return BOTH_CROUCH1WALK;
break;
}
break;
case ANIM_SWIM:
if ( !upper ) {
if ( pm->cmd.forwardmove
|| pm->cmd.rightmove
|| pm->cmd.upmove )
{
return LEGS_SWIM;
}
}
return BOTH_FLOAT1;
case ANIM_FLY:
return BOTH_FLOAT1;
}
return BOTH_STAND1;
}
/**
* Adds a predictable event to playerstate
*/
void PM_AddEvent( int newEvent ) {
BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
}
/*
==============
PM_Use
Generates a use event
==============
*/
#define USE_DELAY 2000
/**
* Generates a use event
*/
void PM_Use( void )
{
playerState_t *ps = pm->ps;
if ( ps->useTime > 0 )
ps->useTime -= 100;
if ( ps->useTime > 0 ) {
return;
}
if ( ! (pm->cmd.buttons & BUTTON_USE ) )
{
pm->useEvent = 0;
ps->useTime = 0;
return;
}
pm->useEvent = EV_USE;
ps->useTime = USE_DELAY;
}
/*
===============
PM_AddTouchEnt
===============
*/
/**
* Adds a touchEnt event.
*/
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++;
}
/**
* Start torso animation
*/
static void PM_StartTorsoAnim( int anim, qboolean overrideEmotes ) {
playerState_t *ps = pm->ps;
if ( ps->stats[EMOTES] & EMOTE_UPPER && !overrideEmotes ) {
return;
}
if ( ps->pm_type >= PM_DEAD &&
anim != BOTH_FALLDEATH1INAIR &&
anim != BOTH_FALLDEATH1LAND )
{ //TiM: UberHack :P
return;
}
if ( ps->stats[TORSOTIMER] > 0 ) {
return; // a high priority animation is running
}
ps->stats[TORSOANIM] = ( ( ps->stats[TORSOANIM] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
| anim;
}
/**
* Start leg animation
*/
static void PM_StartLegsAnim( int anim ) {
playerState_t *ps = pm->ps;
if ( ps->pm_type >= PM_DEAD &&
anim != BOTH_FALLDEATH1INAIR &&
anim != BOTH_FALLDEATH1LAND )
{
return;
}
ps->stats[LEGSANIM] = ( ( ps->stats[LEGSANIM] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
| anim;
}
/**
* Continues the legs animation.
*/
static void PM_ContinueLegsAnim( int anim, qboolean overrideEmote ) {
playerState_t *ps = pm->ps;
//override to return to idle after moving in an emote
if ( (ps->stats[EMOTES] & EMOTE_LOWER ) && ( !( ps->stats[EMOTES] & EMOTE_CLAMP_BODY) && !( ps->stats[EMOTES] & EMOTE_CLAMP_ALL) ) && !overrideEmote ) {
if ( ps->legsTimer>0 && ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT ) != bg_emoteList[ ps->legsTimer ].enumName && ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT ) != BOTH_GET_UP1 ) {
int anim2 = PM_GetAnim( ANIM_IDLE, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse );
ps->stats[LEGSANIM] = ( ( ps->stats[LEGSANIM] & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim2;
}
}
if ( (ps->stats[EMOTES] & EMOTE_CLAMP_BODY || ps->stats[EMOTES] & EMOTE_CLAMP_ALL ) && !overrideEmote ) { //EMOTE_LOWER
return;
}
if ( ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT ) == anim ) {
return;
}
if ( ps->stats[LEGSTIMER] > 0 && !overrideEmote ) { //legsTimer
return; // a high priority animation is running
}
PM_StartLegsAnim( anim );
}
/**
* Continues the torso animation
*/
static void PM_ContinueTorsoAnim( int anim, qboolean overrideEmote ) {
playerState_t *ps = pm->ps;
if ( ps->stats[EMOTES] & EMOTE_UPPER && !overrideEmote ) {
return;
}
if ( ( ps->stats[TORSOANIM] & ~ANIM_TOGGLEBIT ) == anim ) {
return;
}
if ( ps->stats[TORSOTIMER] > 0 ) {
return; // a high priority animation is running
}
PM_StartTorsoAnim( anim, overrideEmote );
}
/**
* Force a legs animation
*/
static void PM_ForceLegsAnim( int anim ) {
playerState_t *ps = pm->ps;
//OMFG UBERHACK
//I'm lazy... client revive spawns players 1 unit over the ground.
//THat small fall enacts this, and subsequently screws up client revive animations
if ( ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT) != BOTH_GET_UP1 ) {
ps->stats[EMOTES] &= ~EMOTE_MASK_LOWER;
ps->stats[LEGSTIMER] = 0; //legsTimer
}
PM_StartLegsAnim( anim );
}
/**
* Force a torso animation
*/
static void PM_ForceTorsoAnim( int anim, qboolean overrideEmotes ) {
playerState_t *ps = pm->ps;
if ( overrideEmotes && ( ps->stats[TORSOANIM] & ~ANIM_TOGGLEBIT) != BOTH_GET_UP1 ) {
ps->stats[EMOTES] &= ~EMOTE_MASK_UPPER;
ps->stats[TORSOTIMER] = 0;
}
PM_StartTorsoAnim( anim, overrideEmotes );
}
/*
==================
PM_ClipVelocity
==================
*/
/**
* Slide off of the impacting surface
*/
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
float backoff;
float change;
int i;
backoff = DotProduct (in, normal);
if ( backoff < 0 ) {
backoff *= overbounce;
} else {
backoff /= overbounce;
}
for ( i=0 ; i<3 ; i++ ) {
change = normal[i]*backoff;
out[i] = in[i] - change;
}
}
/*
==================
PM_Friction
==================
*/
/**
* Handles both ground friction and water friction
*/
static void PM_Friction( void ) {
vec3_t vec;
float *vel;
float speed, newspeed, control;
float drop;
playerState_t *ps = pm->ps;
vel = ps->velocity;
VectorCopy( vel, vec );
if ( pml.walking ) {
vec[2] = 0; // ignore slope movement
}
speed = VectorLength(vec);
if (speed < 1) {
vel[0] = 0;
vel[1] = 0; // allow sinking underwater
// FIXME: still have z friction underwater?
return;
}
drop = 0;
// apply ground friction
if ( (pm->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 ( ! (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 && !(pm->watertype & CONTENTS_LADDER) ) {
drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
}
//RPG-X | Phenix | 8/8/2004
//Apply EVOSUIT friction (small)
if ( ps->powerups[PW_EVOSUIT] )
{
drop += speed*pm_evosuitfriction*pml.frametime;
}
// apply flying friction
if ( ps->powerups[PW_FLIGHT] || 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 ) {
#if 1
// q2 style
int i;
float addspeed, accelspeed, currentspeed;
currentspeed = DotProduct (pm->ps->velocity, wishdir);
addspeed = wishspeed - currentspeed;
if (addspeed <= 0) {
return;
}
accelspeed = accel*pml.frametime*wishspeed;
if (accelspeed > addspeed) {
accelspeed = addspeed;
}
for (i=0 ; i<3 ; i++) {
pm->ps->velocity[i] += accelspeed*wishdir[i];
}
#else
// proper way (avoids strafe jump maxspeed bug), but feels bad
vec3_t wishVelocity;
vec3_t pushDir;
float pushLen;
float canPush;
VectorScale( wishdir, wishspeed, wishVelocity );
VectorSubtract( wishVelocity, pm->ps->velocity, pushDir );
pushLen = VectorNormalize( pushDir );
canPush = accel*pml.frametime*wishspeed;
if (canPush > pushLen) {
canPush = pushLen;
}
VectorMA( pm->ps->velocity, canPush, pushDir, pm->ps->velocity );
#endif
}
/*
============
PM_CmdScale
============
*/
/**
* \return 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
================
*/
/**
* Determines the rotation of the legs reletive
* to the facing dir
*/
static void PM_SetMovementDir( void ) {
playerState_t *ps = pm->ps;
usercmd_t *cmd = &pm->cmd;
if ( cmd->forwardmove || cmd->rightmove ) {
if ( cmd->rightmove == 0 && cmd->forwardmove > 0 ) {
ps->movementDir = 0;
} else if ( cmd->rightmove < 0 && cmd->forwardmove > 0 ) {
ps->movementDir = 1;
} else if ( cmd->rightmove < 0 && cmd->forwardmove == 0 ) {
ps->movementDir = 2;
} else if ( cmd->rightmove < 0 && cmd->forwardmove < 0 ) {
ps->movementDir = 3;
} else if ( cmd->rightmove == 0 && cmd->forwardmove < 0 ) {
ps->movementDir = 4;
} else if ( cmd->rightmove > 0 && cmd->forwardmove < 0 ) {
ps->movementDir = 5;
} else if ( cmd->rightmove > 0 && cmd->forwardmove == 0 ) {
ps->movementDir = 6;
} else if ( cmd->rightmove > 0 && cmd->forwardmove > 0 ) {
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 ( ps->movementDir == 2 ) {
ps->movementDir = 1;
} else if ( ps->movementDir == 6 ) {
ps->movementDir = 7;
}
}
}
/*
=============
PM_CheckJump
=============
*/
/**
* Checks if jumping is allowed
*/
static qboolean PM_CheckJump( void ) {
playerState_t *ps = pm->ps;
if ( 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 ( 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;
ps->pm_flags |= PMF_JUMP_HELD;
ps->groundEntityNum = ENTITYNUM_NONE;
ps->velocity[2] = JUMP_VELOCITY;
PM_AddEvent( EV_JUMP );
if ( pm->cmd.forwardmove >= 0 ) {
PM_ForceLegsAnim( PM_GetAnim( ANIM_JUMP, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ) ); //BOTH_JUMP
if ( ps->weaponstate == WEAPON_READY )
PM_ForceTorsoAnim( PM_GetAnim( ANIM_JUMP, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qtrue );
ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
if ( ps->weaponstate == WEAPON_READY )
PM_ForceTorsoAnim( PM_GetAnim( ANIM_JUMPB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue );
PM_ForceLegsAnim( PM_GetAnim( ANIM_JUMPB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ) ); //LEGS_JUMPB
ps->pm_flags |= PMF_BACKWARDS_JUMP;
}
return qtrue;
}
/*
=============
PM_CheckWaterJump
=============
*/
/**
* Checks if jumping out of water is allowed
*/
static qboolean PM_CheckWaterJump( void ) {
vec3_t spot;
int cont;
vec3_t flatforward;
playerState_t *ps = pm->ps;
if (ps->pm_time) {
return qfalse;
}
// check for water jump
if ( pm->waterlevel != 2 ) {
return qfalse;
}
if ( pm->watertype & CONTENTS_LADDER ) {
if (ps->velocity[2] <= 0)
return qfalse;
}
flatforward[0] = pml.forward[0];
flatforward[1] = pml.forward[1];
flatforward[2] = 0;
VectorNormalize (flatforward);
VectorMA (ps->origin, 30, flatforward, spot);
spot[2] += 4;
cont = pm->pointcontents (spot, ps->clientNum );
if ( !(cont & CONTENTS_SOLID) ) {
return qfalse;
}
spot[2] += 16;
cont = pm->pointcontents (spot, ps->clientNum );
if ( cont ) {
return qfalse;
}
// jump out of water
VectorScale (pml.forward, 200, ps->velocity);
ps->velocity[2] = 350;
ps->pm_flags |= PMF_TIME_WATERJUMP;
ps->pm_time = 2000;
return qtrue;
}
//============================================================================
/*
===================
PM_WaterJumpMove
===================
*/
/**
* Flying out of the water
*/
static void PM_WaterJumpMove( void ) {
playerState_t *ps = pm->ps;
// waterjump has no control, but falls
PM_StepSlideMove( qtrue );
ps->velocity[2] -= ps->gravity * pml.frametime;
if (ps->velocity[2] < 0) {
// cancel as soon as we are falling down again
ps->pm_flags &= ~PMF_ALL_TIMES;
ps->pm_time = 0;
}
}
/*
===================
PM_WaterMove
===================
*/
/**
* Handles movement in water
*/
static void PM_WaterMove( void ) {
int i;
vec3_t wishvel;
float wishspeed;
vec3_t wishdir;
float scale;
float vel;
playerState_t *ps = pm->ps;
if ( PM_CheckWaterJump() ) {
PM_WaterJumpMove();
return;
}
#if 0
// jump = head for surface
if ( pm->cmd.upmove >= 10 ) {
if (ps->velocity[2] > -300) {
if ( pm->watertype == CONTENTS_WATER ) {
ps->velocity[2] = 100;
} else if (pm->watertype == CONTENTS_SLIME) {
ps->velocity[2] = 80;
} else {
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 > ps->speed * pm_ladderScale ) {
wishspeed = ps->speed * pm_ladderScale;
}
PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate );
} else {
if ( wishspeed > ps->speed * pm_swimScale ) {
wishspeed = ps->speed * pm_swimScale;
}
PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate );
}
// make sure we can go up slopes easily under water
if ( pml.groundPlane && DotProduct( ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
vel = VectorLength(ps->velocity);
// slide along the ground plane
PM_ClipVelocity (ps->velocity, pml.groundTrace.plane.normal,
ps->velocity, OVERCLIP );
VectorNormalize(ps->velocity);
VectorScale(ps->velocity, vel, ps->velocity);
}
PM_SlideMove( qfalse );
}
/*
===================
PM_FlyMove
Only with the flight powerup
TiM: Good... if this handles
spectators too, I'm sunk >.<
Oh crap... it does lol
===================
*/
/**
* Handles fly movement (e.g. flight powerup or spectator movement)
*/
static void PM_FlyMove( void ) {
int i;
vec3_t wishvel;
float wishspeed;
vec3_t wishdir;
float scale;
playerState_t *ps = pm->ps;
// 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++) {
if ( ps->pm_type == PM_SPECTATOR ) {
wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
}
else {
//TiM - Vertical is no longer a hardcoded constant direction
wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove +
scale * pml.right[i]*pm->cmd.rightmove +
scale * pml.up[i]* pm->cmd.upmove;
//TiM - Set directions
PM_SetMovementDir();
}
}
if ( ps->pm_type == PM_SPECTATOR )
wishvel[2] += scale * pm->cmd.upmove;
}
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_FLY, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_FLY, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue );
PM_StepSlideMove( qfalse );
}
/*
===================
PM_AirMove
===================
*/
/**
* Handles movement during air time (e.g. falling)
*/
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);
wishspeed *= scale;
// not on ground, so little effect on velocity
PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);
// we may have a ground plane that is very steep, even
// though we don't have a groundentity
// slide along the steep plane
if ( pml.groundPlane ) {
PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
pm->ps->velocity, OVERCLIP );
}
PM_StepSlideMove ( qtrue );
}
/*
===================
PM_WalkMove
===================
*/
/**
* Handles walk movement
*/
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;
playerState_t *ps = pm->ps;
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
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
wishspeed *= scale;
// clamp the speed lower if ducking
if ( ps->pm_flags & PMF_DUCKED ) {
if ( wishspeed > ps->speed * pm_duckScale ) {
wishspeed = 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 > ps->speed * waterScale ) {
wishspeed = 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 ) || ps->pm_flags & PMF_TIME_KNOCKBACK ) {
accelerate = pm_airaccelerate;
} else {
accelerate = pm_accelerate;
}
PM_Accelerate (wishdir, wishspeed, accelerate);
if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || ps->pm_flags & PMF_TIME_KNOCKBACK ) {
ps->velocity[2] -= ps->gravity * pml.frametime;
}
vel = VectorLength(ps->velocity);
// slide along the ground plane
PM_ClipVelocity (ps->velocity, pml.groundTrace.plane.normal,
ps->velocity, OVERCLIP );
// don't decrease velocity when going up or down a slope
VectorNormalize(ps->velocity);
VectorScale(ps->velocity, vel, ps->velocity);
// don't do anything if standing still
if (!ps->velocity[0] && !ps->velocity[1]) {
return;
}
PM_StepSlideMove( qfalse );
}
/*
==============
PM_DeadMove
==============
*/
/**
* Handles movement while dead
*/
static void PM_DeadMove( void ) {
float forward;
playerState_t *ps = pm->ps;
if ( !pml.walking ) {
return;
}
// extra friction
forward = VectorLength (ps->velocity);
forward -= 20;
if ( forward <= 0 ) {
VectorClear (ps->velocity);
} else {
VectorNormalize (ps->velocity);
VectorScale (ps->velocity, forward, ps->velocity);
}
}
/*
===============
PM_NoclipMove
===============
*/
/**
* Handles noclip movement
*/
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;
playerState_t *ps = pm->ps;
ps->viewheight = DEFAULT_VIEWHEIGHT;
// friction
speed = VectorLength (ps->velocity);
if (speed < 1)
{
VectorCopy (vec3_origin, 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 (ps->velocity, newspeed, 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 (ps->origin, pml.frametime, ps->velocity, ps->origin);
}
/*
===================
PM_FreezeMove
===================
*/
/**
* Handles movement whole freezed
*/
static void PM_FreezeMove( void )
{
trace_t trace;
short temp, i;
vec3_t moveto;
playerState_t *ps = pm->ps;
pm->mins[0] = DEFAULT_MINS_0; //-15
pm->mins[1] = DEFAULT_MINS_1;
pm->maxs[0] = DEFAULT_MAXS_0; //15
pm->maxs[1] = DEFAULT_MAXS_1;
pm->mins[2] = MINS_Z;
// stand up if possible
if (ps->pm_flags & PMF_DUCKED)
{
// try to stand up
pm->maxs[2] = 36; //32
pm->trace (&trace, ps->origin, pm->mins, pm->maxs, ps->origin, ps->clientNum, pm->tracemask );
if (!trace.allsolid)
ps->pm_flags &= ~PMF_DUCKED;
}
if (ps->pm_flags & PMF_DUCKED && ( !(ps->stats[EMOTES] & EMOTE_LOWER) && !ps->powerups[PW_FLIGHT] && !( ( ps->powerups[PW_EVOSUIT] ) && ( ps->gravity == 0 ) ) ) )
{
pm->maxs[2] = 16;
ps->viewheight = CROUCH_VIEWHEIGHT;
}
else
{
if ( ps->stats[EMOTES] & EMOTE_LOWER && ps->legsTimer > 0 ) {
pm->maxs[2] = bg_emoteList[ps->legsTimer].hitBoxHeight;
ps->viewheight = bg_emoteList[ps->legsTimer].viewHeight;
}
else {
ps->viewheight = DEFAULT_VIEWHEIGHT;
}
}
// circularly clamp the angles with deltas
for (i=0 ; i<3 ; i++) {
temp = pm->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 - pm->cmd.angles[i];
temp = 16000;
} else if ( temp < -16000 ) {
ps->delta_angles[i] = -16000 - pm->cmd.angles[i];
temp = -16000;
}
}
}
VectorCopy (ps->origin, moveto);
moveto[2] -= 16;
// test the player position if they were a stepheight higher
pm->trace (&trace, ps->origin, pm->mins, pm->maxs, moveto, ps->clientNum, pm->tracemask);
if ( trace.fraction < 1.0)
{ // Something just below, snap to it, to prevent a little "hop" after the holodeck fades in.
VectorCopy (trace.endpos, ps->origin);
ps->groundEntityNum = trace.entityNum;
// Touch it.
PM_AddTouchEnt( trace.entityNum );
}
}
//============================================================================
//RPG-X | GSIO01 | 20/05/2009:
/**
* Get the corresponding landing sound for each surface type
*/
static int PM_LandsoundForSurface( int fallType ) {
if( pm->ps->stats[PW_INVIS] )
return 0;
switch(fallType) {
case 1:
if(pml.groundTrace.surfaceFlags & SURF_GRASS)
return EV_FALL_MEDIUM_GRASS;
else if(pml.groundTrace.surfaceFlags & SURF_GRAVEL)
return EV_FALL_MEDIUM_GRAVEL;
else if(pml.groundTrace.surfaceFlags & SURF_SNOW)
return EV_FALL_MEDIUM_SNOW;
else if(pml.groundTrace.surfaceFlags & SURF_WOOD)
return EV_FALL_MEDIUM_WOOD;
else
return EV_FALL_MEDIUM;
break;
case 2:
if(pml.groundTrace.surfaceFlags & SURF_GRASS)
return EV_FALL_FAR_GRASS;
else if(pml.groundTrace.surfaceFlags & SURF_GRAVEL)
return EV_FALL_FAR_GRAVEL;
else if(pml.groundTrace.surfaceFlags & SURF_SNOW)
return EV_FALL_FAR_SNOW;
else if(pml.groundTrace.surfaceFlags & SURF_WOOD)
return EV_FALL_FAR_WOOD;
else
return EV_FALL_FAR;
break;
default:
if(pml.groundTrace.surfaceFlags & SURF_GRASS)
return EV_FALL_SHORT_GRASS;
else if(pml.groundTrace.surfaceFlags & SURF_GRAVEL)
return EV_FALL_SHORT_GRAVEL;
else if(pml.groundTrace.surfaceFlags & SURF_SNOW)
return EV_FALL_SHORT_SNOW;
else if(pml.groundTrace.surfaceFlags & SURF_WOOD)
return EV_FALL_SHORT_WOOD;
else
return EV_FALL_SHORT;
break;
}
}
/*
================
PM_FootstepForSurface
================
*/
/**
* \return an event number apropriate for the groundsurface
*/
static int PM_FootstepForSurface( void ) {
//cloaked people make no noise
if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS || pm->ps->stats[PW_INVIS] ) {
return 0;
}
if ( pml.groundTrace.surfaceFlags & SURF_METALSTEPS ) {
return EV_FOOTSTEP_METAL;
}
//RPG-X | GSIO01 | 20.05.2009 | START MOD
if ( pml.groundTrace.surfaceFlags & SURF_GRASS ) {
return EV_FOOTSTEP_GRASS;
}
if ( pml.groundTrace.surfaceFlags & SURF_GRAVEL ) {
return EV_FOOTSTEP_GRAVEL;
}
if ( pml.groundTrace.surfaceFlags & SURF_SNOW ) {
return EV_FOOTSTEP_SNOW;
}
if ( pml.groundTrace.surfaceFlags & SURF_WOOD ) {
return EV_FOOTSTEP_WOOD;
}
//RPG-X | GSIO01 | 20.05.2009 | END MOD
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;
playerState_t *ps = pm->ps;
// calculate the exact velocity on landing
dist = ps->origin[2] - pml.previous_origin[2];
vel = pml.previous_velocity[2];
acc = -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 ( 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 > 55 || ps->stats[STAT_HEALTH] <= 1 ) { //60 //TiM a bit hacky, but I want this to play any time we fall when dead
PM_AddEvent( PM_LandsoundForSurface(2) ); //GSIO01 | 20/05/2009
} else if ( delta > 35 ) { //40
// this is a pain grunt, so don't play it if dead
if ( ps->stats[STAT_HEALTH] > 1 ) { //0
PM_AddEvent( PM_LandsoundForSurface(1) ); //GSIO01 | 20/05/2009
}
} else if ( delta > 5 ) { //7
PM_AddEvent( PM_LandsoundForSurface(0) ); //GSIO01 | 20/05/2009
} else {
PM_AddEvent( PM_FootstepForSurface() );
}
}
// start footstep cycle over
ps->bobCycle = 0; //TiM: was commented out... :P
}
/*
=============
PM_CorrectAllSolid
=============
*/
static void PM_CorrectAllSolid( void ) {
if ( pm->debugLevel ) {
Com_Printf("%i:allsolid\n", c_pmove);
}
// 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;
playerState_t *ps = pm->ps;
if ( 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( ps->origin, point );
point[2] -= 64;
pm->trace (&trace, ps->origin, pm->mins, pm->maxs, point, ps->clientNum, pm->tracemask);
if ( trace.fraction == 1.0 && ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT) != BOTH_GET_UP1 ) {
if ( pm->cmd.forwardmove >= 0 ) {
PM_ForceLegsAnim( PM_GetAnim( ANIM_JUMP, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ) );
if ( ps->weaponstate == WEAPON_READY )
PM_ForceTorsoAnim( PM_GetAnim( ANIM_JUMP, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qtrue );
ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
PM_ForceLegsAnim( PM_GetAnim( ANIM_JUMPB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ) );
if ( ps->weaponstate == WEAPON_READY )
PM_ForceTorsoAnim( PM_GetAnim( ANIM_JUMPB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qtrue );
ps->pm_flags |= PMF_BACKWARDS_JUMP;
}
}
}
ps->groundEntityNum = ENTITYNUM_NONE;
pml.groundPlane = qfalse;
pml.walking = qfalse;
}
/*
=============
PM_GroundTrace
=============
*/
/**
* Does ad trace to the ground
*/
static void PM_GroundTrace( void ) {
vec3_t point;
trace_t trace;
playerState_t *ps = pm->ps;
point[0] = ps->origin[0];
point[1] = ps->origin[1];
point[2] = ps->origin[2] - 0.25;
pm->trace (&trace, ps->origin, pm->mins, pm->maxs, point, 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 ( ps->velocity[2] > 0 && DotProduct( ps->velocity, trace.plane.normal ) > 10 && ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT) != BOTH_GET_UP1 ) {
if ( pm->debugLevel ) {
Com_Printf("%i:kickoff\n", c_pmove);
}
// go into jump animation
if ( pm->cmd.forwardmove >= 0 ) {
PM_ForceLegsAnim( PM_GetAnim( ANIM_JUMP, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ) );
if ( ps->weaponstate == WEAPON_READY )
PM_ForceTorsoAnim( PM_GetAnim( ANIM_JUMP, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qtrue );
ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
PM_ForceLegsAnim( PM_GetAnim( ANIM_JUMPB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ) );
if ( ps->weaponstate == WEAPON_READY )
PM_ForceTorsoAnim( PM_GetAnim( ANIM_JUMPB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qtrue );
ps->pm_flags |= PMF_BACKWARDS_JUMP;
}
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)
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 (ps->pm_flags & PMF_TIME_WATERJUMP)
{
ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
ps->pm_time = 0;
}
if ( 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
ps->pm_flags |= PMF_TIME_LAND;
ps->pm_time = 250;
}
}
ps->groundEntityNum = trace.entityNum;
PM_AddTouchEnt( trace.entityNum );
}
/*
=============
PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving
=============
*/
/**
* Set water level
*/
static void PM_SetWaterLevel( void ) {
vec3_t point;
int cont;
int sample1;
int sample2;
playerState_t *ps = pm->ps;
//
// get waterlevel, accounting for ducking
//
pm->waterlevel = 0;
pm->watertype = 0;
point[0] = ps->origin[0];
point[1] = ps->origin[1];
point[2] = ps->origin[2] + MINS_Z + 1;
cont = pm->pointcontents( point, ps->clientNum );
if ( cont & (MASK_WATER|CONTENTS_LADDER) ) {
sample2 = ps->viewheight - MINS_Z;
sample1 = sample2 / 2;
pm->watertype = cont;
pm->waterlevel = 1;
point[2] = ps->origin[2] + MINS_Z + sample1;
cont = pm->pointcontents (point, ps->clientNum );
if ( cont & (MASK_WATER|CONTENTS_LADDER) ) {
pm->waterlevel = 2;
point[2] = ps->origin[2] + MINS_Z + sample2;
cont = pm->pointcontents (point, ps->clientNum );
if ( cont & (MASK_WATER|CONTENTS_LADDER) ){
pm->waterlevel = 3;
}
}
}
}
/*
==============
PM_CheckDuck
==============
*/
/**
* Sets mins, maxs, and pm->ps->viewheight
*/
static void PM_CheckDuck (void)
{
trace_t trace;
playerState_t *ps = pm->ps;
pm->mins[0] = DEFAULT_MINS_0; //-15
pm->mins[1] = DEFAULT_MINS_1;
pm->maxs[0] = DEFAULT_MAXS_0;
pm->maxs[1] = DEFAULT_MAXS_1;
pm->mins[2] = MINS_Z;
if (ps->pm_type == PM_DEAD)
{
pm->maxs[2] = -8;
ps->viewheight = DEAD_VIEWHEIGHT;
return;
}
if (pm->cmd.upmove < 0)
{ // duck
ps->pm_flags |= PMF_DUCKED;
}
else
{ // stand up if possible
if (ps->pm_flags & PMF_DUCKED)
{
// try to stand up
pm->maxs[2] = 36; //32
pm->trace (&trace, ps->origin, pm->mins, pm->maxs, ps->origin, ps->clientNum, pm->tracemask );
if (!trace.allsolid)
ps->pm_flags &= ~PMF_DUCKED;
}
}
if ( ps->pm_flags & PMF_DUCKED && !( (ps->stats[EMOTES] & EMOTE_LOWER) || ps->powerups[PW_FLIGHT] || ( ps->powerups[PW_EVOSUIT] && ps->gravity == 0 ) ) )
{
pm->maxs[2] = 16;
ps->viewheight = CROUCH_VIEWHEIGHT;
}
else
{
if ( ps->stats[EMOTES] & EMOTE_LOWER && ps->legsTimer > 0 ) {
pm->maxs[2] = bg_emoteList[ps->legsTimer].hitBoxHeight;
ps->viewheight = bg_emoteList[ps->legsTimer].viewHeight;
}
else {
pm->maxs[2] = 36;
pm->mins[2] = MINS_Z;
ps->viewheight = DEFAULT_VIEWHEIGHT;
}
}
}
//===================================================================
//static qboolean ps->didFly;
//static
/*
===============
PM_Footsteps
===============
*/
/**
* Does what it name suggests it handles footsteps.
*/
static void PM_Footsteps( void ) {
float bobmove;
int old;
qboolean footstep;
playerState_t *ps = pm->ps;
//
// calculate speed and cycle to be used for
// all cyclic walking effects
//
pm->xyspeed = sqrt( ps->velocity[0] * ps->velocity[0]
+ ps->velocity[1] * ps->velocity[1] );
pm->xyzspeed = sqrt( ps->velocity[0] * ps->velocity[0] //XVel - left + right
+ ps->velocity[1] * ps->velocity[1] //YVel - forward + back
+ ps->velocity[2] * ps->velocity[2] ); //ZVel - up + down
//RPG-X : TiM *****************************************************
//Cheesy Halo style death flying!
if ( ps->stats[STAT_HEALTH] <= 1 && !ps->powerups[PW_QUAD] && !ps->powerups[PW_BEAM_OUT] ) { //if dead
/*TiM: clip brushes register as ENTITYNUM_NONE
(So if they landed on a shuttle model for instance, the fall anim would still loop O_o )
so they gotta be moving as well to trigger this*/
if ( ps->groundEntityNum == ENTITYNUM_NONE && pm->xyzspeed && pm->waterlevel < 2 ) {
ps->pm_flags |= ANIM_DIDFLY;
PM_ContinueLegsAnim( BOTH_FALLDEATH1INAIR, qtrue );
if ( ps->weaponstate == WEAPON_READY )
{
PM_ContinueTorsoAnim( BOTH_FALLDEATH1INAIR, qtrue );
}
}
else {
if ( ps->pm_flags & ANIM_DIDFLY ) {
//TiM: Save flags. Use anim nums if possible
PM_ContinueLegsAnim( BOTH_FALLDEATH1LAND, qtrue );
if ( ps->weaponstate == WEAPON_READY )
{
PM_ContinueTorsoAnim( BOTH_FALLDEATH1LAND, qtrue );
}
}
}
return;
}
else { //Reset splat boolean
if ( ps->pm_flags & ANIM_DIDFLY && ps->pm_type != PM_DEAD ) {
ps->pm_flags &= ~ANIM_DIDFLY;
}
}
//RPG-X : TiM *****************************************************
//Ladder Animations
//If not on ladder, reset
if ( !(pm->watertype & CONTENTS_LADDER) )
{
if ( ps->pm_flags & ANIM_ONLADDER ) {
ps->pm_flags &= ~( ANIM_ONLADDER );
}
if ( ( !(ps->stats[EMOTES] & EMOTE_UPPER ) || !(ps->stats[EMOTES] & EMOTE_LOWER ) )
&& ps->stats[EMOTES] & EMOTE_CLAMP_BODY ) {
ps->stats[EMOTES] &= ~EMOTE_CLAMP_BODY;
}
}
//If on ladder, but not touching the ground
if ( (pm->watertype & CONTENTS_LADDER) && ( ps->groundEntityNum == ENTITYNUM_NONE ) )
{
if ( !(ps->pm_flags & ANIM_ONLADDER) ) {
ps->pm_flags |= ANIM_ONLADDER ;
ps->stats[EMOTES] |= EMOTE_CLAMP_BODY;
}
}
//Transition anim to get off ladder
if ( ( pm->watertype & CONTENTS_LADDER) && ( ps->groundEntityNum != ENTITYNUM_NONE ) && (ps->pm_flags & ANIM_ONLADDER) ) {//We JUST hit a ladder on the ground
PM_ContinueLegsAnim( BOTH_OFFLADDER_BOT1, qtrue );
if ( ps->weaponstate == WEAPON_READY )
{
PM_ContinueTorsoAnim( BOTH_OFFLADDER_BOT1, qtrue );
}
ps->stats[EMOTES] &= ~EMOTE_CLAMP_BODY;
return;
}
//Transition anim to get on ladder
if ( ( pm->watertype & CONTENTS_LADDER) && ( ps->groundEntityNum != ENTITYNUM_NONE ) && !(ps->pm_flags & ANIM_ONLADDER) ) {//We JUST hit a ladder on the ground
PM_ContinueLegsAnim( BOTH_ONLADDER_BOT1, qtrue );
if ( ps->weaponstate == WEAPON_READY )
{
PM_ContinueTorsoAnim( BOTH_ONLADDER_BOT1, qtrue );
}
return;
}
if ( ps->groundEntityNum == ENTITYNUM_NONE ) {
if (pm->watertype & CONTENTS_LADDER)
{//FIXME: check for watertype, save waterlevel for whether to play
//the get off ladder transition anim
if ( ps->velocity[2] )
{//going up or down it
int anim;
if ( ps->velocity[2] > 0 )
{
anim = BOTH_LADDER_UP1;
}
else
{
anim = BOTH_LADDER_DWN1;
}
PM_ContinueLegsAnim( anim, qtrue );
if ( ps->weaponstate == WEAPON_READY )
{
PM_ContinueTorsoAnim( anim, qtrue );
}
if (fabs(ps->velocity[2]) >5) {
bobmove = 0.005 * fabs(ps->velocity[2]); // climbing bobs slow
if (bobmove > 0.3)
bobmove = 0.3F;
}
}
else
{
PM_ContinueLegsAnim( BOTH_LADDER_IDLE, qtrue );
if ( ps->weaponstate == WEAPON_READY )
{
PM_ContinueTorsoAnim( BOTH_LADDER_IDLE, qtrue );
}
}
return;
}//******************************************************************
else {
// airborne leaves position in cycle intact, but doesn't advance
if ( pm->waterlevel > 2 ) { //TiM: swimming is more hardcore now //1
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_SWIM, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qtrue );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_SWIM, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue );
}
return;
}
}
// if not trying to move
if ( ( !ps->speed || pm->xyspeed < 1.0f || !(pm->cmd.forwardmove || pm->cmd.rightmove) )
&& pm->waterlevel < 3
&& !ps->powerups[PW_FLIGHT] && !(( ps->powerups[PW_EVOSUIT] ) && ( ps->gravity == 0 ))
)
{
if ( pm->xyspeed > 1.0f && !( ps->pm_flags & PMF_DUCKED ) && !( ps->stats[EMOTES] & EMOTE_LOWER ) )
{ //TiM: When you want to duck, you will duck. no delays
if ( !( pm->cmd.buttons & BUTTON_WALKING ) && !(ps->pm_flags & PMF_DUCKED) ) {
if ( ps->weaponstate == WEAPON_READY && !PM_HoldingSpillableWeapon() ) {
PM_ContinueTorsoAnim( BOTH_RUN1STOP, qtrue ); //BOTH_RUN1STOP
}
PM_ContinueLegsAnim( BOTH_RUN1STOP, qtrue );
}
return;
}
else { // <5
ps->bobCycle = 0; // start at beginning of cycle again
if ( ps->pm_flags & PMF_DUCKED && !(ps->stats[EMOTES] & EMOTE_LOWER ) ) {
if ( ps->weaponstate == WEAPON_READY ) {
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_CROUCH, ps->weapon, (qboolean)( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
}
PM_ContinueLegsAnim( PM_GetAnim( ANIM_CROUCH, ps->weapon, (qboolean)( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue );
} else {
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_IDLE, ps->weapon, (qboolean)( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_IDLE, ps->weapon, (qboolean)( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qfalse );
}
}
return;
}
footstep = qfalse;
//TiM : Kill this when swimming as it screws up animations
//Also... kill when speed is 0.. running on the spot is silly lol
//Also, disable when flying. It looks ludricrous if we run upside down lol
if ( pm->waterlevel == 3 || ps->speed == 0 || ps->powerups[PW_FLIGHT] > 0 || pm->xyspeed < 1.0f || (( ps->powerups[PW_EVOSUIT] ) && ( ps->gravity == 0 )) )
{
return;
}
if ( ps->pm_flags & PMF_DUCKED ) {
bobmove = 0.5; // ducked characters bob much faster
//HACK coz this damn thing screws up crouch firing anims otherwise T_T
if ( ps->weaponstate == WEAPON_READY ) {
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_CROUCHWALK, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
}
PM_ContinueLegsAnim( PM_GetAnim( ANIM_CROUCHWALK, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue );
// ducked characters never play footsteps
}
else if ( ps->pm_flags & PMF_BACKWARDS_RUN )
{
if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
bobmove = 0.4; // faster speeds bob faster
footstep = qtrue;
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_RUNB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_RUNB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue ); //LEGS_BACK
} else {
bobmove = 0.3;
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_WALKB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_WALKB, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue ); //LEGS_BACK
}
} else {
if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
bobmove = 0.4; // faster speeds bob faster
footstep = qtrue;
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_RUN, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_RUN, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue ); //LEGS_RUN
} else {
bobmove = 0.3; // walking bobs slow //0.3
if ( ps->weaponstate == WEAPON_READY )
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_WALK, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
PM_ContinueLegsAnim( PM_GetAnim( ANIM_WALK, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qfalse ), qtrue ); //LEGS_WALK
}
}
// check for footstep / splash sounds
old = ps->bobCycle;
ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
// if we just crossed a cycle boundary, play an apropriate footstep event
if ( ( ( old + 64 ) ^ ( ps->bobCycle + 64 ) ) & 128 && !ps->stats[PW_INVIS] ) {
if ( pm->watertype & CONTENTS_LADDER ) {// on ladder
if ( !pm->noFootsteps ) {
PM_AddEvent( EV_FOOTSTEP_METAL );
}
} else 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 );
}
}
}
/*
==============
PM_WaterEvents
==============
*/
/**
* Generate sound events for entering and leaving water
*/
static void PM_WaterEvents( void ) { // FIXME?
if ( pm->watertype & CONTENTS_LADDER || pm->ps->stats[PW_INVIS] ) //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 );
}
}
/*
===============
PM_BeginWeaponChange
===============
*/
/**
* Begins weapon change
*/
static void PM_BeginWeaponChange( int weapon ) {
playerState_t *ps = pm->ps;
if ( weapon <= WP_0 || weapon >= WP_NUM_WEAPONS ) {
return;
}
if ( !( ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
return;
}
if ( ps->weaponstate == WEAPON_DROPPING ) {
return;
}
PM_AddEvent( EV_CHANGE_WEAPON );
ps->weaponstate = WEAPON_DROPPING;
ps->weaponTime += 200;
PM_ForceTorsoAnim( TORSO_DROPWEAP1, qfalse );
}
/*
===============
PM_FinishWeaponChange
===============
*/
/**
* Finishs weapon change
*/
static void PM_FinishWeaponChange( void ) {
int weapon;
playerState_t *ps = pm->ps;
weapon = pm->cmd.weapon;
if ( weapon < WP_0 || weapon >= WP_NUM_WEAPONS ) {
weapon = WP_0;
}
if ( !( ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
weapon = WP_0;
}
ps->weapon = weapon;
ps->weaponstate = WEAPON_RAISING;
ps->weaponTime += 250;
PM_ForceTorsoAnim( TORSO_RAISEWEAP1, qfalse );
}
/*
==============
PM_TorsoAnimation
==============
*/
/**
* Once handled torso animation
*/
static void PM_TorsoAnimation( void )
{
return;
}
#define PHASER_AMMO_PER_SHOT 1
#define PHASER_ALT_AMMO_PER_SHOT 2
//! alt ammo usage
int altAmmoUsage[WP_NUM_WEAPONS] =
{
0, //!<WP_0,
1, //!<WP_5,
8, //!<WP_6,
3, //!<WP_1,
5, //!<WP_4,
1, //!<WP_10,
1, //!<WP_8,
2, //!<WP_7,
2, //!<WP_9,
5 //!<WP_13,
};
/*
==============
PM_Weapon
==============
*/
/**
* Generates weapon events and modifes the weapon counter
*/
static void PM_Weapon( void ) {
int addTime;
qboolean altfired = qfalse;
playerState_t *ps = pm->ps;
// don't allow attack until all buttons are up
if ( ps->pm_flags & PMF_RESPAWNED ) {
return;
}
// ignore if spectator
if ( ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
return;
}
ps->rechargeTime -= pml.msec;
// check for dead player
if ( ps->stats[STAT_HEALTH] <= 0 ) {
ps->weapon = WP_0;
return;
}
// check for item using
if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE )
{
if ( ! ( ps->pm_flags
& PMF_USE_ITEM_HELD ) )
{
int tag = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
ps->pm_flags |= PMF_USE_ITEM_HELD;
PM_AddEvent( EV_USE_ITEM0 + tag );
// if we're placing the detpack, don't remove it from our "inventory"
if ( (HI_DETPACK == tag) /* || (HI_TRANSPORTER == tag)) */ &&
(IT_HOLDABLE == bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giType) )
{
// are we placing it?
if (2 == ps->stats[STAT_USEABLE_PLACED])
{
// we've placed the first stage of a 2-stage transporter
}
else if (ps->stats[STAT_USEABLE_PLACED])
{
// we already placed it, we're activating it.
ps->stats[STAT_HOLDABLE_ITEM] = 0;
}
}
else
{
ps->stats[STAT_HOLDABLE_ITEM] = 0;
}
return;
}
}
else
{
ps->pm_flags &= ~PMF_USE_ITEM_HELD;
}
// make weapon function
if ( ps->weaponTime > 0 ) {
ps->weaponTime -= pml.msec;
}
// check for weapon change
// can't change if weapon is firing, but can change
// again if lowering or raising
if ( ps->weaponTime <= 0 || ps->weaponstate != WEAPON_FIRING ) {
if ( ps->weapon != pm->cmd.weapon ) {
PM_BeginWeaponChange( pm->cmd.weapon );
}
}
if ( ps->weaponTime > 0 ) {
return;
}
// change weapon if time
if ( ps->weaponstate == WEAPON_DROPPING ) {
PM_FinishWeaponChange();
return;
}
if ( ps->weaponstate == WEAPON_RAISING )
{
ps->weaponstate = WEAPON_READY;
PM_StartTorsoAnim( PM_GetAnim( ANIM_IDLE, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
return;
}
// check for fire
if ( !(pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_ALT_ATTACK) ) )
{
ps->weaponTime = 0;
ps->weaponstate = WEAPON_READY;
return;
}
// take an ammo away if not infinite
if ( ps->ammo[ ps->weapon ] != -1 )
{
if (pm->cmd.buttons & BUTTON_ALT_ATTACK)
{
altfired = qtrue;
}
else
{
// check for out of ammo
if ( ! ps->ammo[ ps->weapon ] )
{
if ( ps->weapon == WP_5 ) // phaser out of ammo is special case
{
ps->ammo[ps->weapon] = 0;
}
}
}
}
if ( ps->weapon != WP_14 && ps->weapon != WP_4 && ps->weapon != WP_1 )
{
//Little hack. I like the idle poses for these when it crouches :)
if ( ( ( ps->weapon == WP_5 )
|| ( ps->weapon == WP_6 )
|| ( ps->weapon == WP_10 )
|| ( ps->weapon == WP_7 ) )
&& ( ps->pm_flags & PMF_DUCKED ) )
{
PM_ForceTorsoAnim( PM_GetAnim( ANIM_CROUCH, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
}
else
{
PM_ContinueTorsoAnim( PM_GetAnim( ANIM_ATTACK, ps->weapon, ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH), qtrue ), qfalse );
}
//Put in this scope, so holding down the trigger on these 'no-anim' weapons won't lock
//other animations.
//Bots were locked in the jumping anim >.<
ps->weaponstate = WEAPON_FIRING;
}
else
{
ps->weaponstate = WEAPON_READY;
}
// fire weapon
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
{
if (altfired) // it's either a legally altfirable non-phaser, or it's a phaser with ammo left
{
PM_AddEvent( EV_ALT_FIRE ); // fixme, because I'm deducting ammo earlier, the last alt-fire shot turns into a main fire
}
else
{
PM_AddEvent( EV_FIRE_EMPTY_PHASER );
}
switch( ps->weapon ) {
default:
case WP_5:
addTime = 100;
//If the phaser has been fired, delay the next recharge time
ps->rechargeTime = PHASER_RECHARGE_TIME;
break;
case WP_13:
addTime = 0; //500
break;
case WP_8:
addTime = 600;//RPG-X: RedTechie use to be 700
break;
case WP_10:
addTime = DISRUPTOR_DELAY;
break;
case WP_4:
addTime = 0; //700
break;
case WP_9:
addTime = ALT_PHOTON_DELAY;
break;
case WP_1:
addTime = 460; //700
break;
case WP_6:
addTime = 100;
break;
case WP_7:
addTime = 500; //RPG-X: RedTechie - Use to be 1200
break;
case WP_12:
//RPG-X: RedTechie - Admins get faster alt fire for steam effects
if( pm->admin){
addTime = 80;
}else{
addTime = 1000;
}
break;
case WP_14:
addTime = 2000;
break;
case WP_11:
addTime = 0; //1000
break;
case WP_2:
if(pm->admin ){
addTime = ALT_TRICORDER_DELAY;
}else{
addTime = 0;
}
break;
case WP_3:
addTime = 0; //500
break;
case WP_15:
addTime = 0; //1000
break;
}
}
else
{
if (ps->ammo[ps->weapon])
{
PM_AddEvent( EV_FIRE_WEAPON );
}
else
{
PM_AddEvent( EV_FIRE_EMPTY_PHASER );
}
switch( ps->weapon ) {
default:
case WP_5:
addTime = 100;
//If the phaser has been fired, delay the next recharge time
ps->rechargeTime = PHASER_RECHARGE_TIME;
break;
case WP_13:
addTime = 1000; //1000
break;
case WP_8:
addTime = 460;//RPG-X: RedTechie use to be 700
break;
case WP_1:
addTime = 460;
break;
case WP_10:
addTime = 100;
break;
case WP_4:
addTime = 0; //100
break;
case WP_9:
addTime = PHOTON_DELAY;
break;
case WP_6:
addTime = RIFLE_DELAY;
break;
case WP_7:
addTime = TR116_DELAY; //RPG-X: RedTechie - Use to be 1200
break;
case WP_12:
addTime = 1000;
break;
case WP_14:
addTime = 2000; //1000
break;
case WP_11:
addTime = 0; //1000
break;
case WP_2:
addTime = ALT_TRICORDER_DELAY; //1000
break;
case WP_3:
addTime = 0; //500
break;
case WP_15:
addTime = 0; //1000
break;
}
}
if ( ps->powerups[PW_HASTE] ) {
addTime /= 1.3;
}
ps->weaponTime += addTime;
}
/*
================
PM_Animate
================
*/
static void PM_Animate( void ) {
playerState_t *ps = pm->ps;
if ( pm->cmd.buttons & BUTTON_GESTURE ) {
if (ps->pm_type < PM_DEAD ) {
if ( !( ps->eFlags & EF_TALKING ) ) {
ps->eFlags |= EF_TALKING;
}
}
} else {
if ( ( ps->eFlags & EF_TALKING ) ) {
ps->eFlags &= ~EF_TALKING;
}
}
}
/*
================
PM_DropTimers
================
*/
static void PM_DropTimers( void ) {
int newFlags;
playerState_t *ps = pm->ps;
// drop misc timing counter
if ( ps->pm_time ) {
if ( pml.msec >= ps->pm_time )
{
ps->pm_flags &= ~PMF_ALL_TIMES;
ps->pm_time = 0;
}
else
{
ps->pm_time -= pml.msec;
}
}
//Count down the legs anim timer
if ( ps->stats[LEGSTIMER] > 0 ) { //legsTimer
ps->stats[LEGSTIMER] -= pml.msec;
if ( ps->stats[LEGSTIMER] < 0 )
{
ps->stats[LEGSTIMER] = 0;
}
}
//if legs anim timer hit 0
if ( ps->stats[LEGSTIMER] == 0 ) {
if ( (ps->stats[EMOTES] & EMOTE_LOWER ) &&
!(ps->stats[EMOTES] & EMOTE_LOOP_LOWER ) )
{
ps->stats[EMOTES] &= ~EMOTE_MASK_LOWER;
if ( ps->legsAnim > 0 )
{
PM_ForceLegsAnim( bg_emoteList[ps->legsAnim].enumName );
//TiM - Remove any data about torsos here. it's not necessary and it invalidates the check below
newFlags = ( bg_emoteList[ps->legsAnim].animFlags | bg_emoteList[ps->legsAnim].bodyFlags );
newFlags &= ~EMOTE_MASK_UPPER;
ps->stats[EMOTES] |= newFlags;
ps->legsTimer = ps->legsAnim;
}
else
{
ps->legsTimer = 0;
ps->legsAnim = 0;
}
}
}
if ( ps->stats[TORSOTIMER] > 0 ) { //torsoTimer
ps->stats[TORSOTIMER] -= pml.msec;
if ( ps->stats[TORSOTIMER] < 0 )
{
ps->stats[TORSOTIMER] = 0;
}
}
if ( ps->stats[TORSOTIMER] == 0 ) {
if ( (ps->stats[EMOTES] & EMOTE_UPPER) &&
!(ps->stats[EMOTES] & EMOTE_LOOP_UPPER ) )
{
ps->stats[EMOTES] &= ~EMOTE_MASK_UPPER;
if ( ps->torsoAnim > 0 )
{
PM_ForceTorsoAnim( bg_emoteList[ps->torsoAnim].enumName, qtrue );
//TiM - Remove any data about legs here. it's not necessary and it invalidates any subsequent checks
newFlags = ( bg_emoteList[ps->torsoAnim].animFlags | bg_emoteList[ps->torsoAnim].bodyFlags );
newFlags &= ~EMOTE_MASK_LOWER;
ps->stats[EMOTES] |= newFlags;
}
else
{
ps->torsoTimer = 0;
ps->torsoAnim = 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 ) {
return; // no view changes at all
}
if ( ps->pm_type == PM_CCAM) {
return;
}
if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 1 ) { //RPG-X: RedTechie - This use to be 0 but in rpg-x with or without medics revive 1 health means you die!
return; // no view changes at all
}
//TiM - Bookmark
//With a flag here, a change to the client side player rotation code there,
//we could actually make it EVA suit users could rotate fully in all directions
//when in space. :)
// circularly clamp the angles with deltas
for (i=0 ; i<3 ; i++) {
temp = cmd->angles[i] + ps->delta_angles[i];
if ( i == PITCH && !pm->ps->powerups[PW_FLIGHT] && !(( pm->ps->powerups[PW_EVOSUIT] ) && ( pm->ps->gravity == 0 )) ) {
// 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);
}
}
/*
================
PmoveSingle
================
*/
void PmoveSingle (pmove_t *pmove) {
playerState_t *ps = pmove->ps;
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 ( 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 ) {
ps->eFlags |= EF_TALK;
} else {
ps->eFlags &= ~EF_TALK;
}
// set the firing flag for continuous beam weapons
if ( !(ps->pm_flags & PMF_RESPAWNED) &&
ps->pm_type != PM_INTERMISSION &&
ps->pm_type != PM_CCAM &&
( (pm->cmd.buttons & BUTTON_ATTACK) || (pm->cmd.buttons & BUTTON_ALT_ATTACK) ) &&
(ps->ammo[ ps->weapon ] || ps->weapon == WP_5))
{
if (((ps->weapon == WP_5) && (!ps->ammo[ ps->weapon ])) || (!(pm->cmd.buttons & BUTTON_ALT_ATTACK)))
{
ps->eFlags &= ~EF_ALT_FIRING;
}
else
{
ps->eFlags |= EF_ALT_FIRING;
}
// This flag should always get set, even when alt-firing
ps->eFlags |= EF_FIRING;
}
else
{
ps->eFlags &= ~EF_FIRING;
ps->eFlags &= ~EF_ALT_FIRING;
}
// clear the respawned flag if attack and use are cleared
if ( ps->stats[STAT_HEALTH] > 0 &&
!( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
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;
if (pmove->cmd.upmove)
{
if (pmove->cmd.upmove > 0)
{
pmove->cmd.upmove = 1;
}
else
{
pmove->cmd.upmove = -1;//allow a tiny bit to keep the duck anim
}
}
}
//ph34r teh cheezy hack
//Disable the ability to jump when getting up animating.
//This is for the purporse of the medics revive animation
if ( ( ps->stats[LEGSANIM] & ~ANIM_TOGGLEBIT ) == BOTH_GET_UP1 ) {
pm->cmd.upmove = 0;
}
//If the legs are playing a non infinite loop, disable the movement keys.
//Else the player slides on their feet :S
if ( (ps->stats[EMOTES] & EMOTE_LOWER &&( ps->stats[EMOTES] & EMOTE_CLAMP_BODY )) || ( ps->stats[EMOTES] & EMOTE_CLAMP_ALL ) ) { //EMOTE_LOWER
pmove->cmd.forwardmove = 0;
pmove->cmd.rightmove = 0;
}
if ( ps->stats[EMOTES] & ( EMOTE_LOWER | EMOTE_UPPER ) ) {
if (pmove->cmd.buttons & MASK_KILL_EMOTES || pmove->cmd.upmove != 0 ) {
ps->stats[EMOTES] &= ~EMOTE_MASK_BOTH;
ps->legsTimer = 0;
ps->stats[LEGSTIMER] = 0;
}
}
//TiM: Injured system
//Disable the ability to toggle walking,
//and slow player down. :)
if ( ps->stats[STAT_HEALTH] <= INJURED_MODE_HEALTH ) {
pm->cmd.buttons &= ~BUTTON_WALKING;
}
// clear all pmove local vars
memset (&pml, 0, sizeof(pml));
// determine the time
pml.msec = pmove->cmd.serverTime - ps->commandTime;
if ( pml.msec < 1 ) {
pml.msec = 1;
} else if ( pml.msec > 200 ) {
pml.msec = 200;
}
ps->commandTime = pmove->cmd.serverTime;
// save old org in case we get stuck
VectorCopy (ps->origin, pml.previous_origin);
// save old velocity for crashlanding
VectorCopy (ps->velocity, pml.previous_velocity);
pml.frametime = pml.msec * 0.001;
if (ps->pm_type == PM_FREEZE /*|| ps->introTime > pm->cmd.serverTime*/ )
{
PM_FreezeMove();
return; // no movement at all
}
// update the viewangles
PM_UpdateViewAngles( ps, &pm->cmd );
AngleVectors (ps->viewangles, pml.forward, pml.right, pml.up);
if ( pm->cmd.upmove < 10 ) {
// not holding jump
ps->pm_flags &= ~PMF_JUMP_HELD;
}
// decide if backpedaling animations should be used
if ( pm->cmd.forwardmove < 0 ) {
ps->pm_flags |= PMF_BACKWARDS_RUN;
} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
ps->pm_flags &= ~PMF_BACKWARDS_RUN;
}
if ( ps->pm_type >= PM_DEAD ) {
pm->cmd.forwardmove = 0;
pm->cmd.rightmove = 0;
pm->cmd.upmove = 0;
}
if ( ps->pm_type == PM_SPECTATOR ) {
PM_CheckDuck ();
PM_FlyMove ();
PM_DropTimers ();
return;
}
if ( ps->pm_type == PM_SPECTATOR )
{
PM_CheckDuck ();
PM_FlyMove();
PM_DropTimers ();
return;
}
if ( ps->pm_type == PM_NOCLIP ) {
PM_NoclipMove ();
PM_DropTimers ();
return;
}
if ( ps->pm_type == PM_CCAM) {
return;
}
if ( 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_CheckDuck ();
// set groundentity
PM_GroundTrace();
if ( ps->pm_type == PM_DEAD ) {
PM_DeadMove ();
}
if ( ps->powerups[PW_FLIGHT] ) {
// flight powerup doesn't allow jump and has different friction
PM_FlyMove();
} else if ((( ps->powerups[PW_EVOSUIT] ) && ( ps->gravity == 0 ))) {
//RPG-X | Phenix | 8/8/2004
//The player is in 0 G and is wearing an evo suit...
PM_FlyMove();
} else if (ps->pm_flags & PMF_TIME_WATERJUMP) {
PM_WaterJumpMove();
} else if ( pm->waterlevel > 1 ) {
// swimming
PM_WaterMove();
} else if ( pml.walking ) {
// walking on ground
PM_WalkMove();
} else {
// airborne
PM_AirMove();
}
PM_Animate();
// set groundentity, watertype, and waterlevel
PM_GroundTrace();
PM_SetWaterLevel();
// weapons
PM_Weapon();
PM_Use();
// torso animation
PM_TorsoAnimation();
// footstep events / legs animations
PM_Footsteps();
// entering / leaving water splashes
PM_WaterEvents();
//PM_DoEmote();
PM_DropTimers();
// snap some parts of playerstate to save network bandwidth
SnapVector( ps->velocity );
}
/*
================
Pmove
================
*/
/**
* Can be called by either the server or the client
*/
void Pmove (pmove_t *pmove) {
int finalTime;
finalTime = pmove->cmd.serverTime;
if ( finalTime < pmove->ps->commandTime ) {
return; // should not happen
}
if ( finalTime > pmove->ps->commandTime + 1000 ) {
pmove->ps->commandTime = finalTime - 1000;
}
// chop the move up if it is too long, to prevent framerate
// dependent behavior
while ( pmove->ps->commandTime != finalTime ) {
int msec;
msec = finalTime - pmove->ps->commandTime;
if ( msec > 66 ) {
msec = 66;
}
pmove->cmd.serverTime = pmove->ps->commandTime + msec;
PmoveSingle( pmove );
}
}