jediacademy/code/game/FighterNPC.c

1752 lines
54 KiB
C

// leave this line at the top for all g_xxxx.cpp files...
#include "g_headers.h"
//seems to be a compiler bug, it doesn't clean out the #ifdefs between dif-compiles
//or something, so the headers spew errors on these defs from the previous compile.
//this fixes that. -rww
#ifdef _JK2MP
//get rid of all the crazy defs we added for this file
#undef currentAngles
#undef currentOrigin
#undef mins
#undef maxs
#undef legsAnimTimer
#undef torsoAnimTimer
#undef bool
#undef false
#undef true
#undef sqrtf
#undef Q_flrand
#undef MOD_EXPLOSIVE
#endif
#ifdef _JK2 //SP does not have this preprocessor for game like MP does
#ifndef _JK2MP
#define _JK2MP
#endif
#endif
#ifndef _JK2MP //if single player
#ifndef QAGAME //I don't think we have a QAGAME define
#define QAGAME //but define it cause in sp we're always in the game
#endif
#endif
#ifdef QAGAME //including game headers on cgame is FORBIDDEN ^_^
#include "g_local.h"
#elif defined _JK2MP
#include "bg_public.h"
#endif
#ifndef _JK2MP
#include "g_functions.h"
#include "g_vehicles.h"
#else
#include "bg_vehicles.h"
#endif
#ifdef _JK2MP
//this is really horrible, but it works! just be sure not to use any locals or anything
//with these names (exluding bool, false, true). -rww
#define currentAngles r.currentAngles
#define currentOrigin r.currentOrigin
#define mins r.mins
#define maxs r.maxs
#define legsAnimTimer legsTimer
#define torsoAnimTimer torsoTimer
#define bool qboolean
#define false qfalse
#define true qtrue
#define sqrtf sqrt
#define Q_flrand flrand
#define MOD_EXPLOSIVE MOD_SUICIDE
#else
#define bgEntity_t gentity_t
#endif
extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
#ifdef QAGAME //SP or gameside MP
extern vmCvar_t cg_thirdPersonAlpha;
extern vec3_t playerMins;
extern vec3_t playerMaxs;
extern cvar_t *g_speederControlScheme;
extern void ChangeWeapon( gentity_t *ent, int newWeapon );
extern void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
extern int PM_AnimLength( int index, animNumber_t anim );
extern void G_VehicleTrace( trace_t *results, const vec3_t start, const vec3_t tMins, const vec3_t tMaxs, const vec3_t end, int passEntityNum, int contentmask );
#endif
extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
#ifdef _JK2MP
#include "../namespace_begin.h"
extern void BG_SetAnim(playerState_t *ps, animation_t *animations, int setAnimParts,int anim,int setAnimFlags, int blendTime);
extern int BG_GetTime(void);
#endif
extern void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS );
//this stuff has got to be predicted, so..
bool 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 ))
{
vec3_t bottom;
playerState_t *parentPS;
qboolean isDead = qfalse;
#ifdef QAGAME //don't do this on client
// Make sure the riders are not visible or collidable.
pVeh->m_pVehicleInfo->Ghost( pVeh, pVeh->m_pPilot );
#endif
#ifdef _JK2MP
parentPS = pVeh->m_pParentEntity->playerState;
#else
parentPS = &pVeh->m_pParentEntity->client->ps;
#endif
if (!parentPS)
{
Com_Error(ERR_DROP, "NULL PS in BG_FighterUpdate (%s)", pVeh->m_pVehicleInfo->name);
return false;
}
// If we have a pilot, take out gravity (it's a flying craft...).
if ( pVeh->m_pPilot )
{
parentPS->gravity = 0;
#ifndef _JK2MP //don't need this flag in mp, I.. guess
pVeh->m_pParentEntity->svFlags |= SVF_CUSTOM_GRAVITY;
#endif
}
else
{
#ifndef _JK2MP //don't need this flag in mp, I.. guess
pVeh->m_pParentEntity->svFlags &= ~SVF_CUSTOM_GRAVITY;
#else //in MP set grav back to normal gravity
if (pVeh->m_pVehicleInfo->gravity)
{
parentPS->gravity = pVeh->m_pVehicleInfo->gravity;
}
else
{ //it doesn't have gravity specified apparently
parentPS->gravity = gravity;
}
#endif
}
#ifdef _JK2MP
isDead = (qboolean)((parentPS->eFlags&EF_DEAD)!=0);
#else
isDead = (parentPS->stats[STAT_HEALTH] <= 0 );
#endif
/*
if ( isDead ||
(pVeh->m_pVehicleInfo->surfDestruction &&
pVeh->m_iRemovedSurfaces ) )
{//can't land if dead or spiralling out of control
pVeh->m_LandTrace.fraction = 1.0f;
pVeh->m_LandTrace.contents = pVeh->m_LandTrace.surfaceFlags = 0;
VectorClear( pVeh->m_LandTrace.plane.normal );
pVeh->m_LandTrace.allsolid = qfalse;
pVeh->m_LandTrace.startsolid = qfalse;
}
else
{
*/
//argh, no, I need to have a way to see when they impact the ground while damaged. -rww
// Check to see if the fighter has taken off yet (if it's a certain height above ground).
VectorCopy( parentPS->origin, bottom );
bottom[2] -= pVeh->m_pVehicleInfo->landingHeight;
traceFunc( &pVeh->m_LandTrace, parentPS->origin, trMins, trMaxs, bottom, pVeh->m_pParentEntity->s.number, (MASK_NPCSOLID&~CONTENTS_BODY) );
//}
return true;
}
#ifdef QAGAME //ONLY in SP or on server, not cgame
// Like a think or move command, this updates various vehicle properties.
static bool Update( Vehicle_t *pVeh, const usercmd_t *pUcmd )
{
assert(pVeh->m_pParentEntity);
if (!BG_FighterUpdate(pVeh, pUcmd, ((gentity_t *)pVeh->m_pParentEntity)->mins,
((gentity_t *)pVeh->m_pParentEntity)->maxs,
#ifdef _JK2MP
g_gravity.value,
#else
g_gravity->value,
#endif
G_VehicleTrace))
{
return false;
}
if ( !g_vehicleInfo[VEHICLE_BASE].Update( pVeh, pUcmd ) )
{
return false;
}
return true;
}
// Board this Vehicle (get on). The first entity to board an empty vehicle becomes the Pilot.
static bool Board( Vehicle_t *pVeh, bgEntity_t *pEnt )
{
if ( !g_vehicleInfo[VEHICLE_BASE].Board( pVeh, pEnt ) )
return false;
// Set the board wait time (they won't be able to do anything, including getting off, for this amount of time).
pVeh->m_iBoarding = level.time + 1500;
return true;
}
// Eject an entity from the vehicle.
static bool Eject( Vehicle_t *pVeh, bgEntity_t *pEnt, qboolean forceEject )
{
if ( g_vehicleInfo[VEHICLE_BASE].Eject( pVeh, pEnt, forceEject ) )
{
return true;
}
return false;
}
#endif //end game-side only
//method of decrementing the given angle based on the given taking variable frame times into account
static float PredictedAngularDecrement(float scale, float timeMod, float originalAngle)
{
float fixedBaseDec = originalAngle*0.05f;
float r = 0.0f;
if (fixedBaseDec < 0.0f)
{
fixedBaseDec = -fixedBaseDec;
}
fixedBaseDec *= (1.0f+(1.0f-scale));
if (fixedBaseDec < 0.1f)
{ //don't increment in incredibly small fractions, it would eat up unnecessary bandwidth.
fixedBaseDec = 0.1f;
}
fixedBaseDec *= (timeMod*0.1f);
if (originalAngle > 0.0f)
{ //subtract
r = (originalAngle-fixedBaseDec);
if (r < 0.0f)
{
r = 0.0f;
}
}
else if (originalAngle < 0.0f)
{ //add
r = (originalAngle+fixedBaseDec);
if (r > 0.0f)
{
r = 0.0f;
}
}
return r;
}
#ifdef QAGAME//only do this check on GAME side, because if it's CGAME, it's being predicted, and it's only predicted if the local client is the driver
qboolean FighterIsInSpace( gentity_t *gParent )
{
if ( gParent
&& gParent->client
&& gParent->client->inSpaceIndex
&& gParent->client->inSpaceIndex < ENTITYNUM_WORLD )
{
return qtrue;
}
return qfalse;
}
#endif
qboolean FighterOverValidLandingSurface( Vehicle_t *pVeh )
{
if ( pVeh->m_LandTrace.fraction < 1.0f //ground present
&& pVeh->m_LandTrace.plane.normal[2] >= MIN_LANDING_SLOPE )//flat enough
//FIXME: also check for a certain surface flag ... "landing zones"?
{
return qtrue;
}
return qfalse;
}
qboolean FighterIsLanded( Vehicle_t *pVeh, playerState_t *parentPS )
{
if ( FighterOverValidLandingSurface( pVeh )
&& !parentPS->speed )//stopped
{
return qtrue;
}
return qfalse;
}
qboolean FighterIsLanding( Vehicle_t *pVeh, playerState_t *parentPS )
{
if ( FighterOverValidLandingSurface( pVeh )
#ifdef QAGAME//only do this check on GAME side, because if it's CGAME, it's being predicted, and it's only predicted if the local client is the driver
&& pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
#endif
&& (pVeh->m_ucmd.forwardmove < 0||pVeh->m_ucmd.upmove<0) //decelerating or holding crouch button
&& parentPS->speed <= MIN_LANDING_SPEED )//going slow enough to start landing - was using pVeh->m_pVehicleInfo->speedIdle, but that's still too fast
{
return qtrue;
}
return qfalse;
}
qboolean FighterIsLaunching( Vehicle_t *pVeh, playerState_t *parentPS )
{
if ( FighterOverValidLandingSurface( pVeh )
#ifdef QAGAME//only do this check on GAME side, because if it's CGAME, it's being predicted, and it's only predicted if the local client is the driver
&& pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
#endif
&& pVeh->m_ucmd.upmove > 0 //trying to take off
&& parentPS->speed <= 200.0f )//going slow enough to start landing - was using pVeh->m_pVehicleInfo->speedIdle, but that's still too fast
{
return qtrue;
}
return qfalse;
}
qboolean FighterSuspended( Vehicle_t *pVeh, playerState_t *parentPS )
{
#ifdef QAGAME//only do this check on GAME side, because if it's CGAME, it's being predicted, and it's only predicted if the local client is the driver
if (!pVeh->m_pPilot//empty
&& !parentPS->speed//not moving
&& pVeh->m_ucmd.forwardmove <= 0//not trying to go forward for whatever reason
&& pVeh->m_pParentEntity != NULL
&& (((gentity_t *)pVeh->m_pParentEntity)->spawnflags&2) )//SUSPENDED spawnflag is on
{
return qtrue;
}
return qfalse;
#elif CGAME
return qfalse;
#endif
}
#ifdef CGAME
extern void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); //cg_syscalls.c
extern sfxHandle_t trap_S_RegisterSound( const char *sample); //cg_syscalls.c
#endif
//MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
//If you really need to violate this rule for SP, then use ifdefs.
//By BG-compatible, I mean no use of game-specific data - ONLY use
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
//as a gentity, but the MP-compatible access restrictions are based
//on the bgEntity structure in the MP codebase) -rww
// ProcessMoveCommands the Vehicle.
#define FIGHTER_MIN_TAKEOFF_FRACTION 0.7f
static void ProcessMoveCommands( Vehicle_t *pVeh )
{
/************************************************************************************/
/* BEGIN Here is where we move the vehicle (forward or back or whatever). BEGIN */
/************************************************************************************/
//Client sets ucmds and such for speed alterations
float speedInc, speedIdleDec, speedIdle, speedIdleAccel, speedMin, speedMax;
bgEntity_t *parent = pVeh->m_pParentEntity;
qboolean isLandingOrLaunching = qfalse;
#ifndef _JK2MP//SP
int curTime = level.time;
#elif QAGAME//MP GAME
int curTime = level.time;
#elif CGAME//MP CGAME
//FIXME: pass in ucmd? Not sure if this is reliable...
int curTime = pm->cmd.serverTime;
#endif
#ifdef _JK2MP
playerState_t *parentPS = parent->playerState;
#else
playerState_t *parentPS = &parent->client->ps;
#endif
#ifdef _JK2MP
if ( parentPS->hyperSpaceTime
&& curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME )
{//Going to Hyperspace
//totally override movement
float timeFrac = ((float)(curTime-parentPS->hyperSpaceTime))/HYPERSPACE_TIME;
if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
{//for first half, instantly jump to top speed!
if ( !(parentPS->eFlags2&EF2_HYPERSPACE) )
{//waiting to face the right direction, do nothing
parentPS->speed = 0.0f;
}
else
{
if ( parentPS->speed < HYPERSPACE_SPEED )
{//just started hyperspace
//MIKE: This is going to play the sound twice for the predicting client, I suggest using
//a predicted event or only doing it game-side. -rich
#ifdef QAGAME//MP GAME-side
//G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_LOCAL, pVeh->m_pVehicleInfo->soundHyper );
#elif CGAME//MP CGAME-side
trap_S_StartSound( NULL, pm->ps->clientNum, CHAN_LOCAL, pVeh->m_pVehicleInfo->soundHyper );
#endif
}
parentPS->speed = HYPERSPACE_SPEED;
}
}
else
{//slow from top speed to 200...
parentPS->speed = 200.0f + ((1.0f-timeFrac)*(1.0f/HYPERSPACE_TELEPORT_FRAC)*(HYPERSPACE_SPEED-200.0f));
//don't mess with acceleration, just pop to the high velocity
if ( VectorLength( parentPS->velocity ) < parentPS->speed )
{
VectorScale( parentPS->moveDir, parentPS->speed, parentPS->velocity );
}
}
return;
}
#endif
if ( pVeh->m_iDropTime >= curTime )
{//no speed, just drop
parentPS->speed = 0.0f;
parentPS->gravity = 800;
return;
}
isLandingOrLaunching = (FighterIsLanding( pVeh, parentPS )||FighterIsLaunching( pVeh, parentPS ));
// If we are hitting the ground, just allow the fighter to go up and down.
if ( isLandingOrLaunching//going slow enough to start landing
&& (pVeh->m_ucmd.forwardmove<=0||pVeh->m_LandTrace.fraction<=FIGHTER_MIN_TAKEOFF_FRACTION) )//not trying to accelerate away already (or: you are trying to, but not high enough off the ground yet)
{//FIXME: if start to move forward and fly over something low while still going relatively slow, you may try to land even though you don't mean to...
//float fInvFrac = 1.0f - pVeh->m_LandTrace.fraction;
if ( pVeh->m_ucmd.upmove > 0 )
{
#ifdef _JK2MP
if ( parentPS->velocity[2] <= 0
&& pVeh->m_pVehicleInfo->soundTakeOff )
{//taking off for the first time
#ifdef QAGAME//MP GAME-side
G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundTakeOff );
#endif
}
#endif
parentPS->velocity[2] += pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;// * ( /*fInvFrac **/ 1.5f );
}
else if ( pVeh->m_ucmd.upmove < 0 )
{
parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;// * ( /*fInvFrac **/ 1.8f );
}
else if ( pVeh->m_ucmd.forwardmove < 0 )
{
if ( pVeh->m_LandTrace.fraction != 0.0f )
{
parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
}
if ( pVeh->m_LandTrace.fraction <= FIGHTER_MIN_TAKEOFF_FRACTION )
{
//pVeh->m_pParentEntity->client->ps.velocity[0] *= pVeh->m_LandTrace.fraction;
//pVeh->m_pParentEntity->client->ps.velocity[1] *= pVeh->m_LandTrace.fraction;
//remember to always base this stuff on the time modifier! otherwise, you create
//framerate-dependancy issues and break prediction in MP -rww
//parentPS->velocity[2] *= pVeh->m_LandTrace.fraction;
//it's not an angle, but hey
parentPS->velocity[2] = PredictedAngularDecrement(pVeh->m_LandTrace.fraction, pVeh->m_fTimeModifier*5.0f, parentPS->velocity[2]);
parentPS->speed = 0;
}
}
// Make sure they don't pitch as they near the ground.
//pVeh->m_vOrientation[PITCH] *= 0.7f;
pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.7f, pVeh->m_fTimeModifier*10.0f, pVeh->m_vOrientation[PITCH]);
return;
}
if ( (pVeh->m_ucmd.upmove > 0) && pVeh->m_pVehicleInfo->turboSpeed )
{
if ((curTime - pVeh->m_iTurboTime)>pVeh->m_pVehicleInfo->turboRecharge)
{
pVeh->m_iTurboTime = (curTime + pVeh->m_pVehicleInfo->turboDuration);
if (pVeh->m_pVehicleInfo->iTurboStartFX)
{
int i;
for (i=0; i<MAX_VEHICLE_EXHAUSTS; i++)
{
if (pVeh->m_iExhaustTag[i]==-1)
{
break;
}
#ifndef _JK2MP//SP
G_PlayEffect(pVeh->m_pVehicleInfo->iTurboStartFX, pVeh->m_pParentEntity->playerModel, pVeh->m_iExhaustTag[i], pVeh->m_pParentEntity->s.number, pVeh->m_pParentEntity->currentOrigin );
#else
//TODO: MP Play Effect?
#endif
}
}
//NOTE: turbo sound can't be part of effect if effect is played on every muzzle!
if ( pVeh->m_pVehicleInfo->soundTurbo )
{
#ifndef _JK2MP//SP
G_SoundIndexOnEnt( pVeh->m_pParentEntity, CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo );
#elif QAGAME//MP GAME-side
G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo );
#elif CGAME//MP CGAME-side
//trap_S_StartSound( NULL, pVeh->m_pParentEntity->s.number, CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo );
#endif
}
}
}
speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
if ( curTime < pVeh->m_iTurboTime )
{//going turbo speed
speedMax = pVeh->m_pVehicleInfo->turboSpeed;
//double our acceleration
speedInc *= 2.0f;
//force us to move forward
pVeh->m_ucmd.forwardmove = 127;
#ifdef _JK2MP//SP can cheat and just check m_iTurboTime directly... :)
//add flag to let cgame know to draw the iTurboFX effect
parentPS->eFlags |= EF_JETPACK_ACTIVE;
#endif
}
/*
//FIXME: if turbotime is up and we're waiting for it to recharge, should our max speed drop while we recharge?
else if ( (curTime - pVeh->m_iTurboTime)<3000 )
{//still waiting for the recharge
speedMax = pVeh->m_pVehicleInfo->speedMax*0.75;
}
*/
else
{//normal max speed
speedMax = pVeh->m_pVehicleInfo->speedMax;
#ifdef _JK2MP//SP can cheat and just check m_iTurboTime directly... :)
if ( (parentPS->eFlags&EF_JETPACK_ACTIVE) )
{//stop cgame from playing the turbo exhaust effect
parentPS->eFlags &= ~EF_JETPACK_ACTIVE;
}
#endif
}
speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier;
speedIdle = pVeh->m_pVehicleInfo->speedIdle;
speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier;
speedMin = pVeh->m_pVehicleInfo->speedMin;
if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_HEAVY)) )
{//engine has taken heavy damage
speedMax *= 0.8f;//at 80% speed
}
else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_LIGHT)) )
{//engine has taken light damage
speedMax *= 0.6f;//at 60% speed
}
if (pVeh->m_iRemovedSurfaces
|| parentPS->electrifyTime>=curTime)
{ //go out of control
parentPS->speed += speedInc;
//Why set forwardmove? PMove code doesn't use it... does it?
pVeh->m_ucmd.forwardmove = 127;
}
#ifdef QAGAME //well, the thing is always going to be inhabited if it's being predicted!
else if ( FighterSuspended( pVeh, parentPS ) )
{
parentPS->speed = 0;
pVeh->m_ucmd.forwardmove = 0;
}
else if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh )
&& parentPS->speed > 0 )
{//pilot jumped out while we were moving forward (not landing or landed) so just keep the throttle locked
//Why set forwardmove? PMove code doesn't use it... does it?
pVeh->m_ucmd.forwardmove = 127;
}
#endif
else if ( ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE ||
pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 ) && pVeh->m_LandTrace.fraction >= 0.05f )
{
if ( pVeh->m_ucmd.forwardmove > 0 && speedInc )
{
parentPS->speed += speedInc;
pVeh->m_ucmd.forwardmove = 127;
}
else if ( pVeh->m_ucmd.forwardmove < 0
|| pVeh->m_ucmd.upmove < 0 )
{//decelerating or braking
if ( pVeh->m_ucmd.upmove < 0 )
{//braking (trying to land?), slow down faster
if ( pVeh->m_ucmd.forwardmove )
{//decelerator + brakes
speedInc += pVeh->m_pVehicleInfo->braking;
speedIdleDec += pVeh->m_pVehicleInfo->braking;
}
else
{//just brakes
speedInc = speedIdleDec = pVeh->m_pVehicleInfo->braking;
}
}
if ( parentPS->speed > speedIdle )
{
parentPS->speed -= speedInc;
}
else if ( parentPS->speed > speedMin )
{
if ( FighterOverValidLandingSurface( pVeh ) )
{//there's ground below us and we're trying to slow down, slow down faster
parentPS->speed -= speedInc;
}
else
{
parentPS->speed -= speedIdleDec;
if ( parentPS->speed < MIN_LANDING_SPEED )
{//unless you can land, don't drop below the landing speed!!! This way you can't come to a dead stop in mid-air
parentPS->speed = MIN_LANDING_SPEED;
}
}
}
if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
{
pVeh->m_ucmd.forwardmove = 127;
}
else if ( speedMin >= 0 )
{
pVeh->m_ucmd.forwardmove = 0;
}
}
//else not accel, decel or braking
else if ( pVeh->m_pVehicleInfo->throttleSticks )
{//we're using a throttle that sticks at current speed
if ( parentPS->speed <= MIN_LANDING_SPEED )
{//going less than landing speed
if ( FighterOverValidLandingSurface( pVeh ) )
{//close to ground and not going very fast
//slow to a stop if within landing height and not accel/decel/braking
if ( parentPS->speed > 0 )
{//slow down
parentPS->speed -= speedIdleDec;
}
else if ( parentPS->speed < 0 )
{//going backwards, slow down
parentPS->speed += speedIdleDec;
}
}
else
{//not over a valid landing surf, but going too slow
//speed up to idle speed if not over a valid landing surf and not accel/decel/braking
if ( parentPS->speed < speedIdle )
{
parentPS->speed += speedIdleAccel;
if ( parentPS->speed > speedIdle )
{
parentPS->speed = speedIdle;
}
}
}
}
}
else
{//then speed up or slow down to idle speed
//accelerate to cruising speed only, otherwise, just coast
// If they've launched, apply some constant motion.
if ( (pVeh->m_LandTrace.fraction >= 1.0f //no ground
|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )//or can't land on ground below us
&& speedIdle > 0 )
{//not above ground and have an idle speed
//float fSpeed = pVeh->m_pParentEntity->client->ps.speed;
if ( parentPS->speed < speedIdle )
{
parentPS->speed += speedIdleAccel;
if ( parentPS->speed > speedIdle )
{
parentPS->speed = speedIdle;
}
}
else if ( parentPS->speed > 0 )
{//slow down
parentPS->speed -= speedIdleDec;
if ( parentPS->speed < speedIdle )
{
parentPS->speed = speedIdle;
}
}
}
else//either close to ground or no idle speed
{//slow to a stop if no idle speed or within landing height and not accel/decel/braking
if ( parentPS->speed > 0 )
{//slow down
parentPS->speed -= speedIdleDec;
}
else if ( parentPS->speed < 0 )
{//going backwards, slow down
parentPS->speed += speedIdleDec;
}
}
}
}
else
{
if ( pVeh->m_ucmd.forwardmove < 0 )
{
pVeh->m_ucmd.forwardmove = 0;
}
if ( pVeh->m_ucmd.upmove < 0 )
{
pVeh->m_ucmd.upmove = 0;
}
#ifndef _JK2MP
if ( !pVeh->m_pVehicleInfo->strafePerc || (!g_speederControlScheme->value && !pVeh->m_pParentEntity->s.number) )
{//if in a strafe-capable vehicle, clear strafing unless using alternate control scheme
pVeh->m_ucmd.rightmove = 0;
}
#endif
}
#if 1//This is working now, but there are some transitional jitters... Rich?
//STRAFING==============================================================================
if ( pVeh->m_pVehicleInfo->strafePerc
#ifdef QAGAME//only do this check on GAME side, because if it's CGAME, it's being predicted, and it's only predicted if the local client is the driver
&& pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
#endif
&& !pVeh->m_iRemovedSurfaces
&& parentPS->electrifyTime<curTime
&& (pVeh->m_LandTrace.fraction >= 1.0f//no grounf
||pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE//can't land here
||parentPS->speed>MIN_LANDING_SPEED)//going too fast to land
&& pVeh->m_ucmd.rightmove )
{//strafe
vec3_t vAngles, vRight;
float strafeSpeed = (pVeh->m_pVehicleInfo->strafePerc*speedMax)*5.0f;
VectorCopy( pVeh->m_vOrientation, vAngles );
vAngles[PITCH] = vAngles[ROLL] = 0;
AngleVectors( vAngles, NULL, vRight, NULL );
if ( pVeh->m_ucmd.rightmove > 0 )
{//strafe right
//FIXME: this will probably make it possible to cheat and
// go faster than max speed if you keep turning and strafing...
if ( pVeh->m_fStrafeTime > -MAX_STRAFE_TIME )
{//can strafe right for 2 seconds
float curStrafeSpeed = DotProduct( parentPS->velocity, vRight );
if ( curStrafeSpeed > 0.0f )
{//if > 0, already strafing right
strafeSpeed -= curStrafeSpeed;//so it doesn't add up
}
if ( strafeSpeed > 0 )
{
VectorMA( parentPS->velocity, strafeSpeed*pVeh->m_fTimeModifier, vRight, parentPS->velocity );
}
pVeh->m_fStrafeTime -= 50*pVeh->m_fTimeModifier;
}
}
else
{//strafe left
if ( pVeh->m_fStrafeTime < MAX_STRAFE_TIME )
{//can strafe left for 2 seconds
float curStrafeSpeed = DotProduct( parentPS->velocity, vRight );
if ( curStrafeSpeed < 0.0f )
{//if < 0, already strafing left
strafeSpeed += curStrafeSpeed;//so it doesn't add up
}
if ( strafeSpeed > 0 )
{
VectorMA( parentPS->velocity, -strafeSpeed*pVeh->m_fTimeModifier, vRight, parentPS->velocity );
}
pVeh->m_fStrafeTime += 50*pVeh->m_fTimeModifier;
}
}
//strafing takes away from forward speed? If so, strafePerc above should use speedMax
//parentPS->speed *= (1.0f-pVeh->m_pVehicleInfo->strafePerc);
}
else//if ( pVeh->m_fStrafeTime )
{
if ( pVeh->m_fStrafeTime > 0 )
{
pVeh->m_fStrafeTime -= 50*pVeh->m_fTimeModifier;
if ( pVeh->m_fStrafeTime < 0 )
{
pVeh->m_fStrafeTime = 0.0f;
}
}
else if ( pVeh->m_fStrafeTime < 0 )
{
pVeh->m_fStrafeTime += 50*pVeh->m_fTimeModifier;
if ( pVeh->m_fStrafeTime > 0 )
{
pVeh->m_fStrafeTime = 0.0f;
}
}
}
//STRAFING==============================================================================
#endif
if ( parentPS->speed > speedMax )
{
parentPS->speed = speedMax;
}
else if ( parentPS->speed < speedMin )
{
parentPS->speed = speedMin;
}
#ifdef QAGAME//FIXME: get working in GAME and CGAME
if ((pVeh->m_vOrientation[PITCH]*0.1f) > 10.0f)
{ //pitched downward, increase speed more and more based on our tilt
if ( FighterIsInSpace( (gentity_t *)parent ) )
{//in space, do nothing with speed base on pitch...
}
else
{
//really should only do this when on a planet
float mult = pVeh->m_vOrientation[PITCH]*0.1f;
if (mult < 1.0f)
{
mult = 1.0f;
}
parentPS->speed = PredictedAngularDecrement(mult, pVeh->m_fTimeModifier*10.0f, parentPS->speed);
}
}
if (pVeh->m_iRemovedSurfaces
|| parentPS->electrifyTime>=curTime)
{ //going down
if ( FighterIsInSpace( (gentity_t *)parent ) )
{//we're in a valid trigger_space brush
//simulate randomness
if ( !(parent->s.number&3) )
{//even multiple of 3, don't do anything
parentPS->gravity = 0;
}
else if ( !(parent->s.number&2) )
{//even multiple of 2, go up
parentPS->gravity = -500.0f;
parentPS->velocity[2] = 80.0f;
}
else
{//odd number, go down
parentPS->gravity = 500.0f;
parentPS->velocity[2] = -80.0f;
}
}
else
{//over a planet
parentPS->gravity = 500.0f;
parentPS->velocity[2] = -80.0f;
}
}
else if ( FighterSuspended( pVeh, parentPS ) )
{
parentPS->gravity = 0;
}
else if ( (!parentPS->speed||parentPS->speed < speedIdle) && pVeh->m_ucmd.upmove <= 0 )
{//slowing down or stopped and not trying to take off
if ( FighterIsInSpace( (gentity_t *)parent ) )
{//we're in space, stopping doesn't make us drift downward
if ( FighterOverValidLandingSurface( pVeh ) )
{//well, there's something below us to land on, so go ahead and lower us down to it
parentPS->gravity = (speedIdle - parentPS->speed)/4;
}
}
else
{//over a planet
parentPS->gravity = (speedIdle - parentPS->speed)/4;
}
}
else
{
parentPS->gravity = 0;
}
#else//FIXME: get above checks working in GAME and CGAME
parentPS->gravity = 0;
#endif
/********************************************************************************/
/* END Here is where we move the vehicle (forward or back or whatever). END */
/********************************************************************************/
}
extern void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride );
static void FighterWingMalfunctionCheck( Vehicle_t *pVeh, playerState_t *parentPS )
{
float mPitchOverride = 1.0f;
float mYawOverride = 1.0f;
BG_VehicleTurnRateForSpeed( pVeh, parentPS->speed, &mPitchOverride, &mYawOverride );
//check right wing damage
if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_RIGHT_HEAVY)) )
{//right wing has taken heavy damage
pVeh->m_vOrientation[ROLL] += (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*50.0f;
}
else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_RIGHT_LIGHT)) )
{//right wing has taken light damage
pVeh->m_vOrientation[ROLL] += (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*12.5f;
}
//check left wing damage
if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_LEFT_HEAVY)) )
{//left wing has taken heavy damage
pVeh->m_vOrientation[ROLL] -= (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*50.0f;
}
else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_LEFT_LIGHT)) )
{//left wing has taken light damage
pVeh->m_vOrientation[ROLL] -= (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*12.5f;
}
}
static void FighterNoseMalfunctionCheck( Vehicle_t *pVeh, playerState_t *parentPS )
{
float mPitchOverride = 1.0f;
float mYawOverride = 1.0f;
BG_VehicleTurnRateForSpeed( pVeh, parentPS->speed, &mPitchOverride, &mYawOverride );
//check nose damage
if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_FRONT_HEAVY)) )
{//nose has taken heavy damage
//pitch up and down over time
pVeh->m_vOrientation[PITCH] += sin( pVeh->m_ucmd.serverTime*0.001 )*pVeh->m_fTimeModifier*mPitchOverride*50.0f;
}
else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_FRONT_LIGHT)) )
{//nose has taken heavy damage
//pitch up and down over time
pVeh->m_vOrientation[PITCH] += sin( pVeh->m_ucmd.serverTime*0.001 )*pVeh->m_fTimeModifier*mPitchOverride*20.0f;
}
}
static void FighterDamageRoutine( Vehicle_t *pVeh, bgEntity_t *parent, playerState_t *parentPS, playerState_t *riderPS, qboolean isDead )
{
if ( !pVeh->m_iRemovedSurfaces )
{//still in one piece
if ( pVeh->m_pParentEntity && isDead )
{//death spiral
pVeh->m_ucmd.upmove = 0;
//FIXME: don't bias toward pitching down when not in space
/*
if ( FighterIsInSpace( pVeh->m_pParentEntity ) )
{
}
else
*/
if ( !(pVeh->m_pParentEntity->s.number%3) )
{//NOT everyone should do this
pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
if ( pVeh->m_vOrientation[PITCH] > 60.0f )
{
pVeh->m_vOrientation[PITCH] = 60.0f;
}
}
}
else if ( !(pVeh->m_pParentEntity->s.number%2) )
{
pVeh->m_vOrientation[PITCH] -= pVeh->m_fTimeModifier;
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
if ( pVeh->m_vOrientation[PITCH] > -60.0f )
{
pVeh->m_vOrientation[PITCH] = -60.0f;
}
}
}
if ( (pVeh->m_pParentEntity->s.number%2) )
{
pVeh->m_vOrientation[YAW] += pVeh->m_fTimeModifier;
pVeh->m_vOrientation[ROLL] += pVeh->m_fTimeModifier*4.0f;
}
else
{
pVeh->m_vOrientation[YAW] -= pVeh->m_fTimeModifier;
pVeh->m_vOrientation[ROLL] -= pVeh->m_fTimeModifier*4.0f;
}
}
return;
}
//if we get into here we have at least one broken piece
pVeh->m_ucmd.upmove = 0;
//if you're off the ground and not suspended, pitch down
//FIXME: not in space!
if ( pVeh->m_LandTrace.fraction >= 0.1f )
{
if ( !FighterSuspended( pVeh, parentPS ) )
{
//pVeh->m_ucmd.forwardmove = 0;
//FIXME: don't bias towards pitching down when in space...
if ( !(pVeh->m_pParentEntity->s.number%2) )
{//NOT everyone should do this
pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
if ( pVeh->m_vOrientation[PITCH] > 60.0f )
{
pVeh->m_vOrientation[PITCH] = 60.0f;
}
}
}
else if ( !(pVeh->m_pParentEntity->s.number%3) )
{
pVeh->m_vOrientation[PITCH] -= pVeh->m_fTimeModifier;
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
if ( pVeh->m_vOrientation[PITCH] > -60.0f )
{
pVeh->m_vOrientation[PITCH] = -60.0f;
}
}
}
//else: just keep going forward
}
}
#ifdef QAGAME
if ( pVeh->m_LandTrace.fraction < 1.0f )
{ //if you land at all when pieces of your ship are missing, then die
gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
gentity_t *killer = parent;
#ifdef _JK2MP//only have this info in MP...
if (parent->client->ps.otherKiller < ENTITYNUM_WORLD &&
parent->client->ps.otherKillerTime > level.time)
{
gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller];
if (potentialKiller->inuse && potentialKiller->client)
{ //he's valid I guess
killer = potentialKiller;
}
}
#endif
G_Damage(parent, killer, killer, vec3_origin, parent->client->ps.origin, 99999, DAMAGE_NO_ARMOR, MOD_SUICIDE);
}
#endif
if ( ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D)) &&
((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F)) )
{ //wings on both side broken
float factor = 2.0f;
if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
{ //all wings broken
factor *= 2.0f;
}
if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
{//won't yaw, so increase roll factor
factor *= 4.0f;
}
pVeh->m_vOrientation[ROLL] += (pVeh->m_fTimeModifier*factor); //do some spiralling
}
else if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
{ //left wing broken
float factor = 2.0f;
if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
{ //if both are broken..
factor *= 2.0f;
}
if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
{//won't yaw, so increase roll factor
factor *= 4.0f;
}
pVeh->m_vOrientation[ROLL] += factor*pVeh->m_fTimeModifier;
}
else if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
{ //right wing broken
float factor = 2.0f;
if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
{ //if both are broken..
factor *= 2.0f;
}
if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
{//won't yaw, so increase roll factor
factor *= 4.0f;
}
pVeh->m_vOrientation[ROLL] -= factor*pVeh->m_fTimeModifier;
}
}
#ifdef _JK2MP
void FighterYawAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
{
float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
if (parentPS && parentPS->speed)
{
float s = parentPS->speed;
float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
if (s < 0.0f)
{
s = -s;
}
angDif *= s/pVeh->m_pVehicleInfo->speedMax;
if (angDif > maxDif)
{
angDif = maxDif;
}
else if (angDif < -maxDif)
{
angDif = -maxDif;
}
pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f));
}
}
void FighterPitchAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
{
float angDif = AngleSubtract(pVeh->m_vOrientation[PITCH], riderPS->viewangles[PITCH]);
if (parentPS && parentPS->speed)
{
float s = parentPS->speed;
float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
if (s < 0.0f)
{
s = -s;
}
angDif *= s/pVeh->m_pVehicleInfo->speedMax;
if (angDif > maxDif)
{
angDif = maxDif;
}
else if (angDif < -maxDif)
{
angDif = -maxDif;
}
pVeh->m_vOrientation[PITCH] = AngleNormalize360(pVeh->m_vOrientation[PITCH] - angDif*(pVeh->m_fTimeModifier*0.2f));
}
}
#endif
//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
//If you really need to violate this rule for SP, then use ifdefs.
//By BG-compatible, I mean no use of game-specific data - ONLY use
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
//as a gentity, but the MP-compatible access restrictions are based
//on the bgEntity structure in the MP codebase) -rww
// ProcessOrientCommands the Vehicle.
static void ProcessOrientCommands( Vehicle_t *pVeh )
{
/********************************************************************************/
/* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */
/********************************************************************************/
bgEntity_t *parent = pVeh->m_pParentEntity;
playerState_t *parentPS, *riderPS;
float angleTimeMod;
#ifdef QAGAME
const float groundFraction = 0.1f;
#endif
float curRoll = 0.0f;
qboolean isDead = qfalse;
qboolean isLandingOrLanded = qfalse;
#ifndef _JK2MP//SP
int curTime = level.time;
#elif QAGAME//MP GAME
int curTime = level.time;
#elif CGAME//MP CGAME
//FIXME: pass in ucmd? Not sure if this is reliable...
int curTime = pm->cmd.serverTime;
#endif
#ifdef _JK2MP
bgEntity_t *rider = NULL;
if (parent->s.owner != ENTITYNUM_NONE)
{
rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
}
#else
gentity_t *rider = parent->owner;
#endif
#ifdef _JK2MP
if ( !rider )
#else
if ( !rider || !rider->client )
#endif
{
rider = parent;
}
#ifdef _JK2MP
parentPS = parent->playerState;
riderPS = rider->playerState;
isDead = (qboolean)((parentPS->eFlags&EF_DEAD)!=0);
#else
parentPS = &parent->client->ps;
riderPS = &rider->client->ps;
isDead = (parentPS->stats[STAT_HEALTH] <= 0 );
#endif
#ifdef _JK2MP
if ( parentPS->hyperSpaceTime
&& (curTime - parentPS->hyperSpaceTime) < HYPERSPACE_TIME )
{//Going to Hyperspace
VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
VectorCopy( riderPS->viewangles, parentPS->viewangles );
return;
}
#endif
if ( pVeh->m_iDropTime >= curTime )
{//you can only YAW during this
parentPS->viewangles[YAW] = pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
return;
}
angleTimeMod = pVeh->m_fTimeModifier;
if ( isDead || parentPS->electrifyTime>=curTime ||
(pVeh->m_pVehicleInfo->surfDestruction &&
pVeh->m_iRemovedSurfaces &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F)) )
{ //do some special stuff for when all the wings are torn off
FighterDamageRoutine(pVeh, parent, parentPS, riderPS, isDead);
pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
return;
}
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
pVeh->m_vOrientation[ROLL] = PredictedAngularDecrement(0.95f, angleTimeMod*2.0f, pVeh->m_vOrientation[ROLL]);
}
isLandingOrLanded = (FighterIsLanding( pVeh, parentPS )||FighterIsLanded( pVeh, parentPS ));
if (!isLandingOrLanded)
{ //don't do this stuff while landed.. I guess. I don't want ships spinning in place, looks silly.
int m = 0;
float aVelDif;
float dForVel;
FighterWingMalfunctionCheck( pVeh, parentPS );
while (m < 3)
{
aVelDif = pVeh->m_vFullAngleVelocity[m];
if (aVelDif != 0.0f)
{
dForVel = (aVelDif*0.1f)*pVeh->m_fTimeModifier;
if (dForVel > 1.0f || dForVel < -1.0f)
{
pVeh->m_vOrientation[m] += dForVel;
pVeh->m_vOrientation[m] = AngleNormalize180(pVeh->m_vOrientation[m]);
if (m == PITCH)
{ //don't pitch downward into ground even more.
if (pVeh->m_vOrientation[m] > 90.0f && (pVeh->m_vOrientation[m]-dForVel) < 90.0f)
{
pVeh->m_vOrientation[m] = 90.0f;
pVeh->m_vFullAngleVelocity[m] = -pVeh->m_vFullAngleVelocity[m];
}
}
if (riderPS)
{
riderPS->viewangles[m] = pVeh->m_vOrientation[m];
}
pVeh->m_vFullAngleVelocity[m] -= dForVel;
}
else
{
pVeh->m_vFullAngleVelocity[m] = 0.0f;
}
}
m++;
}
}
else
{ //clear decr/incr angles once landed.
VectorClear(pVeh->m_vFullAngleVelocity);
}
curRoll = pVeh->m_vOrientation[ROLL];
// If we're landed, we shouldn't be able to do anything but take off.
if ( isLandingOrLanded //going slow enough to start landing
&& !pVeh->m_iRemovedSurfaces
&& parentPS->electrifyTime<curTime)//not spiraling out of control
{
if ( parentPS->speed > 0.0f )
{//Uh... what? Why?
if ( pVeh->m_LandTrace.fraction < 0.3f )
{
pVeh->m_vOrientation[PITCH] = 0.0f;
}
else
{
pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.83f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
}
}
if ( pVeh->m_LandTrace.fraction > 0.1f
|| pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )
{//off the ground, at least (or not on a valid landing surf)
// Dampen the turn rate based on the current height.
#ifdef _JK2MP
FighterYawAdjust(pVeh, riderPS, parentPS);
#else
pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];//*pVeh->m_LandTrace.fraction;
#endif
}
}
else if ( (pVeh->m_iRemovedSurfaces||parentPS->electrifyTime>=curTime)//spiralling out of control
&& (!(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5)) )
{//no yaw control
}
else if ( pVeh->m_pPilot && pVeh->m_pPilot->s.number < MAX_CLIENTS && parentPS->speed > 0.0f )//&& !( pVeh->m_ucmd.forwardmove > 0 && pVeh->m_LandTrace.fraction != 1.0f ) )
{
if ( BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
VectorCopy( riderPS->viewangles, parentPS->viewangles );
#ifdef _JK2MP
//BG_ExternThisSoICanRecompileInDebug( pVeh, riderPS );
#endif
curRoll = pVeh->m_vOrientation[ROLL];
FighterNoseMalfunctionCheck( pVeh, parentPS );
//VectorCopy( pVeh->m_vOrientation, parentPS->viewangles );
}
else
{
/*
float fTurnAmt[3];
//PITCH
fTurnAmt[PITCH] = riderPS->viewangles[PITCH] * 0.08f;
//YAW
fTurnAmt[YAW] = riderPS->viewangles[YAW] * 0.065f;
fTurnAmt[YAW] *= fTurnAmt[YAW];
// Dampen the turn rate based on the current height.
if ( riderPS->viewangles[YAW] < 0 )
{//must keep it negative because squaring a negative makes it positive
fTurnAmt[YAW] = -fTurnAmt[YAW];
}
fTurnAmt[YAW] *= pVeh->m_LandTrace.fraction;
//ROLL
fTurnAmt[2] = 0.0f;
*/
//Actal YAW
#ifdef _JK2MP
FighterYawAdjust(pVeh, riderPS, parentPS);
#else
pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
#endif
// If we are not hitting the ground, allow the fighter to pitch up and down.
if ( !FighterOverValidLandingSurface( pVeh )
|| parentPS->speed > MIN_LANDING_SPEED )
//if ( ( pVeh->m_LandTrace.fraction >= 1.0f || pVeh->m_ucmd.forwardmove != 0 ) && pVeh->m_LandTrace.fraction >= 0.0f )
{
float fYawDelta;
#ifdef _JK2MP
FighterPitchAdjust(pVeh, riderPS, parentPS);
#else
pVeh->m_vOrientation[PITCH] = riderPS->viewangles[PITCH];
#endif
FighterNoseMalfunctionCheck( pVeh, parentPS );
// Adjust the roll based on the turn amount and dampen it a little.
fYawDelta = AngleSubtract(pVeh->m_vOrientation[YAW], pVeh->m_vPrevOrientation[YAW]); //pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
if ( fYawDelta > 8.0f )
{
fYawDelta = 8.0f;
}
else if ( fYawDelta < -8.0f )
{
fYawDelta = -8.0f;
}
curRoll -= fYawDelta;
curRoll = PredictedAngularDecrement(0.93f, angleTimeMod*2.0f, curRoll);
//cap it reasonably
//NOTE: was hardcoded to 40.0f, now using extern data
if ( pVeh->m_pVehicleInfo->rollLimit != -1 )
{
if (curRoll > pVeh->m_pVehicleInfo->rollLimit )
{
curRoll = pVeh->m_pVehicleInfo->rollLimit;
}
else if (curRoll < -pVeh->m_pVehicleInfo->rollLimit)
{
curRoll = -pVeh->m_pVehicleInfo->rollLimit;
}
}
}
}
}
// If you are directly impacting the ground, even out your pitch.
if ( isLandingOrLanded )
{//only if capable of landing
if ( !isDead
&& parentPS->electrifyTime<curTime
&& (!pVeh->m_pVehicleInfo->surfDestruction || !pVeh->m_iRemovedSurfaces ) )
{//not crashing or spiralling out of control...
if ( pVeh->m_vOrientation[PITCH] > 0 )
{
pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.2f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
}
else
{
pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.75f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
}
}
}
/*
//NOTE: all this is redundant now since we have the FighterDamageRoutine func...
#ifdef _JK2MP //...yeah. Need to send armor across net for prediction to work.
if ( isDead )
#else
if ( pVeh->m_iArmor <= 0 )
#endif
{//going to explode
//FIXME: maybe make it erratically jerk or spin or start and stop?
#ifndef _JK2MP
if ( g_speederControlScheme->value > 0 || !rider || rider->s.number )
#else
if (1)
#endif
{
pVeh->m_ucmd.rightmove = Q_irand( -64, 64 );
}
else
{
pVeh->m_ucmd.rightmove = 0;
}
pVeh->m_ucmd.forwardmove = Q_irand( -32, 127 );
pVeh->m_ucmd.upmove = Q_irand( -127, 127 );
pVeh->m_vOrientation[YAW] += Q_flrand( -10, 10 );
pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
if ( pVeh->m_vOrientation[PITCH] > 60.0f )
{
pVeh->m_vOrientation[PITCH] = 60.0f;
}
if ( pVeh->m_LandTrace.fraction != 0.0f )
{
parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
}
}
*/
// If no one is in this vehicle and it's up in the sky, pitch it forward as it comes tumbling down.
#ifdef QAGAME //never gonna happen on client anyway, we can't be getting predicted unless the predicting client is boarded
if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh )
&& pVeh->m_LandTrace.fraction >= groundFraction
&& !FighterIsInSpace( (gentity_t *)parent )
&& !FighterSuspended( pVeh, parentPS ) )
{
pVeh->m_ucmd.upmove = 0;
//pVeh->m_ucmd.forwardmove = 0;
pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier;
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
if ( pVeh->m_vOrientation[PITCH] > 60.0f )
{
pVeh->m_vOrientation[PITCH] = 60.0f;
}
}
}
#endif
if ( !pVeh->m_fStrafeTime )
{//use that roll
pVeh->m_vOrientation[ROLL] = curRoll;
//NOTE: this seems really backwards...
if ( pVeh->m_vOrientation[ROLL] )
{ //continually adjust the yaw based on the roll..
if ( (pVeh->m_iRemovedSurfaces||parentPS->electrifyTime>=curTime)//spiralling out of control
&& (!(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5)) )
{//leave YAW alone
}
else
{
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{
pVeh->m_vOrientation[YAW] -= ((pVeh->m_vOrientation[ROLL])*0.05f)*pVeh->m_fTimeModifier;
}
}
}
}
else
{//add in strafing roll
float strafeRoll = (pVeh->m_fStrafeTime/MAX_STRAFE_TIME)*pVeh->m_pVehicleInfo->rollLimit;//pVeh->m_pVehicleInfo->bankingSpeed*
float strafeDif = AngleSubtract(strafeRoll, pVeh->m_vOrientation[ROLL]);
pVeh->m_vOrientation[ROLL] += (strafeDif*0.1f)*pVeh->m_fTimeModifier;
if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
{//cap it reasonably
if ( pVeh->m_pVehicleInfo->rollLimit != -1
&& !pVeh->m_iRemovedSurfaces
&& parentPS->electrifyTime<curTime)
{
if (pVeh->m_vOrientation[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
{
pVeh->m_vOrientation[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
}
else if (pVeh->m_vOrientation[ROLL] < -pVeh->m_pVehicleInfo->rollLimit)
{
pVeh->m_vOrientation[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
}
}
}
}
if (pVeh->m_pVehicleInfo->surfDestruction)
{
FighterDamageRoutine(pVeh, parent, parentPS, riderPS, isDead);
}
pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
/********************************************************************************/
/* END Here is where make sure the vehicle is properly oriented. END */
/********************************************************************************/
}
#ifdef QAGAME //ONLY in SP or on server, not cgame
extern void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
// This function makes sure that the vehicle is properly animated.
static void AnimateVehicle( Vehicle_t *pVeh )
{
int Anim = -1;
int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300;
qboolean isLanding = qfalse, isLanded = qfalse;
#ifdef _JK2MP
playerState_t *parentPS = pVeh->m_pParentEntity->playerState;
#else
playerState_t *parentPS = &pVeh->m_pParentEntity->client->ps;
#endif
#ifndef _JK2MP//SP
//nothing
#elif QAGAME//MP GAME
int curTime = level.time;
#elif CGAME//MP CGAME
//FIXME: pass in ucmd? Not sure if this is reliable...
int curTime = pm->cmd.serverTime;
#endif
#ifdef _JK2MP
if ( parentPS->hyperSpaceTime
&& curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME )
{//Going to Hyperspace
//close the wings (FIXME: makes sense on X-Wing, not Shuttle?)
if ( pVeh->m_ulFlags & VEH_WINGSOPEN )
{
pVeh->m_ulFlags &= ~VEH_WINGSOPEN;
Anim = BOTH_WINGS_CLOSE;
}
}
else
#endif
{
isLanding = FighterIsLanding( pVeh, parentPS );
isLanded = FighterIsLanded( pVeh, parentPS );
// if we're above launch height (way up in the air)...
if ( !isLanding && !isLanded )
{
if ( !( pVeh->m_ulFlags & VEH_WINGSOPEN ) )
{
pVeh->m_ulFlags |= VEH_WINGSOPEN;
pVeh->m_ulFlags &= ~VEH_GEARSOPEN;
Anim = BOTH_WINGS_OPEN;
}
}
// otherwise we're below launch height and still taking off.
else
{
if ( (pVeh->m_ucmd.forwardmove < 0 || pVeh->m_ucmd.upmove < 0||isLanded)
&& pVeh->m_LandTrace.fraction <= 0.4f
&& pVeh->m_LandTrace.plane.normal[2] >= MIN_LANDING_SLOPE )
{//already landed or trying to land and close to ground
// Open gears.
if ( !( pVeh->m_ulFlags & VEH_GEARSOPEN ) )
{
#ifdef _JK2MP
if ( pVeh->m_pVehicleInfo->soundLand )
{//just landed?
#ifdef QAGAME//MP GAME-side
G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundLand );
#elif CGAME//MP CGAME-side
//trap_S_StartSound( NULL, pVeh->m_pParentEntity->s.number, CHAN_AUTO, pVeh->m_pVehicleInfo->soundLand );
#endif
}
#endif
pVeh->m_ulFlags |= VEH_GEARSOPEN;
Anim = BOTH_GEARS_OPEN;
}
}
else
{//trying to take off and almost halfway off the ground
// Close gears (if they're open).
if ( pVeh->m_ulFlags & VEH_GEARSOPEN )
{
pVeh->m_ulFlags &= ~VEH_GEARSOPEN;
Anim = BOTH_GEARS_CLOSE;
//iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
}
// If gears are closed, and we are below launch height, close the wings.
else
{
if ( pVeh->m_ulFlags & VEH_WINGSOPEN )
{
pVeh->m_ulFlags &= ~VEH_WINGSOPEN;
Anim = BOTH_WINGS_CLOSE;
}
}
}
}
}
if ( Anim != -1 )
{
#ifdef _JK2MP
BG_SetAnim(pVeh->m_pParentEntity->playerState, bgAllAnims[pVeh->m_pParentEntity->localAnimIndex].anims,
SETANIM_BOTH, Anim, iFlags, iBlend);
#else
NPC_SetAnim( pVeh->m_pParentEntity, SETANIM_BOTH, Anim, iFlags, iBlend );
#endif
}
}
// This function makes sure that the rider's in this vehicle are properly animated.
static void AnimateRiders( Vehicle_t *pVeh )
{
}
#endif //game-only
#ifndef QAGAME
void AttachRidersGeneric( Vehicle_t *pVeh );
#endif
void G_SetFighterVehicleFunctions( vehicleInfo_t *pVehInfo )
{
#ifdef QAGAME //ONLY in SP or on server, not cgame
pVehInfo->AnimateVehicle = AnimateVehicle;
pVehInfo->AnimateRiders = AnimateRiders;
// pVehInfo->ValidateBoard = ValidateBoard;
// pVehInfo->SetParent = SetParent;
// pVehInfo->SetPilot = SetPilot;
// pVehInfo->AddPassenger = AddPassenger;
// pVehInfo->Animate = Animate;
pVehInfo->Board = Board;
pVehInfo->Eject = Eject;
// pVehInfo->EjectAll = EjectAll;
// pVehInfo->StartDeathDelay = StartDeathDelay;
// pVehInfo->DeathUpdate = DeathUpdate;
// pVehInfo->RegisterAssets = RegisterAssets;
// pVehInfo->Initialize = Initialize;
pVehInfo->Update = Update;
// pVehInfo->UpdateRider = UpdateRider;
#endif //game-only
pVehInfo->ProcessMoveCommands = ProcessMoveCommands;
pVehInfo->ProcessOrientCommands = ProcessOrientCommands;
#ifndef QAGAME //cgame prediction attachment func
pVehInfo->AttachRiders = AttachRidersGeneric;
#endif
// pVehInfo->AttachRiders = AttachRiders;
// pVehInfo->Ghost = Ghost;
// pVehInfo->UnGhost = UnGhost;
// pVehInfo->Inhabited = Inhabited;
}
// Following is only in game, not in namespace
#ifdef _JK2MP
#include "../namespace_end.h"
#endif
#ifdef QAGAME
extern void G_AllocateVehicleObject(Vehicle_t **pVeh);
#endif
#ifdef _JK2MP
#include "../namespace_begin.h"
#endif
// Create/Allocate a new Animal Vehicle (initializing it as well).
void G_CreateFighterNPC( Vehicle_t **pVeh, const char *strType )
{
// Allocate the Vehicle.
#ifdef _JK2MP
#ifdef QAGAME
//these will remain on entities on the client once allocated because the pointer is
//never stomped. on the server, however, when an ent is freed, the entity struct is
//memset to 0, so this memory would be lost..
G_AllocateVehicleObject(pVeh);
#else
if (!*pVeh)
{ //only allocate a new one if we really have to
(*pVeh) = (Vehicle_t *) BG_Alloc( sizeof(Vehicle_t) );
}
#endif
memset(*pVeh, 0, sizeof(Vehicle_t));
#else
(*pVeh) = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qtrue );
#endif
(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strType )];
}
#ifdef _JK2MP
#include "../namespace_end.h"
//get rid of all the crazy defs we added for this file
#undef currentAngles
#undef currentOrigin
#undef mins
#undef maxs
#undef legsAnimTimer
#undef torsoAnimTimer
#undef bool
#undef false
#undef true
#undef sqrtf
#undef Q_flrand
#undef MOD_EXPLOSIVE
#endif