mirror of
https://github.com/ioquake/jedi-academy.git
synced 2024-11-25 21:51:04 +00:00
11217 lines
292 KiB
C
11217 lines
292 KiB
C
// 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"
|
|
#include "bg_strap.h"
|
|
#include "../ghoul2/G2.h"
|
|
|
|
#ifdef QAGAME
|
|
#include "g_local.h" //ahahahahhahahaha@$!$!
|
|
#endif
|
|
|
|
#define MAX_WEAPON_CHARGE_TIME 5000
|
|
|
|
#ifdef QAGAME
|
|
extern void G_CheapWeaponFire(int entNum, int ev);
|
|
extern qboolean TryGrapple(gentity_t *ent); //g_cmds.c
|
|
extern void trap_FX_PlayEffect( const char *file, vec3_t org, vec3_t fwd, int vol, int rad );
|
|
#endif
|
|
|
|
#include "../namespace_begin.h"
|
|
extern qboolean BG_FullBodyTauntAnim( int anim );
|
|
extern float PM_WalkableGroundDistance(void);
|
|
extern qboolean PM_GroundSlideOkay( float zNormal );
|
|
extern saberInfo_t *BG_MySaber( int clientNum, int saberNum );
|
|
|
|
pmove_t *pm;
|
|
pml_t pml;
|
|
|
|
bgEntity_t *pm_entSelf = NULL;
|
|
bgEntity_t *pm_entVeh = NULL;
|
|
|
|
qboolean gPMDoSlowFall = qfalse;
|
|
|
|
qboolean pm_cancelOutZoom = qfalse;
|
|
|
|
// movement parameters
|
|
float pm_stopspeed = 100.0f;
|
|
float pm_duckScale = 0.50f;
|
|
float pm_swimScale = 0.50f;
|
|
float pm_wadeScale = 0.70f;
|
|
|
|
float pm_vehicleaccelerate = 36.0f;
|
|
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;
|
|
|
|
float forceSpeedLevels[4] =
|
|
{
|
|
1, //rank 0?
|
|
1.25,
|
|
1.5,
|
|
1.75
|
|
};
|
|
|
|
int forcePowerNeeded[NUM_FORCE_POWER_LEVELS][NUM_FORCE_POWERS] =
|
|
{
|
|
{ //nothing should be usable at rank 0..
|
|
999,//FP_HEAL,//instant
|
|
999,//FP_LEVITATION,//hold/duration
|
|
999,//FP_SPEED,//duration
|
|
999,//FP_PUSH,//hold/duration
|
|
999,//FP_PULL,//hold/duration
|
|
999,//FP_TELEPATHY,//instant
|
|
999,//FP_GRIP,//hold/duration
|
|
999,//FP_LIGHTNING,//hold/duration
|
|
999,//FP_RAGE,//duration
|
|
999,//FP_PROTECT,//duration
|
|
999,//FP_ABSORB,//duration
|
|
999,//FP_TEAM_HEAL,//instant
|
|
999,//FP_TEAM_FORCE,//instant
|
|
999,//FP_DRAIN,//hold/duration
|
|
999,//FP_SEE,//duration
|
|
999,//FP_SABER_OFFENSE,
|
|
999,//FP_SABER_DEFENSE,
|
|
999//FP_SABERTHROW,
|
|
//NUM_FORCE_POWERS
|
|
},
|
|
{
|
|
65,//FP_HEAL,//instant //was 25, but that was way too little
|
|
10,//FP_LEVITATION,//hold/duration
|
|
50,//FP_SPEED,//duration
|
|
20,//FP_PUSH,//hold/duration
|
|
20,//FP_PULL,//hold/duration
|
|
20,//FP_TELEPATHY,//instant
|
|
30,//FP_GRIP,//hold/duration
|
|
1,//FP_LIGHTNING,//hold/duration
|
|
50,//FP_RAGE,//duration
|
|
50,//FP_PROTECT,//duration
|
|
50,//FP_ABSORB,//duration
|
|
50,//FP_TEAM_HEAL,//instant
|
|
50,//FP_TEAM_FORCE,//instant
|
|
20,//FP_DRAIN,//hold/duration
|
|
20,//FP_SEE,//duration
|
|
0,//FP_SABER_OFFENSE,
|
|
2,//FP_SABER_DEFENSE,
|
|
20//FP_SABERTHROW,
|
|
//NUM_FORCE_POWERS
|
|
},
|
|
{
|
|
60,//FP_HEAL,//instant
|
|
10,//FP_LEVITATION,//hold/duration
|
|
50,//FP_SPEED,//duration
|
|
20,//FP_PUSH,//hold/duration
|
|
20,//FP_PULL,//hold/duration
|
|
20,//FP_TELEPATHY,//instant
|
|
30,//FP_GRIP,//hold/duration
|
|
1,//FP_LIGHTNING,//hold/duration
|
|
50,//FP_RAGE,//duration
|
|
25,//FP_PROTECT,//duration
|
|
25,//FP_ABSORB,//duration
|
|
33,//FP_TEAM_HEAL,//instant
|
|
33,//FP_TEAM_FORCE,//instant
|
|
20,//FP_DRAIN,//hold/duration
|
|
20,//FP_SEE,//duration
|
|
0,//FP_SABER_OFFENSE,
|
|
1,//FP_SABER_DEFENSE,
|
|
20//FP_SABERTHROW,
|
|
//NUM_FORCE_POWERS
|
|
},
|
|
{
|
|
50,//FP_HEAL,//instant //You get 5 points of health.. for 50 force points!
|
|
10,//FP_LEVITATION,//hold/duration
|
|
50,//FP_SPEED,//duration
|
|
20,//FP_PUSH,//hold/duration
|
|
20,//FP_PULL,//hold/duration
|
|
20,//FP_TELEPATHY,//instant
|
|
60,//FP_GRIP,//hold/duration
|
|
1,//FP_LIGHTNING,//hold/duration
|
|
50,//FP_RAGE,//duration
|
|
10,//FP_PROTECT,//duration
|
|
10,//FP_ABSORB,//duration
|
|
25,//FP_TEAM_HEAL,//instant
|
|
25,//FP_TEAM_FORCE,//instant
|
|
20,//FP_DRAIN,//hold/duration
|
|
20,//FP_SEE,//duration
|
|
0,//FP_SABER_OFFENSE,
|
|
0,//FP_SABER_DEFENSE,
|
|
20//FP_SABERTHROW,
|
|
//NUM_FORCE_POWERS
|
|
}
|
|
};
|
|
|
|
float forceJumpHeight[NUM_FORCE_POWER_LEVELS] =
|
|
{
|
|
32,//normal jump (+stepheight+crouchdiff = 66)
|
|
96,//(+stepheight+crouchdiff = 130)
|
|
192,//(+stepheight+crouchdiff = 226)
|
|
384//(+stepheight+crouchdiff = 418)
|
|
};
|
|
|
|
float forceJumpStrength[NUM_FORCE_POWER_LEVELS] =
|
|
{
|
|
JUMP_VELOCITY,//normal jump
|
|
420,
|
|
590,
|
|
840
|
|
};
|
|
|
|
//rww - Get a pointer to the bgEntity by the index
|
|
bgEntity_t *PM_BGEntForNum( int num )
|
|
{
|
|
bgEntity_t *ent;
|
|
|
|
if (!pm)
|
|
{
|
|
assert(!"You cannot call PM_BGEntForNum outside of pm functions!");
|
|
return NULL;
|
|
}
|
|
|
|
if (!pm->baseEnt)
|
|
{
|
|
assert(!"Base entity address not set");
|
|
return NULL;
|
|
}
|
|
|
|
if (!pm->entSize)
|
|
{
|
|
assert(!"sizeof(ent) is 0, impossible (not set?)");
|
|
return NULL;
|
|
}
|
|
|
|
assert(num >= 0 && num < MAX_GENTITIES);
|
|
|
|
ent = (bgEntity_t *)((byte *)pm->baseEnt + pm->entSize*(num));
|
|
|
|
return ent;
|
|
}
|
|
|
|
qboolean BG_SabersOff( playerState_t *ps )
|
|
{
|
|
if ( !ps->saberHolstered )
|
|
{
|
|
return qfalse;
|
|
}
|
|
if ( ps->fd.saberAnimLevelBase == SS_DUAL
|
|
|| ps->fd.saberAnimLevelBase == SS_STAFF )
|
|
{
|
|
if ( ps->saberHolstered < 2 )
|
|
{
|
|
return qfalse;
|
|
}
|
|
}
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean BG_KnockDownable(playerState_t *ps)
|
|
{
|
|
if (!ps)
|
|
{ //just for safety
|
|
return qfalse;
|
|
}
|
|
|
|
if (ps->m_iVehicleNum)
|
|
{ //riding a vehicle, don't knock me down
|
|
return qfalse;
|
|
}
|
|
|
|
if (ps->emplacedIndex)
|
|
{ //using emplaced gun or eweb, can't be knocked down
|
|
return qfalse;
|
|
}
|
|
|
|
//ok, I guess?
|
|
return qtrue;
|
|
}
|
|
|
|
//I should probably just do a global inline sometime.
|
|
#ifndef __LCC__
|
|
#define PM_INLINE ID_INLINE
|
|
#else
|
|
#define PM_INLINE //none
|
|
#endif
|
|
|
|
//hacky assumption check, assume any client non-humanoid is a rocket trooper
|
|
qboolean PM_INLINE PM_IsRocketTrooper(void)
|
|
{
|
|
/*
|
|
if (pm->ps->clientNum < MAX_CLIENTS &&
|
|
pm->gametype == GT_SIEGE &&
|
|
pm->nonHumanoid)
|
|
{
|
|
return qtrue;
|
|
}
|
|
*/
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
int PM_GetSaberStance(void)
|
|
{
|
|
int anim = BOTH_STAND2;
|
|
saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
|
|
saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
|
|
|
|
if (!pm->ps->saberEntityNum)
|
|
{ //lost it
|
|
return BOTH_STAND1;
|
|
}
|
|
|
|
if ( BG_SabersOff( pm->ps ) )
|
|
{
|
|
return BOTH_STAND1;
|
|
}
|
|
|
|
if ( saber1
|
|
&& saber1->readyAnim != -1 )
|
|
{
|
|
return saber1->readyAnim;
|
|
}
|
|
|
|
if ( saber2
|
|
&& saber2->readyAnim != -1 )
|
|
{
|
|
return saber2->readyAnim;
|
|
}
|
|
|
|
if ( saber1
|
|
&& saber2
|
|
&& !pm->ps->saberHolstered )
|
|
{//dual sabers, both on
|
|
return BOTH_SABERDUAL_STANCE;
|
|
}
|
|
|
|
switch ( pm->ps->fd.saberAnimLevel )
|
|
{
|
|
case SS_DUAL:
|
|
anim = BOTH_SABERDUAL_STANCE;
|
|
break;
|
|
case SS_STAFF:
|
|
anim = BOTH_SABERSTAFF_STANCE;
|
|
break;
|
|
case SS_FAST:
|
|
case SS_TAVION:
|
|
anim = BOTH_SABERFAST_STANCE;
|
|
break;
|
|
case SS_STRONG:
|
|
anim = BOTH_SABERSLOW_STANCE;
|
|
break;
|
|
case SS_NONE:
|
|
case SS_MEDIUM:
|
|
case SS_DESANN:
|
|
default:
|
|
anim = BOTH_STAND2;
|
|
break;
|
|
}
|
|
return anim;
|
|
}
|
|
|
|
qboolean PM_DoSlowFall(void)
|
|
{
|
|
if ( ( (pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && pm->ps->legsTimer > 500 )
|
|
{
|
|
return qtrue;
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
//begin vehicle functions crudely ported from sp -rww
|
|
/*
|
|
====================================================================
|
|
void pitch_roll_for_slope (edict_t *forwhom, vec3_t *slope, vec3_t storeAngles )
|
|
|
|
MG
|
|
|
|
This will adjust the pitch and roll of a monster to match
|
|
a given slope - if a non-'0 0 0' slope is passed, it will
|
|
use that value, otherwise it will use the ground underneath
|
|
the monster. If it doesn't find a surface, it does nothinh\g
|
|
and returns.
|
|
====================================================================
|
|
*/
|
|
|
|
void PM_pitch_roll_for_slope( bgEntity_t *forwhom, vec3_t pass_slope, vec3_t storeAngles )
|
|
{
|
|
vec3_t slope;
|
|
vec3_t nvf, ovf, ovr, startspot, endspot, new_angles = { 0, 0, 0 };
|
|
float pitch, mod, dot;
|
|
|
|
//if we don't have a slope, get one
|
|
if( !pass_slope || VectorCompare( vec3_origin, pass_slope ) )
|
|
{
|
|
trace_t trace;
|
|
|
|
VectorCopy( pm->ps->origin, startspot );
|
|
startspot[2] += pm->mins[2] + 4;
|
|
VectorCopy( startspot, endspot );
|
|
endspot[2] -= 300;
|
|
pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, endspot, forwhom->s.number, MASK_SOLID );
|
|
// if(trace_fraction>0.05&&forwhom.movetype==MOVETYPE_STEP)
|
|
// forwhom.flags(-)FL_ONGROUND;
|
|
|
|
if ( trace.fraction >= 1.0 )
|
|
return;
|
|
|
|
if( !( &trace.plane ) )
|
|
return;
|
|
|
|
if ( VectorCompare( vec3_origin, trace.plane.normal ) )
|
|
return;
|
|
|
|
VectorCopy( trace.plane.normal, slope );
|
|
}
|
|
else
|
|
{
|
|
VectorCopy( pass_slope, slope );
|
|
}
|
|
|
|
if ( forwhom->s.NPC_class == CLASS_VEHICLE )
|
|
{//special code for vehicles
|
|
Vehicle_t *pVeh = forwhom->m_pVehicle;
|
|
vec3_t tempAngles;
|
|
|
|
tempAngles[PITCH] = tempAngles[ROLL] = 0;
|
|
tempAngles[YAW] = pVeh->m_vOrientation[YAW];
|
|
AngleVectors( tempAngles, ovf, ovr, NULL );
|
|
}
|
|
else
|
|
{
|
|
AngleVectors( pm->ps->viewangles, ovf, ovr, NULL );
|
|
}
|
|
|
|
vectoangles( slope, new_angles );
|
|
pitch = new_angles[PITCH] + 90;
|
|
new_angles[ROLL] = new_angles[PITCH] = 0;
|
|
|
|
AngleVectors( new_angles, nvf, NULL, NULL );
|
|
|
|
mod = DotProduct( nvf, ovr );
|
|
|
|
if ( mod<0 )
|
|
mod = -1;
|
|
else
|
|
mod = 1;
|
|
|
|
dot = DotProduct( nvf, ovf );
|
|
|
|
if ( storeAngles )
|
|
{
|
|
storeAngles[PITCH] = dot * pitch;
|
|
storeAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
|
|
}
|
|
else //if ( forwhom->client )
|
|
{
|
|
float oldmins2;
|
|
|
|
pm->ps->viewangles[PITCH] = dot * pitch;
|
|
pm->ps->viewangles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
|
|
oldmins2 = pm->mins[2];
|
|
pm->mins[2] = -24 + 12 * fabs(pm->ps->viewangles[PITCH])/180.0f;
|
|
//FIXME: if it gets bigger, move up
|
|
if ( oldmins2 > pm->mins[2] )
|
|
{//our mins is now lower, need to move up
|
|
//FIXME: trace?
|
|
pm->ps->origin[2] += (oldmins2 - pm->mins[2]);
|
|
//forwhom->currentOrigin[2] = forwhom->client->ps.origin[2];
|
|
//gi.linkentity( forwhom );
|
|
}
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
forwhom->currentAngles[PITCH] = dot * pitch;
|
|
forwhom->currentAngles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
|
|
}
|
|
*/
|
|
}
|
|
|
|
#define FLY_NONE 0
|
|
#define FLY_NORMAL 1
|
|
#define FLY_VEHICLE 2
|
|
#define FLY_HOVER 3
|
|
static int pm_flying = FLY_NONE;
|
|
|
|
void PM_SetSpecialMoveValues (void)
|
|
{
|
|
bgEntity_t *pEnt;
|
|
|
|
if (pm->ps->clientNum < MAX_CLIENTS)
|
|
{ //we know that real players aren't vehs
|
|
pm_flying = FLY_NONE;
|
|
return;
|
|
}
|
|
|
|
//default until we decide otherwise
|
|
pm_flying = FLY_NONE;
|
|
|
|
pEnt = pm_entSelf;
|
|
|
|
if ( pEnt )
|
|
{
|
|
if ( (pm->ps->eFlags2&EF2_FLYING) )// pm->gent->client->moveType == MT_FLYSWIM )
|
|
{
|
|
pm_flying = FLY_NORMAL;
|
|
}
|
|
else if ( pEnt->s.NPC_class == CLASS_VEHICLE )
|
|
{
|
|
if ( pEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
|
|
{
|
|
pm_flying = FLY_VEHICLE;
|
|
}
|
|
else if ( pEnt->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 )
|
|
{
|
|
pm_flying = FLY_HOVER;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PM_SetVehicleAngles( vec3_t normal )
|
|
{
|
|
bgEntity_t *pEnt = pm_entSelf;
|
|
Vehicle_t *pVeh;
|
|
vec3_t vAngles;
|
|
float vehicleBankingSpeed;
|
|
float pitchBias;
|
|
int i;
|
|
|
|
if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pVeh = pEnt->m_pVehicle;
|
|
|
|
//float curVehicleBankingSpeed;
|
|
vehicleBankingSpeed = (pVeh->m_pVehicleInfo->bankingSpeed*32.0f)*pml.frametime;//0.25f
|
|
|
|
if ( vehicleBankingSpeed <= 0
|
|
|| ( pVeh->m_pVehicleInfo->pitchLimit == 0 && pVeh->m_pVehicleInfo->rollLimit == 0 ) )
|
|
{//don't bother, this vehicle doesn't bank
|
|
return;
|
|
}
|
|
//FIXME: do 3 traces to define a plane and use that... smoothes it out some, too...
|
|
//pitch_roll_for_slope( pm->gent, normal, vAngles );
|
|
//FIXME: maybe have some pitch control in water and/or air?
|
|
|
|
if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
|
|
{
|
|
pitchBias = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
//FIXME: gravity does not matter in SPACE!!!
|
|
//center of gravity affects pitch in air/water (FIXME: what about roll?)
|
|
pitchBias = 90.0f*pVeh->m_pVehicleInfo->centerOfGravity[0];//if centerOfGravity is all the way back (-1.0f), vehicle pitches up 90 degrees when in air
|
|
}
|
|
|
|
VectorClear( vAngles );
|
|
if ( pm->waterlevel > 0 )
|
|
{//in water
|
|
//view pitch has some influence when in water
|
|
//FIXME: take center of gravity into account?
|
|
vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5);
|
|
}
|
|
else if ( normal )
|
|
{//have a valid surface below me
|
|
PM_pitch_roll_for_slope( pEnt, normal, vAngles );
|
|
if ( (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
|
|
{//on water
|
|
//view pitch has some influence when on a fluid surface
|
|
//FIXME: take center of gravity into account
|
|
vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.5f + (pitchBias*0.5f);
|
|
}
|
|
}
|
|
else
|
|
{//in air, let pitch match view...?
|
|
//FIXME: take center of gravity into account
|
|
vAngles[PITCH] = pm->ps->viewangles[PITCH]*0.5f + pitchBias;
|
|
//don't bank so fast when in the air
|
|
vehicleBankingSpeed *= (0.125f*pml.frametime);
|
|
}
|
|
//NOTE: if angles are flat and we're moving through air (not on ground),
|
|
// then pitch/bank?
|
|
if ( pVeh->m_pVehicleInfo->rollLimit > 0 )
|
|
{
|
|
//roll when banking
|
|
vec3_t velocity;
|
|
float speed;
|
|
VectorCopy( pm->ps->velocity, velocity );
|
|
velocity[2] = 0.0f;
|
|
speed = VectorNormalize( velocity );
|
|
if ( speed > 32.0f || speed < -32.0f )
|
|
{
|
|
vec3_t rt, tempVAngles;
|
|
float side;
|
|
float dp;
|
|
|
|
// Magic number fun! Speed is used for banking, so modulate the speed by a sine wave
|
|
//FIXME: this banks too early
|
|
speed *= sin( (150 + pml.frametime) * 0.003 );
|
|
|
|
// Clamp to prevent harsh rolling
|
|
if ( speed > 60 )
|
|
speed = 60;
|
|
|
|
VectorCopy( pVeh->m_vOrientation, tempVAngles );
|
|
tempVAngles[ROLL] = 0;
|
|
AngleVectors( tempVAngles, NULL, rt, NULL );
|
|
dp = DotProduct( velocity, rt );
|
|
side = speed * dp;
|
|
vAngles[ROLL] -= side;
|
|
}
|
|
}
|
|
|
|
//cap
|
|
if ( pVeh->m_pVehicleInfo->pitchLimit != -1 )
|
|
{
|
|
if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
|
|
{
|
|
vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
|
|
}
|
|
else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit )
|
|
{
|
|
vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
|
|
}
|
|
}
|
|
|
|
if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
|
|
{
|
|
vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
|
|
}
|
|
else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit )
|
|
{
|
|
vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
|
|
}
|
|
|
|
//do it
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
if ( i == YAW )
|
|
{//yawing done elsewhere
|
|
continue;
|
|
}
|
|
//bank faster the higher the difference is
|
|
/*
|
|
else if ( i == PITCH )
|
|
{
|
|
curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f);
|
|
}
|
|
else if ( i == ROLL )
|
|
{
|
|
curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f);
|
|
}
|
|
|
|
if ( curVehicleBankingSpeed )
|
|
*/
|
|
{
|
|
if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed )
|
|
{
|
|
pVeh->m_vOrientation[i] -= vehicleBankingSpeed;
|
|
}
|
|
else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed )
|
|
{
|
|
pVeh->m_vOrientation[i] += vehicleBankingSpeed;
|
|
}
|
|
else
|
|
{
|
|
pVeh->m_vOrientation[i] = vAngles[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef QAGAME
|
|
extern vmCvar_t cg_paused;
|
|
#endif
|
|
|
|
void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS )
|
|
{
|
|
/*
|
|
float pitchSubtract, pitchDelta, yawDelta;
|
|
//Com_Printf( S_COLOR_RED"PITCH: %4.2f, YAW: %4.2f, ROLL: %4.2f\n", riderPS->viewangles[0],riderPS->viewangles[1],riderPS->viewangles[2]);
|
|
yawDelta = AngleSubtract(riderPS->viewangles[YAW],pVeh->m_vPrevRiderViewAngles[YAW]);
|
|
#ifndef QAGAME
|
|
if ( !cg_paused.integer )
|
|
{
|
|
//Com_Printf( "%d - yawDelta %4.2f\n", pm->cmd.serverTime, yawDelta );
|
|
}
|
|
#endif
|
|
yawDelta *= (4.0f*pVeh->m_fTimeModifier);
|
|
pVeh->m_vOrientation[ROLL] -= yawDelta;
|
|
|
|
pitchDelta = AngleSubtract(riderPS->viewangles[PITCH],pVeh->m_vPrevRiderViewAngles[PITCH]);
|
|
pitchDelta *= (2.0f*pVeh->m_fTimeModifier);
|
|
pitchSubtract = pitchDelta * (fabs(pVeh->m_vOrientation[ROLL])/90.0f);
|
|
pVeh->m_vOrientation[PITCH] += pitchDelta-pitchSubtract;
|
|
if ( pVeh->m_vOrientation[ROLL] > 0 )
|
|
{
|
|
pVeh->m_vOrientation[YAW] += pitchSubtract;
|
|
}
|
|
else
|
|
{
|
|
pVeh->m_vOrientation[YAW] -= pitchSubtract;
|
|
}
|
|
pVeh->m_vOrientation[PITCH] = AngleNormalize180( pVeh->m_vOrientation[PITCH] );
|
|
pVeh->m_vOrientation[YAW] = AngleNormalize360( pVeh->m_vOrientation[YAW] );
|
|
pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
|
|
|
|
VectorCopy( riderPS->viewangles, pVeh->m_vPrevRiderViewAngles );
|
|
*/
|
|
}
|
|
|
|
void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride )
|
|
{
|
|
if ( pVeh && pVeh->m_pVehicleInfo )
|
|
{
|
|
float speedFrac = 1.0f;
|
|
if ( pVeh->m_pVehicleInfo->speedDependantTurning )
|
|
{
|
|
if ( pVeh->m_LandTrace.fraction >= 1.0f
|
|
|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )
|
|
{
|
|
speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f));
|
|
if ( speedFrac < 0.25f )
|
|
{
|
|
speedFrac = 0.25f;
|
|
}
|
|
else if ( speedFrac > 1.0f )
|
|
{
|
|
speedFrac = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
if ( pVeh->m_pVehicleInfo->mousePitch )
|
|
{
|
|
*mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac;
|
|
}
|
|
if ( pVeh->m_pVehicleInfo->mouseYaw )
|
|
{
|
|
*mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac;
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "../namespace_end.h"
|
|
|
|
// Following couple things don't belong in the DLL namespace!
|
|
#ifdef QAGAME
|
|
typedef struct gentity_s gentity_t;
|
|
gentity_t *G_PlayEffectID(const int fxID, vec3_t org, vec3_t ang);
|
|
#endif
|
|
|
|
#include "../namespace_begin.h"
|
|
|
|
static void PM_GroundTraceMissed( void );
|
|
void PM_HoverTrace( void )
|
|
{
|
|
Vehicle_t *pVeh;
|
|
float hoverHeight;
|
|
vec3_t point, vAng, fxAxis[3];
|
|
trace_t *trace;
|
|
float relativeWaterLevel;
|
|
|
|
bgEntity_t *pEnt = pm_entSelf;
|
|
if ( !pEnt || pEnt->s.NPC_class != CLASS_VEHICLE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pVeh = pEnt->m_pVehicle;
|
|
hoverHeight = pVeh->m_pVehicleInfo->hoverHeight;
|
|
trace = &pml.groundTrace;
|
|
|
|
pml.groundPlane = qfalse;
|
|
|
|
//relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2]));
|
|
relativeWaterLevel = pm->waterlevel; //I.. guess this works
|
|
if ( pm->waterlevel && relativeWaterLevel >= 0 )
|
|
{//in water
|
|
if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f )
|
|
{//sink like a rock
|
|
}
|
|
else
|
|
{//rise up
|
|
float floatHeight = (pVeh->m_pVehicleInfo->bouyancy * ((pm->maxs[2]-pm->mins[2])*0.5f)) - (hoverHeight*0.5f);//1.0f should make you float half-in, half-out of water
|
|
if ( relativeWaterLevel > floatHeight )
|
|
{//too low, should rise up
|
|
pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier;
|
|
}
|
|
}
|
|
//if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] )
|
|
if (pm->waterlevel <= 1)
|
|
{//part of us is sticking out of water
|
|
if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
|
|
{//moving at a decent speed
|
|
if ( Q_irand( pml.frametime, 100 ) >= 50 )
|
|
{//splash
|
|
vec3_t wakeOrg;
|
|
|
|
vAng[PITCH] = vAng[ROLL] = 0;
|
|
vAng[YAW] = pVeh->m_vOrientation[YAW];
|
|
AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
|
|
VectorCopy( pm->ps->origin, wakeOrg );
|
|
//wakeOrg[2] = pm->ps->waterheight;
|
|
if (pm->waterlevel >= 2)
|
|
{
|
|
wakeOrg[2] = pm->ps->origin[2]+16;
|
|
}
|
|
else
|
|
{
|
|
wakeOrg[2] = pm->ps->origin[2];
|
|
}
|
|
#ifdef QAGAME //yeah, this is kind of crappy and makes no use of prediction whatsoever
|
|
if ( pVeh->m_pVehicleInfo->iWakeFX )
|
|
{
|
|
//G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis[0] );
|
|
//tempent use bad!
|
|
G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pVeh->m_pVehicleInfo->iWakeFX);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int traceContents;
|
|
float minNormal = (float)MIN_WALK_NORMAL;
|
|
minNormal = pVeh->m_pVehicleInfo->maxSlope;
|
|
|
|
point[0] = pm->ps->origin[0];
|
|
point[1] = pm->ps->origin[1];
|
|
point[2] = pm->ps->origin[2] - hoverHeight;
|
|
|
|
//FIXME: check for water, too? If over water, go slower and make wave effect
|
|
// If *in* water, go really slow and use bouyancy stat to determine how far below surface to float
|
|
|
|
//NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground.
|
|
// if it's 1.0f, you sink halfway into water. If it's 0, you sink...
|
|
traceContents = pm->tracemask;
|
|
if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f )
|
|
{//sit on water
|
|
traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA);
|
|
}
|
|
pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents );
|
|
if (trace->plane.normal[0] > 0.5f || trace->plane.normal[0] < -0.5f ||
|
|
trace->plane.normal[1] > 0.5f || trace->plane.normal[1] < -0.5f)
|
|
{ //steep slanted hill, don't go up it.
|
|
float d = fabs(trace->plane.normal[0]);
|
|
float e = fabs(trace->plane.normal[1]);
|
|
if (e > d)
|
|
{
|
|
d = e;
|
|
}
|
|
pm->ps->velocity[2] = -300.0f*d;
|
|
}
|
|
else if ( trace->plane.normal[2] >= minNormal )
|
|
{//not a steep slope, so push us up
|
|
if ( trace->fraction < 1.0f )
|
|
{//push up off ground
|
|
float hoverForce = pVeh->m_pVehicleInfo->hoverStrength;
|
|
if ( trace->fraction > 0.5f )
|
|
{
|
|
pm->ps->velocity[2] += (1.0f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier;
|
|
}
|
|
else
|
|
{
|
|
pm->ps->velocity[2] += (0.5f-(trace->fraction*trace->fraction))*hoverForce*2.0f*pVeh->m_fTimeModifier;
|
|
}
|
|
if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) )
|
|
{//hovering on water, make a spash if moving
|
|
if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 )
|
|
{//moving at a decent speed
|
|
if ( Q_irand( pml.frametime, 100 ) >= 50 )
|
|
{//splash
|
|
vAng[PITCH] = vAng[ROLL] = 0;
|
|
vAng[YAW] = pVeh->m_vOrientation[YAW];
|
|
AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] );
|
|
#ifdef QAGAME
|
|
if ( pVeh->m_pVehicleInfo->iWakeFX )
|
|
{
|
|
G_PlayEffectID( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis[0] );
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
pml.groundPlane = qtrue;
|
|
}
|
|
}
|
|
}
|
|
if ( pml.groundPlane )
|
|
{
|
|
PM_SetVehicleAngles( pml.groundTrace.plane.normal );
|
|
// We're on the ground.
|
|
pVeh->m_ulFlags &= ~VEH_FLYING;
|
|
|
|
pVeh->m_vAngularVelocity = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
PM_SetVehicleAngles( NULL );
|
|
// We're flying in the air.
|
|
pVeh->m_ulFlags |= VEH_FLYING;
|
|
//groundTrace
|
|
|
|
if (pVeh->m_vAngularVelocity==0.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
|
|
if (pVeh->m_vAngularVelocity<-15.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity = -15.0f;
|
|
}
|
|
if (pVeh->m_vAngularVelocity> 15.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity = 15.0f;
|
|
}
|
|
}
|
|
//pVeh->m_vAngularVelocity *= 0.95f; // Angular Velocity Decays Over Time
|
|
if (pVeh->m_vAngularVelocity > 0.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity -= pml.frametime;
|
|
if (pVeh->m_vAngularVelocity < 0.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity = 0.0f;
|
|
}
|
|
}
|
|
else if (pVeh->m_vAngularVelocity < 0.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity += pml.frametime;
|
|
if (pVeh->m_vAngularVelocity > 0.0f)
|
|
{
|
|
pVeh->m_vAngularVelocity = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
PM_GroundTraceMissed();
|
|
}
|
|
//end vehicle functions crudely ported from sp -rww
|
|
|
|
/*
|
|
===============
|
|
PM_AddEvent
|
|
|
|
===============
|
|
*/
|
|
void PM_AddEvent( int newEvent ) {
|
|
BG_AddPredictableEventToPlayerstate( newEvent, 0, pm->ps );
|
|
}
|
|
|
|
void PM_AddEventWithParm( int newEvent, int parm )
|
|
{
|
|
BG_AddPredictableEventToPlayerstate( newEvent, parm, pm->ps );
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PM_AddTouchEnt
|
|
===============
|
|
*/
|
|
void PM_AddTouchEnt( int entityNum ) {
|
|
int i;
|
|
|
|
if ( entityNum == ENTITYNUM_WORLD ) {
|
|
return;
|
|
}
|
|
if ( pm->numtouch == MAXTOUCH ) {
|
|
return;
|
|
}
|
|
|
|
// see if it is already added
|
|
for ( i = 0 ; i < pm->numtouch ; i++ ) {
|
|
if ( pm->touchents[ i ] == entityNum ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// add it
|
|
pm->touchents[pm->numtouch] = entityNum;
|
|
pm->numtouch++;
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
PM_ClipVelocity
|
|
|
|
Slide off of the impacting surface
|
|
==================
|
|
*/
|
|
void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) {
|
|
float backoff;
|
|
float change;
|
|
float oldInZ;
|
|
int i;
|
|
|
|
if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
|
|
{//no sliding!
|
|
VectorCopy( in, out );
|
|
return;
|
|
}
|
|
oldInZ = in[2];
|
|
|
|
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;
|
|
}
|
|
if ( pm->stepSlideFix )
|
|
{
|
|
if ( pm->ps->clientNum < MAX_CLIENTS//normal player
|
|
&& pm->ps->groundEntityNum != ENTITYNUM_NONE//on the ground
|
|
&& normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope
|
|
{//if walking on the ground, don't slide up slopes that are too steep to walk on
|
|
out[2] = oldInZ;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
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;
|
|
bgEntity_t *pEnt = NULL;
|
|
|
|
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
|
|
if (pm->ps->pm_type == PM_SPECTATOR)
|
|
{
|
|
vel[2] = 0;
|
|
}
|
|
// FIXME: still have z friction underwater?
|
|
return;
|
|
}
|
|
|
|
drop = 0;
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
pEnt = pm_entSelf;
|
|
}
|
|
|
|
// apply ground friction, even if on ladder
|
|
if (pm_flying != FLY_VEHICLE &&
|
|
pEnt &&
|
|
pEnt->s.NPC_class == CLASS_VEHICLE &&
|
|
pEnt->m_pVehicle &&
|
|
pEnt->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL &&
|
|
pEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
|
|
pEnt->m_pVehicle->m_pVehicleInfo->friction )
|
|
{
|
|
float friction = pEnt->m_pVehicle->m_pVehicleInfo->friction;
|
|
if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) /*&& !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)*/ )
|
|
{
|
|
control = speed < pm_stopspeed ? pm_stopspeed : speed;
|
|
drop += control*friction*pml.frametime;
|
|
/*
|
|
if ( Flying == FLY_HOVER )
|
|
{
|
|
if ( pm->cmd.rightmove )
|
|
{//if turning, increase friction
|
|
control *= 2.0f;
|
|
}
|
|
if ( pm->ps->groundEntityNum < ENTITYNUM_NONE )
|
|
{//on the ground
|
|
drop += control*friction*pml.frametime;
|
|
}
|
|
else if ( pml.groundPlane )
|
|
{//on a slope
|
|
drop += control*friction*2.0f*pml.frametime;
|
|
}
|
|
else
|
|
{//in air
|
|
drop += control*2.0f*friction*pml.frametime;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
else if ( pm_flying != FLY_NORMAL && pm_flying != FLY_VEHICLE )
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pm_flying == FLY_VEHICLE )
|
|
{
|
|
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;
|
|
}
|
|
// If on a client then there is no friction
|
|
else if ( pm->ps->groundEntityNum < MAX_CLIENTS )
|
|
{
|
|
drop = 0;
|
|
}
|
|
|
|
if ( pm->ps->pm_type == PM_SPECTATOR || pm->ps->pm_type == PM_FLOAT )
|
|
{
|
|
if (pm->ps->pm_type == PM_FLOAT)
|
|
{ //almost no friction while floating
|
|
drop += speed*0.1*pml.frametime;
|
|
}
|
|
else
|
|
{
|
|
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 (pm->gametype != GT_SIEGE
|
|
|| pm->ps->m_iVehicleNum
|
|
|| pm->ps->clientNum >= MAX_CLIENTS
|
|
|| pm->ps->pm_type != PM_NORMAL)
|
|
{ //standard method, allows "bunnyhopping" and whatnot
|
|
int i;
|
|
float addspeed, accelspeed, currentspeed;
|
|
|
|
currentspeed = DotProduct (pm->ps->velocity, wishdir);
|
|
addspeed = wishspeed - currentspeed;
|
|
if (addspeed <= 0 && pm->ps->clientNum < MAX_CLIENTS) {
|
|
return;
|
|
}
|
|
|
|
if (addspeed < 0)
|
|
{
|
|
accelspeed = (-accel)*pml.frametime*wishspeed;
|
|
if (accelspeed < addspeed) {
|
|
accelspeed = addspeed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
accelspeed = accel*pml.frametime*wishspeed;
|
|
if (accelspeed > addspeed) {
|
|
accelspeed = addspeed;
|
|
}
|
|
}
|
|
|
|
for (i=0 ; i<3 ; i++) {
|
|
pm->ps->velocity[i] += accelspeed*wishdir[i];
|
|
}
|
|
}
|
|
else
|
|
{ //use the proper way for siege
|
|
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 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
============
|
|
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;
|
|
int umove = 0; //cmd->upmove;
|
|
//don't factor upmove into scaling speed
|
|
|
|
max = abs( cmd->forwardmove );
|
|
if ( abs( cmd->rightmove ) > max ) {
|
|
max = abs( cmd->rightmove );
|
|
}
|
|
if ( abs( umove ) > max ) {
|
|
max = abs( umove );
|
|
}
|
|
if ( !max ) {
|
|
return 0;
|
|
}
|
|
|
|
total = sqrt( (float)(cmd->forwardmove * cmd->forwardmove
|
|
+ cmd->rightmove * cmd->rightmove + umove * umove) );
|
|
scale = (float)pm->ps->speed * max / ( 127.0 * total );
|
|
|
|
return scale;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
PM_SetMovementDir
|
|
|
|
Determine the rotation of the legs reletive
|
|
to the facing dir
|
|
================
|
|
*/
|
|
static void PM_SetMovementDir( void ) {
|
|
if ( pm->cmd.forwardmove || pm->cmd.rightmove ) {
|
|
if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) {
|
|
pm->ps->movementDir = 0;
|
|
} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) {
|
|
pm->ps->movementDir = 1;
|
|
} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) {
|
|
pm->ps->movementDir = 2;
|
|
} else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) {
|
|
pm->ps->movementDir = 3;
|
|
} else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) {
|
|
pm->ps->movementDir = 4;
|
|
} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) {
|
|
pm->ps->movementDir = 5;
|
|
} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) {
|
|
pm->ps->movementDir = 6;
|
|
} else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) {
|
|
pm->ps->movementDir = 7;
|
|
}
|
|
} else {
|
|
// if they aren't actively going directly sideways,
|
|
// change the animation to the diagonal so they
|
|
// don't stop too crooked
|
|
if ( pm->ps->movementDir == 2 ) {
|
|
pm->ps->movementDir = 1;
|
|
} else if ( pm->ps->movementDir == 6 ) {
|
|
pm->ps->movementDir = 7;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define METROID_JUMP 1
|
|
|
|
qboolean PM_ForceJumpingUp(void)
|
|
{
|
|
if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) && pm->ps->fd.forceJumpCharge )
|
|
{//already jumped and let go
|
|
return qfalse;
|
|
}
|
|
|
|
if ( BG_InSpecialJump( pm->ps->legsAnim ) )
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (BG_SaberInSpecial(pm->ps->saberMove))
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (BG_SaberInSpecialAttack(pm->ps->legsAnim))
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (BG_HasYsalamiri(pm->gametype, pm->ps))
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (!BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && //in air
|
|
(pm->ps->pm_flags & PMF_JUMP_HELD) && //jumped
|
|
pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && //force-jump capable
|
|
pm->ps->velocity[2] > 0 )//going up
|
|
{
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
static void PM_JumpForDir( void )
|
|
{
|
|
int anim = BOTH_JUMP1;
|
|
if ( pm->cmd.forwardmove > 0 )
|
|
{
|
|
anim = BOTH_JUMP1;
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
}
|
|
else if ( pm->cmd.forwardmove < 0 )
|
|
{
|
|
anim = BOTH_JUMPBACK1;
|
|
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
|
|
}
|
|
else if ( pm->cmd.rightmove > 0 )
|
|
{
|
|
anim = BOTH_JUMPRIGHT1;
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 )
|
|
{
|
|
anim = BOTH_JUMPLEFT1;
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
}
|
|
else
|
|
{
|
|
anim = BOTH_JUMP1;
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
}
|
|
if(!BG_InDeathAnim(pm->ps->legsAnim))
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE, 100);
|
|
}
|
|
}
|
|
|
|
void PM_SetPMViewAngle(playerState_t *ps, vec3_t angle, usercmd_t *ucmd)
|
|
{
|
|
int i;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{ // set the delta angle
|
|
int cmdAngle;
|
|
|
|
cmdAngle = ANGLE2SHORT(angle[i]);
|
|
ps->delta_angles[i] = cmdAngle - ucmd->angles[i];
|
|
}
|
|
VectorCopy (angle, ps->viewangles);
|
|
}
|
|
|
|
qboolean PM_AdjustAngleForWallRun( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
|
|
{
|
|
if (( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT || (ps->legsAnim) == BOTH_WALL_RUN_LEFT ) && ps->legsTimer > 500 )
|
|
{//wall-running and not at end of anim
|
|
//stick to wall, if there is one
|
|
vec3_t fwd, rt, traceTo, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
float dist, yawAdjust;
|
|
|
|
VectorSet(mins, -15, -15, 0);
|
|
VectorSet(maxs, 15, 15, 24);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
AngleVectors( fwdAngles, fwd, rt, NULL );
|
|
if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
|
|
{
|
|
dist = 128;
|
|
yawAdjust = -90;
|
|
}
|
|
else
|
|
{
|
|
dist = -128;
|
|
yawAdjust = 90;
|
|
}
|
|
VectorMA( ps->origin, dist, rt, traceTo );
|
|
|
|
pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
|
|
|
|
if ( trace.fraction < 1.0f
|
|
&& (trace.plane.normal[2] >= 0.0f && trace.plane.normal[2] <= 0.4f) )//&& ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
|
|
{
|
|
trace_t trace2;
|
|
vec3_t traceTo2;
|
|
vec3_t wallRunFwd, wallRunAngles;
|
|
|
|
VectorClear( wallRunAngles );
|
|
wallRunAngles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
|
|
AngleVectors( wallRunAngles, wallRunFwd, NULL, NULL );
|
|
|
|
VectorMA( pm->ps->origin, 32, wallRunFwd, traceTo2 );
|
|
pm->trace( &trace2, pm->ps->origin, mins, maxs, traceTo2, pm->ps->clientNum, MASK_PLAYERSOLID );
|
|
if ( trace2.fraction < 1.0f && DotProduct( trace2.plane.normal, wallRunFwd ) <= -0.999f )
|
|
{//wall we can't run on in front of us
|
|
trace.fraction = 1.0f;//just a way to get it to kick us off the wall below
|
|
}
|
|
}
|
|
|
|
if ( trace.fraction < 1.0f
|
|
&& (trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2] <= 0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
|
|
{//still a wall there
|
|
if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
|
|
{
|
|
ucmd->rightmove = 127;
|
|
}
|
|
else
|
|
{
|
|
ucmd->rightmove = -127;
|
|
}
|
|
if ( ucmd->upmove < 0 )
|
|
{
|
|
ucmd->upmove = 0;
|
|
}
|
|
//make me face perpendicular to the wall
|
|
ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
|
|
|
|
PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
|
|
|
|
ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
|
|
if ( doMove )
|
|
{
|
|
//push me forward
|
|
float zVel = ps->velocity[2];
|
|
if ( ps->legsTimer > 500 )
|
|
{//not at end of anim yet
|
|
float speed = 175;
|
|
if ( ucmd->forwardmove < 0 )
|
|
{//slower
|
|
speed = 100;
|
|
}
|
|
else if ( ucmd->forwardmove > 0 )
|
|
{
|
|
speed = 250;//running speed
|
|
}
|
|
VectorScale( fwd, speed, ps->velocity );
|
|
}
|
|
ps->velocity[2] = zVel;//preserve z velocity
|
|
//pull me toward the wall, too
|
|
VectorMA( ps->velocity, dist, rt, ps->velocity );
|
|
}
|
|
ucmd->forwardmove = 0;
|
|
return qtrue;
|
|
}
|
|
else if ( doMove )
|
|
{//stop it
|
|
if ( (ps->legsAnim) == BOTH_WALL_RUN_RIGHT )
|
|
{
|
|
PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_RIGHT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
}
|
|
else if ( (ps->legsAnim) == BOTH_WALL_RUN_LEFT )
|
|
{
|
|
PM_SetAnim(SETANIM_BOTH, BOTH_WALL_RUN_LEFT_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean PM_AdjustAnglesForWallRunUpFlipAlt( usercmd_t *ucmd )
|
|
{
|
|
// ucmd->angles[PITCH] = ANGLE2SHORT( pm->ps->viewangles[PITCH] ) - pm->ps->delta_angles[PITCH];
|
|
// ucmd->angles[YAW] = ANGLE2SHORT( pm->ps->viewangles[YAW] ) - pm->ps->delta_angles[YAW];
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean PM_AdjustAngleForWallRunUp( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
|
|
{
|
|
if ( ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
|
|
{//wall-running up
|
|
//stick to wall, if there is one
|
|
vec3_t fwd, traceTo, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
float dist = 128;
|
|
|
|
VectorSet(mins, -15,-15,0);
|
|
VectorSet(maxs, 15,15,24);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
AngleVectors( fwdAngles, fwd, NULL, NULL );
|
|
VectorMA( ps->origin, dist, fwd, traceTo );
|
|
pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
|
|
if ( trace.fraction > 0.5f )
|
|
{//hmm, some room, see if there's a floor right here
|
|
trace_t trace2;
|
|
vec3_t top, bottom;
|
|
|
|
VectorCopy( trace.endpos, top );
|
|
top[2] += (pm->mins[2]*-1) + 4.0f;
|
|
VectorCopy( top, bottom );
|
|
bottom[2] -= 64.0f;
|
|
pm->trace( &trace2, top, pm->mins, pm->maxs, bottom, ps->clientNum, MASK_PLAYERSOLID );
|
|
if ( !trace2.allsolid
|
|
&& !trace2.startsolid
|
|
&& trace2.fraction < 1.0f
|
|
&& trace2.plane.normal[2] > 0.7f )//slope we can stand on
|
|
{//cool, do the alt-flip and land on whetever it is we just scaled up
|
|
VectorScale( fwd, 100, pm->ps->velocity );
|
|
pm->ps->velocity[2] += 400;
|
|
PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_ALT, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
//ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
|
|
//ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
|
|
//G_AddEvent( ent, EV_JUMP, 0 );
|
|
PM_AddEvent(EV_JUMP);
|
|
ucmd->upmove = 0;
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
if ( //ucmd->upmove <= 0 &&
|
|
ps->legsTimer > 0 &&
|
|
ucmd->forwardmove > 0 &&
|
|
trace.fraction < 1.0f &&
|
|
(trace.plane.normal[2] >= 0.0f&&trace.plane.normal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
|
|
{//still a vertical wall there
|
|
//make sure there's not a ceiling above us!
|
|
trace_t trace2;
|
|
VectorCopy( ps->origin, traceTo );
|
|
traceTo[2] += 64;
|
|
pm->trace( &trace2, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
|
|
if ( trace2.fraction < 1.0f )
|
|
{//will hit a ceiling, so force jump-off right now
|
|
//NOTE: hits any entity or clip brush in the way, too, not just architecture!
|
|
}
|
|
else
|
|
{//all clear, keep going
|
|
//FIXME: don't pull around 90 turns
|
|
//FIXME: simulate stepping up steps here, somehow?
|
|
ucmd->forwardmove = 127;
|
|
if ( ucmd->upmove < 0 )
|
|
{
|
|
ucmd->upmove = 0;
|
|
}
|
|
//make me face the wall
|
|
ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+180;
|
|
PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
|
|
/*
|
|
if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
|
|
{//don't clamp angles when looking through a viewEntity
|
|
SetClientViewAngle( ent, ent->client->ps.viewangles );
|
|
}
|
|
*/
|
|
ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
|
|
//if ( ent->s.number || !player_locked )
|
|
if (1) //aslkfhsakf
|
|
{
|
|
if ( doMove )
|
|
{
|
|
//pull me toward the wall
|
|
VectorScale( trace.plane.normal, -dist*trace.fraction, ps->velocity );
|
|
//push me up
|
|
if ( ps->legsTimer > 200 )
|
|
{//not at end of anim yet
|
|
float speed = 300;
|
|
/*
|
|
if ( ucmd->forwardmove < 0 )
|
|
{//slower
|
|
speed = 100;
|
|
}
|
|
else if ( ucmd->forwardmove > 0 )
|
|
{
|
|
speed = 250;//running speed
|
|
}
|
|
*/
|
|
ps->velocity[2] = speed;//preserve z velocity
|
|
}
|
|
}
|
|
}
|
|
ucmd->forwardmove = 0;
|
|
return qtrue;
|
|
}
|
|
}
|
|
//failed!
|
|
if ( doMove )
|
|
{//stop it
|
|
VectorScale( fwd, -300.0f, ps->velocity );
|
|
ps->velocity[2] += 200;
|
|
//NPC_SetAnim( ent, SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
|
|
//why?!?#?#@!%$R@$KR#F:Hdl;asfm
|
|
PM_SetAnim(SETANIM_BOTH, BOTH_FORCEWALLRUNFLIP_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
ps->pm_flags |= PMF_JUMP_HELD;
|
|
//ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
|
|
|
|
//FIXME do I need this in mp?
|
|
//ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
|
|
PM_AddEvent(EV_JUMP);
|
|
ucmd->upmove = 0;
|
|
//return qtrue;
|
|
}
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
#define JUMP_OFF_WALL_SPEED 200.0f
|
|
//nice...
|
|
static float BG_ForceWallJumpStrength( void )
|
|
{
|
|
return (forceJumpStrength[FORCE_LEVEL_3]/2.5f);
|
|
}
|
|
|
|
qboolean PM_AdjustAngleForWallJump( playerState_t *ps, usercmd_t *ucmd, qboolean doMove )
|
|
{
|
|
if ( ( ( BG_InReboundJump( ps->legsAnim ) || BG_InReboundHold( ps->legsAnim ) )
|
|
&& ( BG_InReboundJump( ps->torsoAnim ) || BG_InReboundHold( ps->torsoAnim ) ) )
|
|
|| (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
|
|
{//hugging wall, getting ready to jump off
|
|
//stick to wall, if there is one
|
|
vec3_t checkDir, traceTo, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
float dist = 128.0f, yawAdjust;
|
|
|
|
VectorSet(mins, pm->mins[0],pm->mins[1],0);
|
|
VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
switch ( ps->legsAnim )
|
|
{
|
|
case BOTH_FORCEWALLREBOUND_RIGHT:
|
|
case BOTH_FORCEWALLHOLD_RIGHT:
|
|
AngleVectors( fwdAngles, NULL, checkDir, NULL );
|
|
yawAdjust = -90;
|
|
break;
|
|
case BOTH_FORCEWALLREBOUND_LEFT:
|
|
case BOTH_FORCEWALLHOLD_LEFT:
|
|
AngleVectors( fwdAngles, NULL, checkDir, NULL );
|
|
VectorScale( checkDir, -1, checkDir );
|
|
yawAdjust = 90;
|
|
break;
|
|
case BOTH_FORCEWALLREBOUND_FORWARD:
|
|
case BOTH_FORCEWALLHOLD_FORWARD:
|
|
AngleVectors( fwdAngles, checkDir, NULL, NULL );
|
|
yawAdjust = 180;
|
|
break;
|
|
case BOTH_FORCEWALLREBOUND_BACK:
|
|
case BOTH_FORCEWALLHOLD_BACK:
|
|
AngleVectors( fwdAngles, checkDir, NULL, NULL );
|
|
VectorScale( checkDir, -1, checkDir );
|
|
yawAdjust = 0;
|
|
break;
|
|
default:
|
|
//WTF???
|
|
pm->ps->pm_flags &= ~PMF_STUCK_TO_WALL;
|
|
return qfalse;
|
|
break;
|
|
}
|
|
if ( pm->debugMelee )
|
|
{//uber-skillz
|
|
if ( ucmd->upmove > 0 )
|
|
{//hold on until you let go manually
|
|
if ( BG_InReboundHold( ps->legsAnim ) )
|
|
{//keep holding
|
|
if ( ps->legsTimer < 150 )
|
|
{
|
|
ps->legsTimer = 150;
|
|
}
|
|
}
|
|
else
|
|
{//if got to hold part of anim, play hold anim
|
|
if ( ps->legsTimer <= 300 )
|
|
{
|
|
ps->saberHolstered = 2;
|
|
PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
ps->legsTimer = ps->torsoTimer = 150;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
VectorMA( ps->origin, dist, checkDir, traceTo );
|
|
pm->trace( &trace, ps->origin, mins, maxs, traceTo, ps->clientNum, MASK_PLAYERSOLID );
|
|
if ( //ucmd->upmove <= 0 &&
|
|
ps->legsTimer > 100 &&
|
|
trace.fraction < 1.0f &&
|
|
fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/ )
|
|
{//still a vertical wall there
|
|
//FIXME: don't pull around 90 turns
|
|
/*
|
|
if ( ent->s.number || !player_locked )
|
|
{
|
|
ucmd->forwardmove = 127;
|
|
}
|
|
*/
|
|
if ( ucmd->upmove < 0 )
|
|
{
|
|
ucmd->upmove = 0;
|
|
}
|
|
//align me to the wall
|
|
ps->viewangles[YAW] = vectoyaw( trace.plane.normal )+yawAdjust;
|
|
PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
|
|
/*
|
|
if ( ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD )
|
|
{//don't clamp angles when looking through a viewEntity
|
|
SetClientViewAngle( ent, ent->client->ps.viewangles );
|
|
}
|
|
*/
|
|
ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
|
|
//if ( ent->s.number || !player_locked )
|
|
if (1)
|
|
{
|
|
if ( doMove )
|
|
{
|
|
//pull me toward the wall
|
|
VectorScale( trace.plane.normal, -128.0f, ps->velocity );
|
|
}
|
|
}
|
|
ucmd->upmove = 0;
|
|
ps->pm_flags |= PMF_STUCK_TO_WALL;
|
|
return qtrue;
|
|
}
|
|
else if ( doMove
|
|
&& (ps->pm_flags&PMF_STUCK_TO_WALL))
|
|
{//jump off
|
|
//push off of it!
|
|
ps->pm_flags &= ~PMF_STUCK_TO_WALL;
|
|
ps->velocity[0] = ps->velocity[1] = 0;
|
|
VectorScale( checkDir, -JUMP_OFF_WALL_SPEED, ps->velocity );
|
|
ps->velocity[2] = BG_ForceWallJumpStrength();
|
|
ps->pm_flags |= PMF_JUMP_HELD;//PMF_JUMPING|PMF_JUMP_HELD;
|
|
//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
|
|
ps->fd.forceJumpSound = 1; //this is a stupid thing, i should fix it.
|
|
//ent->client->ps.forcePowersActive |= (1<<FP_LEVITATION);
|
|
if (ps->origin[2] < ps->fd.forceJumpZStart)
|
|
{
|
|
ps->fd.forceJumpZStart = ps->origin[2];
|
|
}
|
|
//FIXME do I need this?
|
|
|
|
BG_ForcePowerDrain( ps, FP_LEVITATION, 10 );
|
|
//no control for half a second
|
|
ps->pm_flags |= PMF_TIME_KNOCKBACK;
|
|
ps->pm_time = 500;
|
|
ucmd->forwardmove = 0;
|
|
ucmd->rightmove = 0;
|
|
ucmd->upmove = 127;
|
|
|
|
if ( BG_InReboundHold( ps->legsAnim ) )
|
|
{//if was in hold pose, release now
|
|
PM_SetAnim( SETANIM_BOTH, BOTH_FORCEWALLRELEASE_FORWARD+(ps->legsAnim-BOTH_FORCEWALLHOLD_FORWARD), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
}
|
|
else
|
|
{
|
|
//PM_JumpForDir();
|
|
PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART, 0);
|
|
}
|
|
|
|
//return qtrue;
|
|
}
|
|
}
|
|
ps->pm_flags &= ~PMF_STUCK_TO_WALL;
|
|
return qfalse;
|
|
}
|
|
|
|
//Set the height for when a force jump was started. If it's 0, nuge it up (slight hack to prevent holding jump over slopes)
|
|
void PM_SetForceJumpZStart(float value)
|
|
{
|
|
pm->ps->fd.forceJumpZStart = value;
|
|
if (!pm->ps->fd.forceJumpZStart)
|
|
{
|
|
pm->ps->fd.forceJumpZStart -= 0.1;
|
|
}
|
|
}
|
|
|
|
float forceJumpHeightMax[NUM_FORCE_POWER_LEVELS] =
|
|
{
|
|
66,//normal jump (32+stepheight(18)+crouchdiff(24) = 74)
|
|
130,//(96+stepheight(18)+crouchdiff(24) = 138)
|
|
226,//(192+stepheight(18)+crouchdiff(24) = 234)
|
|
418//(384+stepheight(18)+crouchdiff(24) = 426)
|
|
};
|
|
|
|
void PM_GrabWallForJump( int anim )
|
|
{//NOTE!!! assumes an appropriate anim is being passed in!!!
|
|
PM_SetAnim( SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
PM_AddEvent( EV_JUMP );//make sound for grab
|
|
pm->ps->pm_flags |= PMF_STUCK_TO_WALL;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
PM_CheckJump
|
|
=============
|
|
*/
|
|
static qboolean PM_CheckJump( void )
|
|
{
|
|
qboolean allowFlips = qtrue;
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
bgEntity_t *pEnt = pm_entSelf;
|
|
|
|
if (pEnt->s.eType == ET_NPC &&
|
|
pEnt->s.NPC_class == CLASS_VEHICLE)
|
|
{ //no!
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
|
|
pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
|
|
pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
if (pm->ps->pm_type == PM_JETPACK)
|
|
{ //there's no actual jumping while we jetpack
|
|
return qfalse;
|
|
}
|
|
|
|
//Don't allow jump until all buttons are up
|
|
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
|
|
return qfalse;
|
|
}
|
|
|
|
if ( PM_InKnockDown( pm->ps ) || BG_InRoll( pm->ps, pm->ps->legsAnim ) )
|
|
{//in knockdown
|
|
return qfalse;
|
|
}
|
|
|
|
if ( pm->ps->weapon == WP_SABER )
|
|
{
|
|
saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
|
|
saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
|
|
if ( saber1
|
|
&& (saber1->saberFlags&SFL_NO_FLIPS) )
|
|
{
|
|
allowFlips = qfalse;
|
|
}
|
|
if ( saber2
|
|
&& (saber2->saberFlags&SFL_NO_FLIPS) )
|
|
{
|
|
allowFlips = qfalse;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->groundEntityNum != ENTITYNUM_NONE || pm->ps->origin[2] < pm->ps->fd.forceJumpZStart)
|
|
{
|
|
pm->ps->fd.forcePowersActive &= ~(1<<FP_LEVITATION);
|
|
}
|
|
|
|
if (pm->ps->fd.forcePowersActive & (1 << FP_LEVITATION))
|
|
{ //Force jump is already active.. continue draining power appropriately until we land.
|
|
if (pm->ps->fd.forcePowerDebounce[FP_LEVITATION] < pm->cmd.serverTime)
|
|
{
|
|
if ( pm->gametype == GT_DUEL
|
|
|| pm->gametype == GT_POWERDUEL )
|
|
{//jump takes less power
|
|
BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 1 );
|
|
}
|
|
else
|
|
{
|
|
BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
|
|
}
|
|
if (pm->ps->fd.forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_2)
|
|
{
|
|
pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 300;
|
|
}
|
|
else
|
|
{
|
|
pm->ps->fd.forcePowerDebounce[FP_LEVITATION] = pm->cmd.serverTime + 200;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pm->ps->forceJumpFlip)
|
|
{ //Forced jump anim
|
|
int anim = BOTH_FORCEINAIR1;
|
|
int parts = SETANIM_BOTH;
|
|
if ( allowFlips )
|
|
{
|
|
if ( pm->cmd.forwardmove > 0 )
|
|
{
|
|
anim = BOTH_FLIP_F;
|
|
}
|
|
else if ( pm->cmd.forwardmove < 0 )
|
|
{
|
|
anim = BOTH_FLIP_B;
|
|
}
|
|
else if ( pm->cmd.rightmove > 0 )
|
|
{
|
|
anim = BOTH_FLIP_R;
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 )
|
|
{
|
|
anim = BOTH_FLIP_L;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pm->cmd.forwardmove > 0 )
|
|
{
|
|
anim = BOTH_FORCEINAIR1;
|
|
}
|
|
else if ( pm->cmd.forwardmove < 0 )
|
|
{
|
|
anim = BOTH_FORCEINAIRBACK1;
|
|
}
|
|
else if ( pm->cmd.rightmove > 0 )
|
|
{
|
|
anim = BOTH_FORCEINAIRRIGHT1;
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 )
|
|
{
|
|
anim = BOTH_FORCEINAIRLEFT1;
|
|
}
|
|
}
|
|
if ( pm->ps->weaponTime )
|
|
{//FIXME: really only care if we're in a saber attack anim...
|
|
parts = SETANIM_LEGS;
|
|
}
|
|
|
|
PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
|
|
pm->ps->forceJumpFlip = qfalse;
|
|
return qtrue;
|
|
}
|
|
#if METROID_JUMP
|
|
if ( pm->waterlevel < 2 )
|
|
{
|
|
if ( pm->ps->gravity > 0 )
|
|
{//can't do this in zero-G
|
|
if ( PM_ForceJumpingUp() )
|
|
{//holding jump in air
|
|
float curHeight = pm->ps->origin[2] - pm->ps->fd.forceJumpZStart;
|
|
//check for max force jump level and cap off & cut z vel
|
|
if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height
|
|
(pm->ps->fd.forcePower&&pm->cmd.upmove>=10) ) &&////still have force power available and still trying to jump up
|
|
curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] &&
|
|
pm->ps->fd.forceJumpZStart)//still below maximum jump height
|
|
{//can still go up
|
|
if ( curHeight > forceJumpHeight[0] )
|
|
{//passed normal jump height *2?
|
|
if ( !(pm->ps->fd.forcePowersActive&(1<<FP_LEVITATION)) )//haven't started forcejump yet
|
|
{
|
|
//start force jump
|
|
pm->ps->fd.forcePowersActive |= (1<<FP_LEVITATION);
|
|
pm->ps->fd.forceJumpSound = 1;
|
|
//play flip
|
|
if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir
|
|
(pm->ps->legsAnim) != BOTH_FLIP_F &&//not already flipping
|
|
(pm->ps->legsAnim) != BOTH_FLIP_B &&
|
|
(pm->ps->legsAnim) != BOTH_FLIP_R &&
|
|
(pm->ps->legsAnim) != BOTH_FLIP_L
|
|
&& allowFlips )
|
|
{
|
|
int anim = BOTH_FORCEINAIR1;
|
|
int parts = SETANIM_BOTH;
|
|
|
|
if ( pm->cmd.forwardmove > 0 )
|
|
{
|
|
anim = BOTH_FLIP_F;
|
|
}
|
|
else if ( pm->cmd.forwardmove < 0 )
|
|
{
|
|
anim = BOTH_FLIP_B;
|
|
}
|
|
else if ( pm->cmd.rightmove > 0 )
|
|
{
|
|
anim = BOTH_FLIP_R;
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 )
|
|
{
|
|
anim = BOTH_FLIP_L;
|
|
}
|
|
if ( pm->ps->weaponTime )
|
|
{
|
|
parts = SETANIM_LEGS;
|
|
}
|
|
|
|
PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
|
|
}
|
|
else if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
|
|
{
|
|
vec3_t facingFwd, facingRight, facingAngles;
|
|
int anim = -1;
|
|
float dotR, dotF;
|
|
|
|
VectorSet(facingAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
AngleVectors( facingAngles, facingFwd, facingRight, NULL );
|
|
dotR = DotProduct( facingRight, pm->ps->velocity );
|
|
dotF = DotProduct( facingFwd, pm->ps->velocity );
|
|
|
|
if ( fabs(dotR) > fabs(dotF) * 1.5 )
|
|
{
|
|
if ( dotR > 150 )
|
|
{
|
|
anim = BOTH_FORCEJUMPRIGHT1;
|
|
}
|
|
else if ( dotR < -150 )
|
|
{
|
|
anim = BOTH_FORCEJUMPLEFT1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( dotF > 150 )
|
|
{
|
|
anim = BOTH_FORCEJUMP1;
|
|
}
|
|
else if ( dotF < -150 )
|
|
{
|
|
anim = BOTH_FORCEJUMPBACK1;
|
|
}
|
|
}
|
|
if ( anim != -1 )
|
|
{
|
|
int parts = SETANIM_BOTH;
|
|
if ( pm->ps->weaponTime )
|
|
{//FIXME: really only care if we're in a saber attack anim...
|
|
parts = SETANIM_LEGS;
|
|
}
|
|
|
|
PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ //jump is already active (the anim has started)
|
|
if ( pm->ps->legsTimer < 1 )
|
|
{//not in the middle of a legsAnim
|
|
int anim = (pm->ps->legsAnim);
|
|
int newAnim = -1;
|
|
switch ( anim )
|
|
{
|
|
case BOTH_FORCEJUMP1:
|
|
newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1;
|
|
break;
|
|
case BOTH_FORCEJUMPBACK1:
|
|
newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1;
|
|
break;
|
|
case BOTH_FORCEJUMPLEFT1:
|
|
newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1;
|
|
break;
|
|
case BOTH_FORCEJUMPRIGHT1:
|
|
newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1;
|
|
break;
|
|
}
|
|
if ( newAnim != -1 )
|
|
{
|
|
int parts = SETANIM_BOTH;
|
|
if ( pm->ps->weaponTime )
|
|
{
|
|
parts = SETANIM_LEGS;
|
|
}
|
|
|
|
PM_SetAnim( parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel
|
|
pm->ps->velocity[2] = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->fd.forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY;
|
|
pm->ps->velocity[2] /= 10;
|
|
pm->ps->velocity[2] += JUMP_VELOCITY;
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
}
|
|
else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] )
|
|
{//still have some headroom, don't totally stop it
|
|
if ( pm->ps->velocity[2] > JUMP_VELOCITY )
|
|
{
|
|
pm->ps->velocity[2] = JUMP_VELOCITY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//pm->ps->velocity[2] = 0;
|
|
//rww - changed for the sake of balance in multiplayer
|
|
|
|
if ( pm->ps->velocity[2] > JUMP_VELOCITY )
|
|
{
|
|
pm->ps->velocity[2] = JUMP_VELOCITY;
|
|
}
|
|
}
|
|
pm->cmd.upmove = 0;
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//Not jumping
|
|
if ( pm->cmd.upmove < 10 && pm->ps->groundEntityNum != ENTITYNUM_NONE) {
|
|
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;
|
|
}
|
|
|
|
if ( pm->ps->gravity <= 0 )
|
|
{//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of
|
|
vec3_t forward, back;
|
|
trace_t trace;
|
|
|
|
AngleVectors( pm->ps->viewangles, forward, NULL, NULL );
|
|
VectorMA( pm->ps->origin, -8, forward, back );
|
|
pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask );
|
|
|
|
if ( trace.fraction <= 1.0f )
|
|
{
|
|
VectorMA( pm->ps->velocity, JUMP_VELOCITY*2, forward, pm->ps->velocity );
|
|
PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART, 150);
|
|
}//else no surf close enough to push off of
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
else if ( pm->cmd.upmove > 0 && pm->waterlevel < 2 &&
|
|
pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 &&
|
|
!(pm->ps->pm_flags&PMF_JUMP_HELD) &&
|
|
(pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE) &&
|
|
!PM_IsRocketTrooper() &&
|
|
!BG_HasYsalamiri(pm->gametype, pm->ps) &&
|
|
BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION) )
|
|
{
|
|
qboolean allowWallRuns = qtrue;
|
|
qboolean allowWallFlips = qtrue;
|
|
qboolean allowFlips = qtrue;
|
|
qboolean allowWallGrabs = qtrue;
|
|
if ( pm->ps->weapon == WP_SABER )
|
|
{
|
|
saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 );
|
|
saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 );
|
|
if ( saber1
|
|
&& (saber1->saberFlags&SFL_NO_WALL_RUNS) )
|
|
{
|
|
allowWallRuns = qfalse;
|
|
}
|
|
if ( saber2
|
|
&& (saber2->saberFlags&SFL_NO_WALL_RUNS) )
|
|
{
|
|
allowWallRuns = qfalse;
|
|
}
|
|
if ( saber1
|
|
&& (saber1->saberFlags&SFL_NO_WALL_FLIPS) )
|
|
{
|
|
allowWallFlips = qfalse;
|
|
}
|
|
if ( saber2
|
|
&& (saber2->saberFlags&SFL_NO_WALL_FLIPS) )
|
|
{
|
|
allowWallFlips = qfalse;
|
|
}
|
|
if ( saber1
|
|
&& (saber1->saberFlags&SFL_NO_FLIPS) )
|
|
{
|
|
allowFlips = qfalse;
|
|
}
|
|
if ( saber2
|
|
&& (saber2->saberFlags&SFL_NO_FLIPS) )
|
|
{
|
|
allowFlips = qfalse;
|
|
}
|
|
if ( saber1
|
|
&& (saber1->saberFlags&SFL_NO_WALL_GRAB) )
|
|
{
|
|
allowWallGrabs = qfalse;
|
|
}
|
|
if ( saber2
|
|
&& (saber2->saberFlags&SFL_NO_WALL_GRAB) )
|
|
{
|
|
allowWallGrabs = qfalse;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//on the ground
|
|
//check for left-wall and right-wall special jumps
|
|
int anim = -1;
|
|
float vertPush = 0;
|
|
if ( pm->cmd.rightmove > 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
|
|
{//strafing right
|
|
if ( pm->cmd.forwardmove > 0 )
|
|
{//wall-run
|
|
if ( allowWallRuns )
|
|
{
|
|
vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
|
|
anim = BOTH_WALL_RUN_RIGHT;
|
|
}
|
|
}
|
|
else if ( pm->cmd.forwardmove == 0 )
|
|
{//wall-flip
|
|
if ( allowWallFlips )
|
|
{
|
|
vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
|
|
anim = BOTH_WALL_FLIP_RIGHT;
|
|
}
|
|
}
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )
|
|
{//strafing left
|
|
if ( pm->cmd.forwardmove > 0 )
|
|
{//wall-run
|
|
if ( allowWallRuns )
|
|
{
|
|
vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f;
|
|
anim = BOTH_WALL_RUN_LEFT;
|
|
}
|
|
}
|
|
else if ( pm->cmd.forwardmove == 0 )
|
|
{//wall-flip
|
|
if ( allowWallFlips )
|
|
{
|
|
vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f;
|
|
anim = BOTH_WALL_FLIP_LEFT;
|
|
}
|
|
}
|
|
}
|
|
else if ( pm->cmd.forwardmove < 0 && !(pm->cmd.buttons&BUTTON_ATTACK) )
|
|
{//backflip
|
|
if ( allowFlips )
|
|
{
|
|
vertPush = JUMP_VELOCITY;
|
|
anim = BOTH_FLIP_BACK1;//BG_PickAnim( BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 );
|
|
}
|
|
}
|
|
|
|
vertPush += 128; //give them an extra shove
|
|
|
|
if ( anim != -1 )
|
|
{
|
|
vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
|
|
vec3_t idealNormal={0}, wallNormal={0};
|
|
trace_t trace;
|
|
qboolean doTrace = qfalse;
|
|
int contents = MASK_SOLID;//MASK_PLAYERSOLID;
|
|
|
|
VectorSet(mins, pm->mins[0],pm->mins[1],0);
|
|
VectorSet(maxs, pm->maxs[0],pm->maxs[1],24);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
memset(&trace, 0, sizeof(trace)); //to shut the compiler up
|
|
|
|
AngleVectors( fwdAngles, fwd, right, NULL );
|
|
|
|
//trace-check for a wall, if necc.
|
|
switch ( anim )
|
|
{
|
|
case BOTH_WALL_FLIP_LEFT:
|
|
//NOTE: purposely falls through to next case!
|
|
case BOTH_WALL_RUN_LEFT:
|
|
doTrace = qtrue;
|
|
VectorMA( pm->ps->origin, -16, right, traceto );
|
|
break;
|
|
|
|
case BOTH_WALL_FLIP_RIGHT:
|
|
//NOTE: purposely falls through to next case!
|
|
case BOTH_WALL_RUN_RIGHT:
|
|
doTrace = qtrue;
|
|
VectorMA( pm->ps->origin, 16, right, traceto );
|
|
break;
|
|
|
|
case BOTH_WALL_FLIP_BACK1:
|
|
doTrace = qtrue;
|
|
VectorMA( pm->ps->origin, 16, fwd, traceto );
|
|
break;
|
|
}
|
|
|
|
if ( doTrace )
|
|
{
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );
|
|
VectorCopy( trace.plane.normal, wallNormal );
|
|
VectorNormalize( wallNormal );
|
|
VectorSubtract( pm->ps->origin, traceto, idealNormal );
|
|
VectorNormalize( idealNormal );
|
|
}
|
|
|
|
if ( !doTrace || (trace.fraction < 1.0f && (trace.entityNum < MAX_CLIENTS || DotProduct(wallNormal,idealNormal) > 0.7)) )
|
|
{//there is a wall there.. or hit a client
|
|
if ( (anim != BOTH_WALL_RUN_LEFT
|
|
&& anim != BOTH_WALL_RUN_RIGHT
|
|
&& anim != BOTH_FORCEWALLRUNFLIP_START)
|
|
|| (wallNormal[2] >= 0.0f&&wallNormal[2]<=0.4f/*MAX_WALL_RUN_Z_NORMAL*/) )
|
|
{//wall-runs can only run on perfectly flat walls, sorry.
|
|
int parts;
|
|
//move me to side
|
|
if ( anim == BOTH_WALL_FLIP_LEFT )
|
|
{
|
|
pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
|
|
VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
|
|
}
|
|
else if ( anim == BOTH_WALL_FLIP_RIGHT )
|
|
{
|
|
pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
|
|
VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
|
|
}
|
|
else if ( anim == BOTH_FLIP_BACK1
|
|
|| anim == BOTH_FLIP_BACK2
|
|
|| anim == BOTH_FLIP_BACK3
|
|
|| anim == BOTH_WALL_FLIP_BACK1 )
|
|
{
|
|
pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
|
|
VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
|
|
}
|
|
|
|
/*
|
|
if ( doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT )
|
|
{
|
|
if (trace.entityNum < MAX_CLIENTS)
|
|
{
|
|
pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
|
|
}
|
|
}
|
|
*/
|
|
|
|
//up
|
|
if ( vertPush )
|
|
{
|
|
pm->ps->velocity[2] = vertPush;
|
|
pm->ps->fd.forcePowersActive |= (1 << FP_LEVITATION);
|
|
}
|
|
//animate me
|
|
parts = SETANIM_LEGS;
|
|
if ( anim == BOTH_BUTTERFLY_LEFT )
|
|
{
|
|
parts = SETANIM_BOTH;
|
|
pm->cmd.buttons&=~BUTTON_ATTACK;
|
|
pm->ps->saberMove = LS_NONE;
|
|
}
|
|
else if ( !pm->ps->weaponTime )
|
|
{
|
|
parts = SETANIM_BOTH;
|
|
}
|
|
PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
if ( anim == BOTH_BUTTERFLY_LEFT )
|
|
{
|
|
pm->ps->weaponTime = pm->ps->torsoTimer;
|
|
}
|
|
PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
pm->cmd.upmove = 0;
|
|
pm->ps->fd.forceJumpSound = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{//in the air
|
|
int legsAnim = pm->ps->legsAnim;
|
|
|
|
if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT )
|
|
{//running on a wall
|
|
vec3_t right, traceto, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
int anim = -1;
|
|
|
|
VectorSet(mins, pm->mins[0], pm->mins[0], 0);
|
|
VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
AngleVectors( fwdAngles, NULL, right, NULL );
|
|
|
|
if ( legsAnim == BOTH_WALL_RUN_LEFT )
|
|
{
|
|
if ( pm->ps->legsTimer > 400 )
|
|
{//not at the end of the anim
|
|
float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_LEFT );
|
|
if ( pm->ps->legsTimer < animLen - 400 )
|
|
{//not at start of anim
|
|
VectorMA( pm->ps->origin, -16, right, traceto );
|
|
anim = BOTH_WALL_RUN_LEFT_FLIP;
|
|
}
|
|
}
|
|
}
|
|
else if ( legsAnim == BOTH_WALL_RUN_RIGHT )
|
|
{
|
|
if ( pm->ps->legsTimer > 400 )
|
|
{//not at the end of the anim
|
|
float animLen = PM_AnimLength( 0, (animNumber_t)BOTH_WALL_RUN_RIGHT );
|
|
if ( pm->ps->legsTimer < animLen - 400 )
|
|
{//not at start of anim
|
|
VectorMA( pm->ps->origin, 16, right, traceto );
|
|
anim = BOTH_WALL_RUN_RIGHT_FLIP;
|
|
}
|
|
}
|
|
}
|
|
if ( anim != -1 )
|
|
{
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
|
|
if ( trace.fraction < 1.0f )
|
|
{//flip off wall
|
|
int parts = 0;
|
|
|
|
if ( anim == BOTH_WALL_RUN_LEFT_FLIP )
|
|
{
|
|
pm->ps->velocity[0] *= 0.5f;
|
|
pm->ps->velocity[1] *= 0.5f;
|
|
VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity );
|
|
}
|
|
else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP )
|
|
{
|
|
pm->ps->velocity[0] *= 0.5f;
|
|
pm->ps->velocity[1] *= 0.5f;
|
|
VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity );
|
|
}
|
|
parts = SETANIM_LEGS;
|
|
if ( !pm->ps->weaponTime )
|
|
{
|
|
parts = SETANIM_BOTH;
|
|
}
|
|
PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
}
|
|
if ( pm->cmd.upmove != 0 )
|
|
{//jump failed, so don't try to do normal jump code, just return
|
|
return qfalse;
|
|
}
|
|
}
|
|
//NEW JKA
|
|
else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START )
|
|
{
|
|
vec3_t fwd, traceto, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
int anim = -1;
|
|
float animLen;
|
|
|
|
VectorSet(mins, pm->mins[0], pm->mins[0], 0.0f);
|
|
VectorSet(maxs, pm->maxs[0], pm->maxs[0], 24.0f);
|
|
//hmm, did you mean [1] and [1]?
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
|
|
AngleVectors( fwdAngles, fwd, NULL, NULL );
|
|
|
|
assert(pm_entSelf); //null pm_entSelf would be a Bad Thing<tm>
|
|
animLen = BG_AnimLength( pm_entSelf->localAnimIndex, BOTH_FORCEWALLRUNFLIP_START );
|
|
if ( pm->ps->legsTimer < animLen - 400 )
|
|
{//not at start of anim
|
|
VectorMA( pm->ps->origin, 16, fwd, traceto );
|
|
anim = BOTH_FORCEWALLRUNFLIP_END;
|
|
}
|
|
if ( anim != -1 )
|
|
{
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY );
|
|
if ( trace.fraction < 1.0f )
|
|
{//flip off wall
|
|
int parts = SETANIM_LEGS;
|
|
|
|
pm->ps->velocity[0] *= 0.5f;
|
|
pm->ps->velocity[1] *= 0.5f;
|
|
VectorMA( pm->ps->velocity, -300, fwd, pm->ps->velocity );
|
|
pm->ps->velocity[2] += 200;
|
|
if ( !pm->ps->weaponTime )
|
|
{//not attacking, set anim on both
|
|
parts = SETANIM_BOTH;
|
|
}
|
|
PM_SetAnim( parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
//FIXME: do damage to traceEnt, like above?
|
|
//pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
|
|
//ha ha, so silly with your silly jumpy fally flags.
|
|
pm->cmd.upmove = 0;
|
|
PM_AddEvent( EV_JUMP );
|
|
}
|
|
}
|
|
if ( pm->cmd.upmove != 0 )
|
|
{//jump failed, so don't try to do normal jump code, just return
|
|
return qfalse;
|
|
}
|
|
}
|
|
/*
|
|
else if ( pm->cmd.forwardmove > 0 //pushing forward
|
|
&& pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
|
|
&& pm->ps->velocity[2] > 200
|
|
&& PM_GroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
|
|
&& !BG_InSpecialJump(pm->ps->legsAnim))
|
|
{//run up wall, flip backwards
|
|
vec3_t fwd, traceto, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
vec3_t idealNormal;
|
|
|
|
VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]);
|
|
VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->maxs[2]);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
AngleVectors( fwdAngles, fwd, NULL, NULL );
|
|
VectorMA( pm->ps->origin, 32, fwd, traceto );
|
|
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, MASK_PLAYERSOLID );//FIXME: clip brushes too?
|
|
VectorSubtract( pm->ps->origin, traceto, idealNormal );
|
|
VectorNormalize( idealNormal );
|
|
|
|
if ( trace.fraction < 1.0f )
|
|
{//there is a wall there
|
|
int parts = SETANIM_LEGS;
|
|
|
|
pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
|
|
VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
|
|
pm->ps->velocity[2] += 128;
|
|
|
|
if ( !pm->ps->weaponTime )
|
|
{
|
|
parts = SETANIM_BOTH;
|
|
}
|
|
PM_SetAnim( parts, BOTH_WALL_FLIP_BACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
|
|
pm->ps->legsTimer -= 600; //I force this anim to play to the end to prevent landing on your head and suddenly flipping over.
|
|
//It is a bit too long at the end though, so I'll just shorten it.
|
|
|
|
PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
|
|
pm->cmd.upmove = 0;
|
|
pm->ps->fd.forceJumpSound = 1;
|
|
BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
|
|
|
|
if (trace.entityNum < MAX_CLIENTS)
|
|
{
|
|
pm->ps->forceKickFlip = trace.entityNum+1; //let the server know that this person gets kicked by this client
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
else if ( pm->cmd.forwardmove > 0 //pushing forward
|
|
&& pm->ps->fd.forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
|
|
&& pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1
|
|
&& PM_WalkableGroundDistance() <= 80 //unfortunately we do not have a happy ground timer like SP (this would use up more bandwidth if we wanted prediction workign right), so we'll just use the actual ground distance.
|
|
&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
|
|
{//run up wall, flip backwards
|
|
if ( allowWallRuns )
|
|
{
|
|
//FIXME: have to be moving... make sure it's opposite the wall... or at least forward?
|
|
int wallWalkAnim = BOTH_WALL_FLIP_BACK1;
|
|
int parts = SETANIM_LEGS;
|
|
int contents = MASK_SOLID;//MASK_PLAYERSOLID;//CONTENTS_SOLID;
|
|
//qboolean kick = qtrue;
|
|
if ( pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 )
|
|
{
|
|
wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START;
|
|
parts = SETANIM_BOTH;
|
|
//kick = qfalse;
|
|
}
|
|
else
|
|
{
|
|
if ( !pm->ps->weaponTime )
|
|
{
|
|
parts = SETANIM_BOTH;
|
|
}
|
|
}
|
|
//if ( PM_HasAnimation( pm->gent, wallWalkAnim ) )
|
|
if (1) //sure, we have it! Because I SAID SO.
|
|
{
|
|
vec3_t fwd, traceto, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
vec3_t idealNormal;
|
|
bgEntity_t *traceEnt;
|
|
|
|
VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
|
|
VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
|
|
|
|
AngleVectors( fwdAngles, fwd, NULL, NULL );
|
|
VectorMA( pm->ps->origin, 32, fwd, traceto );
|
|
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too?
|
|
VectorSubtract( pm->ps->origin, traceto, idealNormal );
|
|
VectorNormalize( idealNormal );
|
|
traceEnt = PM_BGEntForNum(trace.entityNum);
|
|
|
|
if ( trace.fraction < 1.0f
|
|
&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
|
|
{//there is a wall there
|
|
pm->ps->velocity[0] = pm->ps->velocity[1] = 0;
|
|
if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START )
|
|
{
|
|
pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f;
|
|
}
|
|
else
|
|
{
|
|
VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity );
|
|
pm->ps->velocity[2] += 150.0f;
|
|
}
|
|
//animate me
|
|
PM_SetAnim( parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
// pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
|
|
//again with the flags!
|
|
//G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" );
|
|
//yucky!
|
|
PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
|
|
pm->cmd.upmove = 0;
|
|
pm->ps->fd.forceJumpSound = 1;
|
|
BG_ForcePowerDrain( pm->ps, FP_LEVITATION, 5 );
|
|
|
|
//kick if jumping off an ent
|
|
/*
|
|
if ( kick && traceEnt && (traceEnt->s.eType == ET_PLAYER || traceEnt->s.eType == ET_NPC) )
|
|
{ //kick that thang!
|
|
pm->ps->forceKickFlip = traceEnt->s.number+1;
|
|
}
|
|
*/
|
|
pm->cmd.rightmove = pm->cmd.forwardmove= 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( (!BG_InSpecialJump( legsAnim )//not in a special jump anim
|
|
||BG_InReboundJump( legsAnim )//we're already in a rebound
|
|
||BG_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you)
|
|
//&& pm->ps->velocity[2] <= 0
|
|
&& pm->ps->velocity[2] > -1200 //not falling down very fast
|
|
&& !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press
|
|
&& (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction
|
|
//&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period
|
|
&& pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better
|
|
//&& WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 10 )//have enough force power to do another one
|
|
&& BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION)
|
|
&& (pm->ps->origin[2]-pm->ps->fd.forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(BG_ForceWallJumpStrength()/2.0f)) //can fit at least one more wall jump in (yes, using "magic numbers"... for now)
|
|
//&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything
|
|
)
|
|
{//see if we're pushing at a wall and jump off it if so
|
|
if ( allowWallGrabs )
|
|
{
|
|
//FIXME: make sure we have enough force power
|
|
//FIXME: check to see if we can go any higher
|
|
//FIXME: limit to a certain number of these in a row?
|
|
//FIXME: maybe don't require a ucmd direction, just check all 4?
|
|
//FIXME: should stick to the wall for a second, then push off...
|
|
vec3_t checkDir, traceto, mins, maxs, fwdAngles;
|
|
trace_t trace;
|
|
vec3_t idealNormal;
|
|
int anim = -1;
|
|
|
|
VectorSet(mins, pm->mins[0], pm->mins[1], 0.0f);
|
|
VectorSet(maxs, pm->maxs[0], pm->maxs[1], 24.0f);
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0.0f);
|
|
|
|
if ( pm->cmd.rightmove )
|
|
{
|
|
if ( pm->cmd.rightmove > 0 )
|
|
{
|
|
anim = BOTH_FORCEWALLREBOUND_RIGHT;
|
|
AngleVectors( fwdAngles, NULL, checkDir, NULL );
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 )
|
|
{
|
|
anim = BOTH_FORCEWALLREBOUND_LEFT;
|
|
AngleVectors( fwdAngles, NULL, checkDir, NULL );
|
|
VectorScale( checkDir, -1, checkDir );
|
|
}
|
|
}
|
|
else if ( pm->cmd.forwardmove > 0 )
|
|
{
|
|
anim = BOTH_FORCEWALLREBOUND_FORWARD;
|
|
AngleVectors( fwdAngles, checkDir, NULL, NULL );
|
|
}
|
|
else if ( pm->cmd.forwardmove < 0 )
|
|
{
|
|
anim = BOTH_FORCEWALLREBOUND_BACK;
|
|
AngleVectors( fwdAngles, checkDir, NULL, NULL );
|
|
VectorScale( checkDir, -1, checkDir );
|
|
}
|
|
if ( anim != -1 )
|
|
{//trace in the dir we're pushing in and see if there's a vertical wall there
|
|
bgEntity_t *traceEnt;
|
|
|
|
VectorMA( pm->ps->origin, 8, checkDir, traceto );
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );//FIXME: clip brushes too?
|
|
VectorSubtract( pm->ps->origin, traceto, idealNormal );
|
|
VectorNormalize( idealNormal );
|
|
traceEnt = PM_BGEntForNum(trace.entityNum);
|
|
if ( trace.fraction < 1.0f
|
|
&&fabs(trace.plane.normal[2]) <= 0.2f/*MAX_WALL_GRAB_SLOPE*/
|
|
&&((trace.entityNum<ENTITYNUM_WORLD&&traceEnt&&traceEnt->s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) )
|
|
{//there is a wall there
|
|
float dot = DotProduct( pm->ps->velocity, trace.plane.normal );
|
|
if ( dot < 1.0f )
|
|
{//can't be heading *away* from the wall!
|
|
//grab it!
|
|
PM_GrabWallForJump( anim );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//FIXME: if in a butterfly, kick people away?
|
|
}
|
|
//END NEW JKA
|
|
}
|
|
}
|
|
|
|
/*
|
|
if ( pm->cmd.upmove > 0
|
|
&& (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
|
|
&& !PM_IsRocketTrooper()
|
|
&& (pm->ps->weaponTime > 0||pm->cmd.buttons&BUTTON_ATTACK) )
|
|
{//okay, we just jumped and we're in an attack
|
|
if ( !BG_InRoll( pm->ps, pm->ps->legsAnim )
|
|
&& !PM_InKnockDown( pm->ps )
|
|
&& !BG_InDeathAnim(pm->ps->legsAnim)
|
|
&& !BG_FlippingAnim( pm->ps->legsAnim )
|
|
&& !PM_SpinningAnim( pm->ps->legsAnim )
|
|
&& !BG_SaberInSpecialAttack( pm->ps->torsoAnim )
|
|
&& ( BG_SaberInAttack( pm->ps->saberMove ) ) )
|
|
{//not in an anim we shouldn't interrupt
|
|
//see if it's not too late to start a special jump-attack
|
|
float animLength = PM_AnimLength( 0, (animNumber_t)pm->ps->torsoAnim );
|
|
if ( animLength - pm->ps->torsoTimer < 500 )
|
|
{//just started the saberMove
|
|
//check for special-case jump attacks
|
|
if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_2 )
|
|
{//using medium attacks
|
|
if (PM_GroundDistance() < 32 &&
|
|
!BG_InSpecialJump(pm->ps->legsAnim))
|
|
{ //FLIP AND DOWNWARD ATTACK
|
|
//trace_t tr;
|
|
|
|
//if (PM_SomeoneInFront(&tr))
|
|
{
|
|
PM_SetSaberMove(PM_SaberFlipOverAttackMove());
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
VectorClear(pml.groundTrace.plane.normal);
|
|
|
|
pm->ps->weaponTime = pm->ps->torsoTimer;
|
|
}
|
|
}
|
|
}
|
|
else if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_3 )
|
|
{//using strong attacks
|
|
if ( pm->cmd.forwardmove > 0 && //going forward
|
|
(pm->cmd.buttons & BUTTON_ATTACK) && //must be holding attack still
|
|
PM_GroundDistance() < 32 &&
|
|
!BG_InSpecialJump(pm->ps->legsAnim))
|
|
{//strong attack: jump-hack
|
|
PM_SetSaberMove( PM_SaberJumpAttackMove() );
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
VectorClear(pml.groundTrace.plane.normal);
|
|
|
|
pm->ps->weaponTime = pm->ps->torsoTimer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
|
|
{
|
|
return qfalse;
|
|
}
|
|
if ( pm->cmd.upmove > 0 )
|
|
{//no special jumps
|
|
pm->ps->velocity[2] = JUMP_VELOCITY;
|
|
PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
}
|
|
|
|
//Jumping
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
PM_SetForceJumpZStart(pm->ps->origin[2]);
|
|
|
|
PM_AddEvent( EV_JUMP );
|
|
|
|
//Set the animations
|
|
if ( pm->ps->gravity > 0 && !BG_InSpecialJump( pm->ps->legsAnim ) )
|
|
{
|
|
PM_JumpForDir();
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
/*
|
|
=============
|
|
PM_CheckWaterJump
|
|
=============
|
|
*/
|
|
static qboolean PM_CheckWaterJump( void ) {
|
|
vec3_t spot;
|
|
int cont;
|
|
vec3_t flatforward;
|
|
|
|
if (pm->ps->pm_time) {
|
|
return qfalse;
|
|
}
|
|
|
|
// check for water jump
|
|
if ( pm->waterlevel != 2 ) {
|
|
return qfalse;
|
|
}
|
|
|
|
flatforward[0] = pml.forward[0];
|
|
flatforward[1] = pml.forward[1];
|
|
flatforward[2] = 0;
|
|
VectorNormalize (flatforward);
|
|
|
|
VectorMA (pm->ps->origin, 30, flatforward, spot);
|
|
spot[2] += 4;
|
|
cont = pm->pointcontents (spot, pm->ps->clientNum );
|
|
if ( !(cont & CONTENTS_SOLID) ) {
|
|
return qfalse;
|
|
}
|
|
|
|
spot[2] += 16;
|
|
cont = pm->pointcontents (spot, pm->ps->clientNum );
|
|
if ( cont ) {
|
|
return qfalse;
|
|
}
|
|
|
|
// jump out of water
|
|
VectorScale (pml.forward, 200, pm->ps->velocity);
|
|
pm->ps->velocity[2] = 350;
|
|
|
|
pm->ps->pm_flags |= PMF_TIME_WATERJUMP;
|
|
pm->ps->pm_time = 2000;
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_WaterJumpMove
|
|
|
|
Flying out of the water
|
|
===================
|
|
*/
|
|
static void PM_WaterJumpMove( void ) {
|
|
// waterjump has no control, but falls
|
|
|
|
PM_StepSlideMove( qtrue );
|
|
|
|
pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime;
|
|
if (pm->ps->velocity[2] < 0) {
|
|
// cancel as soon as we are falling down again
|
|
pm->ps->pm_flags &= ~PMF_ALL_TIMES;
|
|
pm->ps->pm_time = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
PM_WaterMove
|
|
|
|
===================
|
|
*/
|
|
static void PM_WaterMove( void ) {
|
|
int i;
|
|
vec3_t wishvel;
|
|
float wishspeed;
|
|
vec3_t wishdir;
|
|
float scale;
|
|
float vel;
|
|
|
|
if ( PM_CheckWaterJump() ) {
|
|
PM_WaterJumpMove();
|
|
return;
|
|
}
|
|
#if 0
|
|
// jump = head for surface
|
|
if ( pm->cmd.upmove >= 10 ) {
|
|
if (pm->ps->velocity[2] > -300) {
|
|
if ( pm->watertype == CONTENTS_WATER ) {
|
|
pm->ps->velocity[2] = 100;
|
|
} else if (pm->watertype == CONTENTS_SLIME) {
|
|
pm->ps->velocity[2] = 80;
|
|
} else {
|
|
pm->ps->velocity[2] = 50;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
PM_Friction ();
|
|
|
|
scale = PM_CmdScale( &pm->cmd );
|
|
//
|
|
// user intentions
|
|
//
|
|
if ( !scale ) {
|
|
wishvel[0] = 0;
|
|
wishvel[1] = 0;
|
|
wishvel[2] = -60; // sink towards bottom
|
|
} 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 );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
PM_FlyVehicleMove
|
|
|
|
===================
|
|
*/
|
|
static void PM_FlyVehicleMove( void )
|
|
{
|
|
int i;
|
|
vec3_t wishvel;
|
|
float wishspeed;
|
|
vec3_t wishdir;
|
|
float scale;
|
|
float zVel;
|
|
float fmove = 0.0f, smove = 0.0f;
|
|
|
|
// We don't use these here because we pre-calculate the movedir in the vehicle update anyways, and if
|
|
// you leave this, you get strange motion during boarding (the player can move the vehicle).
|
|
//fmove = pm->cmd.forwardmove;
|
|
//smove = pm->cmd.rightmove;
|
|
|
|
// normal slowdown
|
|
if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE )
|
|
{//falling
|
|
zVel = pm->ps->velocity[2];
|
|
PM_Friction ();
|
|
pm->ps->velocity[2] = zVel;
|
|
}
|
|
else
|
|
{
|
|
PM_Friction ();
|
|
if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{
|
|
pm->ps->velocity[2] = 0; // ignore slope movement
|
|
}
|
|
}
|
|
|
|
scale = PM_CmdScale( &pm->cmd );
|
|
|
|
// Get The WishVel And WishSpeed
|
|
//-------------------------------
|
|
if ( pm->ps->clientNum >= MAX_CLIENTS )
|
|
{//NPC
|
|
|
|
// If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
|
|
//--------------------------------------------------------------------------------------------
|
|
if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin))
|
|
{
|
|
//gi.Printf("Generating MoveDir\n");
|
|
for ( i = 0 ; i < 3 ; i++ )
|
|
{
|
|
wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
|
|
}
|
|
|
|
VectorCopy( wishvel, wishdir );
|
|
wishspeed = VectorNormalize(wishdir);
|
|
wishspeed *= scale;
|
|
}
|
|
// Otherwise, Use The Move Dir
|
|
//-----------------------------
|
|
else
|
|
{
|
|
wishspeed = pm->ps->speed;
|
|
VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
|
|
VectorCopy( pm->ps->moveDir, wishdir );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
}
|
|
|
|
// Handle negative speed.
|
|
if ( wishspeed < 0 )
|
|
{
|
|
wishspeed = wishspeed * -1.0f;
|
|
VectorScale( wishvel, -1.0f, wishvel );
|
|
VectorScale( wishdir, -1.0f, wishdir );
|
|
}
|
|
|
|
VectorCopy( wishvel, wishdir );
|
|
wishspeed = VectorNormalize( wishdir );
|
|
|
|
PM_Accelerate( wishdir, wishspeed, 100 );
|
|
|
|
PM_StepSlideMove( 1 );
|
|
}
|
|
|
|
/*
|
|
===================
|
|
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 );
|
|
|
|
if ( pm->ps->pm_type == PM_SPECTATOR && pm->cmd.buttons & BUTTON_ALT_ATTACK) {
|
|
//turbo boost
|
|
scale *= 10;
|
|
}
|
|
|
|
//
|
|
// user intentions
|
|
//
|
|
if ( !scale ) {
|
|
wishvel[0] = 0;
|
|
wishvel[1] = 0;
|
|
wishvel[2] = pm->ps->speed * (pm->cmd.upmove/127.0f);
|
|
} else {
|
|
for (i=0 ; i<3 ; i++) {
|
|
wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove;
|
|
}
|
|
|
|
wishvel[2] += scale * pm->cmd.upmove;
|
|
}
|
|
|
|
VectorCopy (wishvel, wishdir);
|
|
wishspeed = VectorNormalize(wishdir);
|
|
|
|
PM_Accelerate (wishdir, wishspeed, pm_flyaccelerate);
|
|
|
|
PM_StepSlideMove( qfalse );
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
PM_AirMove
|
|
|
|
===================
|
|
*/
|
|
static void PM_AirMove( void ) {
|
|
int i;
|
|
vec3_t wishvel;
|
|
float fmove, smove;
|
|
vec3_t wishdir;
|
|
float wishspeed;
|
|
float scale;
|
|
float accelerate;
|
|
usercmd_t cmd;
|
|
Vehicle_t *pVeh = NULL;
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
bgEntity_t *pEnt = pm_entSelf;
|
|
|
|
if ( pEnt && pEnt->s.NPC_class == CLASS_VEHICLE )
|
|
{
|
|
pVeh = pEnt->m_pVehicle;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->pm_type != PM_SPECTATOR)
|
|
{
|
|
#if METROID_JUMP
|
|
PM_CheckJump();
|
|
#else
|
|
if (pm->ps->fd.forceJumpZStart &&
|
|
pm->ps->forceJumpFlip)
|
|
{
|
|
PM_CheckJump();
|
|
}
|
|
#endif
|
|
}
|
|
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);
|
|
|
|
if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 )
|
|
{//in a hovering vehicle, have air control
|
|
if ( 1 )
|
|
{
|
|
wishspeed = pm->ps->speed;
|
|
VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
|
|
VectorCopy( pm->ps->moveDir, wishdir );
|
|
scale = 1.0f;
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
float controlMod = 1.0f;
|
|
if ( pml.groundPlane )
|
|
{//on a slope of some kind, shouldn't have much control and should slide a lot
|
|
controlMod = pml.groundTrace.plane.normal[2];
|
|
}
|
|
|
|
vec3_t vfwd, vrt;
|
|
vec3_t vAngles;
|
|
|
|
VectorCopy( pVeh->m_vOrientation, vAngles );
|
|
vAngles[ROLL] = 0;//since we're a hovercraft, we really don't want to stafe up into the air if we're banking
|
|
AngleVectors( vAngles, vfwd, vrt, NULL );
|
|
|
|
float speed = pm->ps->speed;
|
|
float strafeSpeed = 0;
|
|
|
|
if ( fmove < 0 )
|
|
{//going backwards
|
|
if ( speed < 0 )
|
|
{//speed is negative, but since our command is reverse, make speed positive
|
|
speed = fabs( speed );
|
|
/*
|
|
if ( pml.groundPlane )
|
|
{//on a slope, still have some control
|
|
speed = fabs( speed );
|
|
}
|
|
else
|
|
{//can't reverse in air
|
|
speed = 0;
|
|
}
|
|
*/
|
|
}
|
|
else if ( speed > 0 )
|
|
{//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually)
|
|
speed = 0;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->clientNum < MAX_CLIENTS )
|
|
{//do normal adding to wishvel
|
|
VectorScale( vfwd, speed*controlMod*(fmove/127.0f), wishvel );
|
|
//just add strafing
|
|
if ( pVeh->m_pVehicleInfo->strafePerc )
|
|
{//we can strafe
|
|
if ( smove )
|
|
{//trying to strafe
|
|
float minSpeed = pVeh->m_pVehicleInfo->speedMax * 0.5f * pVeh->m_pVehicleInfo->strafePerc;
|
|
strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ))*pVeh->m_pVehicleInfo->strafePerc;
|
|
if ( strafeSpeed < minSpeed )
|
|
{
|
|
strafeSpeed = minSpeed;
|
|
}
|
|
strafeSpeed *= controlMod*((float)(smove))/127.0f;
|
|
if ( strafeSpeed < 0 )
|
|
{//pm_accelerate does not understand negative numbers
|
|
strafeSpeed *= -1;
|
|
VectorScale( vrt, -1, vrt );
|
|
}
|
|
//now just add it to actual velocity
|
|
PM_Accelerate( vrt, strafeSpeed, pVeh->m_pVehicleInfo->traction );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pVeh->m_pVehicleInfo->strafePerc )
|
|
{//we can strafe
|
|
if ( pm->ps->clientNum )
|
|
{//alternate control scheme: can strafe
|
|
if ( smove )
|
|
{
|
|
/*
|
|
if ( fmove > 0 )
|
|
{//actively accelerating
|
|
strafeSpeed = pm->ps->speed;
|
|
}
|
|
else
|
|
{//not stepping on accelerator, only strafe based on magnitude of current forward velocity
|
|
strafeSpeed = fabs(DotProduct( pm->ps->velocity, vfwd ));
|
|
}
|
|
*/
|
|
strafeSpeed = ((float)(smove))/127.0f;
|
|
}
|
|
}
|
|
}
|
|
//strafing takes away from forward speed
|
|
VectorScale( vfwd, (fmove/127.0f)*(1.0f-pVeh->m_pVehicleInfo->strafePerc), wishvel );
|
|
if ( strafeSpeed )
|
|
{
|
|
VectorMA( wishvel, strafeSpeed*pVeh->m_pVehicleInfo->strafePerc, vrt, wishvel );
|
|
}
|
|
VectorNormalize( wishvel );
|
|
VectorScale( wishvel, speed*controlMod, wishvel );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else if ( gPMDoSlowFall )
|
|
{//no air-control
|
|
VectorClear( wishvel );
|
|
}
|
|
else if (pm->ps->pm_type == PM_JETPACK)
|
|
{ //reduced air control while not jetting
|
|
for ( i = 0 ; i < 2 ; i++ )
|
|
{
|
|
wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
|
|
}
|
|
wishvel[2] = 0;
|
|
|
|
if (pm->cmd.upmove <= 0)
|
|
{
|
|
VectorScale(wishvel, 0.8f, wishvel);
|
|
}
|
|
else
|
|
{ //if we are jetting then we have more control than usual
|
|
VectorScale(wishvel, 2.0f, wishvel);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
|
|
accelerate = pm_airaccelerate;
|
|
if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER )
|
|
{//speeders have more control in air
|
|
//in mid-air
|
|
accelerate = pVeh->m_pVehicleInfo->traction;
|
|
if ( pml.groundPlane )
|
|
{//on a slope of some kind, shouldn't have much control and should slide a lot
|
|
accelerate *= 0.5f;
|
|
}
|
|
}
|
|
// not on ground, so little effect on velocity
|
|
PM_Accelerate (wishdir, wishspeed, accelerate);
|
|
|
|
// 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 )
|
|
{
|
|
if ( !(pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
|
|
{//don't slide when stuck to a wall
|
|
if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) )
|
|
{
|
|
PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
|
|
pm->ps->velocity, OVERCLIP );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) )
|
|
{//no grav when stuck to wall
|
|
PM_StepSlideMove( qfalse );
|
|
}
|
|
else
|
|
{
|
|
PM_StepSlideMove( qtrue );
|
|
}
|
|
}
|
|
|
|
/*
|
|
===================
|
|
PM_WalkMove
|
|
|
|
===================
|
|
*/
|
|
static void PM_WalkMove( void ) {
|
|
int i;
|
|
vec3_t wishvel;
|
|
float fmove, smove;
|
|
vec3_t wishdir;
|
|
float wishspeed = 0.0f;
|
|
float scale;
|
|
usercmd_t cmd;
|
|
float accelerate;
|
|
float vel;
|
|
qboolean npcMovement = qfalse;
|
|
|
|
if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) {
|
|
// begin swimming
|
|
PM_WaterMove();
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->pm_type != PM_SPECTATOR)
|
|
{
|
|
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);
|
|
|
|
// Get The WishVel And WishSpeed
|
|
//-------------------------------
|
|
if ( pm->ps->clientNum >= MAX_CLIENTS && !VectorCompare( pm->ps->moveDir, vec3_origin ) )
|
|
{//NPC
|
|
bgEntity_t *pEnt = pm_entSelf;
|
|
|
|
if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
|
|
{
|
|
// If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds
|
|
//--------------------------------------------------------------------------------------------
|
|
if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin))
|
|
{
|
|
//gi.Printf("Generating MoveDir\n");
|
|
for ( i = 0 ; i < 3 ; i++ )
|
|
{
|
|
wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
|
|
}
|
|
|
|
VectorCopy( wishvel, wishdir );
|
|
wishspeed = VectorNormalize(wishdir);
|
|
wishspeed *= scale;
|
|
}
|
|
// Otherwise, Use The Move Dir
|
|
//-----------------------------
|
|
else
|
|
{
|
|
//wishspeed = pm->ps->speed;
|
|
VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel );
|
|
VectorCopy (wishvel, wishdir);
|
|
wishspeed = VectorNormalize(wishdir);
|
|
}
|
|
|
|
npcMovement = qtrue;
|
|
}
|
|
}
|
|
|
|
if (!npcMovement)
|
|
{
|
|
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 ( pm->ps->pm_flags & PMF_DUCKED ) {
|
|
if ( wishspeed > pm->ps->speed * pm_duckScale ) {
|
|
wishspeed = pm->ps->speed * pm_duckScale;
|
|
}
|
|
}
|
|
else if ( (pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
|
|
!PM_InRollComplete(pm->ps, pm->ps->legsAnim))
|
|
{
|
|
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 ( pm_flying == FLY_HOVER )
|
|
{
|
|
accelerate = pm_vehicleaccelerate;
|
|
}
|
|
else 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);
|
|
/*
|
|
if (pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
#ifdef QAGAME
|
|
Com_Printf("^1S: %f, %f\n", wishspeed, pm->ps->speed);
|
|
#else
|
|
Com_Printf("^2C: %f, %f\n", wishspeed, pm->ps->speed);
|
|
#endif
|
|
}
|
|
*/
|
|
|
|
//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;
|
|
}
|
|
|
|
vel = VectorLength(pm->ps->velocity);
|
|
|
|
// slide along the ground plane
|
|
PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal,
|
|
pm->ps->velocity, OVERCLIP );
|
|
|
|
// don't decrease velocity when going up or down a slope
|
|
VectorNormalize(pm->ps->velocity);
|
|
VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
|
|
|
|
// don't do anything if standing still
|
|
if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) {
|
|
return;
|
|
}
|
|
|
|
PM_StepSlideMove( qfalse );
|
|
|
|
//Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity));
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
PM_DeadMove
|
|
==============
|
|
*/
|
|
static void PM_DeadMove( void ) {
|
|
float forward;
|
|
|
|
if ( !pml.walking ) {
|
|
return;
|
|
}
|
|
|
|
// extra friction
|
|
|
|
forward = VectorLength (pm->ps->velocity);
|
|
forward -= 20;
|
|
if ( forward <= 0 ) {
|
|
VectorClear (pm->ps->velocity);
|
|
} else {
|
|
VectorNormalize (pm->ps->velocity);
|
|
VectorScale (pm->ps->velocity, forward, pm->ps->velocity);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PM_NoclipMove
|
|
===============
|
|
*/
|
|
static void PM_NoclipMove( void ) {
|
|
float speed, drop, friction, control, newspeed;
|
|
int i;
|
|
vec3_t wishvel;
|
|
float fmove, smove;
|
|
vec3_t wishdir;
|
|
float wishspeed;
|
|
float scale;
|
|
|
|
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
|
|
|
|
// friction
|
|
|
|
speed = VectorLength (pm->ps->velocity);
|
|
if (speed < 1)
|
|
{
|
|
VectorCopy (vec3_origin, pm->ps->velocity);
|
|
}
|
|
else
|
|
{
|
|
drop = 0;
|
|
|
|
friction = pm_friction*1.5; // extra friction
|
|
control = speed < pm_stopspeed ? pm_stopspeed : speed;
|
|
drop += control*friction*pml.frametime;
|
|
|
|
// scale the velocity
|
|
newspeed = speed - drop;
|
|
if (newspeed < 0)
|
|
newspeed = 0;
|
|
newspeed /= speed;
|
|
|
|
VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity);
|
|
}
|
|
|
|
// accelerate
|
|
scale = PM_CmdScale( &pm->cmd );
|
|
if (pm->cmd.buttons & BUTTON_ATTACK) { //turbo boost
|
|
scale *= 10;
|
|
}
|
|
if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { //turbo boost
|
|
scale *= 10;
|
|
}
|
|
|
|
fmove = pm->cmd.forwardmove;
|
|
smove = pm->cmd.rightmove;
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove;
|
|
wishvel[2] += pm->cmd.upmove;
|
|
|
|
VectorCopy (wishvel, wishdir);
|
|
wishspeed = VectorNormalize(wishdir);
|
|
wishspeed *= scale;
|
|
|
|
PM_Accelerate( wishdir, wishspeed, pm_accelerate );
|
|
|
|
// move
|
|
VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin);
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
================
|
|
PM_FootstepForSurface
|
|
|
|
Returns an event number apropriate for the groundsurface
|
|
================
|
|
*/
|
|
static int PM_FootstepForSurface( void )
|
|
{
|
|
if ( pml.groundTrace.surfaceFlags & SURF_NOSTEPS )
|
|
{
|
|
return 0;
|
|
}
|
|
return ( pml.groundTrace.surfaceFlags & MATERIAL_MASK );
|
|
}
|
|
|
|
extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
|
|
static int PM_TryRoll( void )
|
|
{
|
|
trace_t trace;
|
|
int anim = -1;
|
|
vec3_t fwd, right, traceto, mins, maxs, fwdAngles;
|
|
|
|
if ( BG_SaberInAttack( pm->ps->saberMove ) || BG_SaberInSpecialAttack( pm->ps->torsoAnim )
|
|
|| BG_SpinningSaberAnim( pm->ps->legsAnim )
|
|
|| PM_SaberInStart( pm->ps->saberMove ) )
|
|
{//attacking or spinning (or, if player, starting an attack)
|
|
if ( PM_CanRollFromSoulCal( pm->ps ) )
|
|
{//hehe
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE) ||
|
|
PM_IsRocketTrooper() ||
|
|
BG_HasYsalamiri(pm->gametype, pm->ps) ||
|
|
!BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_LEVITATION))
|
|
{ //Not using saber, or can't use jump
|
|
return 0;
|
|
}
|
|
|
|
if ( pm->ps->weapon == WP_SABER )
|
|
{
|
|
saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 );
|
|
if ( saber
|
|
&& (saber->saberFlags&SFL_NO_ROLLS) )
|
|
{
|
|
return 0;
|
|
}
|
|
saber = BG_MySaber( pm->ps->clientNum, 1 );
|
|
if ( saber
|
|
&& (saber->saberFlags&SFL_NO_ROLLS) )
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
VectorSet(mins, pm->mins[0],pm->mins[1],pm->mins[2]+STEPSIZE);
|
|
VectorSet(maxs, pm->maxs[0],pm->maxs[1],pm->ps->crouchheight);
|
|
|
|
VectorSet(fwdAngles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
AngleVectors( fwdAngles, fwd, right, NULL );
|
|
|
|
if ( pm->cmd.forwardmove )
|
|
{ //check forward/backward rolls
|
|
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
|
|
{
|
|
anim = BOTH_ROLL_B;
|
|
VectorMA( pm->ps->origin, -64, fwd, traceto );
|
|
}
|
|
else
|
|
{
|
|
anim = BOTH_ROLL_F;
|
|
VectorMA( pm->ps->origin, 64, fwd, traceto );
|
|
}
|
|
}
|
|
else if ( pm->cmd.rightmove > 0 )
|
|
{ //right
|
|
anim = BOTH_ROLL_R;
|
|
VectorMA( pm->ps->origin, 64, right, traceto );
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 )
|
|
{ //left
|
|
anim = BOTH_ROLL_L;
|
|
VectorMA( pm->ps->origin, -64, right, traceto );
|
|
}
|
|
|
|
if ( anim != -1 )
|
|
{ //We want to roll. Perform a trace to see if we can, and if so, send us into one.
|
|
pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID );
|
|
if ( trace.fraction >= 1.0f )
|
|
{
|
|
pm->ps->saberMove = LS_NONE;
|
|
return anim;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef QAGAME
|
|
static void PM_CrashLandEffect( void )
|
|
{
|
|
float delta;
|
|
if ( pm->waterlevel )
|
|
{
|
|
return;
|
|
}
|
|
delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );?
|
|
if ( delta >= 30 )
|
|
{
|
|
vec3_t bottom;
|
|
int effectID = -1;
|
|
int material = (pml.groundTrace.surfaceFlags&MATERIAL_MASK);
|
|
VectorSet( bottom, pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1 );
|
|
switch ( material )
|
|
{
|
|
case MATERIAL_MUD:
|
|
effectID = EFFECT_LANDING_MUD;
|
|
break;
|
|
case MATERIAL_SAND:
|
|
effectID = EFFECT_LANDING_SAND;
|
|
break;
|
|
case MATERIAL_DIRT:
|
|
effectID = EFFECT_LANDING_DIRT;
|
|
break;
|
|
case MATERIAL_SNOW:
|
|
effectID = EFFECT_LANDING_SNOW;
|
|
break;
|
|
case MATERIAL_GRAVEL:
|
|
effectID = EFFECT_LANDING_GRAVEL;
|
|
break;
|
|
}
|
|
|
|
if ( effectID != -1 )
|
|
{
|
|
G_PlayEffect( effectID, bottom, pml.groundTrace.plane.normal );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
=================
|
|
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;
|
|
qboolean didRoll = qfalse;
|
|
|
|
// 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 ) {
|
|
pm->ps->inAirAnim = qfalse;
|
|
return;
|
|
}
|
|
t = (-b - sqrt( den ) ) / ( 2 * a );
|
|
|
|
delta = vel + t * acc;
|
|
delta = delta*delta * 0.0001;
|
|
|
|
#ifdef QAGAME
|
|
PM_CrashLandEffect();
|
|
#endif
|
|
// ducking while falling doubles damage
|
|
if ( pm->ps->pm_flags & PMF_DUCKED ) {
|
|
delta *= 2;
|
|
}
|
|
|
|
if (pm->ps->legsAnim == BOTH_A7_KICK_F_AIR ||
|
|
pm->ps->legsAnim == BOTH_A7_KICK_B_AIR ||
|
|
pm->ps->legsAnim == BOTH_A7_KICK_R_AIR ||
|
|
pm->ps->legsAnim == BOTH_A7_KICK_L_AIR)
|
|
{
|
|
int landAnim = -1;
|
|
switch ( pm->ps->legsAnim )
|
|
{
|
|
case BOTH_A7_KICK_F_AIR:
|
|
landAnim = BOTH_FORCELAND1;
|
|
break;
|
|
case BOTH_A7_KICK_B_AIR:
|
|
landAnim = BOTH_FORCELANDBACK1;
|
|
break;
|
|
case BOTH_A7_KICK_R_AIR:
|
|
landAnim = BOTH_FORCELANDRIGHT1;
|
|
break;
|
|
case BOTH_A7_KICK_L_AIR:
|
|
landAnim = BOTH_FORCELANDLEFT1;
|
|
break;
|
|
}
|
|
if ( landAnim != -1 )
|
|
{
|
|
if ( pm->ps->torsoAnim == pm->ps->legsAnim )
|
|
{
|
|
PM_SetAnim(SETANIM_BOTH, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
}
|
|
else
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, landAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
}
|
|
}
|
|
}
|
|
else if (pm->ps->legsAnim == BOTH_FORCEJUMPLEFT1 ||
|
|
pm->ps->legsAnim == BOTH_FORCEJUMPRIGHT1 ||
|
|
pm->ps->legsAnim == BOTH_FORCEJUMPBACK1 ||
|
|
pm->ps->legsAnim == BOTH_FORCEJUMP1)
|
|
{
|
|
int fjAnim;
|
|
switch (pm->ps->legsAnim)
|
|
{
|
|
case BOTH_FORCEJUMPLEFT1:
|
|
fjAnim = BOTH_LANDLEFT1;
|
|
break;
|
|
case BOTH_FORCEJUMPRIGHT1:
|
|
fjAnim = BOTH_LANDRIGHT1;
|
|
break;
|
|
case BOTH_FORCEJUMPBACK1:
|
|
fjAnim = BOTH_LANDBACK1;
|
|
break;
|
|
default:
|
|
fjAnim = BOTH_LAND1;
|
|
break;
|
|
}
|
|
PM_SetAnim(SETANIM_BOTH, fjAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
}
|
|
// decide which landing animation to use
|
|
else if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim && !pm->ps->m_iVehicleNum)
|
|
{ //only play a land animation if we transitioned into an in-air animation while off the ground
|
|
if (!BG_SaberInSpecial(pm->ps->saberMove))
|
|
{
|
|
if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) {
|
|
PM_ForceLegsAnim( BOTH_LANDBACK1 );
|
|
} else {
|
|
PM_ForceLegsAnim( BOTH_LAND1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
|
|
{ //saber handles its own anims
|
|
//This will push us back into our weaponready stance from the land anim.
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
|
|
{
|
|
PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon == WP_EMPLACED_GUN)
|
|
{
|
|
PM_StartTorsoAnim( BOTH_GUNSIT1 );
|
|
}
|
|
else
|
|
{
|
|
PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!BG_InSpecialJump(pm->ps->legsAnim) ||
|
|
pm->ps->legsTimer < 1 ||
|
|
(pm->ps->legsAnim) == BOTH_WALL_RUN_LEFT ||
|
|
(pm->ps->legsAnim) == BOTH_WALL_RUN_RIGHT)
|
|
{ //Only set the timer if we're in an anim that can be interrupted (this would not be, say, a flip)
|
|
if (!BG_InRoll(pm->ps, pm->ps->legsAnim) && pm->ps->inAirAnim)
|
|
{
|
|
if (!BG_SaberInSpecial(pm->ps->saberMove) || pm->ps->weapon != WP_SABER)
|
|
{
|
|
if (pm->ps->legsAnim != BOTH_FORCELAND1 &&
|
|
pm->ps->legsAnim != BOTH_FORCELANDBACK1 &&
|
|
pm->ps->legsAnim != BOTH_FORCELANDRIGHT1 &&
|
|
pm->ps->legsAnim != BOTH_FORCELANDLEFT1)
|
|
{ //don't override if we have started a force land
|
|
pm->ps->legsTimer = TIMER_LAND;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pm->ps->inAirAnim = qfalse;
|
|
|
|
if (pm->ps->m_iVehicleNum)
|
|
{ //don't do fall stuff while on a vehicle
|
|
return;
|
|
}
|
|
|
|
// never take falling damage if completely underwater
|
|
if ( pm->waterlevel == 3 ) {
|
|
return;
|
|
}
|
|
|
|
// reduce falling damage if there is standing water
|
|
if ( pm->waterlevel == 2 ) {
|
|
delta *= 0.25;
|
|
}
|
|
if ( pm->waterlevel == 1 ) {
|
|
delta *= 0.5;
|
|
}
|
|
|
|
if ( delta < 1 ) {
|
|
return;
|
|
}
|
|
|
|
if ( pm->ps->pm_flags & PMF_DUCKED )
|
|
{
|
|
if( delta >= 2 && !PM_InOnGroundAnim( pm->ps->legsAnim ) && !PM_InKnockDown( pm->ps ) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
|
|
pm->ps->forceHandExtend == HANDEXTEND_NONE )
|
|
{//roll!
|
|
int anim = PM_TryRoll();
|
|
|
|
if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
|
|
{
|
|
anim = 0;
|
|
pm->ps->legsTimer = 0;
|
|
pm->ps->legsAnim = 0;
|
|
PM_SetAnim(SETANIM_BOTH,BOTH_LAND1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
|
|
pm->ps->legsTimer = TIMER_LAND;
|
|
}
|
|
|
|
if ( anim )
|
|
{//absorb some impact
|
|
pm->ps->legsTimer = 0;
|
|
delta /= 3; // /= 2 just cancels out the above delta *= 2 when landing while crouched, the roll itself should absorb a little damage
|
|
pm->ps->legsAnim = 0;
|
|
if (pm->ps->torsoAnim == BOTH_A7_SOULCAL)
|
|
{ //get out of it on torso
|
|
pm->ps->torsoTimer = 0;
|
|
}
|
|
PM_SetAnim(SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
|
|
didRoll = qtrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 > 7)
|
|
{
|
|
int delta_send = (int)delta;
|
|
|
|
if (delta_send > 600)
|
|
{ //will never need to know any value above this
|
|
delta_send = 600;
|
|
}
|
|
|
|
if (pm->ps->fd.forceJumpZStart)
|
|
{
|
|
if ((int)pm->ps->origin[2] >= (int)pm->ps->fd.forceJumpZStart)
|
|
{ //was force jumping, landed on higher or same level as when force jump was started
|
|
if (delta_send > 8)
|
|
{
|
|
delta_send = 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (delta_send > 8)
|
|
{
|
|
int dif = ((int)pm->ps->fd.forceJumpZStart - (int)pm->ps->origin[2]);
|
|
int dmgLess = (forceJumpHeight[pm->ps->fd.forcePowerLevel[FP_LEVITATION]] - dif);
|
|
|
|
if (dmgLess < 0)
|
|
{
|
|
dmgLess = 0;
|
|
}
|
|
|
|
delta_send -= (dmgLess*0.3);
|
|
|
|
if (delta_send < 8)
|
|
{
|
|
delta_send = 8;
|
|
}
|
|
|
|
//Com_Printf("Damage sub: %i\n", (int)((dmgLess*0.1)));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (didRoll)
|
|
{ //Add the appropriate event..
|
|
PM_AddEventWithParm( EV_ROLL, delta_send );
|
|
}
|
|
else
|
|
{
|
|
PM_AddEventWithParm( EV_FALL, delta_send );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (didRoll)
|
|
{
|
|
PM_AddEventWithParm( EV_ROLL, 0 );
|
|
}
|
|
else
|
|
{
|
|
PM_AddEventWithParm( EV_FOOTSTEP, PM_FootstepForSurface() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// make sure velocity resets so we don't bounce back up again in case we miss the clear elsewhere
|
|
pm->ps->velocity[2] = 0;
|
|
|
|
// start footstep cycle over
|
|
pm->ps->bobCycle = 0;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
PM_CorrectAllSolid
|
|
=============
|
|
*/
|
|
static int PM_CorrectAllSolid( trace_t *trace ) {
|
|
int i, j, k;
|
|
vec3_t point;
|
|
|
|
if ( pm->debugLevel ) {
|
|
Com_Printf("%i:allsolid\n", c_pmove);
|
|
}
|
|
|
|
// jitter around
|
|
for (i = -1; i <= 1; i++) {
|
|
for (j = -1; j <= 1; j++) {
|
|
for (k = -1; k <= 1; k++) {
|
|
VectorCopy(pm->ps->origin, point);
|
|
point[0] += (float) i;
|
|
point[1] += (float) j;
|
|
point[2] += (float) k;
|
|
pm->trace (trace, point, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
|
|
if ( !trace->allsolid ) {
|
|
point[0] = pm->ps->origin[0];
|
|
point[1] = pm->ps->origin[1];
|
|
point[2] = pm->ps->origin[2] - 0.25;
|
|
|
|
pm->trace (trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
|
|
pml.groundTrace = *trace;
|
|
return qtrue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
|
|
return qfalse;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
PM_GroundTraceMissed
|
|
|
|
The ground trace didn't hit a surface, so we are in freefall
|
|
=============
|
|
*/
|
|
static void PM_GroundTraceMissed( void ) {
|
|
trace_t trace;
|
|
vec3_t point;
|
|
|
|
//rww - don't want to do this when handextend_choke, because you can be standing on the ground
|
|
//while still holding your throat.
|
|
if ( pm->ps->pm_type == PM_FLOAT )
|
|
{
|
|
//we're assuming this is because you're being choked
|
|
int parts = SETANIM_LEGS;
|
|
|
|
//rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
|
|
//a proper anim even when on the ground.
|
|
PM_SetAnim(parts, BOTH_CHOKE3, SETANIM_FLAG_OVERRIDE, 100);
|
|
}
|
|
else if ( pm->ps->pm_type == PM_JETPACK )
|
|
{//jetpacking
|
|
//rww - also don't use SETANIM_FLAG_HOLD, it will cause the legs to float around a bit before going into
|
|
//a proper anim even when on the ground.
|
|
//PM_SetAnim(SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE, 100);
|
|
}
|
|
//If the anim is choke3, act like we just went into the air because we aren't in a float
|
|
else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE || (pm->ps->legsAnim) == BOTH_CHOKE3 )
|
|
{
|
|
// we just transitioned into freefall
|
|
if ( pm->debugLevel ) {
|
|
Com_Printf("%i:lift\n", c_pmove);
|
|
}
|
|
|
|
// if they aren't in a jumping animation and the ground is a ways away, force into it
|
|
// if we didn't do the trace, the player would be backflipping down staircases
|
|
VectorCopy( pm->ps->origin, point );
|
|
point[2] -= 64;
|
|
|
|
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
|
|
if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT ) {
|
|
if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD))
|
|
{
|
|
//PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE, 100);
|
|
PM_SetAnim(SETANIM_LEGS,BOTH_INAIR1,0, 100);
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
}
|
|
else if ( pm->cmd.forwardmove >= 0 )
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE, 100);
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
}
|
|
else
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE, 100);
|
|
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
|
|
}
|
|
|
|
pm->ps->inAirAnim = qtrue;
|
|
}
|
|
}
|
|
else if (!pm->ps->inAirAnim)
|
|
{
|
|
// if they aren't in a jumping animation and the ground is a ways away, force into it
|
|
// if we didn't do the trace, the player would be backflipping down staircases
|
|
VectorCopy( pm->ps->origin, point );
|
|
point[2] -= 64;
|
|
|
|
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
|
|
if ( trace.fraction == 1.0 || pm->ps->pm_type == PM_FLOAT )
|
|
{
|
|
pm->ps->inAirAnim = qtrue;
|
|
}
|
|
}
|
|
|
|
if (PM_InRollComplete(pm->ps, pm->ps->legsAnim))
|
|
{ //Client won't catch an animation restart because it only checks frame against incoming frame, so if you roll when you land after rolling
|
|
//off of something it won't replay the roll anim unless we switch it off in the air. This fixes that.
|
|
PM_SetAnim(SETANIM_BOTH,BOTH_INAIR1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
|
|
pm->ps->inAirAnim = qtrue;
|
|
}
|
|
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
PM_GroundTrace
|
|
=============
|
|
*/
|
|
static void PM_GroundTrace( void ) {
|
|
vec3_t point;
|
|
trace_t trace;
|
|
float minNormal = (float)MIN_WALK_NORMAL;
|
|
|
|
if ( pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
bgEntity_t *pEnt = pm_entSelf;
|
|
|
|
if (pEnt && pEnt->s.NPC_class == CLASS_VEHICLE)
|
|
{
|
|
minNormal = pEnt->m_pVehicle->m_pVehicleInfo->maxSlope;
|
|
}
|
|
}
|
|
|
|
point[0] = pm->ps->origin[0];
|
|
point[1] = pm->ps->origin[1];
|
|
point[2] = pm->ps->origin[2] - 0.25;
|
|
|
|
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask);
|
|
pml.groundTrace = trace;
|
|
|
|
// do something corrective if the trace starts in a solid...
|
|
if ( trace.allsolid ) {
|
|
if ( !PM_CorrectAllSolid(&trace) )
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->pm_type == PM_FLOAT || pm->ps->pm_type == PM_JETPACK)
|
|
{
|
|
PM_GroundTraceMissed();
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
return;
|
|
}
|
|
|
|
// if the trace didn't hit anything, we are in free fall
|
|
if ( trace.fraction == 1.0 ) {
|
|
PM_GroundTraceMissed();
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
return;
|
|
}
|
|
|
|
// check if getting thrown off the ground
|
|
if ( pm->ps->velocity[2] > 0 && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) {
|
|
if ( pm->debugLevel ) {
|
|
Com_Printf("%i:kickoff\n", c_pmove);
|
|
}
|
|
// go into jump animation
|
|
if ( pm->cmd.forwardmove >= 0 ) {
|
|
PM_ForceLegsAnim( BOTH_JUMP1 );
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP;
|
|
} else {
|
|
PM_ForceLegsAnim( BOTH_JUMPBACK1 );
|
|
pm->ps->pm_flags |= PMF_BACKWARDS_JUMP;
|
|
}
|
|
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
pml.groundPlane = qfalse;
|
|
pml.walking = qfalse;
|
|
return;
|
|
}
|
|
|
|
// slopes that are too steep will not be considered onground
|
|
if ( trace.plane.normal[2] < minNormal ) {
|
|
if ( pm->debugLevel ) {
|
|
Com_Printf("%i:steep\n", c_pmove);
|
|
}
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
pml.groundPlane = qtrue;
|
|
pml.walking = qfalse;
|
|
return;
|
|
}
|
|
|
|
pml.groundPlane = qtrue;
|
|
pml.walking = qtrue;
|
|
|
|
// hitting solid ground will end a waterjump
|
|
if (pm->ps->pm_flags & PMF_TIME_WATERJUMP)
|
|
{
|
|
pm->ps->pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND);
|
|
pm->ps->pm_time = 0;
|
|
}
|
|
|
|
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
|
|
// just hit the ground
|
|
if ( pm->debugLevel ) {
|
|
Com_Printf("%i:Land\n", c_pmove);
|
|
}
|
|
|
|
PM_CrashLand();
|
|
|
|
#ifdef QAGAME
|
|
if (pm->ps->clientNum < MAX_CLIENTS &&
|
|
!pm->ps->m_iVehicleNum &&
|
|
trace.entityNum < ENTITYNUM_WORLD &&
|
|
trace.entityNum >= MAX_CLIENTS &&
|
|
!pm->ps->zoomMode &&
|
|
pm_entSelf)
|
|
{ //check if we landed on a vehicle
|
|
gentity_t *trEnt = &g_entities[trace.entityNum];
|
|
if (trEnt->inuse && trEnt->client && trEnt->s.eType == ET_NPC && trEnt->s.NPC_class == CLASS_VEHICLE &&
|
|
!trEnt->client->ps.m_iVehicleNum &&
|
|
trEnt->m_pVehicle &&
|
|
trEnt->m_pVehicle->m_pVehicleInfo->type != VH_WALKER &&
|
|
trEnt->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
|
|
{ //it's a vehicle alright, let's board it.. if it's not an atst or ship
|
|
if (!BG_SaberInSpecial(pm->ps->saberMove) &&
|
|
pm->ps->forceHandExtend == HANDEXTEND_NONE &&
|
|
pm->ps->weaponTime <= 0)
|
|
{
|
|
gentity_t *servEnt = (gentity_t *)pm_entSelf;
|
|
if (g_gametype.integer < GT_TEAM ||
|
|
!trEnt->alliedTeam ||
|
|
(trEnt->alliedTeam == servEnt->client->sess.sessionTeam))
|
|
{ //not belonging to a team, or client is on same team
|
|
trEnt->m_pVehicle->m_pVehicleInfo->Board(trEnt->m_pVehicle, pm_entSelf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// 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;
|
|
pm->ps->lastOnGround = pm->cmd.serverTime;
|
|
|
|
PM_AddTouchEnt( trace.entityNum );
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
PM_SetWaterLevel
|
|
=============
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
qboolean PM_CheckDualForwardJumpDuck( void )
|
|
{
|
|
qboolean resized = qfalse;
|
|
if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
|
|
{
|
|
//dynamically reduce bounding box to let character sail over heads of enemies
|
|
if ( ( pm->ps->legsTimer >= 1450
|
|
&& PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 400 )
|
|
||(pm->ps->legsTimer >= 400
|
|
&& PM_AnimLength( 0, BOTH_JUMPATTACK6 ) - pm->ps->legsTimer >= 1100 ) )
|
|
{//in a part of the anim that we're pretty much sideways in, raise up the mins
|
|
pm->mins[2] = 0;
|
|
pm->ps->pm_flags |= PMF_FIX_MINS;
|
|
resized = qtrue;
|
|
}
|
|
}
|
|
return resized;
|
|
}
|
|
|
|
void PM_CheckFixMins( void )
|
|
{
|
|
if ( (pm->ps->pm_flags&PMF_FIX_MINS) )// pm->mins[2] > DEFAULT_MINS_2 )
|
|
{//drop the mins back down
|
|
//do a trace to make sure it's okay
|
|
trace_t trace;
|
|
vec3_t end, curMins, curMaxs;
|
|
|
|
VectorSet( end, pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2]+MINS_Z );
|
|
VectorSet( curMins, pm->mins[0], pm->mins[1], 0 );
|
|
VectorSet( curMaxs, pm->maxs[0], pm->maxs[1], pm->ps->standheight );
|
|
|
|
pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
|
|
if ( !trace.allsolid && !trace.startsolid )
|
|
{//should never start in solid
|
|
if ( trace.fraction >= 1.0f )
|
|
{//all clear
|
|
//drop the bottom of my bbox back down
|
|
pm->mins[2] = MINS_Z;
|
|
pm->ps->pm_flags &= ~PMF_FIX_MINS;
|
|
}
|
|
else
|
|
{//move me up so the bottom of my bbox will be where the trace ended, at least
|
|
//need to trace up, too
|
|
float updist = ((1.0f-trace.fraction) * -MINS_Z);
|
|
end[2] = pm->ps->origin[2]+updist;
|
|
pm->trace( &trace, pm->ps->origin, curMins, curMaxs, end, pm->ps->clientNum, pm->tracemask );
|
|
if ( !trace.allsolid && !trace.startsolid )
|
|
{//should never start in solid
|
|
if ( trace.fraction >= 1.0f )
|
|
{//all clear
|
|
//move me up
|
|
pm->ps->origin[2] += updist;
|
|
//drop the bottom of my bbox back down
|
|
pm->mins[2] = MINS_Z;
|
|
pm->ps->pm_flags &= ~PMF_FIX_MINS;
|
|
}
|
|
else
|
|
{//crap, no room to expand, so just crouch us
|
|
if ( pm->ps->legsAnim != BOTH_JUMPATTACK6
|
|
|| pm->ps->legsTimer <= 200 )
|
|
{//at the end of the anim, and we can't leave ourselves like this
|
|
//so drop the maxs, put the mins back and move us up
|
|
pm->maxs[2] += MINS_Z;
|
|
pm->ps->origin[2] -= MINS_Z;
|
|
pm->mins[2] = MINS_Z;
|
|
//this way we'll be in a crouch when we're done
|
|
if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
|
|
{
|
|
pm->ps->legsTimer = pm->ps->torsoTimer = 0;
|
|
}
|
|
pm->ps->pm_flags |= PMF_DUCKED;
|
|
//FIXME: do we need to set a crouch anim here?
|
|
pm->ps->pm_flags &= ~PMF_FIX_MINS;
|
|
}
|
|
}
|
|
}//crap, stuck
|
|
}
|
|
}//crap, stuck!
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PM_CheckDuck
|
|
|
|
Sets mins, maxs, and pm->ps->viewheight
|
|
==============
|
|
*/
|
|
static void PM_CheckDuck (void)
|
|
{
|
|
trace_t trace;
|
|
|
|
if ( pm->ps->m_iVehicleNum > 0 && pm->ps->m_iVehicleNum < ENTITYNUM_NONE )
|
|
{//riding a vehicle or are a vehicle
|
|
//no ducking or rolling when on a vehicle
|
|
//right? not even on ones that you just ride on top of?
|
|
pm->ps->pm_flags &= ~PMF_DUCKED;
|
|
pm->ps->pm_flags &= ~PMF_ROLLING;
|
|
//NOTE: we don't clear the pm->cmd.upmove here because
|
|
//the vehicle code may need it later... but, for riders,
|
|
//it should have already been copied over to the vehicle, right?
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
return;
|
|
}
|
|
if (pm_entVeh && pm_entVeh->m_pVehicle &&
|
|
(pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER ||
|
|
pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL))
|
|
{
|
|
trace_t solidTr;
|
|
|
|
pm->mins[0] = -16;
|
|
pm->mins[1] = -16;
|
|
pm->mins[2] = MINS_Z;
|
|
|
|
pm->maxs[0] = 16;
|
|
pm->maxs[1] = 16;
|
|
pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
|
|
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
|
|
|
|
pm->trace (&solidTr, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->m_iVehicleNum, pm->tracemask);
|
|
if (solidTr.startsolid || solidTr.allsolid || solidTr.fraction != 1.0f)
|
|
{ //whoops, can't fit here. Down to 0!
|
|
VectorClear(pm->mins);
|
|
VectorClear(pm->maxs);
|
|
#ifdef QAGAME
|
|
{
|
|
gentity_t *me = &g_entities[pm->ps->clientNum];
|
|
if (me->inuse && me->client)
|
|
{ //yeah, this is a really terrible hack.
|
|
me->client->solidHack = level.time + 200;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->clientNum < MAX_CLIENTS)
|
|
{
|
|
pm->mins[0] = -15;
|
|
pm->mins[1] = -15;
|
|
|
|
pm->maxs[0] = 15;
|
|
pm->maxs[1] = 15;
|
|
}
|
|
|
|
if ( PM_CheckDualForwardJumpDuck() )
|
|
{//special anim resizing us
|
|
}
|
|
else
|
|
{
|
|
PM_CheckFixMins();
|
|
|
|
if ( !pm->mins[2] )
|
|
{
|
|
pm->mins[2] = MINS_Z;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->pm_type == PM_DEAD && pm->ps->clientNum < MAX_CLIENTS)
|
|
{
|
|
pm->maxs[2] = -8;
|
|
pm->ps->viewheight = DEAD_VIEWHEIGHT;
|
|
return;
|
|
}
|
|
|
|
if (BG_InRoll(pm->ps, pm->ps->legsAnim) && !BG_KickingAnim(pm->ps->legsAnim))
|
|
{
|
|
pm->maxs[2] = pm->ps->crouchheight; //CROUCH_MAXS_2;
|
|
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
|
|
pm->ps->pm_flags &= ~PMF_DUCKED;
|
|
pm->ps->pm_flags |= PMF_ROLLING;
|
|
return;
|
|
}
|
|
else if (pm->ps->pm_flags & PMF_ROLLING)
|
|
{
|
|
// try to stand up
|
|
pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
|
|
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
|
|
if (!trace.allsolid)
|
|
pm->ps->pm_flags &= ~PMF_ROLLING;
|
|
}
|
|
else if (pm->cmd.upmove < 0 ||
|
|
pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
|
|
pm->ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
|
|
pm->ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
|
|
{ // duck
|
|
pm->ps->pm_flags |= PMF_DUCKED;
|
|
}
|
|
else
|
|
{ // stand up if possible
|
|
if (pm->ps->pm_flags & PMF_DUCKED)
|
|
{
|
|
// try to stand up
|
|
pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
|
|
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
|
|
if (!trace.allsolid)
|
|
pm->ps->pm_flags &= ~PMF_DUCKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pm->ps->pm_flags & PMF_DUCKED)
|
|
{
|
|
pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
|
|
pm->ps->viewheight = CROUCH_VIEWHEIGHT;
|
|
}
|
|
else if (pm->ps->pm_flags & PMF_ROLLING)
|
|
{
|
|
pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
|
|
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
|
|
}
|
|
else
|
|
{
|
|
pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
|
|
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//===================================================================
|
|
|
|
|
|
|
|
/*
|
|
==============
|
|
PM_Use
|
|
|
|
Generates a use event
|
|
==============
|
|
*/
|
|
#define USE_DELAY 2000
|
|
|
|
void PM_Use( void )
|
|
{
|
|
if ( pm->ps->useTime > 0 )
|
|
pm->ps->useTime -= 100;//pm->cmd.msec;
|
|
|
|
if ( pm->ps->useTime > 0 ) {
|
|
return;
|
|
}
|
|
|
|
if ( ! (pm->cmd.buttons & BUTTON_USE ) )
|
|
{
|
|
pm->useEvent = 0;
|
|
pm->ps->useTime = 0;
|
|
return;
|
|
}
|
|
|
|
pm->useEvent = EV_USE;
|
|
pm->ps->useTime = USE_DELAY;
|
|
}
|
|
|
|
qboolean PM_WalkingAnim( int anim )
|
|
{
|
|
switch ( anim )
|
|
{
|
|
case BOTH_WALK1: //# Normal walk
|
|
case BOTH_WALK2: //# Normal walk with saber
|
|
case BOTH_WALK_STAFF: //# Normal walk with staff
|
|
case BOTH_WALK_DUAL: //# Normal walk with staff
|
|
case BOTH_WALK5: //# Tavion taunting Kyle (cin 22)
|
|
case BOTH_WALK6: //# Slow walk for Luke (cin 12)
|
|
case BOTH_WALK7: //# Fast walk
|
|
case BOTH_WALKBACK1: //# Walk1 backwards
|
|
case BOTH_WALKBACK2: //# Walk2 backwards
|
|
case BOTH_WALKBACK_STAFF: //# Walk backwards with staff
|
|
case BOTH_WALKBACK_DUAL: //# Walk backwards with dual
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean PM_RunningAnim( int anim )
|
|
{
|
|
switch ( (anim) )
|
|
{
|
|
case BOTH_RUN1:
|
|
case BOTH_RUN2:
|
|
case BOTH_RUN_STAFF:
|
|
case BOTH_RUN_DUAL:
|
|
case BOTH_RUNBACK1:
|
|
case BOTH_RUNBACK2:
|
|
case BOTH_RUNBACK_STAFF:
|
|
case BOTH_RUNBACK_DUAL:
|
|
case BOTH_RUN1START: //# Start into full run1
|
|
case BOTH_RUN1STOP: //# Stop from full run1
|
|
case BOTH_RUNSTRAFE_LEFT1: //# Sidestep left: should loop
|
|
case BOTH_RUNSTRAFE_RIGHT1: //# Sidestep right: should loop
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean PM_SwimmingAnim( int anim )
|
|
{
|
|
switch ( anim )
|
|
{
|
|
case BOTH_SWIM_IDLE1: //# Swimming Idle 1
|
|
case BOTH_SWIMFORWARD: //# Swim forward loop
|
|
case BOTH_SWIMBACKWARD: //# Swim backward loop
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean PM_RollingAnim( int anim )
|
|
{
|
|
switch ( anim )
|
|
{
|
|
case BOTH_ROLL_F: //# Roll forward
|
|
case BOTH_ROLL_B: //# Roll backward
|
|
case BOTH_ROLL_L: //# Roll left
|
|
case BOTH_ROLL_R: //# Roll right
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles )
|
|
{
|
|
vec3_t nvf, ovf, ovr, new_angles;
|
|
float pitch, mod, dot;
|
|
|
|
VectorSet( angles, 0, yaw, 0 );
|
|
AngleVectors( angles, ovf, ovr, NULL );
|
|
|
|
vectoangles( slope, new_angles );
|
|
pitch = new_angles[PITCH] + 90;
|
|
new_angles[ROLL] = new_angles[PITCH] = 0;
|
|
|
|
AngleVectors( new_angles, nvf, NULL, NULL );
|
|
|
|
mod = DotProduct( nvf, ovr );
|
|
|
|
if ( mod < 0 )
|
|
mod = -1;
|
|
else
|
|
mod = 1;
|
|
|
|
dot = DotProduct( nvf, ovf );
|
|
|
|
angles[YAW] = 0;
|
|
angles[PITCH] = dot * pitch;
|
|
angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod);
|
|
}
|
|
|
|
void PM_FootSlopeTrace( float *pDiff, float *pInterval )
|
|
{
|
|
vec3_t footLOrg, footROrg, footLBot, footRBot;
|
|
vec3_t footLPoint, footRPoint;
|
|
vec3_t footMins, footMaxs;
|
|
vec3_t footLSlope, footRSlope;
|
|
|
|
trace_t trace;
|
|
float diff, interval;
|
|
|
|
mdxaBone_t boltMatrix;
|
|
vec3_t G2Angles;
|
|
|
|
VectorSet(G2Angles, 0, pm->ps->viewangles[YAW], 0);
|
|
|
|
interval = 4;//?
|
|
|
|
strap_G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_LFoot,
|
|
&boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime,
|
|
NULL, pm->modelScale );
|
|
footLPoint[0] = boltMatrix.matrix[0][3];
|
|
footLPoint[1] = boltMatrix.matrix[1][3];
|
|
footLPoint[2] = boltMatrix.matrix[2][3];
|
|
|
|
strap_G2API_GetBoltMatrix( pm->ghoul2, 0, pm->g2Bolts_RFoot,
|
|
&boltMatrix, G2Angles, pm->ps->origin, pm->cmd.serverTime,
|
|
NULL, pm->modelScale );
|
|
footRPoint[0] = boltMatrix.matrix[0][3];
|
|
footRPoint[1] = boltMatrix.matrix[1][3];
|
|
footRPoint[2] = boltMatrix.matrix[2][3];
|
|
|
|
//get these on the cgame and store it, save ourselves a ghoul2 construct skel call
|
|
VectorCopy( footLPoint, footLOrg );
|
|
VectorCopy( footRPoint, footROrg );
|
|
|
|
//step 2: adjust foot tag z height to bottom of bbox+1
|
|
footLOrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
|
|
footROrg[2] = pm->ps->origin[2] + pm->mins[2] + 1;
|
|
VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 );
|
|
VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 );
|
|
|
|
//step 3: trace down from each, find difference
|
|
VectorSet( footMins, -3, -3, 0 );
|
|
VectorSet( footMaxs, 3, 3, 1 );
|
|
|
|
pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask );
|
|
VectorCopy( trace.endpos, footLBot );
|
|
VectorCopy( trace.plane.normal, footLSlope );
|
|
|
|
pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask );
|
|
VectorCopy( trace.endpos, footRBot );
|
|
VectorCopy( trace.plane.normal, footRSlope );
|
|
|
|
diff = footLBot[2] - footRBot[2];
|
|
|
|
if ( pDiff != NULL )
|
|
{
|
|
*pDiff = diff;
|
|
}
|
|
if ( pInterval != NULL )
|
|
{
|
|
*pInterval = interval;
|
|
}
|
|
}
|
|
|
|
qboolean BG_InSlopeAnim( int anim )
|
|
{
|
|
switch ( anim )
|
|
{
|
|
case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right
|
|
case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
|
|
case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
|
|
case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
|
|
case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
|
|
case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left
|
|
case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
|
|
case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
|
|
case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
|
|
case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
|
|
case LEGS_S1_LUP1:
|
|
case LEGS_S1_LUP2:
|
|
case LEGS_S1_LUP3:
|
|
case LEGS_S1_LUP4:
|
|
case LEGS_S1_LUP5:
|
|
case LEGS_S1_RUP1:
|
|
case LEGS_S1_RUP2:
|
|
case LEGS_S1_RUP3:
|
|
case LEGS_S1_RUP4:
|
|
case LEGS_S1_RUP5:
|
|
case LEGS_S3_LUP1:
|
|
case LEGS_S3_LUP2:
|
|
case LEGS_S3_LUP3:
|
|
case LEGS_S3_LUP4:
|
|
case LEGS_S3_LUP5:
|
|
case LEGS_S3_RUP1:
|
|
case LEGS_S3_RUP2:
|
|
case LEGS_S3_RUP3:
|
|
case LEGS_S3_RUP4:
|
|
case LEGS_S3_RUP5:
|
|
case LEGS_S4_LUP1:
|
|
case LEGS_S4_LUP2:
|
|
case LEGS_S4_LUP3:
|
|
case LEGS_S4_LUP4:
|
|
case LEGS_S4_LUP5:
|
|
case LEGS_S4_RUP1:
|
|
case LEGS_S4_RUP2:
|
|
case LEGS_S4_RUP3:
|
|
case LEGS_S4_RUP4:
|
|
case LEGS_S4_RUP5:
|
|
case LEGS_S5_LUP1:
|
|
case LEGS_S5_LUP2:
|
|
case LEGS_S5_LUP3:
|
|
case LEGS_S5_LUP4:
|
|
case LEGS_S5_LUP5:
|
|
case LEGS_S5_RUP1:
|
|
case LEGS_S5_RUP2:
|
|
case LEGS_S5_RUP3:
|
|
case LEGS_S5_RUP4:
|
|
case LEGS_S5_RUP5:
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
#define SLOPE_RECALC_INT 100
|
|
|
|
qboolean PM_AdjustStandAnimForSlope( void )
|
|
{
|
|
float diff;
|
|
float interval;
|
|
int destAnim;
|
|
int legsAnim;
|
|
#define SLOPERECALCVAR pm->ps->slopeRecalcTime //this is purely convenience
|
|
|
|
if (!pm->ghoul2)
|
|
{ //probably just changed models and not quite in sync yet
|
|
return qfalse;
|
|
}
|
|
|
|
if ( pm->g2Bolts_LFoot == -1 || pm->g2Bolts_RFoot == -1 )
|
|
{//need these bolts!
|
|
return qfalse;
|
|
}
|
|
|
|
//step 1: find the 2 foot tags
|
|
PM_FootSlopeTrace( &diff, &interval );
|
|
|
|
//step 4: based on difference, choose one of the left/right slope-match intervals
|
|
if ( diff >= interval*5 )
|
|
{
|
|
destAnim = LEGS_LEFTUP5;
|
|
}
|
|
else if ( diff >= interval*4 )
|
|
{
|
|
destAnim = LEGS_LEFTUP4;
|
|
}
|
|
else if ( diff >= interval*3 )
|
|
{
|
|
destAnim = LEGS_LEFTUP3;
|
|
}
|
|
else if ( diff >= interval*2 )
|
|
{
|
|
destAnim = LEGS_LEFTUP2;
|
|
}
|
|
else if ( diff >= interval )
|
|
{
|
|
destAnim = LEGS_LEFTUP1;
|
|
}
|
|
else if ( diff <= interval*-5 )
|
|
{
|
|
destAnim = LEGS_RIGHTUP5;
|
|
}
|
|
else if ( diff <= interval*-4 )
|
|
{
|
|
destAnim = LEGS_RIGHTUP4;
|
|
}
|
|
else if ( diff <= interval*-3 )
|
|
{
|
|
destAnim = LEGS_RIGHTUP3;
|
|
}
|
|
else if ( diff <= interval*-2 )
|
|
{
|
|
destAnim = LEGS_RIGHTUP2;
|
|
}
|
|
else if ( diff <= interval*-1 )
|
|
{
|
|
destAnim = LEGS_RIGHTUP1;
|
|
}
|
|
else
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
legsAnim = pm->ps->legsAnim;
|
|
//adjust for current legs anim
|
|
switch ( legsAnim )
|
|
{
|
|
case BOTH_STAND1:
|
|
|
|
case LEGS_S1_LUP1:
|
|
case LEGS_S1_LUP2:
|
|
case LEGS_S1_LUP3:
|
|
case LEGS_S1_LUP4:
|
|
case LEGS_S1_LUP5:
|
|
case LEGS_S1_RUP1:
|
|
case LEGS_S1_RUP2:
|
|
case LEGS_S1_RUP3:
|
|
case LEGS_S1_RUP4:
|
|
case LEGS_S1_RUP5:
|
|
destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1);
|
|
break;
|
|
case BOTH_STAND2:
|
|
case BOTH_SABERFAST_STANCE:
|
|
case BOTH_SABERSLOW_STANCE:
|
|
case BOTH_CROUCH1IDLE:
|
|
case BOTH_CROUCH1:
|
|
case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right
|
|
case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
|
|
case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
|
|
case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
|
|
case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
|
|
case LEGS_RIGHTUP1: //# On a slope with RIGHT foot 4 higher than left
|
|
case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
|
|
case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
|
|
case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
|
|
case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
|
|
//fine
|
|
break;
|
|
case BOTH_STAND3:
|
|
case LEGS_S3_LUP1:
|
|
case LEGS_S3_LUP2:
|
|
case LEGS_S3_LUP3:
|
|
case LEGS_S3_LUP4:
|
|
case LEGS_S3_LUP5:
|
|
case LEGS_S3_RUP1:
|
|
case LEGS_S3_RUP2:
|
|
case LEGS_S3_RUP3:
|
|
case LEGS_S3_RUP4:
|
|
case LEGS_S3_RUP5:
|
|
destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1);
|
|
break;
|
|
case BOTH_STAND4:
|
|
case LEGS_S4_LUP1:
|
|
case LEGS_S4_LUP2:
|
|
case LEGS_S4_LUP3:
|
|
case LEGS_S4_LUP4:
|
|
case LEGS_S4_LUP5:
|
|
case LEGS_S4_RUP1:
|
|
case LEGS_S4_RUP2:
|
|
case LEGS_S4_RUP3:
|
|
case LEGS_S4_RUP4:
|
|
case LEGS_S4_RUP5:
|
|
destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1);
|
|
break;
|
|
case BOTH_STAND5:
|
|
case LEGS_S5_LUP1:
|
|
case LEGS_S5_LUP2:
|
|
case LEGS_S5_LUP3:
|
|
case LEGS_S5_LUP4:
|
|
case LEGS_S5_LUP5:
|
|
case LEGS_S5_RUP1:
|
|
case LEGS_S5_RUP2:
|
|
case LEGS_S5_RUP3:
|
|
case LEGS_S5_RUP4:
|
|
case LEGS_S5_RUP5:
|
|
destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1);
|
|
break;
|
|
case BOTH_STAND6:
|
|
default:
|
|
return qfalse;
|
|
break;
|
|
}
|
|
|
|
//step 5: based on the chosen interval and the current legsAnim, pick the correct anim
|
|
//step 6: increment/decrement to the dest anim, not instant
|
|
if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5)
|
|
|| (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5)
|
|
|| (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5)
|
|
|| (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5)
|
|
|| (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5) )
|
|
{//already in left-side up
|
|
if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
|
|
{
|
|
legsAnim++;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
|
|
{
|
|
legsAnim--;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else //if (SLOPERECALCVAR < pm->cmd.serverTime)
|
|
{
|
|
legsAnim = destAnim;
|
|
}
|
|
|
|
destAnim = legsAnim;
|
|
}
|
|
else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5)
|
|
|| (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5)
|
|
|| (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5)
|
|
|| (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5)
|
|
|| (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5) )
|
|
{//already in right-side up
|
|
if ( destAnim > legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
|
|
{
|
|
legsAnim++;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim < legsAnim && SLOPERECALCVAR < pm->cmd.serverTime )
|
|
{
|
|
legsAnim--;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else //if (SLOPERECALCVAR < pm->cmd.serverTime)
|
|
{
|
|
legsAnim = destAnim;
|
|
}
|
|
|
|
destAnim = legsAnim;
|
|
}
|
|
else
|
|
{//in a stand of some sort?
|
|
switch ( legsAnim )
|
|
{
|
|
case BOTH_STAND1:
|
|
case TORSO_WEAPONREADY1:
|
|
case TORSO_WEAPONREADY2:
|
|
case TORSO_WEAPONREADY3:
|
|
case TORSO_WEAPONREADY10:
|
|
|
|
if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 )
|
|
{//going into left side up
|
|
destAnim = LEGS_S1_LUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 )
|
|
{//going into right side up
|
|
destAnim = LEGS_S1_RUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else
|
|
{//will never get here
|
|
return qfalse;
|
|
}
|
|
break;
|
|
case BOTH_STAND2:
|
|
case BOTH_SABERFAST_STANCE:
|
|
case BOTH_SABERSLOW_STANCE:
|
|
case BOTH_CROUCH1IDLE:
|
|
if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 )
|
|
{//going into left side up
|
|
destAnim = LEGS_LEFTUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 )
|
|
{//going into right side up
|
|
destAnim = LEGS_RIGHTUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else
|
|
{//will never get here
|
|
return qfalse;
|
|
}
|
|
break;
|
|
case BOTH_STAND3:
|
|
if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 )
|
|
{//going into left side up
|
|
destAnim = LEGS_S3_LUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 )
|
|
{//going into right side up
|
|
destAnim = LEGS_S3_RUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else
|
|
{//will never get here
|
|
return qfalse;
|
|
}
|
|
break;
|
|
case BOTH_STAND4:
|
|
if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 )
|
|
{//going into left side up
|
|
destAnim = LEGS_S4_LUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 )
|
|
{//going into right side up
|
|
destAnim = LEGS_S4_RUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else
|
|
{//will never get here
|
|
return qfalse;
|
|
}
|
|
break;
|
|
case BOTH_STAND5:
|
|
if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 )
|
|
{//going into left side up
|
|
destAnim = LEGS_S5_LUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 )
|
|
{//going into right side up
|
|
destAnim = LEGS_S5_RUP1;
|
|
SLOPERECALCVAR = pm->cmd.serverTime + SLOPE_RECALC_INT;
|
|
}
|
|
else
|
|
{//will never get here
|
|
return qfalse;
|
|
}
|
|
break;
|
|
case BOTH_STAND6:
|
|
default:
|
|
return qfalse;
|
|
break;
|
|
}
|
|
}
|
|
//step 7: set the anim
|
|
//PM_SetAnim( SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL, 100 );
|
|
PM_ContinueLegsAnim(destAnim);
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
extern int WeaponReadyLegsAnim[WP_NUM_WEAPONS];
|
|
|
|
//rww - slowly back out of slope leg anims, to prevent skipping between slope anims and general jittering
|
|
int PM_LegsSlopeBackTransition(int desiredAnim)
|
|
{
|
|
int anim = pm->ps->legsAnim;
|
|
int resultingAnim = desiredAnim;
|
|
|
|
switch ( anim )
|
|
{
|
|
case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right
|
|
case LEGS_LEFTUP3: //# On a slope with left foot 12 higher than right
|
|
case LEGS_LEFTUP4: //# On a slope with left foot 16 higher than right
|
|
case LEGS_LEFTUP5: //# On a slope with left foot 20 higher than right
|
|
case LEGS_RIGHTUP2: //# On a slope with RIGHT foot 8 higher than left
|
|
case LEGS_RIGHTUP3: //# On a slope with RIGHT foot 12 higher than left
|
|
case LEGS_RIGHTUP4: //# On a slope with RIGHT foot 16 higher than left
|
|
case LEGS_RIGHTUP5: //# On a slope with RIGHT foot 20 higher than left
|
|
case LEGS_S1_LUP2:
|
|
case LEGS_S1_LUP3:
|
|
case LEGS_S1_LUP4:
|
|
case LEGS_S1_LUP5:
|
|
case LEGS_S1_RUP2:
|
|
case LEGS_S1_RUP3:
|
|
case LEGS_S1_RUP4:
|
|
case LEGS_S1_RUP5:
|
|
case LEGS_S3_LUP2:
|
|
case LEGS_S3_LUP3:
|
|
case LEGS_S3_LUP4:
|
|
case LEGS_S3_LUP5:
|
|
case LEGS_S3_RUP2:
|
|
case LEGS_S3_RUP3:
|
|
case LEGS_S3_RUP4:
|
|
case LEGS_S3_RUP5:
|
|
case LEGS_S4_LUP2:
|
|
case LEGS_S4_LUP3:
|
|
case LEGS_S4_LUP4:
|
|
case LEGS_S4_LUP5:
|
|
case LEGS_S4_RUP2:
|
|
case LEGS_S4_RUP3:
|
|
case LEGS_S4_RUP4:
|
|
case LEGS_S4_RUP5:
|
|
case LEGS_S5_LUP2:
|
|
case LEGS_S5_LUP3:
|
|
case LEGS_S5_LUP4:
|
|
case LEGS_S5_LUP5:
|
|
case LEGS_S5_RUP2:
|
|
case LEGS_S5_RUP3:
|
|
case LEGS_S5_RUP4:
|
|
case LEGS_S5_RUP5:
|
|
if (pm->ps->slopeRecalcTime < pm->cmd.serverTime)
|
|
{
|
|
resultingAnim = anim-1;
|
|
pm->ps->slopeRecalcTime = pm->cmd.serverTime + 8;//SLOPE_RECALC_INT;
|
|
}
|
|
else
|
|
{
|
|
resultingAnim = anim;
|
|
}
|
|
VectorClear(pm->ps->velocity);
|
|
break;
|
|
}
|
|
|
|
return resultingAnim;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PM_Footsteps
|
|
===============
|
|
*/
|
|
static void PM_Footsteps( void ) {
|
|
float bobmove;
|
|
int old;
|
|
qboolean footstep;
|
|
int setAnimFlags = 0;
|
|
|
|
if ( (PM_InSaberAnim( (pm->ps->legsAnim) ) && !BG_SpinningSaberAnim( (pm->ps->legsAnim) ))
|
|
|| (pm->ps->legsAnim) == BOTH_STAND1
|
|
|| (pm->ps->legsAnim) == BOTH_STAND1TO2
|
|
|| (pm->ps->legsAnim) == BOTH_STAND2TO1
|
|
|| (pm->ps->legsAnim) == BOTH_STAND2
|
|
|| (pm->ps->legsAnim) == BOTH_SABERFAST_STANCE
|
|
|| (pm->ps->legsAnim) == BOTH_SABERSLOW_STANCE
|
|
|| (pm->ps->legsAnim) == BOTH_BUTTON_HOLD
|
|
|| (pm->ps->legsAnim) == BOTH_BUTTON_RELEASE
|
|
|| PM_LandingAnim( (pm->ps->legsAnim) )
|
|
|| PM_PainAnim( (pm->ps->legsAnim) ))
|
|
{//legs are in a saber anim, and not spinning, be sure to override it
|
|
setAnimFlags |= SETANIM_FLAG_OVERRIDE;
|
|
}
|
|
|
|
//
|
|
// 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->saberMove == LS_SPINATTACK)
|
|
{
|
|
PM_ContinueLegsAnim( pm->ps->torsoAnim );
|
|
}
|
|
else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {
|
|
|
|
// airborne leaves position in cycle intact, but doesn't advance
|
|
if ( pm->waterlevel > 1 )
|
|
{
|
|
if (pm->xyspeed > 60)
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_SWIMFORWARD );
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_SWIM_IDLE1 );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// if not trying to move
|
|
else if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) {
|
|
if ( pm->xyspeed < 5 ) {
|
|
pm->ps->bobCycle = 0; // start at beginning of cycle again
|
|
if ( pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_RANCOR )
|
|
{
|
|
if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
|
|
{//holding someone
|
|
PM_ContinueLegsAnim( BOTH_STAND4 );
|
|
//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL);
|
|
}
|
|
else if ( (pm->ps->eFlags2&EF2_ALERTED) )
|
|
{//have an enemy or have had one since we spawned
|
|
PM_ContinueLegsAnim( BOTH_STAND2 );
|
|
//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
|
|
}
|
|
else
|
|
{//just stand there
|
|
PM_ContinueLegsAnim( BOTH_STAND1 );
|
|
//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
|
|
}
|
|
}
|
|
else if ( pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_WAMPA )
|
|
{
|
|
if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
|
|
{//holding a victim
|
|
PM_ContinueLegsAnim( BOTH_STAND2 );
|
|
//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL);
|
|
}
|
|
else
|
|
{//not holding a victim
|
|
PM_ContinueLegsAnim( BOTH_STAND1 );
|
|
//PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL);
|
|
}
|
|
}
|
|
else if ( (pm->ps->pm_flags & PMF_DUCKED) || (pm->ps->pm_flags & PMF_ROLLING) ) {
|
|
if ((pm->ps->legsAnim) != BOTH_CROUCH1IDLE)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1IDLE, setAnimFlags, 100);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_CROUCH1IDLE );
|
|
}
|
|
} else {
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
|
|
{
|
|
///???? continue legs anim on a torso anim...??!!!
|
|
//yeah.. the anim has a valid pose for the legs, it uses it (you can't move while using disruptor)
|
|
PM_ContinueLegsAnim( TORSO_WEAPONREADY4 );
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon == WP_SABER && BG_SabersOff( pm->ps ) )
|
|
{
|
|
if (!PM_AdjustStandAnimForSlope())
|
|
{
|
|
//PM_ContinueLegsAnim( BOTH_STAND1 );
|
|
PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(BOTH_STAND1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon != WP_SABER || !PM_AdjustStandAnimForSlope())
|
|
{
|
|
if (pm->ps->weapon == WP_SABER)
|
|
{
|
|
PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(PM_GetSaberStance()));
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim(PM_LegsSlopeBackTransition(WeaponReadyLegsAnim[pm->ps->weapon]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
footstep = qfalse;
|
|
|
|
if (pm->ps->saberMove == LS_SPINATTACK)
|
|
{
|
|
bobmove = 0.2f;
|
|
PM_ContinueLegsAnim( pm->ps->torsoAnim );
|
|
}
|
|
else if ( pm->ps->pm_flags & PMF_DUCKED )
|
|
{
|
|
int rolled = 0;
|
|
|
|
bobmove = 0.5; // ducked characters bob much faster
|
|
|
|
if ( ( (PM_RunningAnim( pm->ps->legsAnim )&&VectorLengthSquared(pm->ps->velocity)>=40000/*200*200*/) || PM_CanRollFromSoulCal( pm->ps ) ) &&
|
|
!BG_InRoll(pm->ps, pm->ps->legsAnim) )
|
|
{//roll!
|
|
rolled = PM_TryRoll();
|
|
}
|
|
if ( !rolled )
|
|
{ //if the roll failed or didn't attempt, do standard crouching anim stuff.
|
|
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) {
|
|
if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags, 100);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
|
|
}
|
|
}
|
|
else {
|
|
if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags, 100);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ //otherwise send us into the roll
|
|
pm->ps->legsTimer = 0;
|
|
pm->ps->legsAnim = 0;
|
|
PM_SetAnim(SETANIM_BOTH,rolled,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 150);
|
|
PM_AddEventWithParm( EV_ROLL, 0 );
|
|
pm->maxs[2] = pm->ps->crouchheight;//CROUCH_MAXS_2;
|
|
pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
|
|
pm->ps->pm_flags &= ~PMF_DUCKED;
|
|
pm->ps->pm_flags |= PMF_ROLLING;
|
|
}
|
|
}
|
|
else if ((pm->ps->pm_flags & PMF_ROLLING) && !BG_InRoll(pm->ps, pm->ps->legsAnim) &&
|
|
!PM_InRollComplete(pm->ps, pm->ps->legsAnim))
|
|
{
|
|
bobmove = 0.5; // ducked characters bob much faster
|
|
|
|
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
|
|
{
|
|
if ((pm->ps->legsAnim) != BOTH_CROUCH1WALKBACK)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags, 100);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_CROUCH1WALKBACK );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pm->ps->legsAnim) != BOTH_CROUCH1WALK)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags, 100);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim( BOTH_CROUCH1WALK );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int desiredAnim = -1;
|
|
|
|
if ((pm->ps->legsAnim == BOTH_FORCELAND1 ||
|
|
pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
|
|
pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
|
|
pm->ps->legsAnim == BOTH_FORCELANDLEFT1) &&
|
|
pm->ps->legsTimer > 0)
|
|
{ //let it finish first
|
|
bobmove = 0.2f;
|
|
}
|
|
else if ( !( pm->cmd.buttons & BUTTON_WALKING ) )
|
|
{//running
|
|
bobmove = 0.4f; // faster speeds bob faster
|
|
if ( pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_WAMPA )
|
|
{
|
|
if ( (pm->ps->eFlags2&EF2_USE_ALT_ANIM) )
|
|
{//full on run, on all fours
|
|
desiredAnim = BOTH_RUN1;
|
|
}
|
|
else
|
|
{//regular, upright run
|
|
desiredAnim = BOTH_RUN2;
|
|
}
|
|
}
|
|
else if ( pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_RANCOR )
|
|
{//no run anims
|
|
if ( (pm->ps->pm_flags&PMF_BACKWARDS_RUN) )
|
|
{
|
|
desiredAnim = BOTH_WALKBACK1;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALK1;
|
|
}
|
|
}
|
|
else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
|
|
{
|
|
switch (pm->ps->fd.saberAnimLevel)
|
|
{
|
|
case SS_STAFF:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{//saber off
|
|
desiredAnim = BOTH_RUNBACK1;
|
|
}
|
|
else
|
|
{
|
|
//desiredAnim = BOTH_RUNBACK_STAFF;
|
|
//hmm.. stuff runback anim is pretty messed up for some reason.
|
|
desiredAnim = BOTH_RUNBACK2;
|
|
}
|
|
break;
|
|
case SS_DUAL:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{//sabers off
|
|
desiredAnim = BOTH_RUNBACK1;
|
|
}
|
|
else
|
|
{
|
|
//desiredAnim = BOTH_RUNBACK_DUAL;
|
|
//and so is the dual
|
|
desiredAnim = BOTH_RUNBACK2;
|
|
}
|
|
break;
|
|
default:
|
|
if ( pm->ps->saberHolstered )
|
|
{//saber off
|
|
desiredAnim = BOTH_RUNBACK1;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_RUNBACK2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (pm->ps->fd.saberAnimLevel)
|
|
{
|
|
case SS_STAFF:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{//blades off
|
|
desiredAnim = BOTH_RUN1;
|
|
}
|
|
else if ( pm->ps->saberHolstered == 1 )
|
|
{//1 blade on
|
|
desiredAnim = BOTH_RUN2;
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->fd.forcePowersActive & (1<<FP_SPEED))
|
|
{
|
|
desiredAnim = BOTH_RUN1;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_RUN_STAFF;
|
|
}
|
|
}
|
|
break;
|
|
case SS_DUAL:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{//blades off
|
|
desiredAnim = BOTH_RUN1;
|
|
}
|
|
else if ( pm->ps->saberHolstered == 1 )
|
|
{//1 saber on
|
|
desiredAnim = BOTH_RUN2;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_RUN_DUAL;
|
|
}
|
|
break;
|
|
default:
|
|
if ( pm->ps->saberHolstered )
|
|
{//saber off
|
|
desiredAnim = BOTH_RUN1;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_RUN2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
footstep = qtrue;
|
|
}
|
|
else
|
|
{
|
|
bobmove = 0.2f; // walking bobs slow
|
|
if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN )
|
|
{
|
|
switch (pm->ps->fd.saberAnimLevel)
|
|
{
|
|
case SS_STAFF:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{
|
|
desiredAnim = BOTH_WALKBACK1;
|
|
}
|
|
else if ( pm->ps->saberHolstered )
|
|
{
|
|
desiredAnim = BOTH_WALKBACK2;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALKBACK_STAFF;
|
|
}
|
|
break;
|
|
case SS_DUAL:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{
|
|
desiredAnim = BOTH_WALKBACK1;
|
|
}
|
|
else if ( pm->ps->saberHolstered )
|
|
{
|
|
desiredAnim = BOTH_WALKBACK2;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALKBACK_DUAL;
|
|
}
|
|
break;
|
|
default:
|
|
if ( pm->ps->saberHolstered )
|
|
{
|
|
desiredAnim = BOTH_WALKBACK1;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALKBACK2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pm->ps->weapon == WP_MELEE )
|
|
{
|
|
desiredAnim = BOTH_WALK1;
|
|
}
|
|
else if ( BG_SabersOff( pm->ps ) )
|
|
{
|
|
desiredAnim = BOTH_WALK1;
|
|
}
|
|
else
|
|
{
|
|
switch (pm->ps->fd.saberAnimLevel)
|
|
{
|
|
case SS_STAFF:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{
|
|
desiredAnim = BOTH_WALK1;
|
|
}
|
|
else if ( pm->ps->saberHolstered )
|
|
{
|
|
desiredAnim = BOTH_WALK2;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALK_STAFF;
|
|
}
|
|
break;
|
|
case SS_DUAL:
|
|
if ( pm->ps->saberHolstered > 1 )
|
|
{
|
|
desiredAnim = BOTH_WALK1;
|
|
}
|
|
else if ( pm->ps->saberHolstered )
|
|
{
|
|
desiredAnim = BOTH_WALK2;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALK_DUAL;
|
|
}
|
|
break;
|
|
default:
|
|
if ( pm->ps->saberHolstered )
|
|
{
|
|
desiredAnim = BOTH_WALK1;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_WALK2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (desiredAnim != -1)
|
|
{
|
|
int ires = PM_LegsSlopeBackTransition(desiredAnim);
|
|
|
|
if ((pm->ps->legsAnim) != desiredAnim && ires == desiredAnim)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, desiredAnim, setAnimFlags, 100);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim(ires);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for footstep / splash sounds
|
|
old = pm->ps->bobCycle;
|
|
pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255;
|
|
|
|
// if we just crossed a cycle boundary, play an apropriate footstep event
|
|
if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 )
|
|
{
|
|
pm->ps->footstepTime = pm->cmd.serverTime + 300;
|
|
if ( pm->waterlevel == 1 ) {
|
|
// splashing
|
|
PM_AddEvent( EV_FOOTSPLASH );
|
|
} else if ( pm->waterlevel == 2 ) {
|
|
// wading / swimming at surface
|
|
PM_AddEvent( EV_SWIM );
|
|
} else if ( pm->waterlevel == 3 ) {
|
|
// no sound when completely underwater
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PM_WaterEvents
|
|
|
|
Generate sound events for entering and leaving water
|
|
==============
|
|
*/
|
|
static void PM_WaterEvents( void ) { // FIXME?
|
|
#ifdef QAGAME
|
|
qboolean impact_splash = qfalse;
|
|
#endif
|
|
//
|
|
// if just entered a water volume, play a sound
|
|
//
|
|
if (!pml.previous_waterlevel && pm->waterlevel) {
|
|
#ifdef QAGAME
|
|
if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
|
|
{
|
|
impact_splash = qtrue;
|
|
}
|
|
#endif
|
|
PM_AddEvent( EV_WATER_TOUCH );
|
|
}
|
|
|
|
//
|
|
// if just completely exited a water volume, play a sound
|
|
//
|
|
if (pml.previous_waterlevel && !pm->waterlevel) {
|
|
#ifdef QAGAME
|
|
if ( VectorLengthSquared( pm->ps->velocity ) > 40000 )
|
|
{
|
|
impact_splash = qtrue;
|
|
}
|
|
#endif
|
|
PM_AddEvent( EV_WATER_LEAVE );
|
|
}
|
|
|
|
#ifdef QAGAME
|
|
if ( impact_splash )
|
|
{
|
|
//play the splash effect
|
|
trace_t tr;
|
|
vec3_t start, end;
|
|
|
|
|
|
VectorCopy( pm->ps->origin, start );
|
|
VectorCopy( pm->ps->origin, end );
|
|
|
|
// FIXME: set start and end better
|
|
start[2] += 10;
|
|
end[2] -= 40;
|
|
|
|
pm->trace( &tr, start, vec3_origin, vec3_origin, end, pm->ps->clientNum, MASK_WATER );
|
|
|
|
if ( tr.fraction < 1.0f )
|
|
{
|
|
if ( (tr.contents&CONTENTS_LAVA) )
|
|
{
|
|
G_PlayEffect( EFFECT_LAVA_SPLASH, tr.endpos, tr.plane.normal );
|
|
}
|
|
else if ( (tr.contents&CONTENTS_SLIME) )
|
|
{
|
|
G_PlayEffect( EFFECT_ACID_SPLASH, tr.endpos, tr.plane.normal );
|
|
}
|
|
else //must be water
|
|
{
|
|
G_PlayEffect( EFFECT_WATER_SPLASH, tr.endpos, tr.plane.normal );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
}
|
|
|
|
void BG_ClearRocketLock( playerState_t *ps )
|
|
{
|
|
if ( ps )
|
|
{
|
|
ps->rocketLockIndex = ENTITYNUM_NONE;
|
|
ps->rocketLastValidTime = 0;
|
|
ps->rocketLockTime = -1;
|
|
ps->rocketTargetTime = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
PM_BeginWeaponChange
|
|
===============
|
|
*/
|
|
void PM_BeginWeaponChange( int weapon ) {
|
|
if ( weapon <= WP_NONE || weapon >= WP_NUM_WEAPONS ) {
|
|
return;
|
|
}
|
|
|
|
if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
|
|
return;
|
|
}
|
|
|
|
// turn of any kind of zooming when weapon switching.
|
|
if (pm->ps->zoomMode)
|
|
{
|
|
pm->ps->zoomMode = 0;
|
|
pm->ps->zoomTime = pm->ps->commandTime;
|
|
}
|
|
|
|
PM_AddEventWithParm( EV_CHANGE_WEAPON, weapon );
|
|
pm->ps->weaponstate = WEAPON_DROPPING;
|
|
pm->ps->weaponTime += 200;
|
|
//PM_StartTorsoAnim( TORSO_DROPWEAP1 );
|
|
PM_SetAnim(SETANIM_TORSO, TORSO_DROPWEAP1, SETANIM_FLAG_OVERRIDE, 0);
|
|
|
|
BG_ClearRocketLock( pm->ps );
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
PM_FinishWeaponChange
|
|
===============
|
|
*/
|
|
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;
|
|
}
|
|
|
|
if (weapon == WP_SABER)
|
|
{
|
|
PM_SetSaberMove(LS_DRAW);
|
|
}
|
|
else
|
|
{
|
|
//PM_StartTorsoAnim( TORSO_RAISEWEAP1);
|
|
PM_SetAnim(SETANIM_TORSO, TORSO_RAISEWEAP1, SETANIM_FLAG_OVERRIDE, 0);
|
|
}
|
|
pm->ps->weapon = weapon;
|
|
pm->ps->weaponstate = WEAPON_RAISING;
|
|
pm->ps->weaponTime += 250;
|
|
}
|
|
|
|
#ifdef QAGAME
|
|
extern void WP_GetVehicleCamPos( gentity_t *ent, gentity_t *pilot, vec3_t camPos );
|
|
#else
|
|
extern void CG_GetVehicleCamPos( vec3_t camPos );
|
|
#endif
|
|
#define MAX_XHAIR_DIST_ACCURACY 20000.0f
|
|
int BG_VehTraceFromCamPos( trace_t *camTrace, bgEntity_t *bgEnt, const vec3_t entOrg, const vec3_t shotStart, const vec3_t end, vec3_t newEnd, vec3_t shotDir, float bestDist )
|
|
{
|
|
//NOTE: this MUST stay up to date with the method used in CG_ScanForCrosshairEntity (where it checks the doExtraVehTraceFromViewPos bool)
|
|
vec3_t viewDir2End, extraEnd, camPos;
|
|
float minAutoAimDist;
|
|
|
|
#ifdef QAGAME
|
|
WP_GetVehicleCamPos( (gentity_t *)bgEnt, (gentity_t *)bgEnt->m_pVehicle->m_pPilot, camPos );
|
|
#else
|
|
CG_GetVehicleCamPos( camPos );
|
|
#endif
|
|
|
|
minAutoAimDist = Distance( entOrg, camPos ) + (bgEnt->m_pVehicle->m_pVehicleInfo->length/2.0f) + 200.0f;
|
|
|
|
VectorCopy( end, newEnd );
|
|
VectorSubtract( end, camPos, viewDir2End );
|
|
VectorNormalize( viewDir2End );
|
|
VectorMA( camPos, MAX_XHAIR_DIST_ACCURACY, viewDir2End, extraEnd );
|
|
|
|
#ifdef QAGAME
|
|
trap_Trace( camTrace, camPos, vec3_origin, vec3_origin, extraEnd,
|
|
bgEnt->s.number, CONTENTS_SOLID|CONTENTS_BODY );
|
|
#else
|
|
pm->trace( camTrace, camPos, vec3_origin, vec3_origin, extraEnd,
|
|
bgEnt->s.number, CONTENTS_SOLID|CONTENTS_BODY );
|
|
#endif
|
|
|
|
if ( !camTrace->allsolid
|
|
&& !camTrace->startsolid
|
|
&& camTrace->fraction < 1.0f
|
|
&& (camTrace->fraction*MAX_XHAIR_DIST_ACCURACY) > minAutoAimDist
|
|
&& ((camTrace->fraction*MAX_XHAIR_DIST_ACCURACY)-Distance( entOrg, camPos )) < bestDist )
|
|
{//this trace hit *something* that's closer than the thing the main trace hit, so use this result instead
|
|
VectorCopy( camTrace->endpos, newEnd );
|
|
VectorSubtract( newEnd, shotStart, shotDir );
|
|
VectorNormalize( shotDir );
|
|
return (camTrace->entityNum+1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void PM_RocketLock( float lockDist, qboolean vehicleLock )
|
|
{
|
|
// Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
|
|
// implement our alt-fire locking stuff
|
|
vec3_t ang;
|
|
trace_t tr;
|
|
|
|
vec3_t muzzleOffPoint, muzzlePoint, forward, right, up;
|
|
|
|
if ( vehicleLock )
|
|
{
|
|
AngleVectors( pm->ps->viewangles, forward, right, up );
|
|
VectorCopy( pm->ps->origin, muzzlePoint );
|
|
VectorMA( muzzlePoint, lockDist, forward, ang );
|
|
}
|
|
else
|
|
{
|
|
AngleVectors( pm->ps->viewangles, forward, right, up );
|
|
|
|
AngleVectors(pm->ps->viewangles, ang, NULL, NULL);
|
|
|
|
VectorCopy( pm->ps->origin, muzzlePoint );
|
|
VectorCopy(WP_MuzzlePoint[WP_ROCKET_LAUNCHER], muzzleOffPoint);
|
|
|
|
VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
|
|
VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
|
|
muzzlePoint[2] += pm->ps->viewheight + muzzleOffPoint[2];
|
|
ang[0] = muzzlePoint[0] + ang[0]*lockDist;
|
|
ang[1] = muzzlePoint[1] + ang[1]*lockDist;
|
|
ang[2] = muzzlePoint[2] + ang[2]*lockDist;
|
|
}
|
|
|
|
|
|
pm->trace(&tr, muzzlePoint, NULL, NULL, ang, pm->ps->clientNum, MASK_PLAYERSOLID);
|
|
|
|
if ( vehicleLock )
|
|
{//vehicles also do a trace from the camera point if the main one misses
|
|
if ( tr.fraction >= 1.0f )
|
|
{
|
|
trace_t camTrace;
|
|
vec3_t newEnd, shotDir;
|
|
if ( BG_VehTraceFromCamPos( &camTrace, PM_BGEntForNum(pm->ps->clientNum), pm->ps->origin, muzzlePoint, tr.endpos, newEnd, shotDir, (tr.fraction*lockDist) ) )
|
|
{
|
|
memcpy( &tr, &camTrace, sizeof(tr) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tr.fraction != 1 && tr.entityNum < ENTITYNUM_NONE && tr.entityNum != pm->ps->clientNum)
|
|
{
|
|
bgEntity_t *bgEnt = PM_BGEntForNum(tr.entityNum);
|
|
if ( bgEnt && (bgEnt->s.powerups&PW_CLOAKED) )
|
|
{
|
|
pm->ps->rocketLockIndex = ENTITYNUM_NONE;
|
|
pm->ps->rocketLockTime = 0;
|
|
}
|
|
else if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC))
|
|
{
|
|
if (pm->ps->rocketLockIndex == ENTITYNUM_NONE)
|
|
{
|
|
pm->ps->rocketLockIndex = tr.entityNum;
|
|
pm->ps->rocketLockTime = pm->cmd.serverTime;
|
|
}
|
|
else if (pm->ps->rocketLockIndex != tr.entityNum && pm->ps->rocketTargetTime < pm->cmd.serverTime)
|
|
{
|
|
pm->ps->rocketLockIndex = tr.entityNum;
|
|
pm->ps->rocketLockTime = pm->cmd.serverTime;
|
|
}
|
|
else if (pm->ps->rocketLockIndex == tr.entityNum)
|
|
{
|
|
if (pm->ps->rocketLockTime == -1)
|
|
{
|
|
pm->ps->rocketLockTime = pm->ps->rocketLastValidTime;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->rocketLockIndex == tr.entityNum)
|
|
{
|
|
pm->ps->rocketTargetTime = pm->cmd.serverTime + 500;
|
|
}
|
|
}
|
|
else if (!vehicleLock)
|
|
{
|
|
if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
|
|
{
|
|
pm->ps->rocketLockIndex = ENTITYNUM_NONE;
|
|
pm->ps->rocketLockTime = 0;
|
|
}
|
|
}
|
|
}
|
|
else if (pm->ps->rocketTargetTime < pm->cmd.serverTime)
|
|
{
|
|
pm->ps->rocketLockIndex = ENTITYNUM_NONE;
|
|
pm->ps->rocketLockTime = 0;
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->rocketLockTime != -1)
|
|
{
|
|
pm->ps->rocketLastValidTime = pm->ps->rocketLockTime;
|
|
}
|
|
pm->ps->rocketLockTime = -1;
|
|
}
|
|
}
|
|
|
|
//---------------------------------------
|
|
static qboolean PM_DoChargedWeapons( qboolean vehicleRocketLock, bgEntity_t *veh )
|
|
//---------------------------------------
|
|
{
|
|
qboolean charging = qfalse,
|
|
altFire = qfalse;
|
|
|
|
if ( vehicleRocketLock )
|
|
{
|
|
if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )
|
|
{//actually charging
|
|
if ( veh
|
|
&& veh->m_pVehicle )
|
|
{//just make sure we have this veh info
|
|
if ( ( (pm->cmd.buttons&BUTTON_ATTACK)
|
|
&&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
|
|
&&pm->ps->ammo[0]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
|
|
||
|
|
( (pm->cmd.buttons&BUTTON_ALT_ATTACK)
|
|
&&g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming
|
|
&&pm->ps->ammo[1]>=g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot ) )
|
|
{//pressing the appropriate fire button for the lock-on/charging weapon
|
|
PM_RocketLock(16384, qtrue);
|
|
charging = qtrue;
|
|
}
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
altFire = qtrue;
|
|
}
|
|
}
|
|
}
|
|
//else, let go and should fire now
|
|
}
|
|
else
|
|
{
|
|
// If you want your weapon to be a charging weapon, just set this bit up
|
|
switch( pm->ps->weapon )
|
|
{
|
|
//------------------
|
|
case WP_BRYAR_PISTOL:
|
|
|
|
// alt-fire charges the weapon
|
|
//if ( pm->gametype == GT_SIEGE )
|
|
if (1)
|
|
{
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
charging = qtrue;
|
|
altFire = qtrue;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WP_CONCUSSION:
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
altFire = qtrue;
|
|
}
|
|
break;
|
|
|
|
case WP_BRYAR_OLD:
|
|
|
|
// alt-fire charges the weapon
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
charging = qtrue;
|
|
altFire = qtrue;
|
|
}
|
|
break;
|
|
|
|
//------------------
|
|
case WP_BOWCASTER:
|
|
|
|
// primary fire charges the weapon
|
|
if ( pm->cmd.buttons & BUTTON_ATTACK )
|
|
{
|
|
charging = qtrue;
|
|
}
|
|
break;
|
|
|
|
//------------------
|
|
case WP_ROCKET_LAUNCHER:
|
|
if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK)
|
|
&& pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] >= weaponData[pm->ps->weapon].altEnergyPerShot )
|
|
{
|
|
PM_RocketLock(2048,qfalse);
|
|
charging = qtrue;
|
|
altFire = qtrue;
|
|
}
|
|
break;
|
|
|
|
//------------------
|
|
case WP_THERMAL:
|
|
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
altFire = qtrue; // override default of not being an alt-fire
|
|
charging = qtrue;
|
|
}
|
|
else if ( pm->cmd.buttons & BUTTON_ATTACK )
|
|
{
|
|
charging = qtrue;
|
|
}
|
|
break;
|
|
|
|
case WP_DEMP2:
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
altFire = qtrue; // override default of not being an alt-fire
|
|
charging = qtrue;
|
|
}
|
|
break;
|
|
|
|
case WP_DISRUPTOR:
|
|
if ((pm->cmd.buttons & BUTTON_ATTACK) &&
|
|
pm->ps->zoomMode == 1 &&
|
|
pm->ps->zoomLocked)
|
|
{
|
|
if (!pm->cmd.forwardmove &&
|
|
!pm->cmd.rightmove &&
|
|
pm->cmd.upmove <= 0)
|
|
{
|
|
charging = qtrue;
|
|
altFire = qtrue;
|
|
}
|
|
else
|
|
{
|
|
charging = qfalse;
|
|
altFire = qfalse;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->zoomMode != 1 &&
|
|
pm->ps->weaponstate == WEAPON_CHARGING_ALT)
|
|
{
|
|
pm->ps->weaponstate = WEAPON_READY;
|
|
charging = qfalse;
|
|
altFire = qfalse;
|
|
}
|
|
|
|
} // end switch
|
|
}
|
|
|
|
// set up the appropriate weapon state based on the button that's down.
|
|
// Note that we ALWAYS return if charging is set ( meaning the buttons are still down )
|
|
if ( charging )
|
|
{
|
|
if ( altFire )
|
|
{
|
|
if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT )
|
|
{
|
|
// charge isn't started, so do it now
|
|
pm->ps->weaponstate = WEAPON_CHARGING_ALT;
|
|
pm->ps->weaponChargeTime = pm->cmd.serverTime;
|
|
pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
|
|
|
|
#ifdef _DEBUG
|
|
Com_Printf("Starting charge\n");
|
|
#endif
|
|
assert(pm->ps->weapon > WP_NONE);
|
|
BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE_ALT, pm->ps->weapon, pm->ps);
|
|
}
|
|
|
|
if ( vehicleRocketLock )
|
|
{//check vehicle ammo
|
|
if ( veh && pm->ps->ammo[1] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].iAmmoPerShot )
|
|
{
|
|
pm->ps->weaponstate = WEAPON_CHARGING_ALT;
|
|
goto rest;
|
|
}
|
|
}
|
|
else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].altChargeSub+weaponData[pm->ps->weapon].altEnergyPerShot))
|
|
{
|
|
pm->ps->weaponstate = WEAPON_CHARGING_ALT;
|
|
|
|
goto rest;
|
|
}
|
|
else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].altMaxCharge)
|
|
{
|
|
if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
|
|
{
|
|
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].altChargeSub;
|
|
pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].altChargeSubTime;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pm->ps->weaponstate != WEAPON_CHARGING )
|
|
{
|
|
// charge isn't started, so do it now
|
|
pm->ps->weaponstate = WEAPON_CHARGING;
|
|
pm->ps->weaponChargeTime = pm->cmd.serverTime;
|
|
pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
|
|
|
|
#ifdef _DEBUG
|
|
Com_Printf("Starting charge\n");
|
|
#endif
|
|
BG_AddPredictableEventToPlayerstate(EV_WEAPON_CHARGE, pm->ps->weapon, pm->ps);
|
|
}
|
|
|
|
if ( vehicleRocketLock )
|
|
{
|
|
if ( veh && pm->ps->ammo[0] < g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].iAmmoPerShot )
|
|
{//check vehicle ammo
|
|
pm->ps->weaponstate = WEAPON_CHARGING;
|
|
goto rest;
|
|
}
|
|
}
|
|
else if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < (weaponData[pm->ps->weapon].chargeSub+weaponData[pm->ps->weapon].energyPerShot))
|
|
{
|
|
pm->ps->weaponstate = WEAPON_CHARGING;
|
|
|
|
goto rest;
|
|
}
|
|
else if ((pm->cmd.serverTime - pm->ps->weaponChargeTime) < weaponData[pm->ps->weapon].maxCharge)
|
|
{
|
|
if (pm->ps->weaponChargeSubtractTime < pm->cmd.serverTime)
|
|
{
|
|
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= weaponData[pm->ps->weapon].chargeSub;
|
|
pm->ps->weaponChargeSubtractTime = pm->cmd.serverTime + weaponData[pm->ps->weapon].chargeSubTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qtrue; // short-circuit rest of weapon code
|
|
}
|
|
rest:
|
|
// Only charging weapons should be able to set these states...so....
|
|
// let's see which fire mode we need to set up now that the buttons are up
|
|
if ( pm->ps->weaponstate == WEAPON_CHARGING )
|
|
{
|
|
// weapon has a charge, so let us do an attack
|
|
#ifdef _DEBUG
|
|
Com_Printf("Firing. Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
|
|
#endif
|
|
|
|
// dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
|
|
pm->cmd.buttons |= BUTTON_ATTACK;
|
|
pm->ps->eFlags |= EF_FIRING;
|
|
}
|
|
else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
|
|
{
|
|
// weapon has a charge, so let us do an alt-attack
|
|
#ifdef _DEBUG
|
|
Com_Printf("Firing. Charge time=%d\n", pm->cmd.serverTime - pm->ps->weaponChargeTime);
|
|
#endif
|
|
|
|
// dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now
|
|
pm->cmd.buttons |= BUTTON_ALT_ATTACK;
|
|
pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING);
|
|
}
|
|
|
|
return qfalse; // continue with the rest of the weapon code
|
|
}
|
|
|
|
|
|
#define BOWCASTER_CHARGE_UNIT 200.0f // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
|
|
#define BRYAR_CHARGE_UNIT 200.0f // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in g_weapon
|
|
|
|
int PM_ItemUsable(playerState_t *ps, int forcedUse)
|
|
{
|
|
vec3_t fwd, fwdorg, dest, pos;
|
|
vec3_t yawonly;
|
|
vec3_t mins, maxs;
|
|
vec3_t trtest;
|
|
trace_t tr;
|
|
|
|
if (ps->m_iVehicleNum)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (ps->pm_flags & PMF_USE_ITEM_HELD)
|
|
{ //force to let go first
|
|
return 0;
|
|
}
|
|
|
|
if (ps->duelInProgress)
|
|
{ //not allowed to use holdables while in a private duel.
|
|
return 0;
|
|
}
|
|
|
|
if (!forcedUse)
|
|
{
|
|
forcedUse = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
|
|
}
|
|
|
|
if (!BG_IsItemSelectable(ps, forcedUse))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
switch (forcedUse)
|
|
{
|
|
case HI_MEDPAC:
|
|
case HI_MEDPAC_BIG:
|
|
if (ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH])
|
|
{
|
|
return 0;
|
|
}
|
|
if (ps->stats[STAT_HEALTH] <= 0 ||
|
|
(ps->eFlags & EF_DEAD))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
case HI_SEEKER:
|
|
if (ps->eFlags & EF_SEEKERDRONE)
|
|
{
|
|
PM_AddEventWithParm(EV_ITEMUSEFAIL, SEEKER_ALREADYDEPLOYED);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
case HI_SENTRY_GUN:
|
|
if (ps->fd.sentryDeployed)
|
|
{
|
|
PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_ALREADYPLACED);
|
|
return 0;
|
|
}
|
|
|
|
yawonly[ROLL] = 0;
|
|
yawonly[PITCH] = 0;
|
|
yawonly[YAW] = ps->viewangles[YAW];
|
|
|
|
VectorSet( mins, -8, -8, 0 );
|
|
VectorSet( maxs, 8, 8, 24 );
|
|
|
|
AngleVectors(yawonly, fwd, NULL, NULL);
|
|
|
|
fwdorg[0] = ps->origin[0] + fwd[0]*64;
|
|
fwdorg[1] = ps->origin[1] + fwd[1]*64;
|
|
fwdorg[2] = ps->origin[2] + fwd[2]*64;
|
|
|
|
trtest[0] = fwdorg[0] + fwd[0]*16;
|
|
trtest[1] = fwdorg[1] + fwd[1]*16;
|
|
trtest[2] = fwdorg[2] + fwd[2]*16;
|
|
|
|
pm->trace(&tr, ps->origin, mins, maxs, trtest, ps->clientNum, MASK_PLAYERSOLID);
|
|
|
|
if ((tr.fraction != 1 && tr.entityNum != ps->clientNum) || tr.startsolid || tr.allsolid)
|
|
{
|
|
PM_AddEventWithParm(EV_ITEMUSEFAIL, SENTRY_NOROOM);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
case HI_SHIELD:
|
|
mins[0] = -8;
|
|
mins[1] = -8;
|
|
mins[2] = 0;
|
|
|
|
maxs[0] = 8;
|
|
maxs[1] = 8;
|
|
maxs[2] = 8;
|
|
|
|
AngleVectors (ps->viewangles, fwd, NULL, NULL);
|
|
fwd[2] = 0;
|
|
VectorMA(ps->origin, 64, fwd, dest);
|
|
pm->trace(&tr, ps->origin, mins, maxs, dest, ps->clientNum, MASK_SHOT );
|
|
if (tr.fraction > 0.9 && !tr.startsolid && !tr.allsolid)
|
|
{
|
|
VectorCopy(tr.endpos, pos);
|
|
VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
|
|
pm->trace( &tr, pos, mins, maxs, dest, ps->clientNum, MASK_SOLID );
|
|
if ( !tr.startsolid && !tr.allsolid )
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
PM_AddEventWithParm(EV_ITEMUSEFAIL, SHIELD_NOROOM);
|
|
return 0;
|
|
case HI_JETPACK: //check for stuff here?
|
|
return 1;
|
|
case HI_HEALTHDISP:
|
|
return 1;
|
|
case HI_AMMODISP:
|
|
return 1;
|
|
case HI_EWEB:
|
|
return 1;
|
|
case HI_CLOAK: //check for stuff here?
|
|
return 1;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
//cheesy vehicle weapon hackery
|
|
qboolean PM_CanSetWeaponAnims(void)
|
|
{
|
|
if (pm->ps->m_iVehicleNum)
|
|
{
|
|
return qfalse;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
//perform player anim overrides while on vehicle.
|
|
extern int PM_irand_timesync(int val1, int val2);
|
|
void PM_VehicleWeaponAnimate(void)
|
|
{
|
|
bgEntity_t *veh = pm_entVeh;
|
|
Vehicle_t *pVeh;
|
|
int iFlags = 0, iBlend = 0, Anim = -1;
|
|
|
|
if (!veh ||
|
|
!veh->m_pVehicle ||
|
|
!veh->m_pVehicle->m_pPilot ||
|
|
!veh->m_pVehicle->m_pPilot->playerState ||
|
|
pm->ps->clientNum != veh->m_pVehicle->m_pPilot->playerState->clientNum)
|
|
{ //make sure the vehicle exists, and its pilot is this player
|
|
return;
|
|
}
|
|
|
|
pVeh = veh->m_pVehicle;
|
|
|
|
if (pVeh->m_pVehicleInfo->type == VH_WALKER ||
|
|
pVeh->m_pVehicleInfo->type == VH_FIGHTER)
|
|
{ //slightly hacky I guess, but whatever.
|
|
return;
|
|
}
|
|
backAgain:
|
|
// If they're firing, play the right fire animation.
|
|
if ( pm->cmd.buttons & ( BUTTON_ATTACK | BUTTON_ALT_ATTACK ) )
|
|
{
|
|
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
|
iBlend = 200;
|
|
|
|
switch ( pm->ps->weapon )
|
|
{
|
|
case WP_SABER:
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{ //don't do anything.. I guess.
|
|
pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
|
|
goto backAgain;
|
|
}
|
|
// If we're already in an attack animation, leave (let it continue).
|
|
if (pm->ps->torsoTimer <= 0)
|
|
{ //we'll be starting a new attack
|
|
PM_AddEvent(EV_SABER_ATTACK);
|
|
}
|
|
|
|
//just set it to something so we have a proper trail. This is a stupid
|
|
//hack (much like the rest of this function)
|
|
pm->ps->saberMove = LS_R_TL2BR;
|
|
|
|
if ( pm->ps->torsoTimer > 0 && (pm->ps->torsoAnim == BOTH_VS_ATR_S ||
|
|
pm->ps->torsoAnim == BOTH_VS_ATL_S) )
|
|
{
|
|
/*
|
|
//FIXME: no need to even call the PM_SetAnim at all in this case
|
|
Anim = (animNumber_t)pm->ps->torsoAnim;
|
|
iFlags = SETANIM_FLAG_NORMAL;
|
|
break;
|
|
*/
|
|
return;
|
|
}
|
|
|
|
// Start the attack.
|
|
if ( pm->cmd.rightmove > 0 ) //right side attack
|
|
{
|
|
Anim = BOTH_VS_ATR_S;
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 ) //left-side attack
|
|
{
|
|
Anim = BOTH_VS_ATL_S;
|
|
}
|
|
else //random
|
|
{
|
|
//FIXME: alternate back and forth or auto-aim?
|
|
//if ( !Q_irand( 0, 1 ) )
|
|
if (!PM_irand_timesync(0, 1))
|
|
{
|
|
Anim = BOTH_VS_ATR_S;
|
|
}
|
|
else
|
|
{
|
|
Anim = BOTH_VS_ATL_S;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->torsoTimer <= 0)
|
|
{ //restart the anim if we are already in it (and finished)
|
|
iFlags |= SETANIM_FLAG_RESTART;
|
|
}
|
|
break;
|
|
|
|
case WP_BLASTER:
|
|
// Override the shoot anim.
|
|
if ( pm->ps->torsoAnim == BOTH_ATTACK3 )
|
|
{
|
|
if ( pm->cmd.rightmove > 0 ) //right side attack
|
|
{
|
|
Anim = BOTH_VS_ATR_G;
|
|
}
|
|
else if ( pm->cmd.rightmove < 0 ) //left side
|
|
{
|
|
Anim = BOTH_VS_ATL_G;
|
|
}
|
|
else //frontal
|
|
{
|
|
Anim = BOTH_VS_ATF_G;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Anim = BOTH_VS_IDLE;
|
|
break;
|
|
}
|
|
}
|
|
else if (veh->playerState && veh->playerState->speed < 0 &&
|
|
pVeh->m_pVehicleInfo->type == VH_ANIMAL)
|
|
{ //tauntaun is going backwards
|
|
Anim = BOTH_VT_WALK_REV;
|
|
iBlend = 600;
|
|
}
|
|
else if (veh->playerState && veh->playerState->speed < 0 &&
|
|
pVeh->m_pVehicleInfo->type == VH_SPEEDER)
|
|
{ //speeder is going backwards
|
|
Anim = BOTH_VS_REV;
|
|
iBlend = 600;
|
|
}
|
|
// They're not firing so play the Idle for the weapon.
|
|
else
|
|
{
|
|
iFlags = SETANIM_FLAG_NORMAL;
|
|
|
|
switch ( pm->ps->weapon )
|
|
{
|
|
case WP_SABER:
|
|
if ( BG_SabersOff( pm->ps ) )
|
|
{ //saber holstered, normal idle
|
|
Anim = BOTH_VS_IDLE;
|
|
}
|
|
// In the Air.
|
|
//else if ( pVeh->m_ulFlags & VEH_FLYING )
|
|
else if (0)
|
|
{
|
|
iBlend = 800;
|
|
Anim = BOTH_VS_AIR_G;
|
|
iFlags = SETANIM_FLAG_OVERRIDE;
|
|
}
|
|
// Crashing.
|
|
//else if ( pVeh->m_ulFlags & VEH_CRASHING )
|
|
else if (0)
|
|
{
|
|
pVeh->m_ulFlags &= ~VEH_CRASHING; // Remove the flag, we are doing the animation.
|
|
iBlend = 800;
|
|
Anim = BOTH_VS_LAND_SR;
|
|
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
|
}
|
|
else
|
|
{
|
|
Anim = BOTH_VS_IDLE_SR;
|
|
}
|
|
break;
|
|
|
|
case WP_BLASTER:
|
|
// In the Air.
|
|
//if ( pVeh->m_ulFlags & VEH_FLYING )
|
|
if (0)
|
|
{
|
|
iBlend = 800;
|
|
Anim = BOTH_VS_AIR_G;
|
|
iFlags = SETANIM_FLAG_OVERRIDE;
|
|
}
|
|
// Crashing.
|
|
//else if ( pVeh->m_ulFlags & VEH_CRASHING )
|
|
else if (0)
|
|
{
|
|
pVeh->m_ulFlags &= ~VEH_CRASHING; // Remove the flag, we are doing the animation.
|
|
iBlend = 800;
|
|
Anim = BOTH_VS_LAND_G;
|
|
iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
|
|
}
|
|
else
|
|
{
|
|
Anim = BOTH_VS_IDLE_G;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Anim = BOTH_VS_IDLE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Anim != -1)
|
|
{ //override it
|
|
if (pVeh->m_pVehicleInfo->type == VH_ANIMAL)
|
|
{ //agh.. remap anims for the tauntaun
|
|
switch (Anim)
|
|
{
|
|
case BOTH_VS_IDLE:
|
|
if (veh->playerState && veh->playerState->speed > 0)
|
|
{
|
|
if (veh->playerState->speed > pVeh->m_pVehicleInfo->speedMax)
|
|
{ //turbo
|
|
Anim = BOTH_VT_TURBO;
|
|
}
|
|
else
|
|
{
|
|
Anim = BOTH_VT_RUN_FWD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Anim = BOTH_VT_IDLE;
|
|
}
|
|
break;
|
|
case BOTH_VS_ATR_S:
|
|
Anim = BOTH_VT_ATR_S;
|
|
break;
|
|
case BOTH_VS_ATL_S:
|
|
Anim = BOTH_VT_ATL_S;
|
|
break;
|
|
case BOTH_VS_ATR_G:
|
|
Anim = BOTH_VT_ATR_G;
|
|
break;
|
|
case BOTH_VS_ATL_G:
|
|
Anim = BOTH_VT_ATL_G;
|
|
break;
|
|
case BOTH_VS_ATF_G:
|
|
Anim = BOTH_VT_ATF_G;
|
|
break;
|
|
case BOTH_VS_IDLE_SL:
|
|
Anim = BOTH_VT_IDLE_S;
|
|
break;
|
|
case BOTH_VS_IDLE_SR:
|
|
Anim = BOTH_VT_IDLE_S;
|
|
break;
|
|
case BOTH_VS_IDLE_G:
|
|
Anim = BOTH_VT_IDLE_G;
|
|
break;
|
|
|
|
//should not happen for tauntaun:
|
|
case BOTH_VS_AIR_G:
|
|
case BOTH_VS_LAND_SL:
|
|
case BOTH_VS_LAND_SR:
|
|
case BOTH_VS_LAND_G:
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
PM_SetAnim(SETANIM_BOTH, Anim, iFlags, iBlend);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PM_Weapon
|
|
|
|
Generates weapon events and modifes the weapon counter
|
|
==============
|
|
*/
|
|
extern int PM_KickMoveForConditions(void);
|
|
static void PM_Weapon( void )
|
|
{
|
|
int addTime;
|
|
int amount;
|
|
int killAfterItem = 0;
|
|
bgEntity_t *veh = NULL;
|
|
qboolean vehicleRocketLock = qfalse;
|
|
|
|
#ifdef QAGAME
|
|
if (pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm->ps->weapon == WP_NONE &&
|
|
pm->cmd.weapon == WP_NONE &&
|
|
pm_entSelf)
|
|
{ //npc with no weapon
|
|
gentity_t *gent = (gentity_t *)pm_entSelf;
|
|
if (gent->inuse && gent->client &&
|
|
!gent->localAnimIndex)
|
|
{ //humanoid
|
|
pm->ps->torsoAnim = pm->ps->legsAnim;
|
|
pm->ps->torsoTimer = pm->ps->legsTimer;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!pm->ps->emplacedIndex &&
|
|
pm->ps->weapon == WP_EMPLACED_GUN)
|
|
{ //oh no!
|
|
int i = 0;
|
|
int weap = -1;
|
|
|
|
while (i < WP_NUM_WEAPONS)
|
|
{
|
|
if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) && i != WP_NONE)
|
|
{ //this one's good
|
|
weap = i;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (weap != -1)
|
|
{
|
|
pm->cmd.weapon = weap;
|
|
pm->ps->weapon = weap;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&&pm->ps->m_iVehicleNum)
|
|
{ //riding a vehicle
|
|
veh = pm_entVeh;
|
|
if ( veh &&
|
|
(veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) )
|
|
{//riding a walker/fighter
|
|
//keep saber off, do no weapon stuff at all!
|
|
pm->ps->saberHolstered = 2;
|
|
#ifdef QAGAME
|
|
pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
|
|
#else
|
|
if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
|
|
|| g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
|
|
{//our vehicle uses a rocket launcher, so do the normal checks
|
|
vehicleRocketLock = qtrue;
|
|
pm->cmd.buttons &= ~BUTTON_ATTACK;
|
|
}
|
|
else
|
|
{
|
|
pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (pm->ps->weapon != WP_DISRUPTOR //not using disruptor
|
|
&& pm->ps->weapon != WP_ROCKET_LAUNCHER//not using rocket launcher
|
|
&& pm->ps->weapon != WP_THERMAL//not using thermals
|
|
&& !pm->ps->m_iVehicleNum )//not a vehicle or in a vehicle
|
|
{ //check for exceeding max charge time if not using disruptor or rocket launcher or thermals
|
|
if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT )
|
|
{
|
|
int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
|
|
|
|
if (timeDif > MAX_WEAPON_CHARGE_TIME)
|
|
{
|
|
pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->weaponstate == WEAPON_CHARGING )
|
|
{
|
|
int timeDif = (pm->cmd.serverTime - pm->ps->weaponChargeTime);
|
|
|
|
if (timeDif > MAX_WEAPON_CHARGE_TIME)
|
|
{
|
|
pm->cmd.buttons &= ~BUTTON_ATTACK;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pm->ps->forceHandExtend == HANDEXTEND_WEAPONREADY &&
|
|
PM_CanSetWeaponAnims())
|
|
{ //reset into weapon stance
|
|
if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE && !PM_IsRocketTrooper())
|
|
{ //saber handles its own anims
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
|
|
{
|
|
//PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
|
|
PM_StartTorsoAnim( TORSO_RAISEWEAP1);
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon == WP_EMPLACED_GUN)
|
|
{
|
|
PM_StartTorsoAnim( BOTH_GUNSIT1 );
|
|
}
|
|
else
|
|
{
|
|
//PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
PM_StartTorsoAnim( TORSO_RAISEWEAP1);
|
|
}
|
|
}
|
|
}
|
|
|
|
//we now go into a weapon raise anim after every force hand extend.
|
|
//this is so that my holster-view-weapon-when-hand-extend stuff works.
|
|
pm->ps->weaponstate = WEAPON_RAISING;
|
|
pm->ps->weaponTime += 250;
|
|
|
|
pm->ps->forceHandExtend = HANDEXTEND_NONE;
|
|
}
|
|
else if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
|
|
{ //nothing else should be allowed to happen during this time, including weapon fire
|
|
int desiredAnim = 0;
|
|
qboolean seperateOnTorso = qfalse;
|
|
qboolean playFullBody = qfalse;
|
|
int desiredOnTorso = 0;
|
|
|
|
switch(pm->ps->forceHandExtend)
|
|
{
|
|
case HANDEXTEND_FORCEPUSH:
|
|
desiredAnim = BOTH_FORCEPUSH;
|
|
break;
|
|
case HANDEXTEND_FORCEPULL:
|
|
desiredAnim = BOTH_FORCEPULL;
|
|
break;
|
|
case HANDEXTEND_FORCE_HOLD:
|
|
if ( (pm->ps->fd.forcePowersActive&(1<<FP_GRIP)) )
|
|
{//gripping
|
|
desiredAnim = BOTH_FORCEGRIP_HOLD;
|
|
}
|
|
else if ( (pm->ps->fd.forcePowersActive&(1<<FP_LIGHTNING)) )
|
|
{//lightning
|
|
if ( pm->ps->weapon == WP_MELEE
|
|
&& pm->ps->activeForcePass > FORCE_LEVEL_2 )
|
|
{//2-handed lightning
|
|
desiredAnim = BOTH_FORCE_2HANDEDLIGHTNING_HOLD;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_FORCELIGHTNING_HOLD;
|
|
}
|
|
}
|
|
else if ( (pm->ps->fd.forcePowersActive&(1<<FP_DRAIN)) )
|
|
{//draining
|
|
desiredAnim = BOTH_FORCEGRIP_HOLD;
|
|
}
|
|
else
|
|
{//???
|
|
desiredAnim = BOTH_FORCEGRIP_HOLD;
|
|
}
|
|
break;
|
|
case HANDEXTEND_SABERPULL:
|
|
desiredAnim = BOTH_SABERPULL;
|
|
break;
|
|
case HANDEXTEND_CHOKE:
|
|
desiredAnim = BOTH_CHOKE3; //left-handed choke
|
|
break;
|
|
case HANDEXTEND_DODGE:
|
|
desiredAnim = pm->ps->forceDodgeAnim;
|
|
break;
|
|
case HANDEXTEND_KNOCKDOWN:
|
|
if (pm->ps->forceDodgeAnim)
|
|
{
|
|
if (pm->ps->forceDodgeAnim > 4)
|
|
{ //this means that we want to play a sepereate anim on the torso
|
|
int originalDAnim = pm->ps->forceDodgeAnim-8; //-8 is the original legs anim
|
|
if (originalDAnim == 2)
|
|
{
|
|
desiredAnim = BOTH_FORCE_GETUP_B1;
|
|
}
|
|
else if (originalDAnim == 3)
|
|
{
|
|
desiredAnim = BOTH_FORCE_GETUP_B3;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_GETUP1;
|
|
}
|
|
|
|
//now specify the torso anim
|
|
seperateOnTorso = qtrue;
|
|
desiredOnTorso = BOTH_FORCEPUSH;
|
|
}
|
|
else if (pm->ps->forceDodgeAnim == 2)
|
|
{
|
|
desiredAnim = BOTH_FORCE_GETUP_B1;
|
|
}
|
|
else if (pm->ps->forceDodgeAnim == 3)
|
|
{
|
|
desiredAnim = BOTH_FORCE_GETUP_B3;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_GETUP1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_KNOCKDOWN1;
|
|
}
|
|
break;
|
|
case HANDEXTEND_DUELCHALLENGE:
|
|
desiredAnim = BOTH_ENGAGETAUNT;
|
|
break;
|
|
case HANDEXTEND_TAUNT:
|
|
desiredAnim = pm->ps->forceDodgeAnim;
|
|
if ( desiredAnim != BOTH_ENGAGETAUNT
|
|
&& VectorCompare( pm->ps->velocity, vec3_origin )
|
|
&& pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{
|
|
playFullBody = qtrue;
|
|
}
|
|
break;
|
|
case HANDEXTEND_PRETHROW:
|
|
desiredAnim = BOTH_A3_TL_BR;
|
|
playFullBody = qtrue;
|
|
break;
|
|
case HANDEXTEND_POSTTHROW:
|
|
desiredAnim = BOTH_D3_TL___;
|
|
playFullBody = qtrue;
|
|
break;
|
|
case HANDEXTEND_PRETHROWN:
|
|
desiredAnim = BOTH_KNEES1;
|
|
playFullBody = qtrue;
|
|
break;
|
|
case HANDEXTEND_POSTTHROWN:
|
|
if (pm->ps->forceDodgeAnim)
|
|
{
|
|
desiredAnim = BOTH_FORCE_GETUP_F2;
|
|
}
|
|
else
|
|
{
|
|
desiredAnim = BOTH_KNOCKDOWN5;
|
|
}
|
|
playFullBody = qtrue;
|
|
break;
|
|
case HANDEXTEND_DRAGGING:
|
|
desiredAnim = BOTH_B1_BL___;
|
|
break;
|
|
case HANDEXTEND_JEDITAUNT:
|
|
desiredAnim = BOTH_GESTURE1;
|
|
//playFullBody = qtrue;
|
|
break;
|
|
//Hmm... maybe use these, too?
|
|
//BOTH_FORCEHEAL_QUICK //quick heal (SP level 2 & 3)
|
|
//BOTH_MINDTRICK1 // wave (maybe for mind trick 2 & 3 - whole area, and for force seeing)
|
|
//BOTH_MINDTRICK2 // tap (maybe for mind trick 1 - one person)
|
|
//BOTH_FORCEGRIP_START //start grip
|
|
//BOTH_FORCEGRIP_HOLD //hold grip
|
|
//BOTH_FORCEGRIP_RELEASE //release grip
|
|
//BOTH_FORCELIGHTNING //quick lightning burst (level 1)
|
|
//BOTH_FORCELIGHTNING_START //start lightning
|
|
//BOTH_FORCELIGHTNING_HOLD //hold lightning
|
|
//BOTH_FORCELIGHTNING_RELEASE //release lightning
|
|
default:
|
|
desiredAnim = BOTH_FORCEPUSH;
|
|
break;
|
|
}
|
|
|
|
if (!seperateOnTorso)
|
|
{ //of seperateOnTorso, handle it after setting the legs
|
|
PM_SetAnim(SETANIM_TORSO, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
|
|
pm->ps->torsoTimer = 1;
|
|
}
|
|
|
|
if (playFullBody)
|
|
{ //sorry if all these exceptions are getting confusing. This one just means play on both legs and torso.
|
|
PM_SetAnim(SETANIM_BOTH, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
|
|
pm->ps->legsTimer = pm->ps->torsoTimer = 1;
|
|
}
|
|
else if (pm->ps->forceHandExtend == HANDEXTEND_DODGE || pm->ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
|
|
(pm->ps->forceHandExtend == HANDEXTEND_CHOKE && pm->ps->groundEntityNum == ENTITYNUM_NONE) )
|
|
{ //special case, play dodge anim on whole body, choke anim too if off ground
|
|
if (seperateOnTorso)
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
|
|
pm->ps->legsTimer = 1;
|
|
|
|
PM_SetAnim(SETANIM_TORSO, desiredOnTorso, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
|
|
pm->ps->torsoTimer = 1;
|
|
}
|
|
else
|
|
{
|
|
PM_SetAnim(SETANIM_LEGS, desiredAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
|
|
pm->ps->legsTimer = 1;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (BG_InSpecialJump(pm->ps->legsAnim) ||
|
|
BG_InRoll(pm->ps, pm->ps->legsAnim) ||
|
|
PM_InRollComplete(pm->ps, pm->ps->legsAnim))
|
|
{
|
|
/*
|
|
if (pm->cmd.weapon != WP_MELEE &&
|
|
pm->ps->weapon != WP_MELEE &&
|
|
(pm->ps->stats[STAT_WEAPONS] & (1<<WP_SABER)))
|
|
{ //it's alright also if we are melee
|
|
pm->cmd.weapon = WP_SABER;
|
|
pm->ps->weapon = WP_SABER;
|
|
}
|
|
*/
|
|
if (pm->ps->weaponTime < pm->ps->legsTimer)
|
|
{
|
|
pm->ps->weaponTime = pm->ps->legsTimer;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->duelInProgress)
|
|
{
|
|
pm->cmd.weapon = WP_SABER;
|
|
pm->ps->weapon = WP_SABER;
|
|
|
|
if (pm->ps->duelTime >= pm->cmd.serverTime)
|
|
{
|
|
pm->cmd.upmove = 0;
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.rightmove = 0;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_SABER && pm->ps->saberMove != LS_READY && pm->ps->saberMove != LS_NONE)
|
|
{
|
|
pm->cmd.weapon = WP_SABER; //don't allow switching out mid-attack
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_SABER)
|
|
{
|
|
//rww - we still need the item stuff, so we won't return immediately
|
|
PM_WeaponLightsaber();
|
|
killAfterItem = 1;
|
|
}
|
|
else if (pm->ps->weapon != WP_EMPLACED_GUN)
|
|
{
|
|
pm->ps->saberHolstered = 0;
|
|
}
|
|
|
|
if (PM_CanSetWeaponAnims())
|
|
{
|
|
if (pm->ps->weapon == WP_THERMAL ||
|
|
pm->ps->weapon == WP_TRIP_MINE ||
|
|
pm->ps->weapon == WP_DET_PACK)
|
|
{
|
|
if (pm->ps->weapon == WP_THERMAL)
|
|
{
|
|
if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
|
|
(pm->ps->weaponTime-200) <= 0)
|
|
{
|
|
PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pm->ps->torsoAnim) == WeaponAttackAnim[pm->ps->weapon] &&
|
|
(pm->ps->weaponTime-700) <= 0)
|
|
{
|
|
PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// don't allow attack until all buttons are up
|
|
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
|
|
return;
|
|
}
|
|
|
|
// ignore if spectator
|
|
if ( pm->ps->clientNum < MAX_CLIENTS && 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 (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&& pm->ps->m_iVehicleNum)
|
|
{//riding a vehicle, can't use holdable items, this button operates as the weapon link/unlink toggle
|
|
return;
|
|
}
|
|
|
|
if (!pm->ps->stats[STAT_HOLDABLE_ITEM])
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!PM_ItemUsable(pm->ps, 0))
|
|
{
|
|
pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag))
|
|
{
|
|
if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
|
|
{ //never use up the binoculars or jetpack or dispensers or cloak or ...
|
|
pm->ps->stats[STAT_HOLDABLE_ITEMS] -= (1 << bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return; //this should not happen...
|
|
}
|
|
|
|
pm->ps->pm_flags |= PMF_USE_ITEM_HELD;
|
|
PM_AddEvent( EV_USE_ITEM0 + bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag );
|
|
|
|
if (bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_BINOCULARS &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_JETPACK &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_HEALTHDISP &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_AMMODISP &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_CLOAK &&
|
|
bg_itemlist[pm->ps->stats[STAT_HOLDABLE_ITEM]].giTag != HI_EWEB)
|
|
{
|
|
pm->ps->stats[STAT_HOLDABLE_ITEM] = 0;
|
|
BG_CycleInven(pm->ps, 1);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
pm->ps->pm_flags &= ~PMF_USE_ITEM_HELD;
|
|
}
|
|
|
|
/*
|
|
if (pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_MELEE)
|
|
{ //we can't toggle zoom while using saber (for obvious reasons) so make sure it's always off
|
|
pm->ps->zoomMode = 0;
|
|
pm->ps->zoomFov = 0;
|
|
pm->ps->zoomLocked = qfalse;
|
|
pm->ps->zoomLockTime = 0;
|
|
}
|
|
*/
|
|
|
|
if (killAfterItem)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// make weapon function
|
|
if ( pm->ps->weaponTime > 0 ) {
|
|
pm->ps->weaponTime -= pml.msec;
|
|
}
|
|
|
|
if (pm->ps->isJediMaster && pm->ps->emplacedIndex)
|
|
{
|
|
pm->ps->emplacedIndex = 0;
|
|
pm->ps->saberHolstered = 0;
|
|
}
|
|
|
|
if (pm->ps->duelInProgress && pm->ps->emplacedIndex)
|
|
{
|
|
pm->ps->emplacedIndex = 0;
|
|
pm->ps->saberHolstered = 0;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_EMPLACED_GUN && pm->ps->emplacedIndex)
|
|
{
|
|
pm->cmd.weapon = WP_EMPLACED_GUN; //No switch for you!
|
|
PM_StartTorsoAnim( BOTH_GUNSIT1 );
|
|
}
|
|
|
|
if (pm->ps->isJediMaster || pm->ps->duelInProgress || pm->ps->trueJedi)
|
|
{
|
|
pm->cmd.weapon = WP_SABER;
|
|
pm->ps->weapon = WP_SABER;
|
|
|
|
if (pm->ps->isJediMaster || pm->ps->trueJedi)
|
|
{
|
|
pm->ps->stats[STAT_WEAPONS] = (1 << WP_SABER);
|
|
}
|
|
}
|
|
|
|
amount = weaponData[pm->ps->weapon].energyPerShot;
|
|
|
|
// take an ammo away if not infinite
|
|
if ( pm->ps->weapon != WP_NONE &&
|
|
pm->ps->weapon == pm->cmd.weapon &&
|
|
(pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) )
|
|
{
|
|
if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
|
|
{
|
|
// enough energy to fire this weapon?
|
|
if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].energyPerShot &&
|
|
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < weaponData[pm->ps->weapon].altEnergyPerShot)
|
|
{ //the weapon is out of ammo essentially because it cannot fire primary or secondary, so do the switch
|
|
//regardless of if the player is attacking or not
|
|
PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
|
|
|
|
if (pm->ps->weaponTime < 500)
|
|
{
|
|
pm->ps->weaponTime += 500;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_DET_PACK && !pm->ps->hasDetPackPlanted && pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] < 1)
|
|
{
|
|
PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
|
|
|
|
if (pm->ps->weaponTime < 500)
|
|
{
|
|
pm->ps->weaponTime += 500;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for weapon change
|
|
// can't change if weapon is firing, but can change
|
|
// again if lowering or raising
|
|
if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
|
|
if ( pm->ps->weapon != pm->cmd.weapon ) {
|
|
PM_BeginWeaponChange( pm->cmd.weapon );
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->weaponTime > 0 ) {
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_DISRUPTOR &&
|
|
pm->ps->zoomMode == 1)
|
|
{
|
|
if (pm_cancelOutZoom)
|
|
{
|
|
pm->ps->zoomMode = 0;
|
|
pm->ps->zoomFov = 0;
|
|
pm->ps->zoomLocked = qfalse;
|
|
pm->ps->zoomLockTime = 0;
|
|
PM_AddEvent( EV_DISRUPTOR_ZOOMSOUND );
|
|
return;
|
|
}
|
|
|
|
if (pm->cmd.forwardmove ||
|
|
pm->cmd.rightmove ||
|
|
pm->cmd.upmove > 0)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// change weapon if time
|
|
if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
|
|
PM_FinishWeaponChange();
|
|
return;
|
|
}
|
|
|
|
if ( pm->ps->weaponstate == WEAPON_RAISING ) {
|
|
pm->ps->weaponstate = WEAPON_READY;
|
|
if (PM_CanSetWeaponAnims())
|
|
{
|
|
if ( pm->ps->weapon == WP_SABER )
|
|
{
|
|
PM_StartTorsoAnim( PM_GetSaberStance() );
|
|
}
|
|
else if (pm->ps->weapon == WP_MELEE || PM_IsRocketTrooper())
|
|
{
|
|
PM_StartTorsoAnim( pm->ps->legsAnim );
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
|
|
{
|
|
PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon == WP_EMPLACED_GUN)
|
|
{
|
|
PM_StartTorsoAnim( BOTH_GUNSIT1 );
|
|
}
|
|
else
|
|
{
|
|
PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (PM_CanSetWeaponAnims() &&
|
|
!PM_IsRocketTrooper() &&
|
|
pm->ps->weaponstate == WEAPON_READY && pm->ps->weaponTime <= 0 &&
|
|
(pm->ps->weapon >= WP_BRYAR_PISTOL || pm->ps->weapon == WP_STUN_BATON) &&
|
|
pm->ps->torsoTimer <= 0 &&
|
|
(pm->ps->torsoAnim) != WeaponReadyAnim[pm->ps->weapon] &&
|
|
pm->ps->torsoAnim != TORSO_WEAPONIDLE3 &&
|
|
pm->ps->weapon != WP_EMPLACED_GUN)
|
|
{
|
|
PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
}
|
|
else if (PM_CanSetWeaponAnims() &&
|
|
pm->ps->weapon == WP_MELEE)
|
|
{
|
|
if (pm->ps->weaponTime <= 0 &&
|
|
pm->ps->forceHandExtend == HANDEXTEND_NONE)
|
|
{
|
|
int desTAnim = pm->ps->legsAnim;
|
|
|
|
if (desTAnim == BOTH_STAND1 ||
|
|
desTAnim == BOTH_STAND2)
|
|
{ //remap the standard standing anims for melee stance
|
|
desTAnim = BOTH_STAND6;
|
|
}
|
|
|
|
if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
|
|
{ //don't do this while holding attack
|
|
if (pm->ps->torsoAnim != desTAnim)
|
|
{
|
|
PM_StartTorsoAnim( desTAnim );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (PM_CanSetWeaponAnims() && PM_IsRocketTrooper())
|
|
{
|
|
int desTAnim = pm->ps->legsAnim;
|
|
|
|
if (!(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
|
|
{ //don't do this while holding attack
|
|
if (pm->ps->torsoAnim != desTAnim)
|
|
{
|
|
PM_StartTorsoAnim( desTAnim );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((pm->ps->torsoAnim) == TORSO_WEAPONREADY4 ||
|
|
(pm->ps->torsoAnim) == BOTH_ATTACK4) &&
|
|
(pm->ps->weapon != WP_DISRUPTOR || pm->ps->zoomMode != 1))
|
|
{
|
|
if (pm->ps->weapon == WP_EMPLACED_GUN)
|
|
{
|
|
PM_StartTorsoAnim( BOTH_GUNSIT1 );
|
|
}
|
|
else if (PM_CanSetWeaponAnims())
|
|
{
|
|
PM_StartTorsoAnim( WeaponReadyAnim[pm->ps->weapon] );
|
|
}
|
|
}
|
|
else if (((pm->ps->torsoAnim) != TORSO_WEAPONREADY4 &&
|
|
(pm->ps->torsoAnim) != BOTH_ATTACK4) &&
|
|
PM_CanSetWeaponAnims() &&
|
|
(pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1))
|
|
{
|
|
PM_StartTorsoAnim( TORSO_WEAPONREADY4 );
|
|
}
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_VEHICLE)
|
|
{//we are a vehicle
|
|
veh = pm_entSelf;
|
|
}
|
|
if ( veh
|
|
&& veh->m_pVehicle )
|
|
{
|
|
if ( g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[0].ID].fHoming
|
|
|| g_vehWeaponInfo[veh->m_pVehicle->m_pVehicleInfo->weapon[1].ID].fHoming )
|
|
{//don't clear the rocket locking ever?
|
|
vehicleRocketLock = qtrue;
|
|
}
|
|
}
|
|
|
|
if ( !vehicleRocketLock )
|
|
{
|
|
if (pm->ps->weapon != WP_ROCKET_LAUNCHER)
|
|
{
|
|
if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&&pm->ps->m_iVehicleNum)
|
|
{//riding a vehicle, the vehicle will tell me my rocketlock stuff...
|
|
}
|
|
else
|
|
{
|
|
pm->ps->rocketLockIndex = ENTITYNUM_NONE;
|
|
pm->ps->rocketLockTime = 0;
|
|
pm->ps->rocketTargetTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( PM_DoChargedWeapons(vehicleRocketLock, veh))
|
|
{
|
|
// In some cases the charged weapon code may want us to short circuit the rest of the firing code
|
|
return;
|
|
}
|
|
|
|
// check for fire
|
|
if ( ! (pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)))
|
|
{
|
|
pm->ps->weaponTime = 0;
|
|
pm->ps->weaponstate = WEAPON_READY;
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_EMPLACED_GUN)
|
|
{
|
|
addTime = weaponData[pm->ps->weapon].fireTime;
|
|
pm->ps->weaponTime += addTime;
|
|
if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
|
|
{
|
|
PM_AddEvent( EV_ALT_FIRE );
|
|
}
|
|
else
|
|
{
|
|
PM_AddEvent( EV_FIRE_WEAPON );
|
|
}
|
|
return;
|
|
}
|
|
else if (pm->ps->m_iVehicleNum
|
|
&& pm_entSelf->s.NPC_class==CLASS_VEHICLE)
|
|
{ //a vehicle NPC that has a pilot
|
|
pm->ps->weaponstate = WEAPON_FIRING;
|
|
pm->ps->weaponTime += 100;
|
|
#ifdef QAGAME //hack, only do it game-side. vehicle weapons don't really need predicting I suppose.
|
|
if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
|
|
{
|
|
G_CheapWeaponFire(pm->ps->clientNum, EV_ALT_FIRE);
|
|
}
|
|
else
|
|
{
|
|
G_CheapWeaponFire(pm->ps->clientNum, EV_FIRE_WEAPON);
|
|
}
|
|
#endif
|
|
/*
|
|
addTime = weaponData[WP_EMPLACED_GUN].fireTime;
|
|
pm->ps->weaponTime += addTime;
|
|
if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) )
|
|
{
|
|
PM_AddEvent( EV_ALT_FIRE );
|
|
}
|
|
else
|
|
{
|
|
PM_AddEvent( EV_FIRE_WEAPON );
|
|
}
|
|
*/
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_DISRUPTOR &&
|
|
(pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
|
|
!pm->ps->zoomLocked)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_DISRUPTOR &&
|
|
(pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
|
|
pm->ps->zoomMode == 2)
|
|
{ //can't use disruptor secondary while zoomed binoculars
|
|
return;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
|
|
{
|
|
PM_StartTorsoAnim( BOTH_ATTACK4 );
|
|
}
|
|
else if (pm->ps->weapon == WP_MELEE)
|
|
{ //special anims for standard melee attacks
|
|
//Alternate between punches and use the anim length as weapon time.
|
|
if (!pm->ps->m_iVehicleNum)
|
|
{ //if riding a vehicle don't do this stuff at all
|
|
if (pm->debugMelee &&
|
|
(pm->cmd.buttons & BUTTON_ATTACK) &&
|
|
(pm->cmd.buttons & BUTTON_ALT_ATTACK))
|
|
{ //ok, grapple time
|
|
#if 0 //eh, I want to try turning the saber off, but can't do that reliably for prediction..
|
|
qboolean icandoit = qtrue;
|
|
if (pm->ps->weaponTime > 0)
|
|
{ //weapon busy
|
|
icandoit = qfalse;
|
|
}
|
|
if (pm->ps->forceHandExtend != HANDEXTEND_NONE)
|
|
{ //force power or knockdown or something
|
|
icandoit = qfalse;
|
|
}
|
|
if (pm->ps->weapon != WP_SABER && pm->ps->weapon != WP_MELEE)
|
|
{
|
|
icandoit = qfalse;
|
|
}
|
|
|
|
if (icandoit)
|
|
{
|
|
//G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
PM_SetAnim(SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
if (pm->ps->torsoAnim == BOTH_KYLE_GRAB)
|
|
{ //providing the anim set succeeded..
|
|
pm->ps->torsoTimer += 500; //make the hand stick out a little longer than it normally would
|
|
if (pm->ps->legsAnim == pm->ps->torsoAnim)
|
|
{
|
|
pm->ps->legsTimer = pm->ps->torsoTimer;
|
|
}
|
|
pm->ps->weaponTime = pm->ps->torsoTimer;
|
|
return;
|
|
}
|
|
}
|
|
#else
|
|
#ifdef QAGAME
|
|
if (pm_entSelf)
|
|
{
|
|
if (TryGrapple((gentity_t *)pm_entSelf))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
#else
|
|
return;
|
|
#endif
|
|
#endif
|
|
}
|
|
else if (pm->debugMelee &&
|
|
(pm->cmd.buttons & BUTTON_ALT_ATTACK))
|
|
{ //kicks
|
|
if (!BG_KickingAnim(pm->ps->torsoAnim) &&
|
|
!BG_KickingAnim(pm->ps->legsAnim))
|
|
{
|
|
int kickMove = PM_KickMoveForConditions();
|
|
if (kickMove == LS_HILT_BASH)
|
|
{ //yeah.. no hilt to bash with!
|
|
kickMove = LS_KICK_F;
|
|
}
|
|
|
|
if (kickMove != -1)
|
|
{
|
|
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
|
|
{//if in air, convert kick to an in-air kick
|
|
float gDist = PM_GroundDistance();
|
|
//let's only allow air kicks if a certain distance from the ground
|
|
//it's silly to be able to do them right as you land.
|
|
//also looks wrong to transition from a non-complete flip anim...
|
|
if ((!BG_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsTimer <= 0) &&
|
|
gDist > 64.0f && //strict minimum
|
|
gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well
|
|
)
|
|
{
|
|
switch ( kickMove )
|
|
{
|
|
case LS_KICK_F:
|
|
kickMove = LS_KICK_F_AIR;
|
|
break;
|
|
case LS_KICK_B:
|
|
kickMove = LS_KICK_B_AIR;
|
|
break;
|
|
case LS_KICK_R:
|
|
kickMove = LS_KICK_R_AIR;
|
|
break;
|
|
case LS_KICK_L:
|
|
kickMove = LS_KICK_L_AIR;
|
|
break;
|
|
default: //oh well, can't do any other kick move while in-air
|
|
kickMove = -1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{ //off ground, but too close to ground
|
|
kickMove = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (kickMove != -1)
|
|
{
|
|
int kickAnim = saberMoveData[kickMove].animToUse;
|
|
|
|
if (kickAnim != -1)
|
|
{
|
|
PM_SetAnim(SETANIM_BOTH, kickAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
if (pm->ps->legsAnim == kickAnim)
|
|
{
|
|
pm->ps->weaponTime = pm->ps->legsTimer;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//if got here then no move to do so put torso into leg idle or whatever
|
|
if (pm->ps->torsoAnim != pm->ps->legsAnim)
|
|
{
|
|
PM_SetAnim(SETANIM_BOTH, pm->ps->legsAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
|
|
}
|
|
pm->ps->weaponTime = 0;
|
|
return;
|
|
}
|
|
else
|
|
{ //just punch
|
|
int desTAnim = BOTH_MELEE1;
|
|
if (pm->ps->torsoAnim == BOTH_MELEE1)
|
|
{
|
|
desTAnim = BOTH_MELEE2;
|
|
}
|
|
PM_StartTorsoAnim( desTAnim );
|
|
|
|
if (pm->ps->torsoAnim == desTAnim)
|
|
{
|
|
pm->ps->weaponTime = pm->ps->torsoTimer;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PM_StartTorsoAnim( WeaponAttackAnim[pm->ps->weapon] );
|
|
}
|
|
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
amount = weaponData[pm->ps->weapon].altEnergyPerShot;
|
|
}
|
|
else
|
|
{
|
|
amount = weaponData[pm->ps->weapon].energyPerShot;
|
|
}
|
|
|
|
pm->ps->weaponstate = WEAPON_FIRING;
|
|
|
|
// take an ammo away if not infinite
|
|
if ( pm->ps->clientNum < MAX_CLIENTS && pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 )
|
|
{
|
|
// enough energy to fire this weapon?
|
|
if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0)
|
|
{
|
|
pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] -= amount;
|
|
}
|
|
else // Not enough energy
|
|
{
|
|
// Switch weapons
|
|
if (pm->ps->weapon != WP_DET_PACK || !pm->ps->hasDetPackPlanted)
|
|
{
|
|
PM_AddEventWithParm( EV_NOAMMO, WP_NUM_WEAPONS+pm->ps->weapon );
|
|
if (pm->ps->weaponTime < 500)
|
|
{
|
|
pm->ps->weaponTime += 500;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) {
|
|
//if ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->gametype != GT_SIEGE )
|
|
if (0)
|
|
{ //kind of a hack for now
|
|
PM_AddEvent( EV_FIRE_WEAPON );
|
|
addTime = weaponData[pm->ps->weapon].fireTime;
|
|
}
|
|
else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode != 1)
|
|
{
|
|
PM_AddEvent( EV_FIRE_WEAPON );
|
|
addTime = weaponData[pm->ps->weapon].fireTime;
|
|
}
|
|
else
|
|
{
|
|
if (pm->ps->weapon != WP_MELEE ||
|
|
!pm->ps->m_iVehicleNum)
|
|
{ //do not fire melee events at all when on vehicle
|
|
PM_AddEvent( EV_ALT_FIRE );
|
|
}
|
|
addTime = weaponData[pm->ps->weapon].altFireTime;
|
|
}
|
|
}
|
|
else {
|
|
if (pm->ps->weapon != WP_MELEE ||
|
|
!pm->ps->m_iVehicleNum)
|
|
{ //do not fire melee events at all when on vehicle
|
|
PM_AddEvent( EV_FIRE_WEAPON );
|
|
}
|
|
addTime = weaponData[pm->ps->weapon].fireTime;
|
|
if ( pm->gametype == GT_SIEGE && pm->ps->weapon == WP_DET_PACK )
|
|
{ // were far too spammy before? So says Rick.
|
|
addTime *= 2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if ( pm->ps->powerups[PW_HASTE] ) {
|
|
addTime /= 1.3;
|
|
}
|
|
*/
|
|
|
|
if (pm->ps->fd.forcePowersActive & (1 << FP_RAGE))
|
|
{
|
|
addTime *= 0.75;
|
|
}
|
|
else if (pm->ps->fd.forceRageRecoveryTime > pm->cmd.serverTime)
|
|
{
|
|
addTime *= 1.5;
|
|
}
|
|
|
|
pm->ps->weaponTime += addTime;
|
|
}
|
|
|
|
/*
|
|
================
|
|
PM_Animate
|
|
================
|
|
*/
|
|
|
|
static void PM_Animate( void ) {
|
|
if ( pm->cmd.buttons & BUTTON_GESTURE ) {
|
|
if (pm->ps->m_iVehicleNum)
|
|
{ //eh, fine, clear it
|
|
if (pm->ps->forceHandExtendTime < pm->cmd.serverTime)
|
|
{
|
|
pm->ps->forceHandExtend = HANDEXTEND_NONE;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->torsoTimer < 1 && pm->ps->forceHandExtend == HANDEXTEND_NONE &&
|
|
pm->ps->legsTimer < 1 && pm->ps->weaponTime < 1 && pm->ps->saberLockTime < pm->cmd.serverTime) {
|
|
|
|
pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
|
|
|
|
//FIXME: random taunt anims?
|
|
pm->ps->forceDodgeAnim = BOTH_ENGAGETAUNT;
|
|
|
|
pm->ps->forceHandExtendTime = pm->cmd.serverTime + 1000;
|
|
|
|
//pm->ps->weaponTime = 100;
|
|
|
|
PM_AddEvent( EV_TAUNT );
|
|
}
|
|
#if 0
|
|
// Here's an interesting bit. The bots in TA used buttons to do additional gestures.
|
|
// I ripped them out because I didn't want too many buttons given the fact that I was already adding some for JK2.
|
|
// We can always add some back in if we want though.
|
|
} 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 //
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
PM_DropTimers
|
|
================
|
|
*/
|
|
static void PM_DropTimers( void ) {
|
|
// drop misc timing counter
|
|
if ( pm->ps->pm_time ) {
|
|
if ( pml.msec >= pm->ps->pm_time ) {
|
|
pm->ps->pm_flags &= ~PMF_ALL_TIMES;
|
|
pm->ps->pm_time = 0;
|
|
} else {
|
|
pm->ps->pm_time -= pml.msec;
|
|
}
|
|
}
|
|
|
|
// drop animation counter
|
|
if ( pm->ps->legsTimer > 0 ) {
|
|
pm->ps->legsTimer -= pml.msec;
|
|
if ( pm->ps->legsTimer < 0 ) {
|
|
pm->ps->legsTimer = 0;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->torsoTimer > 0 ) {
|
|
pm->ps->torsoTimer -= pml.msec;
|
|
if ( pm->ps->torsoTimer < 0 ) {
|
|
pm->ps->torsoTimer = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Following function is stateless (at the moment). And hoisting it out
|
|
// of the namespace here is easier than fixing all the places it's used,
|
|
// which includes files that are also compiled in SP. We do need to make
|
|
// sure we only get one copy in the linker, though.
|
|
|
|
#include "../namespace_end.h"
|
|
|
|
#if !defined(_XBOX) || defined(QAGAME)
|
|
extern vmCvar_t bg_fighterAltControl;
|
|
qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh )
|
|
{
|
|
if ( bg_fighterAltControl.integer
|
|
&& ps->clientNum < MAX_CLIENTS //real client
|
|
&& ps->m_iVehicleNum//in a vehicle
|
|
&& pVeh //valid vehicle data pointer
|
|
&& pVeh->m_pVehicleInfo//valid vehicle info
|
|
&& pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter
|
|
//FIXME: specify per vehicle instead of assuming true for all fighters
|
|
//FIXME: map/server setting?
|
|
{//can roll and pitch without limitation!
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
#else
|
|
extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
|
|
#endif
|
|
|
|
#include "../namespace_begin.h"
|
|
|
|
/*
|
|
================
|
|
PM_UpdateViewAngles
|
|
|
|
This can be used as another entry point when only the viewangles
|
|
are being updated isntead of a full move
|
|
================
|
|
*/
|
|
void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
|
|
short temp;
|
|
int i;
|
|
|
|
if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
|
|
return; // no view changes at all
|
|
}
|
|
|
|
if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
|
|
return; // no view changes at all
|
|
}
|
|
|
|
// circularly clamp the angles with deltas
|
|
for (i=0 ; i<3 ; i++) {
|
|
temp = cmd->angles[i] + ps->delta_angles[i];
|
|
#ifdef VEH_CONTROL_SCHEME_4
|
|
if ( pm_entVeh
|
|
&& pm_entVeh->m_pVehicle
|
|
&& pm_entVeh->m_pVehicle->m_pVehicleInfo
|
|
&& pm_entVeh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER
|
|
&& (cmd->serverTime-pm_entVeh->playerState->hyperSpaceTime) >= HYPERSPACE_TIME )
|
|
{//in a vehicle and not hyperspacing
|
|
if ( i == PITCH )
|
|
{
|
|
int pitchClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[PITCH]+10.0f));
|
|
// don't let the player look up or down more than 22.5 degrees
|
|
if ( temp > pitchClamp )
|
|
{
|
|
ps->delta_angles[i] = pitchClamp - cmd->angles[i];
|
|
temp = pitchClamp;
|
|
}
|
|
else if ( temp < -pitchClamp )
|
|
{
|
|
ps->delta_angles[i] = -pitchClamp - cmd->angles[i];
|
|
temp = -pitchClamp;
|
|
}
|
|
}
|
|
if ( i == YAW )
|
|
{
|
|
int yawClamp = ANGLE2SHORT(AngleNormalize180(pm_entVeh->m_pVehicle->m_vPrevRiderViewAngles[YAW]+10.0f));
|
|
// don't let the player look left or right more than 22.5 degrees
|
|
if ( temp > yawClamp )
|
|
{
|
|
ps->delta_angles[i] = yawClamp - cmd->angles[i];
|
|
temp = yawClamp;
|
|
}
|
|
else if ( temp < -yawClamp )
|
|
{
|
|
ps->delta_angles[i] = -yawClamp - cmd->angles[i];
|
|
temp = -yawClamp;
|
|
}
|
|
}
|
|
}
|
|
#else //VEH_CONTROL_SCHEME_4
|
|
if ( pm_entVeh && BG_UnrestrainedPitchRoll( ps, pm_entVeh->m_pVehicle ) )
|
|
{//in a fighter
|
|
/*
|
|
if ( i == ROLL )
|
|
{//get roll from vehicle
|
|
ps->viewangles[ROLL] = pm_entVeh->playerState->viewangles[ROLL];//->m_pVehicle->m_vOrientation[ROLL];
|
|
continue;
|
|
|
|
}
|
|
*/
|
|
}
|
|
#endif // VEH_CONTROL_SCHEME_4
|
|
else
|
|
{
|
|
if ( i == PITCH ) {
|
|
// don't let the player look up or down more than 90 degrees
|
|
if ( temp > 16000 ) {
|
|
ps->delta_angles[i] = 16000 - cmd->angles[i];
|
|
temp = 16000;
|
|
} else if ( temp < -16000 ) {
|
|
ps->delta_angles[i] = -16000 - cmd->angles[i];
|
|
temp = -16000;
|
|
}
|
|
}
|
|
}
|
|
ps->viewangles[i] = SHORT2ANGLE(temp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
void PM_UpdateViewAngles( playerState_t *ps, const usercmd_t *cmd ) {
|
|
short temp;
|
|
int i;
|
|
float rootPitch = 0, pitchMin=-90, pitchMax=90, yawMin=0, yawMax=0, lockedYawValue = 0; //just to shut up warnings
|
|
qboolean lockedYaw = qfalse, clamped = qfalse;
|
|
bgEntity_t *vehEnt = NULL;
|
|
|
|
if ( ps->pm_type == PM_INTERMISSION || ps->pm_type == PM_SPINTERMISSION) {
|
|
return; // no view changes at all
|
|
}
|
|
|
|
if ( ps->pm_type != PM_SPECTATOR && ps->stats[STAT_HEALTH] <= 0 ) {
|
|
return; // no view changes at all
|
|
}
|
|
|
|
// If we're a vehicle, or we're riding a vehicle...?
|
|
if ( ps->m_iVehicleNum )
|
|
{
|
|
if ( ps->clientNum < MAX_CLIENTS )
|
|
{ //player riding vehicle
|
|
vehEnt = PM_BGEntForNum(ps->m_iVehicleNum);
|
|
}
|
|
else
|
|
{ //vehicle with player pilot
|
|
vehEnt = PM_BGEntForNum(ps->clientNum);
|
|
}
|
|
if ( vehEnt )
|
|
{//there is a vehicle
|
|
Vehicle_t *pVeh = vehEnt->m_pVehicle;
|
|
if ( pVeh && pVeh->m_pVehicleInfo )
|
|
{
|
|
// There is a vehicle...
|
|
if ( pVeh->m_pVehicleInfo->type != VH_ANIMAL )
|
|
{//animals just turn normally, no clamping
|
|
if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
|
|
{
|
|
rootPitch = pVeh->m_vOrientation[PITCH];//gent->owner->client->ps.vehicleAngles[PITCH];//??? what if goes over 90 when add the min/max?
|
|
if ( pVeh->m_pVehicleInfo->pitchLimit == -1 )
|
|
{
|
|
pitchMax = 180;
|
|
}
|
|
else
|
|
{
|
|
pitchMax = pVeh->m_pVehicleInfo->pitchLimit;
|
|
}
|
|
pitchMin = -pitchMax;
|
|
}
|
|
else
|
|
{
|
|
lockedYawValue = 0;//gent->owner->client->ps.vehicleAngles[YAW];
|
|
lockedYaw = qtrue;
|
|
yawMax = pVeh->m_pVehicleInfo->lookYaw;
|
|
yawMin = -yawMax;
|
|
rootPitch = 0;//gent->owner->client->ps.vehicleAngles[PITCH];//??? what if goes over 90 when add the min/max?
|
|
pitchMax = pVeh->m_pVehicleInfo->lookPitch;
|
|
pitchMin = -pitchMax;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ( 1 )
|
|
{
|
|
const short pitchClampMin = ANGLE2SHORT(rootPitch+pitchMin);
|
|
const short pitchClampMax = ANGLE2SHORT(rootPitch+pitchMax);
|
|
const short yawClampMin = ANGLE2SHORT(lockedYawValue+yawMin);
|
|
const short yawClampMax = ANGLE2SHORT(lockedYawValue+yawMax);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
temp = cmd->angles[i] + ps->delta_angles[i];
|
|
if ( i == PITCH )
|
|
{
|
|
//FIXME get this limit from the NPCs stats?
|
|
// don't let the player look up or down more than 90 degrees
|
|
if ( temp > pitchClampMax )
|
|
{
|
|
ps->delta_angles[i] = (pitchClampMax - cmd->angles[i]) & 0xffff; //& clamp to short
|
|
temp = pitchClampMax;
|
|
clamped = qtrue;
|
|
}
|
|
else if ( temp < pitchClampMin )
|
|
{
|
|
ps->delta_angles[i] = (pitchClampMin - cmd->angles[i]) & 0xffff; //& clamp to short
|
|
temp = pitchClampMin;
|
|
clamped = qtrue;
|
|
}
|
|
}
|
|
if ( i == YAW && lockedYaw )
|
|
{
|
|
//FIXME get this limit from the NPCs stats?
|
|
// don't let the player look up or down more than 90 degrees
|
|
if ( temp > yawClampMax )
|
|
{
|
|
ps->delta_angles[i] = (yawClampMax - cmd->angles[i]) & 0xffff; //& clamp to short
|
|
temp = yawClampMax;
|
|
clamped = qtrue;
|
|
}
|
|
else if ( temp < yawClampMin )
|
|
{
|
|
ps->delta_angles[i] = (yawClampMin - cmd->angles[i]) & 0xffff; //& clamp to short
|
|
temp = yawClampMin;
|
|
clamped = qtrue;
|
|
}
|
|
ps->viewangles[i] = SHORT2ANGLE(temp);
|
|
}
|
|
else
|
|
{
|
|
ps->viewangles[i] = SHORT2ANGLE(temp);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// circularly clamp the angles with deltas
|
|
for (i=0 ; i<3 ; i++) {
|
|
temp = cmd->angles[i] + ps->delta_angles[i];
|
|
if ( i == PITCH ) {
|
|
// don't let the player look up or down more than 90 degrees
|
|
if ( temp > 16000 ) {
|
|
ps->delta_angles[i] = 16000 - cmd->angles[i];
|
|
temp = 16000;
|
|
} else if ( temp < -16000 ) {
|
|
ps->delta_angles[i] = -16000 - cmd->angles[i];
|
|
temp = -16000;
|
|
}
|
|
}
|
|
ps->viewangles[i] = SHORT2ANGLE(temp);
|
|
}
|
|
}
|
|
|
|
}
|
|
*/
|
|
|
|
//-------------------------------------------
|
|
void PM_AdjustAttackStates( pmove_t *pm )
|
|
//-------------------------------------------
|
|
{
|
|
int amount;
|
|
|
|
if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&&pm->ps->m_iVehicleNum)
|
|
{ //riding a vehicle
|
|
bgEntity_t *veh = pm_entVeh;
|
|
if ( veh &&
|
|
(veh->m_pVehicle && (veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)) )
|
|
{//riding a walker/fighter
|
|
//not firing, ever
|
|
pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
|
|
return;
|
|
}
|
|
}
|
|
// get ammo usage
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot;
|
|
}
|
|
else
|
|
{
|
|
amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot;
|
|
}
|
|
|
|
// disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player?
|
|
if ( pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_READY )
|
|
{
|
|
if ( !(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) /*&&
|
|
pm->cmd.upmove <= 0 && !pm->cmd.forwardmove && !pm->cmd.rightmove*/)
|
|
{
|
|
// We just pressed the alt-fire key
|
|
if ( !pm->ps->zoomMode && pm->ps->pm_type != PM_DEAD )
|
|
{
|
|
// not already zooming, so do it now
|
|
pm->ps->zoomMode = 1;
|
|
pm->ps->zoomLocked = qfalse;
|
|
pm->ps->zoomFov = 80.0f;//cg_fov.value;
|
|
pm->ps->zoomLockTime = pm->cmd.serverTime + 50;
|
|
PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
|
|
}
|
|
else if (pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
|
|
{ //check for == 1 so we can't turn binoculars off with disruptor alt fire
|
|
// already zooming, so must be wanting to turn it off
|
|
pm->ps->zoomMode = 0;
|
|
pm->ps->zoomTime = pm->ps->commandTime;
|
|
pm->ps->zoomLocked = qfalse;
|
|
PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
|
|
pm->ps->weaponTime = 1000;
|
|
}
|
|
}
|
|
else if ( !(pm->cmd.buttons & BUTTON_ALT_ATTACK ) && pm->ps->zoomLockTime < pm->cmd.serverTime)
|
|
{
|
|
// Not pressing zoom any more
|
|
if ( pm->ps->zoomMode )
|
|
{
|
|
if (pm->ps->zoomMode == 1 && !pm->ps->zoomLocked)
|
|
{ //approximate what level the client should be zoomed at based on how long zoom was held
|
|
pm->ps->zoomFov = ((pm->cmd.serverTime+50) - pm->ps->zoomLockTime) * 0.035f;
|
|
if (pm->ps->zoomFov > 50)
|
|
{
|
|
pm->ps->zoomFov = 50;
|
|
}
|
|
if (pm->ps->zoomFov < 1)
|
|
{
|
|
pm->ps->zoomFov = 1;
|
|
}
|
|
}
|
|
// were zooming in, so now lock the zoom
|
|
pm->ps->zoomLocked = qtrue;
|
|
}
|
|
}
|
|
//This seemed like a good idea, but apparently it confuses people. So disabled for now.
|
|
/*
|
|
else if (!(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
|
|
(pm->cmd.upmove > 0 || pm->cmd.forwardmove || pm->cmd.rightmove))
|
|
{ //if you try to zoom while moving, just convert it into a primary attack
|
|
pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
|
|
pm->cmd.buttons |= BUTTON_ATTACK;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
if (pm->cmd.upmove > 0 || pm->cmd.forwardmove || pm->cmd.rightmove)
|
|
{
|
|
if (pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
|
|
{ //check for == 1 so we can't turn binoculars off with disruptor alt fire
|
|
pm->ps->zoomMode = 0;
|
|
pm->ps->zoomTime = pm->ps->commandTime;
|
|
pm->ps->zoomLocked = qfalse;
|
|
PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
|
|
}
|
|
}
|
|
*/
|
|
|
|
if ( pm->cmd.buttons & BUTTON_ATTACK )
|
|
{
|
|
// If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll
|
|
// just use whatever ammo was selected from above
|
|
if ( pm->ps->zoomMode )
|
|
{
|
|
amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] -
|
|
weaponData[pm->ps->weapon].altEnergyPerShot;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// alt-fire button pressing doesn't use any ammo
|
|
amount = 0;
|
|
}
|
|
}
|
|
/*
|
|
else if (pm->ps->weapon == WP_DISRUPTOR) //still perform certain checks, even if the weapon is not ready
|
|
{
|
|
if (pm->cmd.upmove > 0 || pm->cmd.forwardmove || pm->cmd.rightmove)
|
|
{
|
|
if (pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
|
|
{ //check for == 1 so we can't turn binoculars off with disruptor alt fire
|
|
pm->ps->zoomMode = 0;
|
|
pm->ps->zoomTime = pm->ps->commandTime;
|
|
pm->ps->zoomLocked = qfalse;
|
|
PM_AddEvent(EV_DISRUPTOR_ZOOMSOUND);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// set the firing flag for continuous beam weapons, saber will fire even if out of ammo
|
|
if ( !(pm->ps->pm_flags & PMF_RESPAWNED) &&
|
|
pm->ps->pm_type != PM_INTERMISSION &&
|
|
( pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) &&
|
|
( amount >= 0 || pm->ps->weapon == WP_SABER ))
|
|
{
|
|
if ( pm->cmd.buttons & BUTTON_ALT_ATTACK )
|
|
{
|
|
pm->ps->eFlags |= EF_ALT_FIRING;
|
|
}
|
|
else
|
|
{
|
|
pm->ps->eFlags &= ~EF_ALT_FIRING;
|
|
}
|
|
|
|
// This flag should always get set, even when alt-firing
|
|
pm->ps->eFlags |= EF_FIRING;
|
|
}
|
|
else
|
|
{
|
|
// Clear 'em out
|
|
pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
|
|
}
|
|
|
|
// disruptor should convert a main fire to an alt-fire if the gun is currently zoomed
|
|
if ( pm->ps->weapon == WP_DISRUPTOR)
|
|
{
|
|
if ( pm->cmd.buttons & BUTTON_ATTACK && pm->ps->zoomMode == 1 && pm->ps->zoomLocked)
|
|
{
|
|
// converting the main fire to an alt-fire
|
|
pm->cmd.buttons |= BUTTON_ALT_ATTACK;
|
|
pm->ps->eFlags |= EF_ALT_FIRING;
|
|
}
|
|
else if ( pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->ps->zoomMode == 1 && pm->ps->zoomLocked)
|
|
{
|
|
pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
|
|
pm->ps->eFlags &= ~EF_ALT_FIRING;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BG_CmdForRoll( playerState_t *ps, int anim, usercmd_t *pCmd )
|
|
{
|
|
switch ( (anim) )
|
|
{
|
|
case BOTH_ROLL_F:
|
|
pCmd->forwardmove = 127;
|
|
pCmd->rightmove = 0;
|
|
break;
|
|
case BOTH_ROLL_B:
|
|
pCmd->forwardmove = -127;
|
|
pCmd->rightmove = 0;
|
|
break;
|
|
case BOTH_ROLL_R:
|
|
pCmd->forwardmove = 0;
|
|
pCmd->rightmove = 127;
|
|
break;
|
|
case BOTH_ROLL_L:
|
|
pCmd->forwardmove = 0;
|
|
pCmd->rightmove = -127;
|
|
break;
|
|
case BOTH_GETUP_BROLL_R:
|
|
pCmd->forwardmove = 0;
|
|
pCmd->rightmove = 48;
|
|
//NOTE: speed is 400
|
|
break;
|
|
|
|
case BOTH_GETUP_FROLL_R:
|
|
if ( ps->legsTimer <= 250 )
|
|
{//end of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else
|
|
{
|
|
pCmd->forwardmove = 0;
|
|
pCmd->rightmove = 48;
|
|
//NOTE: speed is 400
|
|
}
|
|
break;
|
|
|
|
case BOTH_GETUP_BROLL_L:
|
|
pCmd->forwardmove = 0;
|
|
pCmd->rightmove = -48;
|
|
//NOTE: speed is 400
|
|
break;
|
|
|
|
case BOTH_GETUP_FROLL_L:
|
|
if ( ps->legsTimer <= 250 )
|
|
{//end of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else
|
|
{
|
|
pCmd->forwardmove = 0;
|
|
pCmd->rightmove = -48;
|
|
//NOTE: speed is 400
|
|
}
|
|
break;
|
|
|
|
case BOTH_GETUP_BROLL_B:
|
|
if ( ps->torsoTimer <= 250 )
|
|
{//end of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 350 )
|
|
{//beginning of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else
|
|
{
|
|
//FIXME: ramp down over length of anim
|
|
pCmd->forwardmove = -64;
|
|
pCmd->rightmove = 0;
|
|
//NOTE: speed is 400
|
|
}
|
|
break;
|
|
|
|
case BOTH_GETUP_FROLL_B:
|
|
if ( ps->torsoTimer <= 100 )
|
|
{//end of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 200 )
|
|
{//beginning of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else
|
|
{
|
|
//FIXME: ramp down over length of anim
|
|
pCmd->forwardmove = -64;
|
|
pCmd->rightmove = 0;
|
|
//NOTE: speed is 400
|
|
}
|
|
break;
|
|
|
|
case BOTH_GETUP_BROLL_F:
|
|
if ( ps->torsoTimer <= 550 )
|
|
{//end of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else if ( PM_AnimLength( 0, (animNumber_t)ps->legsAnim ) - ps->torsoTimer < 150 )
|
|
{//beginning of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else
|
|
{
|
|
pCmd->forwardmove = 64;
|
|
pCmd->rightmove = 0;
|
|
//NOTE: speed is 400
|
|
}
|
|
break;
|
|
|
|
case BOTH_GETUP_FROLL_F:
|
|
if ( ps->torsoTimer <= 100 )
|
|
{//end of anim
|
|
pCmd->forwardmove = pCmd->rightmove = 0;
|
|
}
|
|
else
|
|
{
|
|
//FIXME: ramp down over length of anim
|
|
pCmd->forwardmove = 64;
|
|
pCmd->rightmove = 0;
|
|
//NOTE: speed is 400
|
|
}
|
|
break;
|
|
}
|
|
pCmd->upmove = 0;
|
|
}
|
|
|
|
qboolean PM_SaberInTransition( int move );
|
|
|
|
void BG_AdjustClientSpeed(playerState_t *ps, usercmd_t *cmd, int svTime)
|
|
{
|
|
saberInfo_t *saber;
|
|
|
|
if (ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
bgEntity_t *bgEnt = pm_entSelf;
|
|
|
|
if (bgEnt && bgEnt->s.NPC_class == CLASS_VEHICLE)
|
|
{ //vehicles manage their own speed
|
|
return;
|
|
}
|
|
}
|
|
|
|
//For prediction, always reset speed back to the last known server base speed
|
|
//If we didn't do this, under lag we'd eventually dwindle speed down to 0 even though
|
|
//that would not be the correct predicted value.
|
|
ps->speed = ps->basespeed;
|
|
|
|
if (ps->forceHandExtend == HANDEXTEND_DODGE)
|
|
{
|
|
ps->speed = 0;
|
|
}
|
|
|
|
if (ps->forceHandExtend == HANDEXTEND_KNOCKDOWN ||
|
|
ps->forceHandExtend == HANDEXTEND_PRETHROWN ||
|
|
ps->forceHandExtend == HANDEXTEND_POSTTHROWN)
|
|
{
|
|
ps->speed = 0;
|
|
}
|
|
|
|
|
|
if ( cmd->forwardmove < 0 && !(cmd->buttons&BUTTON_WALKING) && pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//running backwards is slower than running forwards (like SP)
|
|
ps->speed *= 0.75;
|
|
}
|
|
|
|
if (ps->fd.forcePowersActive & (1 << FP_GRIP))
|
|
{
|
|
ps->speed *= 0.4;
|
|
}
|
|
|
|
if (ps->fd.forcePowersActive & (1 << FP_SPEED))
|
|
{
|
|
ps->speed *= 1.7;
|
|
}
|
|
else if (ps->fd.forcePowersActive & (1 << FP_RAGE))
|
|
{
|
|
ps->speed *= 1.3;
|
|
}
|
|
else if (ps->fd.forceRageRecoveryTime > svTime)
|
|
{
|
|
ps->speed *= 0.75;
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_DISRUPTOR &&
|
|
pm->ps->zoomMode == 1 && pm->ps->zoomLockTime < pm->cmd.serverTime)
|
|
{
|
|
ps->speed *= 0.5f;
|
|
}
|
|
|
|
if (ps->fd.forceGripCripple)
|
|
{
|
|
if (ps->fd.forcePowersActive & (1 << FP_RAGE))
|
|
{
|
|
ps->speed *= 0.9;
|
|
}
|
|
else if (ps->fd.forcePowersActive & (1 << FP_SPEED))
|
|
{ //force speed will help us escape
|
|
ps->speed *= 0.8;
|
|
}
|
|
else
|
|
{
|
|
ps->speed *= 0.2;
|
|
}
|
|
}
|
|
|
|
if ( BG_SaberInAttack( ps->saberMove ) && cmd->forwardmove < 0 )
|
|
{//if running backwards while attacking, don't run as fast.
|
|
switch( ps->fd.saberAnimLevel )
|
|
{
|
|
case FORCE_LEVEL_1:
|
|
ps->speed *= 0.75f;
|
|
break;
|
|
case FORCE_LEVEL_2:
|
|
case SS_DUAL:
|
|
case SS_STAFF:
|
|
ps->speed *= 0.60f;
|
|
break;
|
|
case FORCE_LEVEL_3:
|
|
ps->speed *= 0.45f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if ( BG_SpinningSaberAnim( ps->legsAnim ) )
|
|
{
|
|
if (ps->fd.saberAnimLevel == FORCE_LEVEL_3)
|
|
{
|
|
ps->speed *= 0.3f;
|
|
}
|
|
else
|
|
{
|
|
ps->speed *= 0.5f;
|
|
}
|
|
}
|
|
else if ( ps->weapon == WP_SABER && BG_SaberInAttack( ps->saberMove ) )
|
|
{//if attacking with saber while running, drop your speed
|
|
switch( ps->fd.saberAnimLevel )
|
|
{
|
|
case FORCE_LEVEL_2:
|
|
case SS_DUAL:
|
|
case SS_STAFF:
|
|
ps->speed *= 0.85f;
|
|
break;
|
|
case FORCE_LEVEL_3:
|
|
ps->speed *= 0.55f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (ps->weapon == WP_SABER && ps->fd.saberAnimLevel == FORCE_LEVEL_3 &&
|
|
PM_SaberInTransition(ps->saberMove))
|
|
{ //Now, we want to even slow down in transitions for level 3 (since it has chains and stuff now)
|
|
if (cmd->forwardmove < 0)
|
|
{
|
|
ps->speed *= 0.4f;
|
|
}
|
|
else
|
|
{
|
|
ps->speed *= 0.6f;
|
|
}
|
|
}
|
|
|
|
if ( BG_InRoll( ps, ps->legsAnim ) && ps->speed > 50 )
|
|
{ //can't roll unless you're able to move normally
|
|
if ((ps->legsAnim) == BOTH_ROLL_B)
|
|
{ //backwards roll is pretty fast, should also be slower
|
|
if (ps->legsTimer > 800)
|
|
{
|
|
ps->speed = ps->legsTimer/2.5;
|
|
}
|
|
else
|
|
{
|
|
ps->speed = ps->legsTimer/6.0;//450;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ps->legsTimer > 800)
|
|
{
|
|
ps->speed = ps->legsTimer/1.5;//450;
|
|
}
|
|
else
|
|
{
|
|
ps->speed = ps->legsTimer/5.0;//450;
|
|
}
|
|
}
|
|
if (ps->speed > 600)
|
|
{
|
|
ps->speed = 600;
|
|
}
|
|
//Automatically slow down as the roll ends.
|
|
}
|
|
|
|
saber = BG_MySaber( ps->clientNum, 0 );
|
|
if ( saber
|
|
&& saber->moveSpeedScale != 1.0f )
|
|
{
|
|
ps->speed *= saber->moveSpeedScale;
|
|
}
|
|
saber = BG_MySaber( ps->clientNum, 1 );
|
|
if ( saber
|
|
&& saber->moveSpeedScale != 1.0f )
|
|
{
|
|
ps->speed *= saber->moveSpeedScale;
|
|
}
|
|
}
|
|
|
|
qboolean BG_InRollAnim( entityState_t *cent )
|
|
{
|
|
switch ( (cent->legsAnim) )
|
|
{
|
|
case BOTH_ROLL_F:
|
|
case BOTH_ROLL_B:
|
|
case BOTH_ROLL_R:
|
|
case BOTH_ROLL_L:
|
|
return qtrue;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean BG_InKnockDown( int anim )
|
|
{
|
|
switch ( (anim) )
|
|
{
|
|
case BOTH_KNOCKDOWN1:
|
|
case BOTH_KNOCKDOWN2:
|
|
case BOTH_KNOCKDOWN3:
|
|
case BOTH_KNOCKDOWN4:
|
|
case BOTH_KNOCKDOWN5:
|
|
return qtrue;
|
|
break;
|
|
case BOTH_GETUP1:
|
|
case BOTH_GETUP2:
|
|
case BOTH_GETUP3:
|
|
case BOTH_GETUP4:
|
|
case BOTH_GETUP5:
|
|
case BOTH_FORCE_GETUP_F1:
|
|
case BOTH_FORCE_GETUP_F2:
|
|
case BOTH_FORCE_GETUP_B1:
|
|
case BOTH_FORCE_GETUP_B2:
|
|
case BOTH_FORCE_GETUP_B3:
|
|
case BOTH_FORCE_GETUP_B4:
|
|
case BOTH_FORCE_GETUP_B5:
|
|
case BOTH_GETUP_BROLL_B:
|
|
case BOTH_GETUP_BROLL_F:
|
|
case BOTH_GETUP_BROLL_L:
|
|
case BOTH_GETUP_BROLL_R:
|
|
case BOTH_GETUP_FROLL_B:
|
|
case BOTH_GETUP_FROLL_F:
|
|
case BOTH_GETUP_FROLL_L:
|
|
case BOTH_GETUP_FROLL_R:
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
qboolean BG_InRollES( entityState_t *ps, int anim )
|
|
{
|
|
switch ( (anim) )
|
|
{
|
|
case BOTH_ROLL_F:
|
|
case BOTH_ROLL_B:
|
|
case BOTH_ROLL_R:
|
|
case BOTH_ROLL_L:
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
void BG_IK_MoveArm(void *ghoul2, int lHandBolt, int time, entityState_t *ent, int basePose, vec3_t desiredPos, qboolean *ikInProgress,
|
|
vec3_t origin, vec3_t angles, vec3_t scale, int blendTime, qboolean forceHalt)
|
|
{
|
|
mdxaBone_t lHandMatrix;
|
|
vec3_t lHand;
|
|
vec3_t torg;
|
|
float distToDest;
|
|
|
|
if (!ghoul2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
assert(bgHumanoidAnimations[basePose].firstFrame > 0);
|
|
|
|
if (!*ikInProgress && !forceHalt)
|
|
{
|
|
int baseposeAnim = basePose;
|
|
sharedSetBoneIKStateParams_t ikP;
|
|
|
|
//restrict the shoulder joint
|
|
//VectorSet(ikP.pcjMins,-50.0f,-80.0f,-15.0f);
|
|
//VectorSet(ikP.pcjMaxs,15.0f,40.0f,15.0f);
|
|
|
|
//for now, leaving it unrestricted, but restricting elbow joint.
|
|
//This lets us break the arm however we want in order to fling people
|
|
//in throws, and doesn't look bad.
|
|
VectorSet(ikP.pcjMins,0,0,0);
|
|
VectorSet(ikP.pcjMaxs,0,0,0);
|
|
|
|
//give the info on our entity.
|
|
ikP.blendTime = blendTime;
|
|
VectorCopy(origin, ikP.origin);
|
|
VectorCopy(angles, ikP.angles);
|
|
ikP.angles[PITCH] = 0;
|
|
ikP.pcjOverrides = 0;
|
|
ikP.radius = 10.0f;
|
|
VectorCopy(scale, ikP.scale);
|
|
|
|
//base pose frames for the limb
|
|
ikP.startFrame = bgHumanoidAnimations[baseposeAnim].firstFrame + bgHumanoidAnimations[baseposeAnim].numFrames;
|
|
ikP.endFrame = bgHumanoidAnimations[baseposeAnim].firstFrame + bgHumanoidAnimations[baseposeAnim].numFrames;
|
|
|
|
ikP.forceAnimOnBone = qfalse; //let it use existing anim if it's the same as this one.
|
|
|
|
//we want to call with a null bone name first. This will init all of the
|
|
//ik system stuff on the g2 instance, because we need ragdoll effectors
|
|
//in order for our pcj's to know how to angle properly.
|
|
if (!strap_G2API_SetBoneIKState(ghoul2, time, NULL, IKS_DYNAMIC, &ikP))
|
|
{
|
|
assert(!"Failed to init IK system for g2 instance!");
|
|
}
|
|
|
|
//Now, create our IK bone state.
|
|
if (strap_G2API_SetBoneIKState(ghoul2, time, "lhumerus", IKS_DYNAMIC, &ikP))
|
|
{
|
|
//restrict the elbow joint
|
|
VectorSet(ikP.pcjMins,-90.0f,-20.0f,-20.0f);
|
|
VectorSet(ikP.pcjMaxs,30.0f,20.0f,-20.0f);
|
|
|
|
if (strap_G2API_SetBoneIKState(ghoul2, time, "lradius", IKS_DYNAMIC, &ikP))
|
|
{ //everything went alright.
|
|
*ikInProgress = qtrue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*ikInProgress && !forceHalt)
|
|
{ //actively update our ik state.
|
|
sharedIKMoveParams_t ikM;
|
|
sharedRagDollUpdateParams_t tuParms;
|
|
vec3_t tAngles;
|
|
|
|
//set the argument struct up
|
|
VectorCopy(desiredPos, ikM.desiredOrigin); //we want the bone to move here.. if possible
|
|
|
|
VectorCopy(angles, tAngles);
|
|
tAngles[PITCH] = tAngles[ROLL] = 0;
|
|
|
|
strap_G2API_GetBoltMatrix(ghoul2, 0, lHandBolt, &lHandMatrix, tAngles, origin, time, 0, scale);
|
|
//Get the point position from the matrix.
|
|
lHand[0] = lHandMatrix.matrix[0][3];
|
|
lHand[1] = lHandMatrix.matrix[1][3];
|
|
lHand[2] = lHandMatrix.matrix[2][3];
|
|
|
|
VectorSubtract(lHand, desiredPos, torg);
|
|
distToDest = VectorLength(torg);
|
|
|
|
//closer we are, more we want to keep updated.
|
|
//if we're far away we don't want to be too fast or we'll start twitching all over.
|
|
if (distToDest < 2)
|
|
{ //however if we're this close we want very precise movement
|
|
ikM.movementSpeed = 0.4f;
|
|
}
|
|
else if (distToDest < 16)
|
|
{
|
|
ikM.movementSpeed = 0.9f;//8.0f;
|
|
}
|
|
else if (distToDest < 32)
|
|
{
|
|
ikM.movementSpeed = 0.8f;//4.0f;
|
|
}
|
|
else if (distToDest < 64)
|
|
{
|
|
ikM.movementSpeed = 0.7f;//2.0f;
|
|
}
|
|
else
|
|
{
|
|
ikM.movementSpeed = 0.6f;
|
|
}
|
|
VectorCopy(origin, ikM.origin); //our position in the world.
|
|
|
|
ikM.boneName[0] = 0;
|
|
if (strap_G2API_IKMove(ghoul2, time, &ikM))
|
|
{
|
|
//now do the standard model animate stuff with ragdoll update params.
|
|
VectorCopy(angles, tuParms.angles);
|
|
tuParms.angles[PITCH] = 0;
|
|
|
|
VectorCopy(origin, tuParms.position);
|
|
VectorCopy(scale, tuParms.scale);
|
|
|
|
tuParms.me = ent->number;
|
|
VectorClear(tuParms.velocity);
|
|
|
|
strap_G2API_AnimateG2Models(ghoul2, time, &tuParms);
|
|
}
|
|
else
|
|
{
|
|
*ikInProgress = qfalse;
|
|
}
|
|
}
|
|
else if (*ikInProgress)
|
|
{ //kill it
|
|
float cFrame, animSpeed;
|
|
int sFrame, eFrame, flags;
|
|
|
|
strap_G2API_SetBoneIKState(ghoul2, time, "lhumerus", IKS_NONE, NULL);
|
|
strap_G2API_SetBoneIKState(ghoul2, time, "lradius", IKS_NONE, NULL);
|
|
|
|
//then reset the angles/anims on these PCJs
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "lhumerus", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "lradius", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, time);
|
|
|
|
//Get the anim/frames that the pelvis is on exactly, and match the left arm back up with them again.
|
|
strap_G2API_GetBoneAnim(ghoul2, "pelvis", (const int)time, &cFrame, &sFrame, &eFrame, &flags, &animSpeed, 0, 0);
|
|
strap_G2API_SetBoneAnim(ghoul2, 0, "lhumerus", sFrame, eFrame, flags, animSpeed, time, sFrame, 300);
|
|
strap_G2API_SetBoneAnim(ghoul2, 0, "lradius", sFrame, eFrame, flags, animSpeed, time, sFrame, 300);
|
|
|
|
//And finally, get rid of all the ik state effector data by calling with null bone name (similar to how we init it).
|
|
strap_G2API_SetBoneIKState(ghoul2, time, NULL, IKS_NONE, NULL);
|
|
|
|
*ikInProgress = qfalse;
|
|
}
|
|
}
|
|
|
|
//Adjust the head/neck desired angles
|
|
void BG_UpdateLookAngles( int lookingDebounceTime, vec3_t lastHeadAngles, int time, vec3_t lookAngles, float lookSpeed, float minPitch, float maxPitch, float minYaw, float maxYaw, float minRoll, float maxRoll )
|
|
{
|
|
static const float fFrameInter = 0.1f;
|
|
static vec3_t oldLookAngles;
|
|
static vec3_t lookAnglesDiff;
|
|
static int ang;
|
|
|
|
if ( lookingDebounceTime > time )
|
|
{
|
|
//clamp so don't get "Exorcist" effect
|
|
if ( lookAngles[PITCH] > maxPitch )
|
|
{
|
|
lookAngles[PITCH] = maxPitch;
|
|
}
|
|
else if ( lookAngles[PITCH] < minPitch )
|
|
{
|
|
lookAngles[PITCH] = minPitch;
|
|
}
|
|
if ( lookAngles[YAW] > maxYaw )
|
|
{
|
|
lookAngles[YAW] = maxYaw;
|
|
}
|
|
else if ( lookAngles[YAW] < minYaw )
|
|
{
|
|
lookAngles[YAW] = minYaw;
|
|
}
|
|
if ( lookAngles[ROLL] > maxRoll )
|
|
{
|
|
lookAngles[ROLL] = maxRoll;
|
|
}
|
|
else if ( lookAngles[ROLL] < minRoll )
|
|
{
|
|
lookAngles[ROLL] = minRoll;
|
|
}
|
|
|
|
//slowly lerp to this new value
|
|
//Remember last headAngles
|
|
VectorCopy( lastHeadAngles, oldLookAngles );
|
|
VectorSubtract( lookAngles, oldLookAngles, lookAnglesDiff );
|
|
|
|
for ( ang = 0; ang < 3; ang++ )
|
|
{
|
|
lookAnglesDiff[ang] = AngleNormalize180( lookAnglesDiff[ang] );
|
|
}
|
|
|
|
if( VectorLengthSquared( lookAnglesDiff ) )
|
|
{
|
|
lookAngles[PITCH] = AngleNormalize180( oldLookAngles[PITCH]+(lookAnglesDiff[PITCH]*fFrameInter*lookSpeed) );
|
|
lookAngles[YAW] = AngleNormalize180( oldLookAngles[YAW]+(lookAnglesDiff[YAW]*fFrameInter*lookSpeed) );
|
|
lookAngles[ROLL] = AngleNormalize180( oldLookAngles[ROLL]+(lookAnglesDiff[ROLL]*fFrameInter*lookSpeed) );
|
|
}
|
|
}
|
|
//Remember current lookAngles next time
|
|
VectorCopy( lookAngles, lastHeadAngles );
|
|
}
|
|
|
|
//for setting visual look (headturn) angles
|
|
static void BG_G2ClientNeckAngles( void *ghoul2, int time, const vec3_t lookAngles, vec3_t headAngles, vec3_t neckAngles, vec3_t thoracicAngles, vec3_t headClampMinAngles, vec3_t headClampMaxAngles )
|
|
{
|
|
vec3_t lA;
|
|
VectorCopy( lookAngles, lA );
|
|
//clamp the headangles (which should now be relative to the cervical (neck) angles
|
|
if ( lA[PITCH] < headClampMinAngles[PITCH] )
|
|
{
|
|
lA[PITCH] = headClampMinAngles[PITCH];
|
|
}
|
|
else if ( lA[PITCH] > headClampMaxAngles[PITCH] )
|
|
{
|
|
lA[PITCH] = headClampMaxAngles[PITCH];
|
|
}
|
|
|
|
if ( lA[YAW] < headClampMinAngles[YAW] )
|
|
{
|
|
lA[YAW] = headClampMinAngles[YAW];
|
|
}
|
|
else if ( lA[YAW] > headClampMaxAngles[YAW] )
|
|
{
|
|
lA[YAW] = headClampMaxAngles[YAW];
|
|
}
|
|
|
|
if ( lA[ROLL] < headClampMinAngles[ROLL] )
|
|
{
|
|
lA[ROLL] = headClampMinAngles[ROLL];
|
|
}
|
|
else if ( lA[ROLL] > headClampMaxAngles[ROLL] )
|
|
{
|
|
lA[ROLL] = headClampMaxAngles[ROLL];
|
|
}
|
|
|
|
//split it up between the neck and cranium
|
|
if ( thoracicAngles[PITCH] )
|
|
{//already been set above, blend them
|
|
thoracicAngles[PITCH] = (thoracicAngles[PITCH] + (lA[PITCH] * 0.4)) * 0.5f;
|
|
}
|
|
else
|
|
{
|
|
thoracicAngles[PITCH] = lA[PITCH] * 0.4;
|
|
}
|
|
if ( thoracicAngles[YAW] )
|
|
{//already been set above, blend them
|
|
thoracicAngles[YAW] = (thoracicAngles[YAW] + (lA[YAW] * 0.1)) * 0.5f;
|
|
}
|
|
else
|
|
{
|
|
thoracicAngles[YAW] = lA[YAW] * 0.1;
|
|
}
|
|
if ( thoracicAngles[ROLL] )
|
|
{//already been set above, blend them
|
|
thoracicAngles[ROLL] = (thoracicAngles[ROLL] + (lA[ROLL] * 0.1)) * 0.5f;
|
|
}
|
|
else
|
|
{
|
|
thoracicAngles[ROLL] = lA[ROLL] * 0.1;
|
|
}
|
|
|
|
neckAngles[PITCH] = lA[PITCH] * 0.2f;
|
|
neckAngles[YAW] = lA[YAW] * 0.3f;
|
|
neckAngles[ROLL] = lA[ROLL] * 0.3f;
|
|
|
|
headAngles[PITCH] = lA[PITCH] * 0.4;
|
|
headAngles[YAW] = lA[YAW] * 0.6;
|
|
headAngles[ROLL] = lA[ROLL] * 0.6;
|
|
|
|
/* //non-applicable SP code
|
|
if ( G_RidingVehicle( cent->gent ) )// && type == VH_SPEEDER ?
|
|
{//aim torso forward too
|
|
headAngles[YAW] = neckAngles[YAW] = thoracicAngles[YAW] = 0;
|
|
}
|
|
*/
|
|
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cranium", headAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cervical", neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "thoracic", thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
}
|
|
|
|
//rww - Finally decided to convert all this stuff to BG form.
|
|
static void BG_G2ClientSpineAngles( void *ghoul2, int motionBolt, vec3_t cent_lerpOrigin, vec3_t cent_lerpAngles, entityState_t *cent,
|
|
int time, vec3_t viewAngles, int ciLegs, int ciTorso, const vec3_t angles, vec3_t thoracicAngles,
|
|
vec3_t ulAngles, vec3_t llAngles, vec3_t modelScale, float *tPitchAngle, float *tYawAngle, int *corrTime )
|
|
{
|
|
qboolean doCorr = qfalse;
|
|
|
|
//*tPitchAngle = viewAngles[PITCH];
|
|
viewAngles[YAW] = AngleDelta( cent_lerpAngles[YAW], angles[YAW] );
|
|
//*tYawAngle = viewAngles[YAW];
|
|
|
|
#if 1
|
|
if ( !BG_FlippingAnim( cent->legsAnim ) &&
|
|
!BG_SpinningSaberAnim( cent->legsAnim ) &&
|
|
!BG_SpinningSaberAnim( cent->torsoAnim ) &&
|
|
!BG_InSpecialJump( cent->legsAnim ) &&
|
|
!BG_InSpecialJump( cent->torsoAnim ) &&
|
|
!BG_InDeathAnim(cent->legsAnim) &&
|
|
!BG_InDeathAnim(cent->torsoAnim) &&
|
|
!BG_InRollES(cent, cent->legsAnim) &&
|
|
!BG_InRollAnim(cent) &&
|
|
!BG_SaberInSpecial(cent->saberMove) &&
|
|
!BG_SaberInSpecialAttack(cent->torsoAnim) &&
|
|
!BG_SaberInSpecialAttack(cent->legsAnim) &&
|
|
|
|
!BG_InKnockDown(cent->torsoAnim) &&
|
|
!BG_InKnockDown(cent->legsAnim) &&
|
|
!BG_InKnockDown(ciTorso) &&
|
|
!BG_InKnockDown(ciLegs) &&
|
|
|
|
!BG_FlippingAnim( ciLegs ) &&
|
|
!BG_SpinningSaberAnim( ciLegs ) &&
|
|
!BG_SpinningSaberAnim( ciTorso ) &&
|
|
!BG_InSpecialJump( ciLegs ) &&
|
|
!BG_InSpecialJump( ciTorso ) &&
|
|
!BG_InDeathAnim(ciLegs) &&
|
|
!BG_InDeathAnim(ciTorso) &&
|
|
!BG_SaberInSpecialAttack(ciTorso) &&
|
|
!BG_SaberInSpecialAttack(ciLegs) &&
|
|
|
|
!(cent->eFlags & EF_DEAD) &&
|
|
(cent->legsAnim) != (cent->torsoAnim) &&
|
|
(ciLegs) != (ciTorso) &&
|
|
!cent->m_iVehicleNum)
|
|
{
|
|
doCorr = qtrue;
|
|
}
|
|
#else
|
|
if ( ((!BG_FlippingAnim( cent->legsAnim )
|
|
&& !BG_SpinningSaberAnim( cent->legsAnim )
|
|
&& !BG_SpinningSaberAnim( cent->torsoAnim )
|
|
&& (cent->legsAnim) != (cent->torsoAnim)) //NOTE: presumes your legs & torso are on the same frame, though they *should* be because PM_SetAnimFinal tries to keep them in synch
|
|
||
|
|
(!BG_FlippingAnim( ciLegs )
|
|
&& !BG_SpinningSaberAnim( ciLegs )
|
|
&& !BG_SpinningSaberAnim( ciTorso )
|
|
&& (ciLegs) != (ciTorso)))
|
|
||
|
|
ciLegs != cent->legsAnim
|
|
||
|
|
ciTorso != cent->torsoAnim)
|
|
{
|
|
doCorr = qtrue;
|
|
*corrTime = time + 1000; //continue correcting for a second after to smooth things out. SP doesn't need this for whatever reason but I can't find a way around it.
|
|
}
|
|
else if (*corrTime >= time)
|
|
{
|
|
if (!BG_FlippingAnim( cent->legsAnim )
|
|
&& !BG_SpinningSaberAnim( cent->legsAnim )
|
|
&& !BG_SpinningSaberAnim( cent->torsoAnim )
|
|
&& !BG_FlippingAnim( ciLegs )
|
|
&& !BG_SpinningSaberAnim( ciLegs )
|
|
&& !BG_SpinningSaberAnim( ciTorso ))
|
|
{
|
|
doCorr = qtrue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (doCorr)
|
|
{//FIXME: no need to do this if legs and torso on are same frame
|
|
//adjust for motion offset
|
|
mdxaBone_t boltMatrix;
|
|
vec3_t motionFwd, motionAngles;
|
|
vec3_t motionRt, tempAng;
|
|
int ang;
|
|
|
|
strap_G2API_GetBoltMatrix_NoRecNoRot( ghoul2, 0, motionBolt, &boltMatrix, vec3_origin, cent_lerpOrigin, time, 0, modelScale);
|
|
//BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, motionFwd );
|
|
motionFwd[0] = -boltMatrix.matrix[0][1];
|
|
motionFwd[1] = -boltMatrix.matrix[1][1];
|
|
motionFwd[2] = -boltMatrix.matrix[2][1];
|
|
|
|
vectoangles( motionFwd, motionAngles );
|
|
|
|
//BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_X, motionRt );
|
|
motionRt[0] = -boltMatrix.matrix[0][0];
|
|
motionRt[1] = -boltMatrix.matrix[1][0];
|
|
motionRt[2] = -boltMatrix.matrix[2][0];
|
|
|
|
vectoangles( motionRt, tempAng );
|
|
motionAngles[ROLL] = -tempAng[PITCH];
|
|
|
|
for ( ang = 0; ang < 3; ang++ )
|
|
{
|
|
viewAngles[ang] = AngleNormalize180( viewAngles[ang] - AngleNormalize180( motionAngles[ang] ) );
|
|
}
|
|
}
|
|
|
|
//distribute the angles differently up the spine
|
|
//NOTE: each of these distributions must add up to 1.0f
|
|
thoracicAngles[PITCH] = viewAngles[PITCH]*0.20f;
|
|
llAngles[PITCH] = viewAngles[PITCH]*0.40f;
|
|
ulAngles[PITCH] = viewAngles[PITCH]*0.40f;
|
|
|
|
thoracicAngles[YAW] = viewAngles[YAW]*0.20f;
|
|
ulAngles[YAW] = viewAngles[YAW]*0.35f;
|
|
llAngles[YAW] = viewAngles[YAW]*0.45f;
|
|
|
|
thoracicAngles[ROLL] = viewAngles[ROLL]*0.20f;
|
|
ulAngles[ROLL] = viewAngles[ROLL]*0.35f;
|
|
llAngles[ROLL] = viewAngles[ROLL]*0.45f;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_SwingAngles
|
|
==================
|
|
*/
|
|
static float BG_SwingAngles( float destination, float swingTolerance, float clampTolerance,
|
|
float speed, float *angle, qboolean *swinging, int frametime ) {
|
|
float swing;
|
|
float move;
|
|
float scale;
|
|
|
|
if ( !*swinging ) {
|
|
// see if a swing should be started
|
|
swing = AngleSubtract( *angle, destination );
|
|
if ( swing > swingTolerance || swing < -swingTolerance ) {
|
|
*swinging = qtrue;
|
|
}
|
|
}
|
|
|
|
if ( !*swinging ) {
|
|
return 0;
|
|
}
|
|
|
|
// modify the speed depending on the delta
|
|
// so it doesn't seem so linear
|
|
swing = AngleSubtract( destination, *angle );
|
|
scale = fabs( swing );
|
|
if ( scale < swingTolerance * 0.5 ) {
|
|
scale = 0.5;
|
|
} else if ( scale < swingTolerance ) {
|
|
scale = 1.0;
|
|
} else {
|
|
scale = 2.0;
|
|
}
|
|
|
|
// swing towards the destination angle
|
|
if ( swing >= 0 ) {
|
|
move = frametime * scale * speed;
|
|
if ( move >= swing ) {
|
|
move = swing;
|
|
*swinging = qfalse;
|
|
}
|
|
*angle = AngleMod( *angle + move );
|
|
} else if ( swing < 0 ) {
|
|
move = frametime * scale * -speed;
|
|
if ( move <= swing ) {
|
|
move = swing;
|
|
*swinging = qfalse;
|
|
}
|
|
*angle = AngleMod( *angle + move );
|
|
}
|
|
|
|
// clamp to no more than tolerance
|
|
swing = AngleSubtract( destination, *angle );
|
|
if ( swing > clampTolerance ) {
|
|
*angle = AngleMod( destination - (clampTolerance - 1) );
|
|
} else if ( swing < -clampTolerance ) {
|
|
*angle = AngleMod( destination + (clampTolerance - 1) );
|
|
}
|
|
|
|
return swing;
|
|
}
|
|
|
|
//#define BONE_BASED_LEG_ANGLES
|
|
|
|
//I apologize for this function
|
|
qboolean BG_InRoll2( entityState_t *es )
|
|
{
|
|
switch ( (es->legsAnim) )
|
|
{
|
|
case BOTH_GETUP_BROLL_B:
|
|
case BOTH_GETUP_BROLL_F:
|
|
case BOTH_GETUP_BROLL_L:
|
|
case BOTH_GETUP_BROLL_R:
|
|
case BOTH_GETUP_FROLL_B:
|
|
case BOTH_GETUP_FROLL_F:
|
|
case BOTH_GETUP_FROLL_L:
|
|
case BOTH_GETUP_FROLL_R:
|
|
case BOTH_ROLL_F:
|
|
case BOTH_ROLL_B:
|
|
case BOTH_ROLL_R:
|
|
case BOTH_ROLL_L:
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
|
|
extern qboolean BG_SaberLockBreakAnim( int anim ); //bg_panimate.c
|
|
void BG_G2PlayerAngles(void *ghoul2, int motionBolt, entityState_t *cent, int time, vec3_t cent_lerpOrigin,
|
|
vec3_t cent_lerpAngles, vec3_t legs[3], vec3_t legsAngles, qboolean *tYawing,
|
|
qboolean *tPitching, qboolean *lYawing, float *tYawAngle, float *tPitchAngle,
|
|
float *lYawAngle, int frametime, vec3_t turAngles, vec3_t modelScale, int ciLegs,
|
|
int ciTorso, int *corrTime, vec3_t lookAngles, vec3_t lastHeadAngles, int lookTime,
|
|
entityState_t *emplaced, int *crazySmoothFactor)
|
|
{
|
|
int adddir = 0;
|
|
static int dir;
|
|
static int i;
|
|
static int movementOffsets[8] = { 0, 22, 45, -22, 0, 22, -45, -22 };
|
|
float degrees_negative = 0;
|
|
float degrees_positive = 0;
|
|
static float dif;
|
|
static float dest;
|
|
static float speed; //, speed_dif, speed_desired;
|
|
static const float lookSpeed = 1.5f;
|
|
#ifdef BONE_BASED_LEG_ANGLES
|
|
static float legBoneYaw;
|
|
#endif
|
|
static vec3_t eyeAngles;
|
|
static vec3_t neckAngles;
|
|
static vec3_t velocity;
|
|
static vec3_t torsoAngles, headAngles;
|
|
static vec3_t velPos, velAng;
|
|
static vec3_t ulAngles, llAngles, viewAngles, angles, thoracicAngles = {0,0,0};
|
|
static vec3_t headClampMinAngles = {-25,-55,-10}, headClampMaxAngles = {50,50,10};
|
|
|
|
if ( cent->m_iVehicleNum || cent->forceFrame || BG_SaberLockBreakAnim(cent->legsAnim) || BG_SaberLockBreakAnim(cent->torsoAnim) )
|
|
{ //a vehicle or riding a vehicle - in either case we don't need to be in here
|
|
vec3_t forcedAngles;
|
|
|
|
VectorClear(forcedAngles);
|
|
forcedAngles[YAW] = cent_lerpAngles[YAW];
|
|
forcedAngles[ROLL] = cent_lerpAngles[ROLL];
|
|
AnglesToAxis( forcedAngles, legs );
|
|
VectorCopy(forcedAngles, legsAngles);
|
|
|
|
if (cent->number < MAX_CLIENTS)
|
|
{
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "thoracic", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((time+2000) < *corrTime)
|
|
{
|
|
*corrTime = 0;
|
|
}
|
|
|
|
VectorCopy( cent_lerpAngles, headAngles );
|
|
headAngles[YAW] = AngleMod( headAngles[YAW] );
|
|
VectorClear( legsAngles );
|
|
VectorClear( torsoAngles );
|
|
// --------- yaw -------------
|
|
|
|
// allow yaw to drift a bit
|
|
if ((( cent->legsAnim ) != BOTH_STAND1) ||
|
|
( cent->torsoAnim ) != WeaponReadyAnim[cent->weapon] )
|
|
{
|
|
// if not standing still, always point all in the same direction
|
|
//cent->pe.torso.yawing = qtrue; // always center
|
|
*tYawing = qtrue;
|
|
//cent->pe.torso.pitching = qtrue; // always center
|
|
*tPitching = qtrue;
|
|
//cent->pe.legs.yawing = qtrue; // always center
|
|
*lYawing = qtrue;
|
|
}
|
|
|
|
// adjust legs for movement dir
|
|
if ( cent->eFlags & EF_DEAD ) {
|
|
// don't let dead bodies twitch
|
|
dir = 0;
|
|
} else {
|
|
dir = cent->angles2[YAW];
|
|
if ( dir < 0 || dir > 7 ) {
|
|
Com_Error( ERR_DROP, "Bad player movement angle (%i)", dir );
|
|
}
|
|
}
|
|
|
|
torsoAngles[YAW] = headAngles[YAW];
|
|
|
|
//for now, turn torso instantly and let the legs swing to follow
|
|
*tYawAngle = torsoAngles[YAW];
|
|
|
|
// --------- pitch -------------
|
|
|
|
VectorCopy( cent->pos.trDelta, velocity );
|
|
|
|
if (BG_InRoll2(cent))
|
|
{ //don't affect angles based on vel then
|
|
VectorClear(velocity);
|
|
}
|
|
else if (cent->weapon == WP_SABER &&
|
|
BG_SaberInSpecial(cent->saberMove))
|
|
{
|
|
VectorClear(velocity);
|
|
}
|
|
|
|
speed = VectorNormalize( velocity );
|
|
|
|
if (!speed)
|
|
{
|
|
torsoAngles[YAW] = headAngles[YAW];
|
|
}
|
|
|
|
// only show a fraction of the pitch angle in the torso
|
|
if ( headAngles[PITCH] > 180 ) {
|
|
dest = (-360 + headAngles[PITCH]) * 0.75;
|
|
} else {
|
|
dest = headAngles[PITCH] * 0.75;
|
|
}
|
|
|
|
if (cent->m_iVehicleNum)
|
|
{ //swing instantly on vehicles
|
|
*tPitchAngle = dest;
|
|
}
|
|
else
|
|
{
|
|
BG_SwingAngles( dest, 15, 30, 0.1, tPitchAngle, tPitching, frametime );
|
|
}
|
|
torsoAngles[PITCH] = *tPitchAngle;
|
|
|
|
// --------- roll -------------
|
|
|
|
if ( speed ) {
|
|
vec3_t axis[3];
|
|
float side;
|
|
|
|
speed *= 0.05;
|
|
|
|
AnglesToAxis( legsAngles, axis );
|
|
side = speed * DotProduct( velocity, axis[1] );
|
|
legsAngles[ROLL] -= side;
|
|
|
|
side = speed * DotProduct( velocity, axis[0] );
|
|
legsAngles[PITCH] += side;
|
|
}
|
|
|
|
//legsAngles[YAW] = headAngles[YAW] + (movementOffsets[ dir ]*speed_dif);
|
|
|
|
//rww - crazy velocity-based leg angle calculation
|
|
legsAngles[YAW] = headAngles[YAW];
|
|
velPos[0] = cent_lerpOrigin[0] + velocity[0];
|
|
velPos[1] = cent_lerpOrigin[1] + velocity[1];
|
|
velPos[2] = cent_lerpOrigin[2];// + velocity[2];
|
|
|
|
if ( cent->groundEntityNum == ENTITYNUM_NONE ||
|
|
cent->forceFrame ||
|
|
(cent->weapon == WP_EMPLACED_GUN && emplaced) )
|
|
{ //off the ground, no direction-based leg angles (same if in saberlock)
|
|
VectorCopy(cent_lerpOrigin, velPos);
|
|
}
|
|
|
|
VectorSubtract(cent_lerpOrigin, velPos, velAng);
|
|
|
|
if (!VectorCompare(velAng, vec3_origin))
|
|
{
|
|
vectoangles(velAng, velAng);
|
|
|
|
if (velAng[YAW] <= legsAngles[YAW])
|
|
{
|
|
degrees_negative = (legsAngles[YAW] - velAng[YAW]);
|
|
degrees_positive = (360 - legsAngles[YAW]) + velAng[YAW];
|
|
}
|
|
else
|
|
{
|
|
degrees_negative = legsAngles[YAW] + (360 - velAng[YAW]);
|
|
degrees_positive = (velAng[YAW] - legsAngles[YAW]);
|
|
}
|
|
|
|
if ( degrees_negative < degrees_positive )
|
|
{
|
|
dif = degrees_negative;
|
|
adddir = 0;
|
|
}
|
|
else
|
|
{
|
|
dif = degrees_positive;
|
|
adddir = 1;
|
|
}
|
|
|
|
if (dif > 90)
|
|
{
|
|
dif = (180 - dif);
|
|
}
|
|
|
|
if (dif > 60)
|
|
{
|
|
dif = 60;
|
|
}
|
|
|
|
//Slight hack for when playing is running backward
|
|
if (dir == 3 || dir == 5)
|
|
{
|
|
dif = -dif;
|
|
}
|
|
|
|
if (adddir)
|
|
{
|
|
legsAngles[YAW] -= dif;
|
|
}
|
|
else
|
|
{
|
|
legsAngles[YAW] += dif;
|
|
}
|
|
}
|
|
|
|
if (cent->m_iVehicleNum)
|
|
{ //swing instantly on vehicles
|
|
*lYawAngle = legsAngles[YAW];
|
|
}
|
|
else
|
|
{
|
|
BG_SwingAngles( legsAngles[YAW], /*40*/0, 90, 0.65, lYawAngle, lYawing, frametime );
|
|
}
|
|
legsAngles[YAW] = *lYawAngle;
|
|
|
|
/*
|
|
// pain twitch
|
|
CG_AddPainTwitch( cent, torsoAngles );
|
|
*/
|
|
|
|
legsAngles[ROLL] = 0;
|
|
torsoAngles[ROLL] = 0;
|
|
|
|
// VectorCopy(legsAngles, turAngles);
|
|
|
|
// pull the angles back out of the hierarchial chain
|
|
AnglesSubtract( headAngles, torsoAngles, headAngles );
|
|
AnglesSubtract( torsoAngles, legsAngles, torsoAngles );
|
|
|
|
legsAngles[PITCH] = 0;
|
|
|
|
if (cent->heldByClient)
|
|
{ //keep the base angles clear when doing the IK stuff, it doesn't compensate for it.
|
|
//rwwFIXMEFIXME: Store leg angles off and add them to all the fed in angles for G2 functions?
|
|
VectorClear(legsAngles);
|
|
legsAngles[YAW] = cent_lerpAngles[YAW];
|
|
}
|
|
|
|
#ifdef BONE_BASED_LEG_ANGLES
|
|
legBoneYaw = legsAngles[YAW];
|
|
VectorClear(legsAngles);
|
|
legsAngles[YAW] = cent_lerpAngles[YAW];
|
|
#endif
|
|
|
|
VectorCopy(legsAngles, turAngles);
|
|
|
|
AnglesToAxis( legsAngles, legs );
|
|
|
|
VectorCopy( cent_lerpAngles, viewAngles );
|
|
viewAngles[YAW] = viewAngles[ROLL] = 0;
|
|
viewAngles[PITCH] *= 0.5;
|
|
|
|
VectorSet( angles, 0, legsAngles[1], 0 );
|
|
|
|
angles[0] = legsAngles[0];
|
|
if ( angles[0] > 30 )
|
|
{
|
|
angles[0] = 30;
|
|
}
|
|
else if ( angles[0] < -30 )
|
|
{
|
|
angles[0] = -30;
|
|
}
|
|
|
|
if (cent->weapon == WP_EMPLACED_GUN &&
|
|
emplaced)
|
|
{ //if using an emplaced gun, then we want to make sure we're angled to "hold" it right
|
|
vec3_t facingAngles;
|
|
|
|
VectorSubtract(emplaced->pos.trBase, cent_lerpOrigin, facingAngles);
|
|
vectoangles(facingAngles, facingAngles);
|
|
|
|
if (emplaced->weapon == WP_NONE)
|
|
{ //e-web
|
|
VectorCopy(facingAngles, legsAngles);
|
|
AnglesToAxis( legsAngles, legs );
|
|
}
|
|
else
|
|
{ //misc emplaced
|
|
float dif = AngleSubtract(cent_lerpAngles[YAW], facingAngles[YAW]);
|
|
|
|
/*
|
|
if (emplaced->weapon == WP_NONE)
|
|
{ //offset is a little bit different for the e-web
|
|
dif -= 16.0f;
|
|
}
|
|
*/
|
|
|
|
VectorSet(facingAngles, -16.0f, -dif, 0.0f);
|
|
|
|
if (cent->legsAnim == BOTH_STRAFE_LEFT1 || cent->legsAnim == BOTH_STRAFE_RIGHT1)
|
|
{ //try to adjust so it doesn't look wrong
|
|
if (crazySmoothFactor)
|
|
{ //want to smooth a lot during this because it chops around and looks like ass
|
|
*crazySmoothFactor = time + 1000;
|
|
}
|
|
|
|
BG_G2ClientSpineAngles(ghoul2, motionBolt, cent_lerpOrigin, cent_lerpAngles, cent, time,
|
|
viewAngles, ciLegs, ciTorso, angles, thoracicAngles, ulAngles, llAngles, modelScale,
|
|
tPitchAngle, tYawAngle, corrTime);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
|
|
VectorAdd(facingAngles, thoracicAngles, facingAngles);
|
|
|
|
if (cent->legsAnim == BOTH_STRAFE_LEFT1)
|
|
{ //this one needs some further correction
|
|
facingAngles[YAW] -= 32.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//strap_G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
//strap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cranium", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
}
|
|
|
|
VectorScale(facingAngles, 0.6f, facingAngles);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
VectorScale(facingAngles, 0.8f, facingAngles);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
VectorScale(facingAngles, 0.8f, facingAngles);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "thoracic", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
|
|
//Now we want the head angled toward where we are facing
|
|
VectorSet(facingAngles, 0.0f, dif, 0.0f);
|
|
VectorScale(facingAngles, 0.6f, facingAngles);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "cervical", facingAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
|
|
return; //don't have to bother with the rest then
|
|
}
|
|
}
|
|
|
|
BG_G2ClientSpineAngles(ghoul2, motionBolt, cent_lerpOrigin, cent_lerpAngles, cent, time,
|
|
viewAngles, ciLegs, ciTorso, angles, thoracicAngles, ulAngles, llAngles, modelScale,
|
|
tPitchAngle, tYawAngle, corrTime);
|
|
|
|
VectorCopy(cent_lerpAngles, eyeAngles);
|
|
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
lookAngles[i] = AngleNormalize180( lookAngles[i] );
|
|
eyeAngles[i] = AngleNormalize180( eyeAngles[i] );
|
|
}
|
|
AnglesSubtract( lookAngles, eyeAngles, lookAngles );
|
|
|
|
BG_UpdateLookAngles(lookTime, lastHeadAngles, time, lookAngles, lookSpeed, -50.0f, 50.0f, -70.0f, 70.0f, -30.0f, 30.0f);
|
|
|
|
BG_G2ClientNeckAngles(ghoul2, time, lookAngles, headAngles, neckAngles, thoracicAngles, headClampMinAngles, headClampMaxAngles);
|
|
|
|
#ifdef BONE_BASED_LEG_ANGLES
|
|
{
|
|
vec3_t bLAngles;
|
|
VectorClear(bLAngles);
|
|
bLAngles[ROLL] = AngleNormalize180((legBoneYaw - cent_lerpAngles[YAW]));
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "model_root", bLAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
|
|
if (!llAngles[YAW])
|
|
{
|
|
llAngles[YAW] -= bLAngles[ROLL];
|
|
}
|
|
}
|
|
#endif
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "lower_lumbar", llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "upper_lumbar", ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "thoracic", thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
//strap_G2API_SetBoneAngles(ghoul2, 0, "cervical", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
}
|
|
|
|
void BG_G2ATSTAngles(void *ghoul2, int time, vec3_t cent_lerpAngles )
|
|
{// up right fwd
|
|
strap_G2API_SetBoneAngles(ghoul2, 0, "thoracic", cent_lerpAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, 0, 0, time);
|
|
}
|
|
|
|
static qboolean PM_AdjustAnglesForDualJumpAttack( playerState_t *ps, usercmd_t *ucmd )
|
|
{
|
|
//ucmd->angles[PITCH] = ANGLE2SHORT( ps->viewangles[PITCH] ) - ps->delta_angles[PITCH];
|
|
//ucmd->angles[YAW] = ANGLE2SHORT( ps->viewangles[YAW] ) - ps->delta_angles[YAW];
|
|
return qtrue;
|
|
}
|
|
|
|
#ifdef __LCC__
|
|
static void PM_CmdForSaberMoves(usercmd_t *ucmd)
|
|
#else
|
|
static ID_INLINE void PM_CmdForSaberMoves(usercmd_t *ucmd)
|
|
#endif
|
|
{
|
|
//DUAL FORWARD+JUMP+ATTACK
|
|
if ( ( pm->ps->legsAnim == BOTH_JUMPATTACK6
|
|
&& pm->ps->saberMove == LS_JUMPATTACK_DUAL )
|
|
|| ( pm->ps->legsAnim == BOTH_BUTTERFLY_FL1
|
|
&& pm->ps->saberMove == LS_JUMPATTACK_STAFF_LEFT )
|
|
|| ( pm->ps->legsAnim == BOTH_BUTTERFLY_FR1
|
|
&& pm->ps->saberMove == LS_JUMPATTACK_STAFF_RIGHT )
|
|
|| ( pm->ps->legsAnim == BOTH_BUTTERFLY_RIGHT
|
|
&& pm->ps->saberMove == LS_BUTTERFLY_RIGHT )
|
|
|| ( pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT
|
|
&& pm->ps->saberMove == LS_BUTTERFLY_LEFT ) )
|
|
{
|
|
int aLen = PM_AnimLength(0, BOTH_JUMPATTACK6);
|
|
|
|
ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
|
|
|
|
if ( pm->ps->legsAnim == BOTH_JUMPATTACK6 )
|
|
{ //dual stance attack
|
|
if ( pm->ps->legsTimer >= 100 //not at end
|
|
&& (aLen - pm->ps->legsTimer) >= 250 ) //not in beginning
|
|
{ //middle of anim
|
|
//push forward
|
|
ucmd->forwardmove = 127;
|
|
}
|
|
|
|
if ( (pm->ps->legsTimer >= 900 //not at end
|
|
&& aLen - pm->ps->legsTimer >= 950 ) //not in beginning
|
|
|| ( pm->ps->legsTimer >= 1600
|
|
&& aLen - pm->ps->legsTimer >= 400 ) ) //not in beginning
|
|
{ //one of the two jumps
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{ //still on ground?
|
|
if ( pm->ps->groundEntityNum >= MAX_CLIENTS )
|
|
{
|
|
//jump!
|
|
pm->ps->velocity[2] = 250;//400;
|
|
pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
|
|
//pm->ps->pm_flags |= PMF_JUMPING;
|
|
//FIXME: NPCs yell?
|
|
PM_AddEvent(EV_JUMP);
|
|
//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
|
|
}
|
|
}
|
|
else
|
|
{ //FIXME: if this is the second jump, maybe we should just stop the anim?
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ //saberstaff attacks
|
|
int aLen = PM_AnimLength(0, (animNumber_t)pm->ps->legsAnim);
|
|
float lenMin = 1700.0f;
|
|
float lenMax = 1800.0f;
|
|
|
|
if (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT)
|
|
{
|
|
lenMin = 1200.0f;
|
|
lenMax = 1400.0f;
|
|
}
|
|
|
|
//FIXME: don't slide off people/obstacles?
|
|
if ( pm->ps->legsAnim == BOTH_BUTTERFLY_RIGHT
|
|
|| pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT )
|
|
{
|
|
if ( pm->ps->legsTimer > 450 )
|
|
{
|
|
switch ( pm->ps->legsAnim )
|
|
{
|
|
case BOTH_BUTTERFLY_LEFT:
|
|
ucmd->rightmove = -127;
|
|
break;
|
|
case BOTH_BUTTERFLY_RIGHT:
|
|
ucmd->rightmove = 127;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pm->ps->legsTimer >= 100 //not at end
|
|
&& aLen - pm->ps->legsTimer >= 250 )//not in beginning
|
|
{//middle of anim
|
|
//push forward
|
|
ucmd->forwardmove = 127;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->legsTimer >= lenMin && pm->ps->legsTimer < lenMax )
|
|
{//one of the two jumps
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//still on ground?
|
|
//jump!
|
|
if (pm->ps->legsAnim == BOTH_BUTTERFLY_LEFT)
|
|
{
|
|
pm->ps->velocity[2] = 350;
|
|
}
|
|
else
|
|
{
|
|
pm->ps->velocity[2] = 250;
|
|
}
|
|
pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
|
|
//pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
|
|
//FIXME: NPCs yell?
|
|
PM_AddEvent(EV_JUMP);
|
|
//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
|
|
}
|
|
else
|
|
{//FIXME: if this is the second jump, maybe we should just stop the anim?
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
|
|
{//can only turn when your feet hit the ground
|
|
if (PM_AdjustAnglesForDualJumpAttack(pm->ps, ucmd))
|
|
{
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
|
|
}
|
|
}
|
|
//rwwFIXMEFIXME: Bother with bbox resizing like sp?
|
|
}
|
|
//STAFF BACK+JUMP+ATTACK
|
|
else if (pm->ps->saberMove == LS_A_BACKFLIP_ATK &&
|
|
pm->ps->legsAnim == BOTH_JUMPATTACK7)
|
|
{
|
|
int aLen = PM_AnimLength(0, BOTH_JUMPATTACK7);
|
|
|
|
if ( pm->ps->legsTimer > 800 //not at end
|
|
&& aLen - pm->ps->legsTimer >= 400 )//not in beginning
|
|
{//middle of anim
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//still on ground?
|
|
vec3_t yawAngles, backDir;
|
|
|
|
//push backwards some?
|
|
VectorSet( yawAngles, 0, pm->ps->viewangles[YAW]+180, 0 );
|
|
AngleVectors( yawAngles, backDir, 0, 0 );
|
|
VectorScale( backDir, 100, pm->ps->velocity );
|
|
|
|
//jump!
|
|
pm->ps->velocity[2] = 300;
|
|
pm->ps->fd.forceJumpZStart = pm->ps->origin[2]; //so we don't take damage if we land at same height
|
|
//pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
|
|
|
|
//FIXME: NPCs yell?
|
|
PM_AddEvent(EV_JUMP);
|
|
//G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
|
|
ucmd->upmove = 0; //clear any actual jump command
|
|
}
|
|
}
|
|
ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
|
|
}
|
|
//STAFF/DUAL SPIN ATTACK
|
|
else if (pm->ps->saberMove == LS_SPINATTACK ||
|
|
pm->ps->saberMove == LS_SPINATTACK_DUAL)
|
|
{
|
|
ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
|
|
//lock their viewangles during these attacks.
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, ucmd);
|
|
}
|
|
}
|
|
|
|
//constrain him based on the angles of his vehicle and the caps
|
|
void PM_VehicleViewAngles(playerState_t *ps, bgEntity_t *veh, usercmd_t *ucmd)
|
|
{
|
|
Vehicle_t *pVeh = veh->m_pVehicle;
|
|
qboolean setAngles = qtrue;
|
|
vec3_t clampMin;
|
|
vec3_t clampMax;
|
|
int i;
|
|
|
|
if ( veh->m_pVehicle->m_pPilot
|
|
&& veh->m_pVehicle->m_pPilot->s.number == ps->clientNum )
|
|
{//set the pilot's viewangles to the vehicle's viewangles
|
|
#ifdef VEH_CONTROL_SCHEME_4
|
|
if ( 1 )
|
|
#else //VEH_CONTROL_SCHEME_4
|
|
if ( !BG_UnrestrainedPitchRoll( ps, veh->m_pVehicle ) )
|
|
#endif //VEH_CONTROL_SCHEME_4
|
|
{//only if not if doing special free-roll/pitch control
|
|
setAngles = qtrue;
|
|
clampMin[PITCH] = -pVeh->m_pVehicleInfo->lookPitch;
|
|
clampMax[PITCH] = pVeh->m_pVehicleInfo->lookPitch;
|
|
clampMin[YAW] = clampMax[YAW] = 0;
|
|
clampMin[ROLL] = clampMax[ROLL] = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//NOTE: passengers can look around freely, UNLESS they're controlling a turret!
|
|
for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
|
|
{
|
|
if ( veh->m_pVehicle->m_pVehicleInfo->turret[i].passengerNum == ps->generic1 )
|
|
{//this turret is my station
|
|
//nevermind, don't clamp
|
|
return;
|
|
/*
|
|
setAngles = qtrue;
|
|
clampMin[PITCH] = veh->m_pVehicle->m_pVehicleInfo->turret[i].pitchClampUp;
|
|
clampMax[PITCH] = veh->m_pVehicle->m_pVehicleInfo->turret[i].pitchClampDown;
|
|
clampMin[YAW] = veh->m_pVehicle->m_pVehicleInfo->turret[i].yawClampRight;
|
|
clampMax[YAW] = veh->m_pVehicle->m_pVehicleInfo->turret[i].yawClampLeft;
|
|
clampMin[ROLL] = clampMax[ROLL] = 0;
|
|
break;
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
if ( setAngles )
|
|
{
|
|
for ( i = 0; i < 3; i++ )
|
|
{//clamp viewangles
|
|
if ( clampMin[i] == -1 || clampMax[i] == -1 )
|
|
{//no clamp
|
|
}
|
|
else if ( !clampMin[i] && !clampMax[i] )
|
|
{//no allowance
|
|
//ps->viewangles[i] = veh->playerState->viewangles[i];
|
|
}
|
|
else
|
|
{//allowance
|
|
if (ps->viewangles[i] > clampMax[i])
|
|
{
|
|
ps->viewangles[i] = clampMax[i];
|
|
}
|
|
else if (ps->viewangles[i] < clampMin[i])
|
|
{
|
|
ps->viewangles[i] = clampMin[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
|
|
}
|
|
}
|
|
|
|
/*
|
|
//constrain him based on the angles of his vehicle and the caps
|
|
void PM_VehicleViewAngles(playerState_t *ps, bgEntity_t *veh, usercmd_t *ucmd)
|
|
{
|
|
Vehicle_t *pVeh = veh->m_pVehicle;
|
|
|
|
//now set the viewangles to the vehicle's directly
|
|
ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
|
|
|
|
//constrain the viewangles pitch based on the vehicle properties
|
|
if ( !pVeh->m_pVehicleInfo->lookPitch )
|
|
{//not allowed to look up & down! ....???
|
|
ps->viewangles[PITCH] = veh->playerState->viewangles[PITCH];
|
|
}
|
|
else
|
|
{//clamp
|
|
if (ps->viewangles[PITCH] > pVeh->m_pVehicleInfo->lookPitch)
|
|
{
|
|
ps->viewangles[PITCH] = pVeh->m_pVehicleInfo->lookPitch;
|
|
}
|
|
else if (ps->viewangles[PITCH] < -pVeh->m_pVehicleInfo->lookPitch)
|
|
{
|
|
ps->viewangles[PITCH] = -pVeh->m_pVehicleInfo->lookPitch;
|
|
}
|
|
}
|
|
|
|
PM_SetPMViewAngle(ps, ps->viewangles, ucmd);
|
|
}
|
|
*/
|
|
//see if a weapon is ok to use on a vehicle
|
|
qboolean PM_WeaponOkOnVehicle( int weapon )
|
|
{
|
|
//FIXME: check g_vehicleInfo for our vehicle?
|
|
switch ( weapon )
|
|
{
|
|
//case WP_NONE:
|
|
case WP_MELEE:
|
|
case WP_SABER:
|
|
case WP_BLASTER:
|
|
//case WP_THERMAL:
|
|
return qtrue;
|
|
break;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
//do we have a weapon that's ok for using on the vehicle?
|
|
int PM_GetOkWeaponForVehicle(void)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < WP_NUM_WEAPONS)
|
|
{
|
|
if ((pm->ps->stats[STAT_WEAPONS] & (1 << i)) &&
|
|
PM_WeaponOkOnVehicle(i))
|
|
{ //this one's good
|
|
return i;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
//oh dear!
|
|
//assert(!"No valid veh weaps");
|
|
return -1;
|
|
}
|
|
|
|
//force the vehicle to turn and travel to its forced destination point
|
|
void PM_VehForcedTurning(bgEntity_t *veh)
|
|
{
|
|
bgEntity_t *dst = PM_BGEntForNum(veh->playerState->vehTurnaroundIndex);
|
|
float pitchD, yawD;
|
|
vec3_t dir;
|
|
|
|
if (!veh || !veh->m_pVehicle)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!dst)
|
|
{ //can't find dest ent?
|
|
return;
|
|
}
|
|
|
|
pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
|
|
pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
|
|
pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
|
|
|
|
VectorSubtract(dst->s.origin, veh->playerState->origin, dir);
|
|
vectoangles(dir, dir);
|
|
|
|
yawD = AngleSubtract(pm->ps->viewangles[YAW], dir[YAW]);
|
|
pitchD = AngleSubtract(pm->ps->viewangles[PITCH], dir[PITCH]);
|
|
|
|
yawD *= 0.6f*pml.frametime;
|
|
pitchD *= 0.6f*pml.frametime;
|
|
|
|
#ifdef VEH_CONTROL_SCHEME_4
|
|
veh->playerState->viewangles[YAW] = AngleSubtract(veh->playerState->viewangles[YAW], yawD);
|
|
veh->playerState->viewangles[PITCH] = AngleSubtract(veh->playerState->viewangles[PITCH], pitchD);
|
|
pm->ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
|
|
pm->ps->viewangles[PITCH] = 0;
|
|
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
PM_SetPMViewAngle(veh->playerState, veh->playerState->viewangles, &pm->cmd);
|
|
VectorClear( veh->m_pVehicle->m_vPrevRiderViewAngles );
|
|
veh->m_pVehicle->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(pm->ps->viewangles[YAW]);
|
|
|
|
#else //VEH_CONTROL_SCHEME_4
|
|
|
|
pm->ps->viewangles[YAW] = AngleSubtract(pm->ps->viewangles[YAW], yawD);
|
|
pm->ps->viewangles[PITCH] = AngleSubtract(pm->ps->viewangles[PITCH], pitchD);
|
|
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
#endif //VEH_CONTROL_SCHEME_4
|
|
}
|
|
|
|
#ifdef VEH_CONTROL_SCHEME_4
|
|
void PM_VehFaceHyperspacePoint(bgEntity_t *veh)
|
|
{
|
|
|
|
if (!veh || !veh->m_pVehicle)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
float timeFrac = ((float)(pm->cmd.serverTime-veh->playerState->hyperSpaceTime))/HYPERSPACE_TIME;
|
|
float turnRate, aDelta;
|
|
int i, matchedAxes = 0;
|
|
|
|
pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
|
|
pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
|
|
pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
|
|
|
|
turnRate = (90.0f*pml.frametime);
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]);
|
|
if ( fabs( aDelta ) < turnRate )
|
|
{//all is good
|
|
veh->playerState->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
|
|
matchedAxes++;
|
|
}
|
|
else
|
|
{
|
|
aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->playerState->viewangles[i]);
|
|
if ( fabs( aDelta ) < turnRate )
|
|
{
|
|
veh->playerState->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
|
|
}
|
|
else if ( aDelta > 0 )
|
|
{
|
|
if ( i == YAW )
|
|
{
|
|
veh->playerState->viewangles[i] = AngleNormalize360( veh->playerState->viewangles[i]+turnRate );
|
|
}
|
|
else
|
|
{
|
|
veh->playerState->viewangles[i] = AngleNormalize180( veh->playerState->viewangles[i]+turnRate );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( i == YAW )
|
|
{
|
|
veh->playerState->viewangles[i] = AngleNormalize360( veh->playerState->viewangles[i]-turnRate );
|
|
}
|
|
else
|
|
{
|
|
veh->playerState->viewangles[i] = AngleNormalize180( veh->playerState->viewangles[i]-turnRate );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pm->ps->viewangles[YAW] = veh->playerState->viewangles[YAW];
|
|
pm->ps->viewangles[PITCH] = 0.0f;
|
|
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
PM_SetPMViewAngle(veh->playerState, veh->playerState->viewangles, &pm->cmd);
|
|
VectorClear( veh->m_pVehicle->m_vPrevRiderViewAngles );
|
|
veh->m_pVehicle->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(pm->ps->viewangles[YAW]);
|
|
|
|
if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
|
|
{//haven't gone through yet
|
|
if ( matchedAxes < 3 )
|
|
{//not facing the right dir yet
|
|
//keep hyperspace time up to date
|
|
veh->playerState->hyperSpaceTime += pml.msec;
|
|
}
|
|
else if ( !(veh->playerState->eFlags2&EF2_HYPERSPACE))
|
|
{//flag us as ready to hyperspace!
|
|
veh->playerState->eFlags2 |= EF2_HYPERSPACE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#else //VEH_CONTROL_SCHEME_4
|
|
|
|
void PM_VehFaceHyperspacePoint(bgEntity_t *veh)
|
|
{
|
|
|
|
if (!veh || !veh->m_pVehicle)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
float timeFrac = ((float)(pm->cmd.serverTime-veh->playerState->hyperSpaceTime))/HYPERSPACE_TIME;
|
|
float turnRate, aDelta;
|
|
int i, matchedAxes = 0;
|
|
|
|
pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127;
|
|
pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0;
|
|
pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0;
|
|
|
|
turnRate = (90.0f*pml.frametime);
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]);
|
|
if ( fabs( aDelta ) < turnRate )
|
|
{//all is good
|
|
pm->ps->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
|
|
matchedAxes++;
|
|
}
|
|
else
|
|
{
|
|
aDelta = AngleSubtract(veh->playerState->hyperSpaceAngles[i], pm->ps->viewangles[i]);
|
|
if ( fabs( aDelta ) < turnRate )
|
|
{
|
|
pm->ps->viewangles[i] = veh->playerState->hyperSpaceAngles[i];
|
|
}
|
|
else if ( aDelta > 0 )
|
|
{
|
|
if ( i == YAW )
|
|
{
|
|
pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]+turnRate );
|
|
}
|
|
else
|
|
{
|
|
pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]+turnRate );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( i == YAW )
|
|
{
|
|
pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]-turnRate );
|
|
}
|
|
else
|
|
{
|
|
pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]-turnRate );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
|
|
if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
|
|
{//haven't gone through yet
|
|
if ( matchedAxes < 3 )
|
|
{//not facing the right dir yet
|
|
//keep hyperspace time up to date
|
|
veh->playerState->hyperSpaceTime += pml.msec;
|
|
}
|
|
else if ( !(veh->playerState->eFlags2&EF2_HYPERSPACE))
|
|
{//flag us as ready to hyperspace!
|
|
veh->playerState->eFlags2 |= EF2_HYPERSPACE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //VEH_CONTROL_SCHEME_4
|
|
|
|
void BG_VehicleAdjustBBoxForOrientation( Vehicle_t *veh, vec3_t origin, vec3_t mins, vec3_t maxs,
|
|
int clientNum, int tracemask,
|
|
void (*localTrace)(trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask))
|
|
{
|
|
if ( !veh
|
|
|| !veh->m_pVehicleInfo->length
|
|
|| !veh->m_pVehicleInfo->width
|
|
|| !veh->m_pVehicleInfo->height )
|
|
//|| veh->m_LandTrace.fraction < 1.0f )
|
|
{
|
|
return;
|
|
}
|
|
else if ( veh->m_pVehicleInfo->type != VH_FIGHTER
|
|
//&& veh->m_pVehicleInfo->type != VH_SPEEDER
|
|
&& veh->m_pVehicleInfo->type != VH_FLIER )
|
|
{//only those types of vehicles have dynamic bboxes, the rest just use a static bbox
|
|
VectorSet( maxs, veh->m_pVehicleInfo->width/2.0f, veh->m_pVehicleInfo->width/2.0f, veh->m_pVehicleInfo->height+DEFAULT_MINS_2 );
|
|
VectorSet( mins, veh->m_pVehicleInfo->width/-2.0f, veh->m_pVehicleInfo->width/-2.0f, DEFAULT_MINS_2 );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
vec3_t axis[3], point[8];
|
|
vec3_t newMins, newMaxs;
|
|
int curAxis = 0, i;
|
|
trace_t trace;
|
|
|
|
AnglesToAxis( veh->m_vOrientation, axis );
|
|
VectorMA( origin, veh->m_pVehicleInfo->length/2.0f, axis[0], point[0] );
|
|
VectorMA( origin, -veh->m_pVehicleInfo->length/2.0f, axis[0], point[1] );
|
|
//extrapolate each side up and down
|
|
VectorMA( point[0], veh->m_pVehicleInfo->height/2.0f, axis[2], point[0] );
|
|
VectorMA( point[0], -veh->m_pVehicleInfo->height, axis[2], point[2] );
|
|
VectorMA( point[1], veh->m_pVehicleInfo->height/2.0f, axis[2], point[1] );
|
|
VectorMA( point[1], -veh->m_pVehicleInfo->height, axis[2], point[3] );
|
|
|
|
VectorMA( origin, veh->m_pVehicleInfo->width/2.0f, axis[1], point[4] );
|
|
VectorMA( origin, -veh->m_pVehicleInfo->width/2.0f, axis[1], point[5] );
|
|
//extrapolate each side up and down
|
|
VectorMA( point[4], veh->m_pVehicleInfo->height/2.0f, axis[2], point[4] );
|
|
VectorMA( point[4], -veh->m_pVehicleInfo->height, axis[2], point[6] );
|
|
VectorMA( point[5], veh->m_pVehicleInfo->height/2.0f, axis[2], point[5] );
|
|
VectorMA( point[5], -veh->m_pVehicleInfo->height, axis[2], point[7] );
|
|
/*
|
|
VectorMA( origin, veh->m_pVehicleInfo->height/2.0f, axis[2], point[4] );
|
|
VectorMA( origin, -veh->m_pVehicleInfo->height/2.0f, axis[2], point[5] );
|
|
*/
|
|
//Now inflate a bbox around these points
|
|
VectorCopy( origin, newMins );
|
|
VectorCopy( origin, newMaxs );
|
|
for ( curAxis = 0; curAxis < 3; curAxis++ )
|
|
{
|
|
for ( i = 0; i < 8; i++ )
|
|
{
|
|
if ( point[i][curAxis] > newMaxs[curAxis] )
|
|
{
|
|
newMaxs[curAxis] = point[i][curAxis];
|
|
}
|
|
else if ( point[i][curAxis] < newMins[curAxis] )
|
|
{
|
|
newMins[curAxis] = point[i][curAxis];
|
|
}
|
|
}
|
|
}
|
|
VectorSubtract( newMins, origin, newMins );
|
|
VectorSubtract( newMaxs, origin, newMaxs );
|
|
//now see if that's a valid way to be
|
|
if (localTrace)
|
|
{
|
|
localTrace( &trace, origin, newMins, newMaxs, origin, clientNum, tracemask );
|
|
}
|
|
else
|
|
{ //don't care about solid stuff then
|
|
trace.startsolid = trace.allsolid = 0;
|
|
}
|
|
if ( !trace.startsolid && !trace.allsolid )
|
|
{//let's use it!
|
|
VectorCopy( newMins, mins );
|
|
VectorCopy( newMaxs, maxs );
|
|
}
|
|
//else: just use the last one, I guess...?
|
|
//FIXME: make it as close as possible? Or actually prevent the change in m_vOrientation? Or push away from anything we hit?
|
|
}
|
|
}
|
|
/*
|
|
================
|
|
PmoveSingle
|
|
|
|
================
|
|
*/
|
|
extern void trap_SnapVector( float *v );
|
|
extern int BG_EmplacedView(vec3_t baseAngles, vec3_t angles, float *newYaw, float constraint);
|
|
extern qboolean BG_FighterUpdate(Vehicle_t *pVeh, const usercmd_t *pUcmd, vec3_t trMins, vec3_t trMaxs, float gravity,
|
|
void (*traceFunc)( trace_t *results, const vec3_t start, const vec3_t lmins, const vec3_t lmaxs, const vec3_t end, int passEntityNum, int contentMask )); //FighterNPC.c
|
|
|
|
#define JETPACK_HOVER_HEIGHT 64
|
|
|
|
//#define _TESTING_VEH_PREDICTION
|
|
|
|
void PM_MoveForKata(usercmd_t *ucmd)
|
|
{
|
|
if ( pm->ps->legsAnim == BOTH_A7_SOULCAL
|
|
&& pm->ps->saberMove == LS_STAFF_SOULCAL )
|
|
{//forward spinning staff attack
|
|
ucmd->upmove = 0;
|
|
|
|
if ( PM_CanRollFromSoulCal( pm->ps ) )
|
|
{
|
|
ucmd->upmove = -127;
|
|
ucmd->rightmove = 0;
|
|
if (ucmd->forwardmove < 0)
|
|
{
|
|
ucmd->forwardmove = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ucmd->rightmove = 0;
|
|
//FIXME: don't slide off people/obstacles?
|
|
if ( pm->ps->legsTimer >= 2750 )
|
|
{//not at end
|
|
//push forward
|
|
ucmd->forwardmove = 64;
|
|
}
|
|
else
|
|
{
|
|
ucmd->forwardmove = 0;
|
|
}
|
|
}
|
|
if ( pm->ps->legsTimer >= 2650
|
|
&& pm->ps->legsTimer < 2850 )
|
|
{//the jump
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//still on ground?
|
|
//jump!
|
|
pm->ps->velocity[2] = 250;
|
|
pm->ps->fd.forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height
|
|
// pm->ps->pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
|
|
//FIXME: NPCs yell?
|
|
PM_AddEvent(EV_JUMP);
|
|
}
|
|
}
|
|
}
|
|
else if (pm->ps->legsAnim == BOTH_A2_SPECIAL)
|
|
{ //medium kata
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
if (pm->ps->legsTimer < 2700 && pm->ps->legsTimer > 2300)
|
|
{
|
|
pm->cmd.forwardmove = 127;
|
|
}
|
|
else if (pm->ps->legsTimer < 900 && pm->ps->legsTimer > 500)
|
|
{
|
|
pm->cmd.forwardmove = 127;
|
|
}
|
|
else
|
|
{
|
|
pm->cmd.forwardmove = 0;
|
|
}
|
|
}
|
|
else if (pm->ps->legsAnim == BOTH_A3_SPECIAL)
|
|
{ //strong kata
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
if (pm->ps->legsTimer < 1700 && pm->ps->legsTimer > 1000)
|
|
{
|
|
pm->cmd.forwardmove = 127;
|
|
}
|
|
else
|
|
{
|
|
pm->cmd.forwardmove = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
}
|
|
|
|
void PmoveSingle (pmove_t *pmove) {
|
|
qboolean stiffenedUp = qfalse;
|
|
float gDist = 0;
|
|
qboolean noAnimate = qfalse;
|
|
int savedGravity = 0;
|
|
|
|
pm = pmove;
|
|
|
|
if (pm->ps->emplacedIndex)
|
|
{
|
|
if (pm->cmd.buttons & BUTTON_ALT_ATTACK)
|
|
{ //hackerrific.
|
|
pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
|
|
pm->cmd.buttons |= BUTTON_ATTACK;
|
|
}
|
|
}
|
|
|
|
//set up these "global" bg ents
|
|
pm_entSelf = PM_BGEntForNum(pm->ps->clientNum);
|
|
if (pm->ps->m_iVehicleNum)
|
|
{
|
|
if (pm->ps->clientNum < MAX_CLIENTS)
|
|
{ //player riding vehicle
|
|
pm_entVeh = PM_BGEntForNum(pm->ps->m_iVehicleNum);
|
|
}
|
|
else
|
|
{ //vehicle with player pilot
|
|
pm_entVeh = PM_BGEntForNum(pm->ps->m_iVehicleNum-1);
|
|
}
|
|
}
|
|
else
|
|
{ //no vehicle ent
|
|
pm_entVeh = NULL;
|
|
}
|
|
|
|
gPMDoSlowFall = PM_DoSlowFall();
|
|
|
|
// this counter lets us debug movement problems with a journal
|
|
// by setting a conditional breakpoint fot the previous frame
|
|
c_pmove++;
|
|
|
|
// clear results
|
|
pm->numtouch = 0;
|
|
pm->watertype = 0;
|
|
pm->waterlevel = 0;
|
|
|
|
if (PM_IsRocketTrooper())
|
|
{ //kind of nasty, don't let them crouch or anything if nonhumanoid (probably a rockettrooper)
|
|
if (pm->cmd.upmove < 0)
|
|
{
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
}
|
|
|
|
if (pm->ps->pm_type == PM_FLOAT)
|
|
{ //You get no control over where you go in grip movement
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if (pm->ps->eFlags & EF_DISINTEGRATION)
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if ( BG_SaberLockBreakAnim( pm->ps->legsAnim )
|
|
|| BG_SaberLockBreakAnim( pm->ps->torsoAnim )
|
|
|| pm->ps->saberLockTime >= pm->cmd.serverTime )
|
|
{//can't move or turn
|
|
stiffenedUp = qtrue;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
else if ( pm->ps->saberMove == LS_A_BACK || pm->ps->saberMove == LS_A_BACK_CR ||
|
|
pm->ps->saberMove == LS_A_BACKSTAB || pm->ps->saberMove == LS_A_FLIP_STAB ||
|
|
pm->ps->saberMove == LS_A_FLIP_SLASH || pm->ps->saberMove == LS_A_JUMP_T__B_ ||
|
|
pm->ps->saberMove == LS_DUAL_LR || pm->ps->saberMove == LS_DUAL_FB)
|
|
{
|
|
if (pm->ps->legsAnim == BOTH_JUMPFLIPSTABDOWN ||
|
|
pm->ps->legsAnim == BOTH_JUMPFLIPSLASHDOWN1)
|
|
{ //flipover medium stance attack
|
|
if (pm->ps->legsTimer < 1600 && pm->ps->legsTimer > 900)
|
|
{
|
|
pm->ps->viewangles[YAW] += pml.frametime*240.0f;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
}
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if ((pm->ps->legsAnim) == (BOTH_A2_STABBACK1) ||
|
|
(pm->ps->legsAnim) == (BOTH_ATTACK_BACK) ||
|
|
(pm->ps->legsAnim) == (BOTH_CROUCHATTACKBACK1) ||
|
|
(pm->ps->legsAnim) == (BOTH_FORCELEAP2_T__B_) ||
|
|
(pm->ps->legsAnim) == (BOTH_JUMPFLIPSTABDOWN) ||
|
|
(pm->ps->legsAnim) == (BOTH_JUMPFLIPSLASHDOWN1))
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if (pm->ps->legsAnim == BOTH_ROLL_STAB)
|
|
{
|
|
stiffenedUp = qtrue;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
else if (pm->ps->heldByClient)
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if (BG_KickMove(pm->ps->saberMove) || BG_KickingAnim(pm->ps->legsAnim))
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if (BG_InGrappleMove(pm->ps->torsoAnim))
|
|
{
|
|
stiffenedUp = qtrue;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
else if ( pm->ps->saberMove == LS_STABDOWN_DUAL ||
|
|
pm->ps->saberMove == LS_STABDOWN_STAFF ||
|
|
pm->ps->saberMove == LS_STABDOWN)
|
|
{//FIXME: need to only move forward until we bump into our target...?
|
|
if (pm->ps->legsTimer < 800)
|
|
{ //freeze movement near end of anim
|
|
stiffenedUp = qtrue;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
else
|
|
{ //force forward til then
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
pm->cmd.forwardmove = 64;
|
|
}
|
|
}
|
|
else if (pm->ps->saberMove == LS_PULL_ATTACK_STAB ||
|
|
pm->ps->saberMove == LS_PULL_ATTACK_SWING)
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
else if (BG_SaberInKata(pm->ps->saberMove) ||
|
|
BG_InKataAnim(pm->ps->torsoAnim) ||
|
|
BG_InKataAnim(pm->ps->legsAnim))
|
|
{
|
|
PM_MoveForKata(&pm->cmd);
|
|
}
|
|
else if ( BG_FullBodyTauntAnim( pm->ps->legsAnim )
|
|
&& BG_FullBodyTauntAnim( pm->ps->torsoAnim ) )
|
|
{
|
|
if ( (pm->cmd.buttons&BUTTON_ATTACK)
|
|
|| (pm->cmd.buttons&BUTTON_ALT_ATTACK)
|
|
|| (pm->cmd.buttons&BUTTON_FORCEPOWER)
|
|
|| (pm->cmd.buttons&BUTTON_FORCEGRIP)
|
|
|| (pm->cmd.buttons&BUTTON_FORCE_LIGHTNING)
|
|
|| (pm->cmd.buttons&BUTTON_FORCE_DRAIN)
|
|
|| pm->cmd.upmove )
|
|
{//stop the anim
|
|
if ( pm->ps->legsAnim == BOTH_MEDITATE
|
|
&& pm->ps->torsoAnim == BOTH_MEDITATE )
|
|
{
|
|
PM_SetAnim( SETANIM_BOTH, BOTH_MEDITATE_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
|
|
}
|
|
else
|
|
{
|
|
pm->ps->legsTimer = pm->ps->torsoTimer = 0;
|
|
}
|
|
if ( pm->ps->forceHandExtend == HANDEXTEND_TAUNT )
|
|
{
|
|
pm->ps->forceHandExtend = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pm->ps->legsAnim == BOTH_MEDITATE )
|
|
{
|
|
if ( pm->ps->legsTimer < 100 )
|
|
{
|
|
pm->ps->legsTimer = 100;
|
|
}
|
|
}
|
|
if ( pm->ps->torsoAnim == BOTH_MEDITATE )
|
|
{
|
|
if ( pm->ps->torsoTimer < 100 )
|
|
{
|
|
pm->ps->legsTimer = 100;
|
|
}
|
|
pm->ps->forceHandExtend = HANDEXTEND_TAUNT;
|
|
pm->ps->forceHandExtendTime = pm->cmd.serverTime + 100;
|
|
}
|
|
if ( pm->ps->legsTimer > 0 || pm->ps->torsoTimer > 0 )
|
|
{
|
|
stiffenedUp = qtrue;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.buttons = 0;
|
|
}
|
|
}
|
|
}
|
|
else if ( pm->ps->legsAnim == BOTH_MEDITATE_END
|
|
&& pm->ps->legsTimer > 0 )
|
|
{
|
|
stiffenedUp = qtrue;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.buttons = 0;
|
|
}
|
|
else if (pm->ps->legsAnim == BOTH_FORCELAND1 ||
|
|
pm->ps->legsAnim == BOTH_FORCELANDBACK1 ||
|
|
pm->ps->legsAnim == BOTH_FORCELANDRIGHT1 ||
|
|
pm->ps->legsAnim == BOTH_FORCELANDLEFT1)
|
|
{ //can't move while in a force land
|
|
stiffenedUp = qtrue;
|
|
}
|
|
|
|
if ( pm->ps->saberMove == LS_A_LUNGE )
|
|
{//can't move during lunge
|
|
pm->cmd.rightmove = pm->cmd.upmove = 0;
|
|
if ( pm->ps->legsTimer > 500 )
|
|
{
|
|
pm->cmd.forwardmove = 127;
|
|
}
|
|
else
|
|
{
|
|
pm->cmd.forwardmove = 0;
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->saberMove == LS_A_JUMP_T__B_ )
|
|
{//can't move during leap
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//hit the ground
|
|
pm->cmd.forwardmove = 0;
|
|
}
|
|
pm->cmd.rightmove = pm->cmd.upmove = 0;
|
|
}
|
|
|
|
#if 0
|
|
if ((pm->ps->legsAnim) == BOTH_KISSER1LOOP ||
|
|
(pm->ps->legsAnim) == BOTH_KISSEE1LOOP)
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
#endif
|
|
|
|
if (pm->ps->emplacedIndex)
|
|
{
|
|
if (pm->cmd.forwardmove < 0 || PM_GroundDistance() > 32.0f)
|
|
{
|
|
pm->ps->emplacedIndex = 0;
|
|
pm->ps->saberHolstered = 0;
|
|
}
|
|
else
|
|
{
|
|
stiffenedUp = qtrue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_CHARGING_ALT)
|
|
{ //not allowed to move while charging the disruptor
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.rightmove = 0;
|
|
if (pm->cmd.upmove > 0)
|
|
{
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
}
|
|
*/
|
|
|
|
if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->weaponstate == WEAPON_CHARGING_ALT)
|
|
{ //not allowed to move while charging the disruptor
|
|
if (pm->cmd.forwardmove ||
|
|
pm->cmd.rightmove ||
|
|
pm->cmd.upmove > 0)
|
|
{ //get out
|
|
pm->ps->weaponstate = WEAPON_READY;
|
|
pm->ps->weaponTime = 1000;
|
|
PM_AddEventWithParm(EV_WEAPON_CHARGE, WP_DISRUPTOR); //cut the weapon charge sound
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
}
|
|
else if (pm->ps->weapon == WP_DISRUPTOR && pm->ps->zoomMode == 1)
|
|
{ //can't jump
|
|
if (pm->cmd.upmove > 0)
|
|
{
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
}
|
|
|
|
if (stiffenedUp)
|
|
{
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
|
|
if (pm->ps->fd.forceGripCripple)
|
|
{ //don't let attack or alt attack if being gripped I guess
|
|
pm->cmd.buttons &= ~BUTTON_ATTACK;
|
|
pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
|
|
}
|
|
|
|
if ( BG_InRoll( pm->ps, pm->ps->legsAnim ) )
|
|
{ //can't roll unless you're able to move normally
|
|
BG_CmdForRoll( pm->ps, pm->ps->legsAnim, &pm->cmd );
|
|
}
|
|
|
|
PM_CmdForSaberMoves(&pm->cmd);
|
|
|
|
BG_AdjustClientSpeed(pm->ps, &pm->cmd, pm->cmd.serverTime);
|
|
|
|
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
|
|
pm->tracemask &= ~CONTENTS_BODY; // corpses can fly through bodies
|
|
}
|
|
|
|
// make sure walking button is clear if they are running, to avoid
|
|
// proxy no-footsteps cheats
|
|
if ( abs( pm->cmd.forwardmove ) > 64 || abs( pm->cmd.rightmove ) > 64 ) {
|
|
pm->cmd.buttons &= ~BUTTON_WALKING;
|
|
}
|
|
|
|
// set the talk balloon flag
|
|
if ( pm->cmd.buttons & BUTTON_TALK ) {
|
|
pm->ps->eFlags |= EF_TALK;
|
|
} else {
|
|
pm->ps->eFlags &= ~EF_TALK;
|
|
}
|
|
|
|
pm_cancelOutZoom = qfalse;
|
|
if (pm->ps->weapon == WP_DISRUPTOR &&
|
|
pm->ps->zoomMode == 1)
|
|
{
|
|
if ((pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
|
|
!(pm->cmd.buttons & BUTTON_ATTACK) &&
|
|
pm->ps->zoomLocked)
|
|
{
|
|
pm_cancelOutZoom = qtrue;
|
|
}
|
|
}
|
|
// In certain situations, we may want to control which attack buttons are pressed and what kind of functionality
|
|
// is attached to them
|
|
PM_AdjustAttackStates( pm );
|
|
|
|
// 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;
|
|
}
|
|
|
|
/*
|
|
if (pm->ps->clientNum >= MAX_CLIENTS)
|
|
{
|
|
#ifdef QAGAME
|
|
Com_Printf( "^1 SERVER N%i msec %d\n", pm->ps->clientNum, pml.msec );
|
|
#else
|
|
Com_Printf( "^2 CLIENT N%i msec %d\n", pm->ps->clientNum, pml.msec );
|
|
#endif
|
|
}
|
|
*/
|
|
|
|
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;
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_VEHICLE)
|
|
{ //we are a vehicle
|
|
bgEntity_t *veh = pm_entSelf;
|
|
assert( veh && veh->m_pVehicle);
|
|
if ( veh && veh->m_pVehicle )
|
|
{
|
|
veh->m_pVehicle->m_fTimeModifier = pml.frametime*60.0f;
|
|
}
|
|
}
|
|
else if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&&pm->ps->m_iVehicleNum)
|
|
{
|
|
bgEntity_t *veh = pm_entVeh;
|
|
|
|
if (veh && veh->playerState &&
|
|
(pm->cmd.serverTime-veh->playerState->hyperSpaceTime) < HYPERSPACE_TIME)
|
|
{ //going into hyperspace, turn to face the right angles
|
|
PM_VehFaceHyperspacePoint(veh);
|
|
}
|
|
else if (veh && veh->playerState &&
|
|
veh->playerState->vehTurnaroundIndex &&
|
|
veh->playerState->vehTurnaroundTime > pm->cmd.serverTime)
|
|
{ //riding this vehicle, turn my view too
|
|
PM_VehForcedTurning(veh);
|
|
}
|
|
}
|
|
|
|
if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_ALT &&
|
|
pm->ps->legsTimer > 0 )
|
|
{
|
|
vec3_t vFwd, fwdAng;
|
|
VectorSet(fwdAng, 0.0f, pm->ps->viewangles[YAW], 0.0f);
|
|
|
|
AngleVectors( fwdAng, vFwd, NULL, NULL );
|
|
if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )
|
|
{
|
|
float savZ = pm->ps->velocity[2];
|
|
VectorScale( vFwd, 100, pm->ps->velocity );
|
|
pm->ps->velocity[2] = savZ;
|
|
}
|
|
pm->cmd.forwardmove = pm->cmd.rightmove = pm->cmd.upmove = 0;
|
|
PM_AdjustAnglesForWallRunUpFlipAlt( &pm->cmd );
|
|
}
|
|
|
|
// PM_AdjustAngleForWallRun(pm->ps, &pm->cmd, qtrue);
|
|
// PM_AdjustAnglesForStabDown( pm->ps, &pm->cmd );
|
|
PM_AdjustAngleForWallJump( pm->ps, &pm->cmd, qtrue );
|
|
PM_AdjustAngleForWallRunUp( pm->ps, &pm->cmd, qtrue );
|
|
PM_AdjustAngleForWallRun( pm->ps, &pm->cmd, qtrue );
|
|
|
|
if (pm->ps->saberMove == LS_A_JUMP_T__B_ || pm->ps->saberMove == LS_A_LUNGE ||
|
|
pm->ps->saberMove == LS_A_BACK_CR || pm->ps->saberMove == LS_A_BACK ||
|
|
pm->ps->saberMove == LS_A_BACKSTAB)
|
|
{
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
|
|
#if 0
|
|
if ((pm->ps->legsAnim) == BOTH_KISSER1LOOP ||
|
|
(pm->ps->legsAnim) == BOTH_KISSEE1LOOP)
|
|
{
|
|
pm->ps->viewangles[PITCH] = 0;
|
|
PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd);
|
|
}
|
|
#endif
|
|
|
|
PM_SetSpecialMoveValues();
|
|
|
|
// update the viewangles
|
|
PM_UpdateViewAngles( pm->ps, &pm->cmd );
|
|
|
|
AngleVectors (pm->ps->viewangles, pml.forward, pml.right, pml.up);
|
|
|
|
if ( pm->cmd.upmove < 10 && !(pm->ps->pm_flags & PMF_STUCK_TO_WALL)) {
|
|
// not holding jump
|
|
pm->ps->pm_flags &= ~PMF_JUMP_HELD;
|
|
}
|
|
|
|
// decide if backpedaling animations should be used
|
|
if ( pm->cmd.forwardmove < 0 ) {
|
|
pm->ps->pm_flags |= PMF_BACKWARDS_RUN;
|
|
} else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) {
|
|
pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN;
|
|
}
|
|
|
|
if ( pm->ps->pm_type >= PM_DEAD ) {
|
|
pm->cmd.forwardmove = 0;
|
|
pm->cmd.rightmove = 0;
|
|
pm->cmd.upmove = 0;
|
|
}
|
|
|
|
/*
|
|
if (pm->ps->fd.saberAnimLevel == SS_STAFF &&
|
|
(pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
|
|
pm->cmd.upmove > 0)
|
|
{ //this is how you do kick-for-condition
|
|
pm->cmd.upmove = 0;
|
|
pm->ps->pm_flags |= PMF_JUMP_HELD;
|
|
}
|
|
*/
|
|
|
|
if (pm->ps->saberLockTime >= pm->cmd.serverTime)
|
|
{
|
|
pm->cmd.upmove = 0;
|
|
pm->cmd.forwardmove = 0;//50;
|
|
pm->cmd.rightmove = 0;//*= 0.1;
|
|
}
|
|
|
|
if ( pm->ps->pm_type == PM_SPECTATOR ) {
|
|
PM_CheckDuck ();
|
|
if (!pm->noSpecMove)
|
|
{
|
|
PM_FlyMove ();
|
|
}
|
|
PM_DropTimers ();
|
|
return;
|
|
}
|
|
|
|
if ( pm->ps->pm_type == PM_NOCLIP ) {
|
|
if (pm->ps->clientNum < MAX_CLIENTS)
|
|
{
|
|
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
|
|
PM_SetWaterLevel();
|
|
pml.previous_waterlevel = pmove->waterlevel;
|
|
|
|
// set mins, maxs, and viewheight
|
|
PM_CheckDuck ();
|
|
|
|
if (pm->ps->pm_type == PM_JETPACK)
|
|
{
|
|
gDist = PM_GroundDistance();
|
|
savedGravity = pm->ps->gravity;
|
|
|
|
if (gDist < JETPACK_HOVER_HEIGHT+64)
|
|
{
|
|
pm->ps->gravity *= 0.1f;
|
|
}
|
|
else
|
|
{
|
|
pm->ps->gravity *= 0.25f;
|
|
}
|
|
}
|
|
else if (gPMDoSlowFall)
|
|
{
|
|
savedGravity = pm->ps->gravity;
|
|
pm->ps->gravity *= 0.5;
|
|
}
|
|
|
|
//if we're in jetpack mode then see if we should be jetting around
|
|
if (pm->ps->pm_type == PM_JETPACK)
|
|
{
|
|
if (pm->cmd.rightmove > 0)
|
|
{
|
|
PM_ContinueLegsAnim(BOTH_INAIRRIGHT1);
|
|
}
|
|
else if (pm->cmd.rightmove < 0)
|
|
{
|
|
PM_ContinueLegsAnim(BOTH_INAIRLEFT1);
|
|
}
|
|
else if (pm->cmd.forwardmove > 0)
|
|
{
|
|
PM_ContinueLegsAnim(BOTH_INAIR1);
|
|
}
|
|
else if (pm->cmd.forwardmove < 0)
|
|
{
|
|
PM_ContinueLegsAnim(BOTH_INAIRBACK1);
|
|
}
|
|
else
|
|
{
|
|
PM_ContinueLegsAnim(BOTH_INAIR1);
|
|
}
|
|
|
|
if (pm->ps->weapon == WP_SABER &&
|
|
BG_SpinningSaberAnim( pm->ps->legsAnim ))
|
|
{ //make him stir around since he shouldn't have any real control when spinning
|
|
pm->ps->velocity[0] += Q_irand(-100, 100);
|
|
pm->ps->velocity[1] += Q_irand(-100, 100);
|
|
}
|
|
|
|
if (pm->cmd.upmove > 0 && pm->ps->velocity[2] < 256)
|
|
{ //cap upward velocity off at 256. Seems reasonable.
|
|
float addIn = 12.0f;
|
|
|
|
/*
|
|
//Add based on our distance to the ground if we're already travelling upward
|
|
if (pm->ps->velocity[2] > 0)
|
|
{
|
|
while (gDist > 64)
|
|
{ //subtract 1 for every 64 units off the ground we get
|
|
addIn--;
|
|
|
|
gDist -= 64;
|
|
|
|
if (addIn <= 0)
|
|
{ //break out if we're not even going to add anything
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
if (pm->ps->velocity[2] > 0)
|
|
{
|
|
addIn = 12.0f - (gDist / 64.0f);
|
|
}
|
|
|
|
if (addIn > 0.0f)
|
|
{
|
|
pm->ps->velocity[2] += addIn;
|
|
}
|
|
|
|
pm->ps->eFlags |= EF_JETPACK_FLAMING; //going up
|
|
}
|
|
else
|
|
{
|
|
pm->ps->eFlags &= ~EF_JETPACK_FLAMING; //idling
|
|
|
|
if (pm->ps->velocity[2] < 256)
|
|
{
|
|
if (pm->ps->velocity[2] < -100)
|
|
{
|
|
pm->ps->velocity[2] = -100;
|
|
}
|
|
if (gDist < JETPACK_HOVER_HEIGHT)
|
|
{ //make sure we're always hovering off the ground somewhat while jetpack is active
|
|
pm->ps->velocity[2] += 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf && pm_entSelf->m_pVehicle)
|
|
{ //Now update our mins/maxs to match our m_vOrientation based on our length, width & height
|
|
BG_VehicleAdjustBBoxForOrientation( pm_entSelf->m_pVehicle, pm->ps->origin, pm->mins, pm->maxs, pm->ps->clientNum, pm->tracemask, pm->trace );
|
|
}
|
|
|
|
// set groundentity
|
|
PM_GroundTrace();
|
|
if ( pm_flying == FLY_HOVER )
|
|
{//never stick to the ground
|
|
PM_HoverTrace();
|
|
}
|
|
|
|
if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )
|
|
{//on ground
|
|
pm->ps->fd.forceJumpZStart = 0;
|
|
}
|
|
|
|
if ( pm->ps->pm_type == PM_DEAD ) {
|
|
if (pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_VEHICLE &&
|
|
pm_entSelf->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL)
|
|
{//vehicles don't use deadmove
|
|
}
|
|
else
|
|
{
|
|
PM_DeadMove ();
|
|
}
|
|
}
|
|
|
|
PM_DropTimers();
|
|
|
|
#ifdef _TESTING_VEH_PREDICTION
|
|
#ifndef QAGAME
|
|
{
|
|
vec3_t blah;
|
|
VectorMA(pm->ps->origin, 128.0f, pm->ps->moveDir, blah);
|
|
CG_TestLine(pm->ps->origin, blah, 1, 0x0000ff, 1);
|
|
|
|
VectorMA(pm->ps->origin, 1.0f, pm->ps->velocity, blah);
|
|
CG_TestLine(pm->ps->origin, blah, 1, 0xff0000, 1);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&&pm->ps->m_iVehicleNum)
|
|
{ //a player riding a vehicle
|
|
bgEntity_t *veh = pm_entVeh;
|
|
|
|
if ( veh && veh->m_pVehicle &&
|
|
(veh->m_pVehicle->m_pVehicleInfo->type == VH_WALKER || veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) )
|
|
{//*sigh*, until we get forced weapon-switching working?
|
|
pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK);
|
|
pm->ps->eFlags &= ~(EF_FIRING|EF_ALT_FIRING);
|
|
//pm->cmd.weapon = pm->ps->weapon;
|
|
}
|
|
}
|
|
|
|
if (!pm->ps->m_iVehicleNum &&
|
|
pm_entSelf->s.NPC_class!=CLASS_VEHICLE&&
|
|
pm_entSelf->s.NPC_class!=CLASS_RANCOR&&
|
|
pm->ps->groundEntityNum < ENTITYNUM_WORLD &&
|
|
pm->ps->groundEntityNum >= MAX_CLIENTS)
|
|
{ //I am a player client, not riding on a vehicle, and potentially standing on an NPC
|
|
bgEntity_t *pEnt = PM_BGEntForNum(pm->ps->groundEntityNum);
|
|
|
|
if (pEnt && pEnt->s.eType == ET_NPC &&
|
|
pEnt->s.NPC_class != CLASS_VEHICLE) //don't bounce on vehicles
|
|
{ //this is actually an NPC, let's try to bounce of its head to make sure we can't just stand around on top of it.
|
|
if (pm->ps->velocity[2] < 270)
|
|
{ //try forcing velocity up and also force him to jump
|
|
pm->ps->velocity[2] = 270; //seems reasonable
|
|
pm->cmd.upmove = 127;
|
|
}
|
|
}
|
|
#ifdef QAGAME
|
|
else if ( !pm->ps->zoomMode &&
|
|
pm_entSelf //I exist
|
|
&& pEnt->m_pVehicle )//ent has a vehicle
|
|
{
|
|
gentity_t *gEnt = (gentity_t*)pEnt;
|
|
if ( gEnt->client
|
|
&& !gEnt->client->ps.m_iVehicleNum //vehicle is empty
|
|
&& (gEnt->spawnflags&2) )//SUSPENDED
|
|
{//it's a vehicle, see if we should get in it
|
|
//if land on an empty, suspended vehicle, get in it
|
|
pEnt->m_pVehicle->m_pVehicleInfo->Board( pEnt->m_pVehicle, (bgEntity_t *)pm_entSelf );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_VEHICLE)
|
|
{ //we are a vehicle
|
|
bgEntity_t *veh = pm_entSelf;
|
|
|
|
assert(veh && veh->playerState && veh->m_pVehicle && veh->s.number >= MAX_CLIENTS);
|
|
|
|
if (veh->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER)
|
|
{ //kind of hacky, don't want to do this for flying vehicles
|
|
veh->m_pVehicle->m_vOrientation[PITCH] = pm->ps->viewangles[PITCH];
|
|
}
|
|
|
|
if (!pm->ps->m_iVehicleNum)
|
|
{ //no one is driving, just update and get out
|
|
#ifdef QAGAME
|
|
veh->m_pVehicle->m_pVehicleInfo->Update(veh->m_pVehicle, &pm->cmd);
|
|
veh->m_pVehicle->m_pVehicleInfo->Animate(veh->m_pVehicle);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
bgEntity_t *self = pm_entVeh;
|
|
#ifdef QAGAME
|
|
int i = 0;
|
|
#endif
|
|
|
|
assert(self && self->playerState && self->s.number < MAX_CLIENTS);
|
|
|
|
if (pm->ps->pm_type == PM_DEAD &&
|
|
(veh->m_pVehicle->m_ulFlags & VEH_CRASHING))
|
|
{
|
|
veh->m_pVehicle->m_ulFlags &= ~VEH_CRASHING;
|
|
}
|
|
|
|
if (self->playerState->m_iVehicleNum)
|
|
{ //only do it if they still have a vehicle (didn't get ejected this update or something)
|
|
PM_VehicleViewAngles(self->playerState, veh, &veh->m_pVehicle->m_ucmd);
|
|
}
|
|
|
|
#ifdef QAGAME
|
|
veh->m_pVehicle->m_pVehicleInfo->Update(veh->m_pVehicle, &veh->m_pVehicle->m_ucmd);
|
|
veh->m_pVehicle->m_pVehicleInfo->Animate(veh->m_pVehicle);
|
|
|
|
veh->m_pVehicle->m_pVehicleInfo->UpdateRider(veh->m_pVehicle, self, &veh->m_pVehicle->m_ucmd);
|
|
//update the passengers
|
|
while (i < veh->m_pVehicle->m_iNumPassengers)
|
|
{
|
|
if (veh->m_pVehicle->m_ppPassengers[i])
|
|
{
|
|
gentity_t *thePassenger = (gentity_t *)veh->m_pVehicle->m_ppPassengers[i]; //yes, this is, in fact, ass.
|
|
if (thePassenger->inuse && thePassenger->client)
|
|
{
|
|
veh->m_pVehicle->m_pVehicleInfo->UpdateRider(veh->m_pVehicle, veh->m_pVehicle->m_ppPassengers[i], &thePassenger->client->pers.cmd);
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
#else
|
|
if (!veh->playerState->vehBoarding )//|| veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
|
|
{
|
|
if (veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER)
|
|
{ //client must explicitly call this for prediction
|
|
BG_FighterUpdate(veh->m_pVehicle, &veh->m_pVehicle->m_ucmd, pm->mins, pm->maxs, self->playerState->gravity, pm->trace);
|
|
}
|
|
|
|
if (veh->m_pVehicle->m_iBoarding == 0)
|
|
{
|
|
vec3_t vRollAng;
|
|
|
|
//make sure we are set as its pilot cgame side
|
|
veh->m_pVehicle->m_pPilot = self;
|
|
|
|
// Keep track of the old orientation.
|
|
VectorCopy( veh->m_pVehicle->m_vOrientation, veh->m_pVehicle->m_vPrevOrientation );
|
|
|
|
veh->m_pVehicle->m_pVehicleInfo->ProcessOrientCommands(veh->m_pVehicle);
|
|
PM_SetPMViewAngle(veh->playerState, veh->m_pVehicle->m_vOrientation, &veh->m_pVehicle->m_ucmd);
|
|
veh->m_pVehicle->m_pVehicleInfo->ProcessMoveCommands(veh->m_pVehicle);
|
|
|
|
vRollAng[YAW] = self->playerState->viewangles[YAW];
|
|
vRollAng[PITCH] = self->playerState->viewangles[PITCH];
|
|
vRollAng[ROLL] = veh->m_pVehicle->m_vOrientation[ROLL];
|
|
PM_SetPMViewAngle(self->playerState, vRollAng, &pm->cmd);
|
|
|
|
// Setup the move direction.
|
|
if ( veh->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
|
|
{
|
|
AngleVectors( veh->m_pVehicle->m_vOrientation, veh->playerState->moveDir, NULL, NULL );
|
|
}
|
|
else
|
|
{
|
|
vec3_t vVehAngles;
|
|
|
|
VectorSet(vVehAngles, 0, veh->m_pVehicle->m_vOrientation[YAW], 0);
|
|
AngleVectors( vVehAngles, veh->playerState->moveDir, NULL, NULL );
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
veh->playerState->speed = 0.0f;
|
|
PM_SetPMViewAngle(self->playerState, veh->playerState->viewangles, &veh->m_pVehicle->m_ucmd);
|
|
}
|
|
*/
|
|
else if (veh->playerState)
|
|
{
|
|
veh->playerState->speed = 0.0f;
|
|
if (veh->m_pVehicle)
|
|
{
|
|
PM_SetPMViewAngle(self->playerState, veh->m_pVehicle->m_vOrientation, &pm->cmd);
|
|
PM_SetPMViewAngle(veh->playerState, veh->m_pVehicle->m_vOrientation, &pm->cmd);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
noAnimate = qtrue;
|
|
}
|
|
|
|
if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&&pm->ps->m_iVehicleNum)
|
|
{//don't even run physics on a player if he's on a vehicle - he goes where the vehicle goes
|
|
}
|
|
else
|
|
{ //don't even run physics on a player if he's on a vehicle - he goes where the vehicle goes
|
|
if (pm->ps->pm_type == PM_FLOAT
|
|
||pm_flying == FLY_NORMAL)
|
|
{
|
|
PM_FlyMove ();
|
|
}
|
|
else if ( pm_flying == FLY_VEHICLE )
|
|
{
|
|
PM_FlyVehicleMove();
|
|
}
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!noAnimate)
|
|
{
|
|
PM_Animate();
|
|
}
|
|
|
|
// set groundentity, watertype, and waterlevel
|
|
PM_GroundTrace();
|
|
if ( pm_flying == FLY_HOVER )
|
|
{//never stick to the ground
|
|
PM_HoverTrace();
|
|
}
|
|
PM_SetWaterLevel();
|
|
if (pm->cmd.forcesel != -1 && (pm->ps->fd.forcePowersKnown & (1 << pm->cmd.forcesel)))
|
|
{
|
|
pm->ps->fd.forcePowerSelected = pm->cmd.forcesel;
|
|
}
|
|
if (pm->cmd.invensel != -1 && (pm->ps->stats[STAT_HOLDABLE_ITEMS] & (1 << pm->cmd.invensel)))
|
|
{
|
|
pm->ps->stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(pm->cmd.invensel, IT_HOLDABLE);
|
|
}
|
|
|
|
if (pm->ps->m_iVehicleNum
|
|
/*&&pm_entSelf->s.NPC_class!=CLASS_VEHICLE*/
|
|
&& pm->ps->clientNum < MAX_CLIENTS)
|
|
{//a client riding a vehicle
|
|
if ( (pm->ps->eFlags&EF_NODRAW) )
|
|
{//inside the vehicle, do nothing
|
|
}
|
|
else if (!PM_WeaponOkOnVehicle(pm->cmd.weapon) || !PM_WeaponOkOnVehicle(pm->ps->weapon))
|
|
{ //this weapon is not legal for the vehicle, force to our current one
|
|
if (!PM_WeaponOkOnVehicle(pm->ps->weapon))
|
|
{ //uh-oh!
|
|
int weap = PM_GetOkWeaponForVehicle();
|
|
|
|
if (weap != -1)
|
|
{
|
|
pm->cmd.weapon = weap;
|
|
pm->ps->weapon = weap;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pm->cmd.weapon = pm->ps->weapon;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pm->ps->m_iVehicleNum //not a vehicle and not riding one
|
|
|| pm_entSelf->s.NPC_class==CLASS_VEHICLE //you are a vehicle NPC
|
|
|| (!(pm->ps->eFlags&EF_NODRAW)&&PM_WeaponOkOnVehicle(pm->cmd.weapon)) )//you're not inside the vehicle and the weapon you're holding can be used when riding this vehicle
|
|
{ //only run weapons if a valid weapon is selected
|
|
// weapons
|
|
PM_Weapon();
|
|
}
|
|
|
|
PM_Use();
|
|
|
|
if (!pm->ps->m_iVehicleNum &&
|
|
(pm->ps->clientNum < MAX_CLIENTS ||
|
|
!pm_entSelf ||
|
|
pm_entSelf->s.NPC_class != CLASS_VEHICLE))
|
|
{ //don't do this if we're on a vehicle, or we are one
|
|
// footstep events / legs animations
|
|
PM_Footsteps();
|
|
}
|
|
|
|
// entering / leaving water splashes
|
|
PM_WaterEvents();
|
|
|
|
// snap some parts of playerstate to save network bandwidth
|
|
trap_SnapVector( pm->ps->velocity );
|
|
|
|
if (pm->ps->pm_type == PM_JETPACK || gPMDoSlowFall )
|
|
{
|
|
pm->ps->gravity = savedGravity;
|
|
}
|
|
|
|
if (//pm->ps->m_iVehicleNum &&
|
|
pm->ps->clientNum >= MAX_CLIENTS &&
|
|
pm_entSelf &&
|
|
pm_entSelf->s.NPC_class == CLASS_VEHICLE)
|
|
{ //a vehicle with passengers
|
|
bgEntity_t *veh;
|
|
|
|
veh = pm_entSelf;
|
|
|
|
assert(veh->m_pVehicle);
|
|
|
|
//this could be kind of "inefficient" because it's called after every passenger pmove too.
|
|
//Maybe instead of AttachRiders we should have each rider call attach for himself?
|
|
if (veh->m_pVehicle && veh->ghoul2)
|
|
{
|
|
veh->m_pVehicle->m_pVehicleInfo->AttachRiders( veh->m_pVehicle );
|
|
}
|
|
}
|
|
|
|
if (pm_entSelf->s.NPC_class!=CLASS_VEHICLE
|
|
&& pm->ps->m_iVehicleNum)
|
|
{ //riding a vehicle, see if we should do some anim overrides
|
|
PM_VehicleWeaponAnimate();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
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;
|
|
}
|
|
|
|
if (pmove->ps->fallingToDeath)
|
|
{
|
|
pmove->cmd.forwardmove = 0;
|
|
pmove->cmd.rightmove = 0;
|
|
pmove->cmd.upmove = 0;
|
|
pmove->cmd.buttons = 0;
|
|
}
|
|
|
|
pmove->ps->pmove_framecount = (pmove->ps->pmove_framecount+1) & ((1<<PS_PMOVEFRAMECOUNTBITS)-1);
|
|
|
|
// chop the move up if it is too long, to prevent framerate
|
|
// dependent behavior
|
|
while ( pmove->ps->commandTime != finalTime ) {
|
|
int msec;
|
|
|
|
msec = finalTime - pmove->ps->commandTime;
|
|
|
|
if ( pmove->pmove_fixed ) {
|
|
if ( msec > pmove->pmove_msec ) {
|
|
msec = pmove->pmove_msec;
|
|
}
|
|
}
|
|
else {
|
|
if ( msec > 66 ) {
|
|
msec = 66;
|
|
}
|
|
}
|
|
pmove->cmd.serverTime = pmove->ps->commandTime + msec;
|
|
|
|
PmoveSingle( pmove );
|
|
|
|
if ( pmove->ps->pm_flags & PMF_JUMP_HELD ) {
|
|
pmove->cmd.upmove = 20;
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "../namespace_end.h"
|