q3rally/engine/code/game/bg_pmove.c
zturtleman 0d5fb492cd ioquake3 resync to revision 3444 from 3393.
Fix GCC 6 misleading-indentation warning
add SECURITY.md
OpenGL2: Restore adding fixed ambient light when HDR is enabled
Few LCC memory fixes.
fix a few potential buffer overwrite in Game VM
Enable compiler optimization on all macOS architectures
Don't allow qagame module to create "botlib.log" at ANY filesystem location
Make FS_BuildOSPath for botlib.log consistent with typical usage
tiny readme thing
Remove extra plus sign from Huff_Compress()
Fix VMs being able to change CVAR_PROTECTED cvars
Don't register fs_game cvar everywhere just to get the value
Don't let VMs change engine latch cvars immediately
Fix fs_game '..' reading outside of home and base path
Fix VMs forcing engine latch cvar to update to latched value
Revert my recent cvar latch changes
Revert "Don't let VMs change engine latch cvars immediately"
Partially revert "Fix fs_game '..' reading outside of home and base path"
Revert "Fix VMs forcing engine latch cvar to update to latched value"
Fix exploit to bypass filename restrictions on Windows
Changes to systemd q3a.service
Fix Q_vsnprintf for mingw-w64
Fix timelimit causing an infinite map ending loop
Fix invalid access to cluster 0 in AAS_AreaRouteToGoalArea()
Fix negative frag/capturelimit causing an infinite map end loop
OpenGL2: Fix dark lightmap on shader in mpteam6
Make FS_InvalidGameDir() consider subdirectories invalid
[qcommon] Remove dead serialization code
[qcommon] Make several zone variables and functions static.
Fix MAC_OS_X_VERSION_MIN_REQUIRED for macOS 10.10 and later
Increase q3_ui .arena filename list buffer size to 4096 bytes
OpenGL2: Fix crash when BSP has deluxe maps and vertex lit surfaces
Support Unicode characters greater than 0xFF in cl_consoleKeys
Fix macOS app bundle with space in name
OpenGL1: Use glGenTextures instead of hardcoded values
Remove CON_FlushIn function and where STDIN needs flushing, use tcflush POSIX function
Update libogg from 1.3.2 to 1.3.3
Rename (already updated) libogg-1.3.2 to libogg-1.3.3
Update libvorbis from 1.3.5 to 1.3.6
* Fix CVE-2018-5146 - out-of-bounds write on codebook decoding.
* Fix CVE-2017-14632 - free() on unitialized data
* Fix CVE-2017-14633 - out-of-bounds read
Rename (already updated) libvorbis-1.3.5 to libvorbis-1.3.6
Update opus from 1.1.4 to 1.2.1
Rename (already updated) opus-1.1.4 to opus-1.2.1
Update opusfile from 0.8 to 0.9
Rename (already updated) opusfile-0.8 to opusfile-0.9
First swing at a CONTRIBUTING.md
Allow loading system OpenAL library on macOS again
Remove duplicate setting of FREETYPE_CFLAGS in Makefile
Fix exploit to reset player by sending wrong serverId
Fix "Going to CS_ZOMBIE for [clientname]" developer message
Fix MSG_Read*String*() functions not being able to read last byte from message
2018-04-07 23:02:52 +00:00

2889 lines
64 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2002-2015 Q3Rally Team (Per Thormann - q3rally@gmail.com)
This file is part of q3rally source code.
q3rally source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
q3rally source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with q3rally; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
// bg_pmove.c -- both games player movement code
// takes a playerstate and a usercmd as input and returns a modifed playerstate
#include "../qcommon/q_shared.h"
#include "bg_public.h"
#include "bg_local.h"
pmove_t *pm;
pml_t pml;
// movement parameters
float pm_stopspeed = 100.0f;
float pm_duckScale = 0.25f;
float pm_swimScale = 0.50f;
float pm_accelerate = 10.0f;
float pm_airaccelerate = 1.0f;
float pm_wateraccelerate = 4.0f;
float pm_flyaccelerate = 8.0f;
float pm_friction = 6.0f;
float pm_waterfriction = 1.0f;
float pm_flightfriction = 3.0f;
float pm_spectatorfriction = 5.0f;
int c_pmove = 0;
// STONELANCE
int curDelay;
// END
/*
===============
PM_AddEvent
===============
*/
void PM_AddEvent( int newEvent ) {
BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
}
/*
===============
PM_AddTouchEnt
===============
*/
// STONELANCE
// void PM_AddTouchEnt( int entityNum ) {
void PM_AddTouchEnt( int entityNum, vec3_t hitOrigin ) {
// END
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;
// STONELANCE
VectorCopy(hitOrigin, pm->touchPos[pm->numtouch]);
// END
pm->numtouch++;
}
/*
===================
PM_StartTorsoAnim
===================
*/
static void PM_StartTorsoAnim( int anim ) {
if ( pm->ps->pm_type >= PM_DEAD ) {
return;
}
// STONELANCE
/*
pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
| anim;
*/
// END
}
#if 0
static void PM_StartLegsAnim( int anim ) {
if ( pm->ps->pm_type >= PM_DEAD ) {
return;
}
// STONELANCE
/*
if ( pm->ps->legsTimer > 0 ) {
return; // a high priority animation is running
}
pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
| anim;
*/
// END
}
static void PM_ContinueLegsAnim( int anim ) {
if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) {
return;
}
// STONELANCE
/*
if ( pm->ps->legsTimer > 0 ) {
return; // a high priority animation is running
}
PM_StartLegsAnim( anim );
*/
// END
}
static void PM_ContinueTorsoAnim( int anim ) {
if ( ( pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim ) {
return;
}
// STONELANCE
/*
if ( pm->ps->torsoTimer > 0 ) {
return; // a high priority animation is running
}
PM_StartTorsoAnim( anim );
*/
// END
}
static void PM_ForceLegsAnim( int anim ) {
pm->ps->legsTimer = 0;
PM_StartLegsAnim( anim );
}
#endif
/*
==================
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;
vel = pm->ps->velocity;
VectorCopy( vel, vec );
if ( pml.walking ) {
vec[2] = 0; // ignore slope movement
}
speed = VectorLength(vec);
if (speed < 1) {
vel[0] = 0;
vel[1] = 0; // allow sinking underwater
// FIXME: still have z friction underwater?
return;
}
drop = 0;
// apply ground friction
if ( pm->waterlevel <= 1 ) {
if ( pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK) ) {
// if getting knocked back, no friction
if ( ! (pm->ps->pm_flags & PMF_TIME_KNOCKBACK) ) {
control = speed < pm_stopspeed ? pm_stopspeed : speed;
drop += control*pm_friction*pml.frametime;
}
}
}
// apply water friction even if just wading
if ( pm->waterlevel ) {
drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime;
}
// apply flying friction
// STONELANCE
/*
if ( pm->ps->powerups[PW_FLIGHT]) {
drop += speed*pm_flightfriction*pml.frametime;
}
*/
// END
if ( pm->ps->pm_type == PM_SPECTATOR) {
drop += speed*pm_spectatorfriction*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
Returns the scale factor to apply to cmd movements
This allows the clients to use axial -127 to 127 values for all directions
without getting a sqrt(2) distortion in speed.
============
*/
static float PM_CmdScale( usercmd_t *cmd ) {
int max;
float total;
float scale;
max = abs( cmd->forwardmove );
if ( abs( cmd->rightmove ) > max ) {
max = abs( cmd->rightmove );
}
if ( abs( cmd->upmove ) > max ) {
max = abs( cmd->upmove );
}
if ( !max ) {
return 0;
}
total = sqrt( cmd->forwardmove * cmd->forwardmove
+ cmd->rightmove * cmd->rightmove + cmd->upmove * cmd->upmove );
scale = (float)pm->ps->speed * max / ( 127.0 * total );
return scale;
}
/*
================
PM_SetMovementDir
Determine the rotation of the legs relative
to the facing dir
================
*/
// STONELANCE - removed
/*
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;
}
}
}
*/
// END
/*
=============
PM_CheckJump
=============
*/
// STONELANCE - removed
/*
static qboolean PM_CheckJump( void ) {
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return qfalse; // don't allow jump until all buttons are up
}
if ( pm->cmd.upmove < 10 ) {
// not holding jump
return qfalse;
}
// must wait for jump to be released
if ( pm->ps->pm_flags & PMF_JUMP_HELD ) {
// clear upmove so cmdscale doesn't lower running speed
pm->cmd.upmove = 0;
return qfalse;
}
pml.groundPlane = qfalse; // jumping away
pml.walking = qfalse;
pm->ps->pm_flags |= PMF_JUMP_HELD;
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pm->ps->velocity[2] = JUMP_VELOCITY;
PM_AddEvent( EV_JUMP );
if ( pm->cmd.forwardmove >= 0 ) {
PM_ForceLegsAnim( LEGS_JUMP );
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
PM_ForceLegsAnim( LEGS_JUMPB );
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
}
return qtrue;
}
*/
// END
/*
=============
PM_CheckWaterJump
=============
*/
// STOENLANCE - removed function
/*
static qboolean PM_CheckWaterJump( void ) {
vec3_t spot;
int cont;
vec3_t flatforward;
if (pm->ps->pm_time) {
return qfalse;
}
// check for water jump
if ( pm->waterlevel != 2 ) {
return qfalse;
}
flatforward[0] = pml.forward[0];
flatforward[1] = pml.forward[1];
flatforward[2] = 0;
VectorNormalize (flatforward);
VectorMA (pm->ps->origin, 30, flatforward, spot);
spot[2] += 4;
cont = pm->pointcontents (spot, pm->ps->clientNum );
if ( !(cont & CONTENTS_SOLID) ) {
return qfalse;
}
spot[2] += 16;
cont = pm->pointcontents (spot, pm->ps->clientNum );
if ( cont & (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY) ) {
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;
}
*/
// END
//============================================================================
/*
===================
PM_WaterJumpMove
Flying out of the water
===================
*/
// STONELANCE - removed function
/*
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;
}
}
*/
// END
/*
===================
PM_WaterMove
===================
*/
// STONELANCE - removed
/*
static void PM_WaterMove( void ) {
int i;
vec3_t wishvel;
float wishspeed;
vec3_t wishdir;
float scale;
float vel;
if ( PM_CheckWaterJump() ) {
PM_WaterJumpMove();
return;
}
#if 0
// jump = head for surface
if ( pm->cmd.upmove >= 10 ) {
if (pm->ps->velocity[2] > -300) {
if ( pm->watertype & CONTENTS_WATER ) {
pm->ps->velocity[2] = 100;
} else if ( pm->watertype & CONTENTS_SLIME ) {
pm->ps->velocity[2] = 80;
} else {
pm->ps->velocity[2] = 50;
}
}
}
#endif
PM_Friction ();
scale = PM_CmdScale( &pm->cmd );
//
// user intentions
//
if ( !scale ) {
wishvel[0] = 0;
wishvel[1] = 0;
wishvel[2] = -60; // sink towards bottom
} else {
for (i=0 ; i<3 ; i++)
wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
wishvel[2] += scale * pm->cmd.upmove;
}
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
if ( wishspeed > pm->ps->speed * pm_swimScale ) {
wishspeed = pm->ps->speed * pm_swimScale;
}
PM_Accelerate (wishdir, wishspeed, pm_wateraccelerate);
// make sure we can go up slopes easily under water
if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) {
vel = VectorLength(pm->ps->velocity);
// slide along the ground plane
PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
pm->ps->velocity, OVERCLIP );
VectorNormalize(pm->ps->velocity);
VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
}
PM_SlideMove( qfalse );
}
*/
// END
#ifdef MISSIONPACK
/*
===================
PM_InvulnerabilityMove
Only with the invulnerability powerup
===================
*/
static void PM_InvulnerabilityMove( void ) {
pm->cmd.forwardmove = 0;
pm->cmd.rightmove = 0;
pm->cmd.upmove = 0;
VectorClear(pm->ps->velocity);
}
#endif
/*
===================
PM_FlyMove
Only with the flight powerup
===================
*/
static void PM_FlyMove( void ) {
int i;
vec3_t wishvel;
float wishspeed;
vec3_t wishdir;
float scale;
// normal slowdown
PM_Friction ();
scale = PM_CmdScale( &pm->cmd );
//
// user intentions
//
if ( !scale ) {
wishvel[0] = 0;
wishvel[1] = 0;
wishvel[2] = 0;
} else {
for (i=0 ; i<3 ; i++) {
wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
}
wishvel[2] += scale * pm->cmd.upmove;
}
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
PM_StepSlideMove( qfalse );
}
/*
===================
PM_AirMove
===================
*/
// STONELANCE - removed
/*
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 );
}
#if 0
//ZOID: If we are on the grapple, try stair-stepping
//this allows a player to use the grapple to pull himself
//over a ledge
if (pm->ps->pm_flags & PMF_GRAPPLE_PULL)
PM_StepSlideMove ( qtrue );
else
PM_SlideMove ( qtrue );
#endif
PM_StepSlideMove ( qtrue );
}
*/
// END
/*
===================
PM_GrappleMove
===================
*/
// STONELANCE - removed
/*
static void PM_GrappleMove( void ) {
vec3_t vel, v;
float vlen;
VectorScale(pml.forward, -16, v);
VectorAdd(pm->ps->grapplePoint, v, v);
VectorSubtract(v, pm->ps->origin, vel);
vlen = VectorLength(vel);
VectorNormalize( vel );
if (vlen <= 100)
VectorScale(vel, 10 * vlen, vel);
else
VectorScale(vel, 800, vel);
VectorCopy(vel, pm->ps->velocity);
pml.groundPlane = qfalse;
}
*/
// END
/*
===================
PM_WalkMove
===================
*/
// STONELANCE - removed
/*
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));
}
*/
// END
/*
==============
PM_DeadMove
==============
*/
// STONELANCE - removed function
/*
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);
}
}
*/
// END
/*
===============
PM_NoclipMove
===============
*/
static void PM_NoclipMove( void ) {
float speed, drop, friction, control, newspeed;
int i;
vec3_t wishvel;
float fmove, smove;
vec3_t wishdir;
float wishspeed;
float scale;
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
// friction
speed = VectorLength (pm->ps->velocity);
if (speed < 1)
{
VectorCopy (vec3_origin, pm->ps->velocity);
}
else
{
drop = 0;
friction = pm_friction*1.5; // extra friction
control = speed < pm_stopspeed ? pm_stopspeed : speed;
drop += control*friction*pml.frametime;
// scale the velocity
newspeed = speed - drop;
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
}
// accelerate
scale = PM_CmdScale( &pm->cmd );
fmove = pm->cmd.forwardmove;
smove = pm->cmd.rightmove;
for (i=0 ; i<3 ; i++)
wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
wishvel[2] += pm->cmd.upmove;
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalize(wishdir);
wishspeed *= scale;
PM_Accelerate( wishdir, wishspeed, pm_accelerate );
// move
VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
// STONELANCE
pm->ps->viewangles[YAW] = SHORT2ANGLE(pm->ps->damageYaw);
PM_InitializeVehicle(pm->car, pm->ps->origin, pm->ps->viewangles, pm->ps->velocity /* , pm->car_frontweight_dist */ );
// END
}
//============================================================================
/*
================
PM_FootstepForSurface
Returns an event number appropriate for the groundsurface
================
*/
// STONELANCE - removed function
/*
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;
}
*/
// END
/*
=================
PM_CrashLand
Check for hard landings that generate sound events
=================
*/
// STONELANCE - removed function
/*
static void PM_CrashLand( void ) {
float delta;
float dist;
float vel, acc;
float t;
float a, b, c, den;
// decide which landing animation to use
if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
PM_ForceLegsAnim( LEGS_LANDB );
} else {
PM_ForceLegsAnim( LEGS_LAND );
}
pm->ps->legsTimer = TIMER_LAND;
// calculate the exact velocity on landing
dist = pm->ps->origin[2] - pml.previous_origin[2];
vel = pml.previous_velocity[2];
acc = -pm->ps->gravity;
a = acc / 2;
b = vel;
c = -dist;
den = b * b - 4 * a * c;
if ( den < 0 ) {
return;
}
t = (-b - sqrt( den ) ) / ( 2 * a );
delta = vel + t * acc;
delta = delta*delta * 0.0001;
// ducking while falling doubles damage
if ( pm->ps->pm_flags & PMF_DUCKED ) {
delta *= 2;
}
// never take falling damage if completely underwater
if ( pm->waterlevel == 3 ) {
return;
}
// reduce falling damage if there is standing water
if ( pm->waterlevel == 2 ) {
delta *= 0.25;
}
if ( pm->waterlevel == 1 ) {
delta *= 0.5;
}
if ( delta < 1 ) {
return;
}
// create a local entity event to play the sound
// SURF_NODAMAGE is used for bounce pads where you don't ever
// want to take damage or play a crunch sound
if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) {
if ( delta > 60 ) {
PM_AddEvent( EV_FALL_FAR );
} else if ( delta > 40 ) {
// this is a pain grunt, so don't play it if dead
if ( pm->ps->stats[STAT_HEALTH] > 0 ) {
PM_AddEvent( EV_FALL_MEDIUM );
}
} else if ( delta > 7 ) {
PM_AddEvent( EV_FALL_SHORT );
} else {
PM_AddEvent( PM_FootstepForSurface() );
}
}
// start footstep cycle over
pm->ps->bobCycle = 0;
}
*/
// END
/*
=============
PM_CheckStuck
=============
*/
/*
void PM_CheckStuck(void) {
trace_t trace;
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask);
if (trace.allsolid) {
//int shit = qtrue;
}
}
*/
/*
=============
PM_CorrectAllSolid
=============
*/
// STONELANCE - removed
/*
static int PM_CorrectAllSolid( trace_t *trace ) {
int i, j, k;
vec3_t point;
if ( pm->debugLevel ) {
Com_Printf("%i:allsolid\n", c_pmove);
}
// jitter around
for (i = -1; i <= 1; i++) {
for (j = -1; j <= 1; j++) {
for (k = -1; k <= 1; k++) {
VectorCopy(pm->ps->origin, point);
point[0] += (float) i;
point[1] += (float) j;
point[2] += (float) k;
pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
if ( !trace->allsolid ) {
point[0] = pm->ps->origin[0];
point[1] = pm->ps->origin[1];
point[2] = pm->ps->origin[2] - 0.25;
pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
pml.groundTrace = *trace;
return qtrue;
}
}
}
}
pm->ps->groundEntityNum = ENTITYNUM_NONE;
pml.groundPlane = qfalse;
pml.walking = qfalse;
return qfalse;
}
*/
// END
/*
=============
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);
// STONELANCE
/*
if ( trace.fraction == 1.0 ) {
if ( pm->cmd.forwardmove >= 0 ) {
PM_ForceLegsAnim( LEGS_JUMP );
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
PM_ForceLegsAnim( LEGS_JUMPB );
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
}
}
*/
// END
}
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...
// STONELANCE - dont bother with this
/*
if ( trace.allsolid ) {
if ( !PM_CorrectAllSolid(&trace) )
return;
}
*/
// END
// 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
// STONELANCE
/*
if ( pm->cmd.forwardmove >= 0 ) {
PM_ForceLegsAnim( LEGS_JUMP );
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
} else {
PM_ForceLegsAnim( LEGS_JUMPB );
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
}
*/
// END
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);
}
// STONELANCE - removed function
// PM_CrashLand();
// END
// don't do landing time if we were just going down a slope
if ( pml.previous_velocity[2] < -200 ) {
// don't allow another jump for a little while
pm->ps->pm_flags |= PMF_TIME_LAND;
pm->ps->pm_time = 250;
}
}
pm->ps->groundEntityNum = trace.entityNum;
// don't reset the z velocity for slopes
// pm->ps->velocity[2] = 0;
// STONELANCE - handled in bg_physics trace_points
// PM_AddTouchEnt( trace.entityNum );
// END
}
/*
=============
PM_SetWaterLevel FIXME: avoid this twice? certainly if not moving
=============
*/
static void PM_SetWaterLevel( void ) {
vec3_t point;
int cont;
int sample1;
int sample2;
//
// get waterlevel, accounting for ducking
//
pm->waterlevel = 0;
pm->watertype = 0;
point[0] = pm->ps->origin[0];
point[1] = pm->ps->origin[1];
point[2] = pm->ps->origin[2] + MINS_Z + 1;
cont = pm->pointcontents( point, pm->ps->clientNum );
if ( cont & MASK_WATER ) {
sample2 = pm->ps->viewheight - MINS_Z;
sample1 = sample2 / 2;
pm->watertype = cont;
pm->waterlevel = 1;
point[2] = pm->ps->origin[2] + MINS_Z + sample1;
cont = pm->pointcontents (point, pm->ps->clientNum );
if ( cont & MASK_WATER ) {
pm->waterlevel = 2;
point[2] = pm->ps->origin[2] + MINS_Z + sample2;
cont = pm->pointcontents (point, pm->ps->clientNum );
if ( cont & MASK_WATER ){
pm->waterlevel = 3;
}
}
}
}
/*
==============
PM_CheckDuck
Sets mins, maxs, and pm->ps->viewheight
==============
*/
static void PM_CheckDuck (void)
{
trace_t trace;
if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
if ( pm->ps->pm_flags & PMF_INVULEXPAND ) {
// invulnerability sphere has a 42 units radius
VectorSet( pm->mins, -42, -42, -42 );
VectorSet( pm->maxs, 42, 42, 42 );
}
else {
VectorSet( pm->mins, -15, -15, MINS_Z );
VectorSet( pm->maxs, 15, 15, 16 );
}
pm->ps->pm_flags |= PMF_DUCKED;
pm->ps->viewheight = CROUCH_VIEWHEIGHT;
return;
}
pm->ps->pm_flags &= ~PMF_INVULEXPAND;
pm->mins[0] = -15;
pm->mins[1] = -15;
pm->maxs[0] = 15;
pm->maxs[1] = 15;
pm->mins[2] = MINS_Z;
if (pm->ps->pm_type == PM_DEAD)
{
pm->maxs[2] = -8;
pm->ps->viewheight = DEAD_VIEWHEIGHT;
return;
}
if (pm->cmd.upmove < 0)
{ // duck
pm->ps->pm_flags |= PMF_DUCKED;
}
else
{ // stand up if possible
if (pm->ps->pm_flags & PMF_DUCKED)
{
// try to stand up
pm->maxs[2] = 32;
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
if (!trace.allsolid)
pm->ps->pm_flags &= ~PMF_DUCKED;
}
}
if (pm->ps->pm_flags & PMF_DUCKED)
{
pm->maxs[2] = 16;
pm->ps->viewheight = CROUCH_VIEWHEIGHT;
}
else
{
pm->maxs[2] = 32;
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
}
}
//===================================================================
/*
===============
PM_Footsteps
===============
*/
// STOENLANCE - no feet = no footstep sounds - removed function
#if 0
static void PM_Footsteps( void ) {
float bobmove;
int old;
qboolean footstep;
//
// calculate speed and cycle to be used for
// all cyclic walking effects
//
pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0]
+ pm->ps->velocity[1] * pm->ps->velocity[1] );
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
PM_ContinueLegsAnim( LEGS_IDLECR );
}
// airborne leaves position in cycle intact, but doesn't advance
if ( pm->waterlevel > 1 ) {
PM_ContinueLegsAnim( LEGS_SWIM );
}
return;
}
// if not trying to move
if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
if ( pm->xyspeed < 5 ) {
pm->ps->bobCycle = 0; // start at beginning of cycle again
if ( pm->ps->pm_flags & PMF_DUCKED ) {
PM_ContinueLegsAnim( LEGS_IDLECR );
} else {
PM_ContinueLegsAnim( LEGS_IDLE );
}
}
return;
}
footstep = qfalse;
if ( pm->ps->pm_flags & PMF_DUCKED ) {
bobmove = 0.5; // ducked characters bob much faster
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
PM_ContinueLegsAnim( LEGS_BACKCR );
}
else {
PM_ContinueLegsAnim( LEGS_WALKCR );
}
// ducked characters never play footsteps
/*
} else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
bobmove = 0.4; // faster speeds bob faster
footstep = qtrue;
} else {
bobmove = 0.3;
}
PM_ContinueLegsAnim( LEGS_BACK );
*/
} else {
if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) {
bobmove = 0.4f; // faster speeds bob faster
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
PM_ContinueLegsAnim( LEGS_BACK );
}
else {
PM_ContinueLegsAnim( LEGS_RUN );
}
footstep = qtrue;
} else {
bobmove = 0.3f; // walking bobs slow
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
PM_ContinueLegsAnim( LEGS_BACKWALK );
}
else {
PM_ContinueLegsAnim( LEGS_WALK );
}
}
}
// check for footstep / splash sounds
old = pm->ps->bobCycle;
pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
// if we just crossed a cycle boundary, play an appropriate footstep event
if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) {
if ( pm->waterlevel == 0 ) {
// on ground will only play sounds if running
if ( footstep && !pm->noFootsteps ) {
PM_AddEvent( PM_FootstepForSurface() );
}
} else if ( pm->waterlevel == 1 ) {
// splashing
PM_AddEvent( EV_FOOTSPLASH );
} else if ( pm->waterlevel == 2 ) {
// wading / swimming at surface
PM_AddEvent( EV_SWIM );
} else if ( pm->waterlevel == 3 ) {
// no sound when completely underwater
}
}
}
#endif
// END
/*
==============
PM_WaterEvents
Generate sound events for entering and leaving water
==============
*/
static void PM_WaterEvents( void ) { // FIXME?
//
// if just entered a water volume, play a sound
//
if (!pml.previous_waterlevel && pm->waterlevel) {
PM_AddEvent( EV_WATER_TOUCH );
}
//
// if just completely exited a water volume, play a sound
//
if (pml.previous_waterlevel && !pm->waterlevel) {
PM_AddEvent( EV_WATER_LEAVE );
}
//
// check for head just going under water
//
if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) {
PM_AddEvent( EV_WATER_UNDER );
}
//
// check for head just coming out of water
//
if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) {
PM_AddEvent( EV_WATER_CLEAR );
}
}
/*
===============
PM_BeginWeaponChange
===============
*/
static void PM_BeginWeaponChange( int weapon ) {
// STONELANCE
// if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
if ( weapon <= WP_NONE || weapon >= RWP_SMOKE ) {
// END
return;
}
if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
return;
}
if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
return;
}
PM_AddEvent( EV_CHANGE_WEAPON );
pm->ps->weaponstate = WEAPON_DROPPING;
pm->ps->weaponTime += 200;
PM_StartTorsoAnim( TORSO_DROP );
}
/*
===============
PM_FinishWeaponChange
===============
*/
static void PM_FinishWeaponChange( void ) {
int weapon;
weapon = pm->cmd.weapon;
if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) {
weapon = WP_NONE;
}
if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
weapon = WP_NONE;
}
pm->ps->weapon = weapon;
pm->ps->weaponstate = WEAPON_RAISING;
pm->ps->weaponTime += 250;
PM_StartTorsoAnim( TORSO_RAISE );
}
/*
==============
PM_TorsoAnimation
==============
*/
// STONELANCE - removed
/*
static void PM_TorsoAnimation( void ) {
if ( pm->ps->weaponstate == WEAPON_READY ) {
if ( pm->ps->weapon == WP_GAUNTLET ) {
PM_ContinueTorsoAnim( TORSO_STAND2 );
} else {
PM_ContinueTorsoAnim( TORSO_STAND );
}
return;
}
}
*/
// END
/*
==============
PM_Weapon
Generates weapon events and modifes the weapon counter
==============
*/
static void PM_Weapon( void ) {
int addTime;
// don't allow attack until all buttons are up
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return;
}
// ignore if spectator
if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
return;
}
// check for dead player
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
pm->ps->weapon = WP_NONE;
return;
}
// check for item using
if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
&& pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
// don't use medkit if at max health
} else {
pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
}
return;
}
} else {
pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
}
// make weapon function
// STONELANCE
// if ( pm->ps->weaponTime > 0 ) {
// pm->ps->weaponTime -= pml.msec;
if ( (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK) > 0 ) {
if ((pml.msec & NORMAL_WEAPON_TIME_MASK) >= (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK))
pm->ps->weaponTime &= REAR_WEAPON_TIME_MASK; // = 0
else
pm->ps->weaponTime -= (pml.msec & NORMAL_WEAPON_TIME_MASK);
// END
}
// check for weapon change
// can't change if weapon is firing, but can change
// again if lowering or raising
// STONELANCE
// if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
if ( (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK) <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
// END
if ( pm->ps->weapon != pm->cmd.weapon ) {
PM_BeginWeaponChange( pm->cmd.weapon );
}
}
// STONELANCE
// if ( pm->ps->weaponTime > 0 ) {
if ( (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK) > 0 ) {
return;
}
// END
// change weapon if time
if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
PM_FinishWeaponChange();
return;
}
if ( pm->ps->weaponstate == WEAPON_RAISING ) {
pm->ps->weaponstate = WEAPON_READY;
// STONELANCE
/*
if ( pm->ps->weapon == WP_GAUNTLET ) {
PM_StartTorsoAnim( TORSO_STAND2 );
} else {
PM_StartTorsoAnim( TORSO_STAND );
}
*/
// END
return;
}
// STONELANCE
if (pm->ps->weapon == WP_NONE){
return;
}
// END
// check for fire
if ( ! (pm->cmd.buttons & BUTTON_ATTACK) ) {
// STONELANCE
// pm->ps->weaponTime = 0;
pm->ps->weaponTime &= REAR_WEAPON_TIME_MASK;
// END
pm->ps->weaponstate = WEAPON_READY;
return;
}
// start the animation even if out of ammo
if ( pm->ps->weapon == WP_GAUNTLET ) {
// the guantlet only "fires" when it actually hits something
if ( !pm->gauntletHit ) {
// STONELANCE
// pm->ps->weaponTime = 0;
pm->ps->weaponTime &= REAR_WEAPON_TIME_MASK;
// END
pm->ps->weaponstate = WEAPON_READY;
return;
}
// STONELANCE
}
/*
PM_StartTorsoAnim( TORSO_ATTACK2 );
} else {
PM_StartTorsoAnim( TORSO_ATTACK );
}
*/
// END
pm->ps->weaponstate = WEAPON_FIRING;
// check for out of ammo
if ( ! pm->ps->ammo[ pm->ps->weapon ] ) {
PM_AddEvent( EV_NOAMMO );
// STONELANCE
// pm->ps->weaponTime += 500;
pm->ps->weaponTime += (500 & NORMAL_WEAPON_TIME_MASK);
// END
return;
}
// take an ammo away if not infinite
if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) {
pm->ps->ammo[ pm->ps->weapon ]--;
}
// fire weapon
PM_AddEvent( EV_FIRE_WEAPON );
switch( pm->ps->weapon ) {
default:
case WP_GAUNTLET:
addTime = 400;
break;
case WP_LIGHTNING:
addTime = 50;
break;
case WP_SHOTGUN:
addTime = 1000;
break;
case WP_MACHINEGUN:
addTime = 100;
break;
case WP_GRENADE_LAUNCHER:
//addTime = 800; //TBB - too fast
addTime = 1000; //TBB
break;
case WP_ROCKET_LAUNCHER:
//addTime = 800; //TBB - too fast
addTime = 1200; //TBB
break;
case WP_PLASMAGUN:
//addTime = 100; //TBB - too fast
addTime = 300; //TBB - compensate for slight increase damage relative to machine gun
break;
case WP_RAILGUN:
addTime = 1500;
break;
case WP_BFG:
addTime = 200;
break;
// Q3Rally Code Start
/*
case WP_GRAPPLING_HOOK:
addTime = 400;
break;
*/
case WP_FLAME_THROWER:
//addTime = 40; //TBB bit too short
addTime = 150;
break;
// Q3Rally Code END
#ifdef MISSIONPACK
case WP_NAILGUN:
addTime = 1000;
break;
case WP_PROX_LAUNCHER:
addTime = 800;
break;
case WP_CHAINGUN:
addTime = 30;
break;
#endif
}
#ifdef MISSIONPACK
if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
addTime /= 1.5;
}
else
if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
addTime /= 1.3;
}
else
#endif
if ( pm->ps->powerups[PW_HASTE] ) {
// STONELANCE
// addTime /= 1.3;
addTime /= 2.0;
// END
}
// STONELANCE
// pm->ps->weaponTime += addTime;
pm->ps->weaponTime += (addTime & NORMAL_WEAPON_TIME_MASK);
// END
}
/*
==============
PM_ALT_Weapon
Generates alt_weapon events and modifes the alt_weapon counter
==============
*/
static void PM_Alt_Weapon( void ) {
int addTime;
int ammo_use;
// don't allow attack until all buttons are up
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return;
}
// ignore if spectator
if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
return;
}
// check for dead player
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
pm->ps->weapon = WP_NONE;
return;
}
// check for item using
if ( pm->cmd.buttons & BUTTON_USE_HOLDABLE ) {
if ( ! ( pm->ps->pm_flags & PMF_USE_ITEM_HELD ) ) {
if ( bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag == HI_MEDKIT
&& pm->ps->stats[STAT_HEALTH] >= (pm->ps->stats[STAT_MAX_HEALTH] + 25) ) {
// don't use medkit if at max health
} else {
pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
}
return;
}
} else {
pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
}
// make weapon function
// STONELANCE
// if ( pm->ps->weaponTime > 0 ) {
// pm->ps->weaponTime -= pml.msec;
if ( (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK) > 0 ) {
if ((pml.msec & NORMAL_WEAPON_TIME_MASK) >= (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK))
pm->ps->weaponTime &= REAR_WEAPON_TIME_MASK; // = 0
else
pm->ps->weaponTime -= (pml.msec & NORMAL_WEAPON_TIME_MASK);
// END
}
// check for weapon change
// can't change if weapon is firing, but can change
// again if lowering or raising
// STONELANCE
// if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
if ( (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK) <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
// END
if ( pm->ps->weapon != pm->cmd.weapon ) {
PM_BeginWeaponChange( pm->cmd.weapon );
}
}
// STONELANCE
// if ( pm->ps->weaponTime > 0 ) {
if ( (pm->ps->weaponTime & NORMAL_WEAPON_TIME_MASK) > 0 ) {
return;
}
// END
// change weapon if time
if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
PM_FinishWeaponChange();
return;
}
if ( pm->ps->weaponstate == WEAPON_RAISING ) {
pm->ps->weaponstate = WEAPON_READY;
// STONELANCE
/*
if ( pm->ps->weapon == WP_GAUNTLET ) {
PM_StartTorsoAnim( TORSO_STAND2 );
} else {
PM_StartTorsoAnim( TORSO_STAND );
}
*/
// END
return;
}
// STONELANCE
if (pm->ps->weapon == WP_NONE){
return;
}
// END
// check for fire
if ( ! (pm->cmd.buttons & BUTTON_ALT_ATTACK) ) {
// STONELANCE
// pm->ps->weaponTime = 0;
pm->ps->weaponTime &= REAR_WEAPON_TIME_MASK;
// END
pm->ps->weaponstate = WEAPON_READY;
return;
}
// start the animation even if out of ammo
if ( pm->ps->weapon == WP_GAUNTLET ) {
// the guantlet only "fires" when it actually hits something
if ( !pm->gauntletHit ) {
// STONELANCE
// pm->ps->weaponTime = 0;
pm->ps->weaponTime &= REAR_WEAPON_TIME_MASK;
// END
pm->ps->weaponstate = WEAPON_READY;
return;
}
// STONELANCE
}
/*
PM_StartTorsoAnim( TORSO_ATTACK2 );
} else {
PM_StartTorsoAnim( TORSO_ATTACK );
}
*/
// END
pm->ps->weaponstate = WEAPON_FIRING;
// check for out of ammo
if ( ! pm->ps->ammo[ pm->ps->weapon ] ){
//if ( ! pm->ps->ammo[ pm->ps->weapon ] && pm->ps->ammo[ pm->ps->weapon ] < -1 ) {
PM_AddEvent( EV_NOAMMO );
// STONELANCE
// pm->ps->weaponTime += 500;
pm->ps->weaponTime += (500 & NORMAL_WEAPON_TIME_MASK);
// END
return;
}
/*
// take an ammo away if not infinite
if ( pm->ps->ammo[ pm->ps->weapon ] != -1 ) {
pm->ps->ammo[ pm->ps->weapon ]--;
}
*/
/* TBB - AMMO MANAGEMENT, the goal:
if there is not enough ammo to activate alt,
keep the same ammo amount without switching the weapon */
switch( pm->ps->weapon ) {
case WP_RAILGUN:
ammo_use = 3; //megarailgun
break;
case WP_LIGHTNING:
ammo_use = 1;
break;
case WP_MACHINEGUN:
ammo_use = 1;
break;
case WP_SHOTGUN:
ammo_use = 1;
break;
case WP_GRENADE_LAUNCHER:
ammo_use = 3; //there are 6 trajectories in g_missile.c: G_ExplodeCluster()
break;
case WP_ROCKET_LAUNCHER:
ammo_use = 1; //homing
break;
case WP_PLASMAGUN:
ammo_use = 1; //rotational defense
break;
case WP_BFG:
ammo_use = 1;
break;
case WP_FLAME_THROWER:
ammo_use = 3;
break;
default:
ammo_use = 1;
break;
}
if ( pm->ps->ammo[ pm->ps->weapon ] > ammo_use ) {
pm->ps->ammo[ pm->ps->weapon ] -= ammo_use;
} else if ( pm->ps->ammo[ pm->ps->weapon ] == ammo_use ) {
pm->ps->ammo[ pm->ps->weapon ] = 0;
} else if ( pm->ps->ammo[ pm->ps->weapon ] < ammo_use) {
pm->ps->ammo[ pm->ps->weapon ] = pm->ps->ammo[ pm->ps->weapon ];
return;
}
//TBB FIN
// fire weapon
PM_AddEvent( EV_ALTFIRE_WEAPON );
switch( pm->ps->weapon ) {
default:
case WP_GAUNTLET:
addTime = 400;
break;
case WP_LIGHTNING:
addTime = 50;
break;
case WP_SHOTGUN:
addTime = 1000;
break;
case WP_MACHINEGUN:
addTime = 100;
break;
case WP_GRENADE_LAUNCHER:
addTime = 1200;
break;
case WP_ROCKET_LAUNCHER:
addTime = 2000;
break;
case WP_PLASMAGUN:
//TBB - using alt plasma, will use up 2 more cells from regular 1
addTime = 1000; //TBB new plasmagun
//TBB FIN
break;
case WP_RAILGUN:
addTime = 3000;
break;
case WP_BFG:
addTime = 200;
break;
// Q3Rally Code Start
/*
case WP_GRAPPLING_HOOK:
addTime = 400;
break;
*/
case WP_FLAME_THROWER:
//TBB using alt flame, will use up 2 more flames from regular 1
//addTime = 40;
addTime = 300; //increased addtime for more control of ammo use per mouse click
//TBB FIN
break;
// Q3Rally Code END
#ifdef MISSIONPACK
case WP_NAILGUN:
addTime = 1000;
break;
case WP_PROX_LAUNCHER:
addTime = 800;
break;
case WP_CHAINGUN:
addTime = 30;
break;
#endif
}
#ifdef MISSIONPACK
if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_SCOUT ) {
addTime /= 1.5;
}
else
if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) {
addTime /= 1.3;
}
else
#endif
if ( pm->ps->powerups[PW_HASTE] ) {
// STONELANCE
// addTime /= 1.3;
addTime /= 2.0;
// END
}
// STONELANCE
// pm->ps->weaponTime += addTime;
pm->ps->weaponTime += (addTime & NORMAL_WEAPON_TIME_MASK);
// END
}
// STONELANCE
/*
==============
PM_RearWeapon
Generates rearweapon events and modifes the weapon counter
==============
*/
void PM_RearWeapon( void ) {
int weapon, addTime, i;
// don't allow attack until all buttons are up
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return;
}
// ignore if spectator
if ( pm->ps->persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
return;
}
// check for dead player
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
pm->ps->weapon = WP_NONE;
return;
}
for (i = RWP_SMOKE; i < WP_NUM_WEAPONS; i++){
if (pm->ps->stats[STAT_WEAPONS] & ( 1 << i ) && !pm->ps->ammo[ i ]){
pm->ps->stats[STAT_WEAPONS] &= ~( 1 << i );
}
if (pm->ps->stats[STAT_WEAPONS] & ( 1 << i ) && pm->ps->ammo[ i ])
break;
}
if (i < WP_NUM_WEAPONS){
weapon = i;
}
else {
return; // no weapon
}
// make weapon function
if ( (pm->ps->weaponTime & REAR_WEAPON_TIME_MASK) > 0 ) {
if ((pml.msec & NORMAL_WEAPON_TIME_MASK) >= ((pm->ps->weaponTime & REAR_WEAPON_TIME_MASK) >> 16))
pm->ps->weaponTime &= NORMAL_WEAPON_TIME_MASK; // = 0
else
pm->ps->weaponTime -= ((pml.msec & NORMAL_WEAPON_TIME_MASK) << 16);
}
if ( (pm->ps->weaponTime & REAR_WEAPON_TIME_MASK) > 0 ) {
return;
}
// check for fire
if ( !(pm->cmd.buttons & BUTTON_REARATTACK) ) {
pm->ps->weaponTime &= NORMAL_WEAPON_TIME_MASK;
return;
}
// check for out of ammo
if ( !pm->ps->ammo[ weapon ] ) {
// PM_AddEvent( EV_NOAMMO );
pm->ps->weaponTime += (500 & NORMAL_WEAPON_TIME_MASK) << 16;
return;
}
// take an ammo away if not infinite
if ( pm->ps->ammo[ weapon ] != -1 ) {
pm->ps->ammo[ weapon ]--;
}
// Com_Printf( "pm->ps->weaponTime %i\n", ( pm->ps->weaponTime & REAR_WEAPON_TIME_MASK ) >> 16 );
// fire weapon
#ifndef CGAME
BG_AddPredictableEventToPlayerstate( EV_FIRE_REARWEAPON, weapon, pm->ps );
#endif
switch( weapon ) {
default:
case RWP_OIL:
addTime = 200;
break;
case RWP_SMOKE:
addTime = 600;
break;
case RWP_BIO:
addTime = 200;
break;
case RWP_MINE:
addTime = 1500;
break;
case RWP_FLAME:
addTime = 600;
break;
}
if ( pm->ps->powerups[PW_HASTE] ) {
addTime /= 2.0;
}
pm->ps->weaponTime += ((addTime & NORMAL_WEAPON_TIME_MASK) << 16);
// Com_Printf( "pm->ps->weaponTime %i\n", ( pm->ps->weaponTime & REAR_WEAPON_TIME_MASK ) >> 16 );
}
// END
/*
================
PM_Animate
================
*/
// STONELANCE (removed function)
/*
static void PM_Animate( void ) {
if ( pm->cmd.buttons & BUTTON_GESTURE ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_GESTURE );
pm->ps->torsoTimer = TIMER_GESTURE;
PM_AddEvent( EV_TAUNT );
}
#ifdef MISSIONPACK
} else if ( pm->cmd.buttons & BUTTON_GETFLAG ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_GETFLAG );
pm->ps->torsoTimer = 600; //TIMER_GESTURE;
}
} else if ( pm->cmd.buttons & BUTTON_GUARDBASE ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_GUARDBASE );
pm->ps->torsoTimer = 600; //TIMER_GESTURE;
}
} else if ( pm->cmd.buttons & BUTTON_PATROL ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_PATROL );
pm->ps->torsoTimer = 600; //TIMER_GESTURE;
}
} else if ( pm->cmd.buttons & BUTTON_FOLLOWME ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_FOLLOWME );
pm->ps->torsoTimer = 600; //TIMER_GESTURE;
}
} else if ( pm->cmd.buttons & BUTTON_AFFIRMATIVE ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_AFFIRMATIVE);
pm->ps->torsoTimer = 600; //TIMER_GESTURE;
}
} else if ( pm->cmd.buttons & BUTTON_NEGATIVE ) {
if ( pm->ps->torsoTimer == 0 ) {
PM_StartTorsoAnim( TORSO_NEGATIVE );
pm->ps->torsoTimer = 600; //TIMER_GESTURE;
}
#endif
}
}
*/
// END
/*
================
PM_DropTimers
================
*/
static void PM_DropTimers( void ) {
// drop misc timing counter
if ( pm->ps->pm_time ) {
if ( pml.msec >= pm->ps->pm_time ) {
pm->ps->pm_flags &= ~PMF_ALL_TIMES;
pm->ps->pm_time = 0;
} else {
pm->ps->pm_time -= pml.msec;
}
}
// drop animation counter
// STONELANCE
/*
if ( pm->ps->legsTimer > 0 ) {
pm->ps->legsTimer -= pml.msec;
if ( pm->ps->legsTimer < 0 ) {
pm->ps->legsTimer = 0;
}
}
if ( pm->ps->torsoTimer > 0 ) {
pm->ps->torsoTimer -= pml.msec;
if ( pm->ps->torsoTimer < 0 ) {
pm->ps->torsoTimer = 0;
}
}
*/
// END
}
/*
================
PM_UpdateViewAngles
This can be used as another entry point when only the viewangles
are being updated instead of a full move
================
*/
// STONELANCE
void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd, int controlMode ) {
// void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
// END
short temp;
int i;
if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
return; // no view changes at all
}
// STONELANCE - can still look around when dead
/*
if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
return; // no view changes at all
}
*/
// END
// circularly clamp the angles with deltas
for (i=0 ; i<3 ; i++) {
temp = cmd->angles[i] + ps->delta_angles[i];
if ( i == PITCH ) {
// don't let the player look up or down more than 90 degrees
if ( temp > 16000 ) {
ps->delta_angles[i] = 16000 - cmd->angles[i];
temp = 16000;
} else if ( temp < -16000 ) {
ps->delta_angles[i] = -16000 - cmd->angles[i];
temp = -16000;
}
}
// STONELANCE use damage yaw and pitch for view angles
if (controlMode == CT_MOUSE){
if (i == PITCH){
ps->damagePitch = temp;
}
if (i == YAW){
ps->damageYaw = temp;
}
}
// END
// SKWID
// ps->viewangles[i] = SHORT2ANGLE(temp);
}
// END
}
/*
================
PmoveSingle
================
*/
void trap_SnapVector( float *v );
void PmoveSingle (pmove_t *pmove) {
// STONELANCE
vec3_t delta;
float dot;
// END
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;
// STONELANCE
/*
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies
}
*/
// END
// make sure walking button is clear if they are running, to avoid
// proxy no-footsteps cheats
// STONELANCE
/*
if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
pm->cmd.buttons &= ~BUTTON_WALKING;
}
*/
// END
// set the talk balloon flag
if ( pm->cmd.buttons & BUTTON_TALK ) {
pm->ps->eFlags |= EF_TALK;
} else {
pm->ps->eFlags &= ~EF_TALK;
}
// set the firing flag for continuous beam weapons
if ( !(pm->ps->pm_flags & PMF_RESPAWNED) && pm->ps->pm_type != PM_INTERMISSION && pm->ps->pm_type != PM_NOCLIP
&& ( pm->cmd.buttons & BUTTON_ATTACK ) && pm->ps->ammo[ pm->ps->weapon ] ) {
pm->ps->eFlags |= EF_FIRING;
} else {
pm->ps->eFlags &= ~EF_FIRING;
}
// clear the respawned flag if attack and use are cleared
if ( pm->ps->stats[STAT_HEALTH] > 0 &&
!( pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE) ) ) {
pm->ps->pm_flags &= ~PMF_RESPAWNED;
}
// if talk button is down, dissallow all other input
// this is to prevent any possible intercept proxy from
// adding fake talk balloons
if ( pmove->cmd.buttons & BUTTON_TALK ) {
// keep the talk button set tho for when the cmd.serverTime > 66 msec
// and the same cmd is used multiple times in Pmove
pmove->cmd.buttons = BUTTON_TALK;
pmove->cmd.forwardmove = 0;
pmove->cmd.rightmove = 0;
pmove->cmd.upmove = 0;
}
// clear all pmove local vars
memset (&pml, 0, sizeof(pml));
// determine the time
pml.msec = pmove->cmd.serverTime - pm->ps->commandTime;
if ( pml.msec < 1 ) {
pml.msec = 1;
} else if ( pml.msec > 200 ) {
pml.msec = 200;
}
pm->ps->commandTime = pmove->cmd.serverTime;
// save old org in case we get stuck
VectorCopy (pm->ps->origin, pml.previous_origin);
// save old velocity for crashlanding
VectorCopy (pm->ps->velocity, pml.previous_velocity);
pml.frametime = pml.msec * 0.001;
// update the viewangles
// STONELANCE
// PM_UpdateViewAngles( pm->ps, &pm->cmd );
PM_UpdateViewAngles( pm->ps, &pm->cmd, pm->controlMode );
// AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
delta[YAW] = SHORT2ANGLE(pm->ps->damageYaw);
delta[PITCH] = SHORT2ANGLE(pm->ps->damagePitch);
delta[ROLL] = 0;
AngleVectors (delta, pml.forward, pml.right, pml.up);
// END
if ( pm->cmd.upmove < 10 ) {
// not holding jump
pm->ps->pm_flags &= ~PMF_JUMP_HELD;
}
// decide if backpedaling animations should be used
// STONELANCE
/*
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;
}
*/
// END
if ( pm->ps->pm_type >= PM_DEAD ) {
pm->cmd.forwardmove = 0;
pm->cmd.rightmove = 0;
pm->cmd.upmove = 0;
}
if ( pm->ps->pm_type == PM_SPECTATOR ) {
PM_CheckDuck ();
PM_FlyMove ();
PM_DropTimers ();
return;
}
if ( pm->ps->pm_type == PM_NOCLIP ) {
PM_NoclipMove ();
PM_DropTimers ();
return;
}
if (pm->ps->pm_type == PM_FREEZE) {
return; // no movement at all
}
if ( pm->ps->pm_type == PM_INTERMISSION || pm->ps->pm_type == PM_SPINTERMISSION) {
return; // no movement at all
}
// set watertype, and waterlevel
// STONELANCE - FIXME calculate water levels in bg_physics
PM_SetWaterLevel();
pml.previous_waterlevel = pmove->waterlevel;
// set mins, maxs, and viewheight
// STONELANCE
// PM_CheckDuck ();
VectorSet(pm->mins, -CAR_WIDTH/2, -CAR_WIDTH/2, -CAR_WIDTH/2);
VectorSet(pm->maxs, CAR_WIDTH/2, CAR_WIDTH/2, CAR_WIDTH/2);
pm->ps->viewheight = 0;
// END
// set groundentity
// STONELANCE
/*
PM_GroundTrace();
if ( pm->ps->pm_type == PM_DEAD ) {
PM_DeadMove ();
}
*/
// END
PM_DropTimers();
// SKWID( PM_DriveMove does it all )
// STONELANCE
// dont do physics if the player has been gibed
if ( pm->ps->stats[STAT_HEALTH] > GIB_HEALTH ){
int i;
pml.physicsSplit = 0;
PM_DriveMove(pm->car, pml.frametime, qtrue);
if( VectorNAN( pm->car->sBody.r ) )
VectorClear( pm->car->sBody.r );
if( VectorNAN( pm->car->sBody.v ) )
VectorClear( pm->car->sBody.v );
if( VectorNAN( pm->car->sBody.L ) )
VectorClear( pm->car->sBody.L );
// translate car values to player angles, etc
VectorCopy(pm->car->sBody.v, pm->ps->velocity);
VectorCopy(pm->car->sBody.r, pm->ps->origin);
VectorCopy(pm->car->sBody.L, pm->ps->angularMomentum);
OrientationToAngles(pm->car->sBody.t, pm->ps->viewangles);
if( VectorNAN( pm->ps->viewangles ) )
VectorClear( pm->ps->viewangles );
// VectorSubtract(pm->car->sPoints[FL_FRAME].r, pm->car->sPoints[FL_WHEEL].r, delta);
// dot = DotProduct(delta, pm->car->sBody.up);
dot = pm->car->sBody.curSpringLengths[FL_WHEEL] - CP_SPRING_MINLEN;
if (dot > CP_SPRING_MAXLEN - CP_SPRING_MINLEN)
dot = CP_SPRING_MAXLEN - CP_SPRING_MINLEN;
else if (dot < 0)
dot = 0;
pm->ps->legsTimer = (int)(CP_SPRING_SCALE * dot);
// VectorSubtract(pm->car->sPoints[FR_FRAME].r, pm->car->sPoints[FR_WHEEL].r, delta);
// dot = DotProduct(delta, pm->car->sBody.up);
dot = pm->car->sBody.curSpringLengths[FR_WHEEL] - CP_SPRING_MINLEN;
if (dot > CP_SPRING_MAXLEN - CP_SPRING_MINLEN)
dot = CP_SPRING_MAXLEN - CP_SPRING_MINLEN;
else if (dot < 0)
dot = 0;
pm->ps->legsAnim = (int)(CP_SPRING_SCALE * dot);
// VectorSubtract(pm->car->sPoints[RL_FRAME].r, pm->car->sPoints[RL_WHEEL].r, delta);
// dot = DotProduct(delta, pm->car->sBody.up);
dot = pm->car->sBody.curSpringLengths[RL_WHEEL] - CP_SPRING_MINLEN;
if (dot > CP_SPRING_MAXLEN - CP_SPRING_MINLEN)
dot = CP_SPRING_MAXLEN - CP_SPRING_MINLEN;
else if (dot < 0)
dot = 0;
pm->ps->torsoTimer = (int)(CP_SPRING_SCALE * dot);
// VectorSubtract(pm->car->sPoints[RR_FRAME].r, pm->car->sPoints[RR_WHEEL].r, delta);
// dot = DotProduct(delta, pm->car->sBody.up);
dot = pm->car->sBody.curSpringLengths[RR_WHEEL] - CP_SPRING_MINLEN;
if (dot > CP_SPRING_MAXLEN - CP_SPRING_MINLEN)
dot = CP_SPRING_MAXLEN - CP_SPRING_MINLEN;
else if (dot < 0)
dot = 0;
pm->ps->torsoAnim = (int)(CP_SPRING_SCALE * dot);
pm->ps->stats[STAT_RPM] = pm->car->rpm;
pm->ps->stats[STAT_GEAR] = pm->car->gear;
// used to keep track of time since last onGround for resetCar
/*
VectorSet(delta, 0, 0, 1);
for (i = 0; i < NUM_CAR_POINTS; i++){
if (pm->car->sPoints[i].onGround && DotProduct(pm->car->sPoints[i].normals[0], delta) > 0.3){
pm->car->sPoints[i].onGroundTime = pm->cmd.serverTime;
pm->car->tPoints[i].onGroundTime = pm->cmd.serverTime;
}
else {
pm->car->sPoints[i].offGroundTime = pm->cmd.serverTime;
pm->car->tPoints[i].offGroundTime = pm->cmd.serverTime;
}
}
*/
for (i = 0; i < FL_FRAME; i++)
{
if( pm->car->sPoints[i].onGround && pm->car->sPoints[i].normals[0][2] > 0.3f )
{
pm->car->wheelOnGroundTime = pm->cmd.serverTime;
break;
}
}
if( pm->car->wheelOnGroundTime != pm->cmd.serverTime )
pm->car->wheelsOffGroundTime = pm->cmd.serverTime;
for (i = FL_FRAME; i < NUM_CAR_POINTS; i++)
{
if( pm->car->sPoints[i].onGround )
{
pm->car->onGroundTime = pm->cmd.serverTime;
break;
}
}
if( pm->car->onGroundTime < pm->cmd.serverTime - 100 )
pm->car->offGroundTime = pm->cmd.serverTime;
}
// END
/*
#ifdef MISSIONPACK
if ( pm->ps->powerups[PW_INVULNERABILITY] ) {
PM_InvulnerabilityMove();
} else
#endif
if ( pm->ps->powerups[PW_FLIGHT] ) {
// flight powerup doesn't allow jump and has different friction
PM_FlyMove();
} else if (pm->ps->pm_flags & PMF_GRAPPLE_PULL) {
PM_GrappleMove();
// We can wiggle a bit
PM_AirMove();
} else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) {
PM_WaterJumpMove();
} else if ( pm->waterlevel > 1 ) {
// swimming
PM_WaterMove();
} else if ( pml.walking ) {
// walking on ground
PM_WalkMove();
} else {
// airborne
PM_AirMove();
}
*/
// END
// STONELANCE
// PM_Animate();
// END
// set groundentity, watertype, and waterlevel
PM_GroundTrace();
PM_SetWaterLevel();
// weapons
PM_Weapon();
PM_Alt_Weapon();
// STONELANCE
PM_RearWeapon();
// END
// torso animation
// STONELANCE
// PM_TorsoAnimation();
// END
// footstep events / legs animations
// SKWID( removed function )
// PM_Footsteps();
// END
// entering / leaving water splashes
PM_WaterEvents();
// snap some parts of playerstate to save network bandwidth
// STONELANCE
// trap_SnapVector( pm->ps->velocity );
// END
pm = NULL;
}
/*
================
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;
}
pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
// STONELANCE
/*
if (pmove->client){
Com_Printf("client time %d, msec %d\n", pmove->ps->commandTime, finalTime - pmove->ps->commandTime);
else
Com_Printf("server time %d, msec %d\n", pmove->ps->commandTime, finalTime - pmove->ps->commandTime);
Com_Printf("r %f, %f, %f\n", pmove->car->sBody.r[0], pmove->car->sBody.r[1], pmove->car->sBody.r[2]);
Com_Printf("v %f, %f, %f\n", pmove->car->sBody.v[0], pmove->car->sBody.v[1], pmove->car->sBody.v[2]);
Com_Printf("w %f, %f, %f\n", pmove->car->sBody.w[0], pmove->car->sBody.w[1], pmove->car->sBody.w[2]);
Com_Printf("L %f, %f, %f\n", pmove->car->sBody.L[0], pmove->car->sBody.L[1], pmove->car->sBody.L[2]);
Com_Printf("CoM %f, %f, %f\n", pmove->car->sBody.CoM[0], pmove->car->sBody.CoM[1], pmove->car->sBody.CoM[2]);
Com_Printf("t:\n");
Com_Printf("%f, %f, %f\n", pmove->car->sBody.t[0][0], pmove->car->sBody.t[0][1], pmove->car->sBody.t[0][2]);
Com_Printf("%f, %f, %f\n", pmove->car->sBody.t[1][0], pmove->car->sBody.t[1][1], pmove->car->sBody.t[1][2]);
Com_Printf("%f, %f, %f\n", pmove->car->sBody.t[2][0], pmove->car->sBody.t[2][1], pmove->car->sBody.t[2][2]);
*/
/*
if (pmove->client){
Com_Printf("client time %d, msec %d\n", pmove->ps->commandTime, finalTime - pmove->ps->commandTime);
else
Com_Printf("server time %d, msec %d\n", pmove->ps->commandTime, finalTime - pmove->ps->commandTime);
Com_Printf("springStrength %f\n", pmove->car->springStrength);
Com_Printf("springMaxLength %f\n", pmove->car->springMaxLength);
Com_Printf("springMinLength %f\n", pmove->car->springMinLength);
Com_Printf("shockStrength %f\n", pmove->car->shockStrength);
Com_Printf("wheelAngle %f\n", pmove->car->wheelAngle);
Com_Printf("throttle %f\n", pmove->car->throttle);
Com_Printf("gear %d\n", pmove->car->gear);
Com_Printf("rpm %f\n", pmove->car->rpm);
Com_Printf("aCOF %f\n", pmove->car->aCOF);
Com_Printf("sCOF %f\n", pmove->car->sCOF);
Com_Printf("kCOF %f\n", pmove->car->kCOF);
Com_Printf("dfCOF %f\n", pmove->car->dfCOF);
Com_Printf("ewCOF %f\n", pmove->car->ewCOF);
Com_Printf("inverseBodyInertiaTensor:\n");
Com_Printf("%f, %f, %f\n", pmove->car->inverseBodyInertiaTensor[0][0], pmove->car->inverseBodyInertiaTensor[0][1], pmove->car->inverseBodyInertiaTensor[0][2]);
Com_Printf("%f, %f, %f\n", pmove->car->inverseBodyInertiaTensor[1][0], pmove->car->inverseBodyInertiaTensor[1][1], pmove->car->inverseBodyInertiaTensor[1][2]);
Com_Printf("%f, %f, %f\n", pmove->car->inverseBodyInertiaTensor[2][0], pmove->car->inverseBodyInertiaTensor[2][1], pmove->car->inverseBodyInertiaTensor[2][2]);
*/
/*
if (pmove->client){
Com_Printf("client time %d, msec %d\n", pmove->ps->commandTime, finalTime - pmove->ps->commandTime);
else
Com_Printf("server time %d, msec %d\n", pmove->ps->commandTime, finalTime - pmove->ps->commandTime);
Com_Printf("r %f, %f, %f\n", pmove->car->sPoints[i].r[0], pmove->car->sPoints[i].r[1], pmove->car->sPoints[i].r[2]);
Com_Printf("v %f, %f, %f\n", pmove->car->sPoints[i].v[0], pmove->car->sPoints[i].v[1], pmove->car->sPoints[i].v[2]);
Com_Printf("w %f\n", pmove->car->sPoints[i].w);
Com_Printf("netForce %f, %f, %f\n", pmove->car->sPoints[i].netForce[0], pmove->car->sPoints[i].netForce[1], pmove->car->sPoints[i].netForce[2]);
Com_Printf("netMoment %f\n", pmove->car->sPoints[i].netMoment);
Com_Printf("normals %f, %f, %f\n", pmove->car->sPoints[i].normals[0][0], pmove->car->sPoints[i].normals[0][1], pmove->car->sPoints[i].normals[0][2]);
Com_Printf("mass %f\n", pmove->car->sPoints[i].mass);
Com_Printf("elasticity %f\n", pmove->car->sPoints[i].elasticity);
Com_Printf("kcof %f\n", pmove->car->sPoints[i].kcof);
Com_Printf("scof %f\n", pmove->car->sPoints[i].scof);
Com_Printf("fluidDensity %f\n", pmove->car->sPoints[i].fluidDensity);
Com_Printf("onGround %d\n", pmove->car->sPoints[i].onGround);
Com_Printf("slipping %d\n", pmove->car->sPoints[i].slipping);
*/
// END
// chop the move up if it is too long, to prevent framerate
// dependent behavior
while ( pmove->ps->commandTime != finalTime ) {
int msec;
msec = finalTime - pmove->ps->commandTime;
if ( pmove->pmove_fixed ) {
if ( msec > pmove->pmove_msec ) {
msec = pmove->pmove_msec;
}
}
else {
// STONELANCE
// if ( msec > 66 ) {
// msec = 66;
// }
if ( msec > 12 ) {
msec = 12;
}
// END
}
pmove->cmd.serverTime = pmove->ps->commandTime + msec;
PmoveSingle( pmove );
// STONELANCE
// if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
// pmove->cmd.upmove = 20;
// }
// END
}
//PM_CheckStuck();
}