codemp/game/FighterNPC.c

Go to the documentation of this file.
00001 // leave this line at the top for all g_xxxx.cpp files...
00002 #include "g_headers.h"
00003 
00004 //seems to be a compiler bug, it doesn't clean out the #ifdefs between dif-compiles
00005 //or something, so the headers spew errors on these defs from the previous compile.
00006 //this fixes that. -rww
00007 #ifdef _JK2MP
00008 //get rid of all the crazy defs we added for this file
00009 #undef currentAngles
00010 #undef currentOrigin
00011 #undef mins
00012 #undef maxs
00013 #undef legsAnimTimer
00014 #undef torsoAnimTimer
00015 #undef bool
00016 #undef false
00017 #undef true
00018 
00019 #undef sqrtf
00020 #undef Q_flrand
00021 
00022 #undef MOD_EXPLOSIVE
00023 #endif
00024 
00025 #ifdef _JK2 //SP does not have this preprocessor for game like MP does
00026 #ifndef _JK2MP
00027 #define _JK2MP
00028 #endif
00029 #endif
00030 
00031 #ifndef _JK2MP //if single player
00032 #ifndef QAGAME //I don't think we have a QAGAME define
00033 #define QAGAME //but define it cause in sp we're always in the game
00034 #endif
00035 #endif
00036 
00037 #ifdef QAGAME //including game headers on cgame is FORBIDDEN ^_^
00038 #include "g_local.h"
00039 #elif defined _JK2MP
00040 #include "bg_public.h"
00041 #endif
00042 
00043 #ifndef _JK2MP
00044 #include "g_functions.h"
00045 #include "g_vehicles.h"
00046 #else
00047 #include "bg_vehicles.h"
00048 #endif
00049 
00050 #ifdef _JK2MP
00051 //this is really horrible, but it works! just be sure not to use any locals or anything
00052 //with these names (exluding bool, false, true). -rww
00053 #define currentAngles r.currentAngles
00054 #define currentOrigin r.currentOrigin
00055 #define mins r.mins
00056 #define maxs r.maxs
00057 #define legsAnimTimer legsTimer
00058 #define torsoAnimTimer torsoTimer
00059 #define bool qboolean
00060 #define false qfalse
00061 #define true qtrue
00062 
00063 #define sqrtf sqrt
00064 #define Q_flrand flrand
00065 
00066 #define MOD_EXPLOSIVE MOD_SUICIDE
00067 #else
00068 #define bgEntity_t gentity_t
00069 #endif
00070 
00071 extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
00072 #ifdef QAGAME //SP or gameside MP
00073 extern vmCvar_t cg_thirdPersonAlpha;
00074 extern vec3_t playerMins;
00075 extern vec3_t playerMaxs;
00076 extern cvar_t   *g_speederControlScheme;
00077 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
00078 extern void PM_SetAnim(pmove_t  *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
00079 extern int PM_AnimLength( int index, animNumber_t anim );
00080 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 );
00081 #endif
00082 
00083 extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
00084 
00085 #ifdef _JK2MP
00086 
00087 #include "../namespace_begin.h"
00088 
00089 extern void BG_SetAnim(playerState_t *ps, animation_t *animations, int setAnimParts,int anim,int setAnimFlags, int blendTime);
00090 extern int BG_GetTime(void);
00091 #endif
00092 
00093 extern void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS );
00094 
00095 //this stuff has got to be predicted, so..
00096 bool BG_FighterUpdate(Vehicle_t *pVeh, const usercmd_t *pUcmd, vec3_t trMins, vec3_t trMaxs, float gravity,
00097                                           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 ))
00098 {
00099         vec3_t          bottom;
00100         playerState_t *parentPS;
00101         qboolean        isDead = qfalse;
00102 #ifdef QAGAME //don't do this on client
00103         int i;
00104 
00105         // Make sure the riders are not visible or collidable.
00106         pVeh->m_pVehicleInfo->Ghost( pVeh, pVeh->m_pPilot );
00107         for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
00108         {
00109                 pVeh->m_pVehicleInfo->Ghost( pVeh, pVeh->m_ppPassengers[i] );
00110         }
00111 #endif
00112 
00113 
00114 #ifdef _JK2MP
00115         parentPS = pVeh->m_pParentEntity->playerState;
00116 #else
00117         parentPS = &pVeh->m_pParentEntity->client->ps;
00118 #endif
00119 
00120         if (!parentPS)
00121         {
00122                 Com_Error(ERR_DROP, "NULL PS in BG_FighterUpdate (%s)", pVeh->m_pVehicleInfo->name);
00123                 return false;
00124         }
00125 
00126         // If we have a pilot, take out gravity (it's a flying craft...).
00127         if ( pVeh->m_pPilot )
00128         {
00129                 parentPS->gravity = 0;
00130 #ifndef _JK2MP //don't need this flag in mp, I.. guess
00131                 pVeh->m_pParentEntity->svFlags |= SVF_CUSTOM_GRAVITY;
00132 #endif
00133         }
00134         else
00135         {
00136 #ifndef _JK2MP //don't need this flag in mp, I.. guess
00137                 pVeh->m_pParentEntity->svFlags &= ~SVF_CUSTOM_GRAVITY;
00138 #else //in MP set grav back to normal gravity
00139                 if (pVeh->m_pVehicleInfo->gravity)
00140                 {
00141                         parentPS->gravity = pVeh->m_pVehicleInfo->gravity;
00142                 }
00143                 else
00144                 { //it doesn't have gravity specified apparently
00145                         parentPS->gravity = gravity;
00146                 }
00147 #endif
00148         }
00149 
00150 #ifdef _JK2MP
00151         isDead = (qboolean)((parentPS->eFlags&EF_DEAD)!=0);
00152 #else
00153         isDead = (parentPS->stats[STAT_HEALTH] <= 0 );
00154 #endif
00155 
00156         /*
00157         if ( isDead || 
00158                 (pVeh->m_pVehicleInfo->surfDestruction &&
00159                         pVeh->m_iRemovedSurfaces ) )
00160         {//can't land if dead or spiralling out of control
00161                 pVeh->m_LandTrace.fraction = 1.0f;
00162                 pVeh->m_LandTrace.contents = pVeh->m_LandTrace.surfaceFlags = 0;
00163                 VectorClear( pVeh->m_LandTrace.plane.normal );
00164                 pVeh->m_LandTrace.allsolid = qfalse;
00165                 pVeh->m_LandTrace.startsolid = qfalse;
00166         }
00167         else
00168         {
00169         */
00170         //argh, no, I need to have a way to see when they impact the ground while damaged. -rww
00171 
00172                 // Check to see if the fighter has taken off yet (if it's a certain height above ground).
00173                 VectorCopy( parentPS->origin, bottom );
00174                 bottom[2] -= pVeh->m_pVehicleInfo->landingHeight;
00175 
00176                 traceFunc( &pVeh->m_LandTrace, parentPS->origin, trMins, trMaxs, bottom, pVeh->m_pParentEntity->s.number, (MASK_NPCSOLID&~CONTENTS_BODY) );
00177         //}
00178 
00179         return true;
00180 }
00181 
00182 #ifdef QAGAME //ONLY in SP or on server, not cgame
00183 
00184 // Like a think or move command, this updates various vehicle properties.
00185 static bool Update( Vehicle_t *pVeh, const usercmd_t *pUcmd )
00186 {
00187         assert(pVeh->m_pParentEntity);
00188         if (!BG_FighterUpdate(pVeh, pUcmd, ((gentity_t *)pVeh->m_pParentEntity)->mins,
00189                 ((gentity_t *)pVeh->m_pParentEntity)->maxs, 
00190 #ifdef _JK2MP
00191                 g_gravity.value,
00192 #else
00193                 g_gravity->value,
00194 #endif
00195                 G_VehicleTrace))
00196         {
00197                 return false;
00198         }
00199 
00200         if ( !g_vehicleInfo[VEHICLE_BASE].Update( pVeh, pUcmd ) )
00201         {
00202                 return false;
00203         }
00204 
00205         return true;
00206 }
00207 
00208 // Board this Vehicle (get on). The first entity to board an empty vehicle becomes the Pilot.
00209 static bool Board( Vehicle_t *pVeh, bgEntity_t *pEnt )
00210 {
00211         if ( !g_vehicleInfo[VEHICLE_BASE].Board( pVeh, pEnt ) )
00212                 return false;
00213 
00214         // Set the board wait time (they won't be able to do anything, including getting off, for this amount of time).
00215         pVeh->m_iBoarding = level.time + 1500;
00216 
00217         return true;
00218 }
00219 
00220 // Eject an entity from the vehicle.
00221 static bool Eject( Vehicle_t *pVeh, bgEntity_t *pEnt, qboolean forceEject )
00222 {
00223         if ( g_vehicleInfo[VEHICLE_BASE].Eject( pVeh, pEnt, forceEject ) )
00224         {
00225                 return true;
00226         }
00227         
00228         return false;
00229 }
00230 
00231 #endif //end game-side only
00232 
00233 //method of decrementing the given angle based on the given taking variable frame times into account
00234 static float PredictedAngularDecrement(float scale, float timeMod, float originalAngle)
00235 {
00236         float fixedBaseDec = originalAngle*0.05f;
00237         float r = 0.0f;
00238 
00239         if (fixedBaseDec < 0.0f)
00240         {
00241                 fixedBaseDec = -fixedBaseDec;
00242         }
00243         
00244         fixedBaseDec *= (1.0f+(1.0f-scale));
00245 
00246         if (fixedBaseDec < 0.1f)
00247         { //don't increment in incredibly small fractions, it would eat up unnecessary bandwidth.
00248                 fixedBaseDec = 0.1f;
00249         }
00250 
00251         fixedBaseDec *= (timeMod*0.1f);
00252         if (originalAngle > 0.0f)
00253         { //subtract
00254                 r = (originalAngle-fixedBaseDec);
00255                 if (r < 0.0f)
00256                 {
00257                         r = 0.0f;
00258                 }
00259         }
00260         else if (originalAngle < 0.0f)
00261         { //add
00262                 r = (originalAngle+fixedBaseDec);
00263                 if (r > 0.0f)
00264                 {
00265                         r = 0.0f;
00266                 }
00267         }
00268 
00269         return r;
00270 }
00271 
00272 #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
00273 qboolean FighterIsInSpace( gentity_t *gParent )
00274 {
00275         if ( gParent 
00276                 && gParent->client 
00277                 && gParent->client->inSpaceIndex
00278                 && gParent->client->inSpaceIndex < ENTITYNUM_WORLD )
00279         {
00280                 return qtrue;
00281         }
00282         return qfalse;
00283 }
00284 #endif
00285 
00286 qboolean FighterOverValidLandingSurface( Vehicle_t *pVeh )
00287 {
00288         if ( pVeh->m_LandTrace.fraction < 1.0f //ground present
00289                 && pVeh->m_LandTrace.plane.normal[2] >= MIN_LANDING_SLOPE )//flat enough
00290                 //FIXME: also check for a certain surface flag ... "landing zones"?
00291         {
00292                 return qtrue;
00293         }
00294         return qfalse;
00295 }
00296 
00297 qboolean FighterIsLanded( Vehicle_t *pVeh, playerState_t *parentPS )
00298 {
00299         if ( FighterOverValidLandingSurface( pVeh )
00300                 && !parentPS->speed )//stopped
00301         {
00302                 return qtrue;
00303         }
00304         return qfalse;
00305 }
00306 
00307 qboolean FighterIsLanding( Vehicle_t *pVeh, playerState_t *parentPS )
00308 {
00309 
00310         if ( FighterOverValidLandingSurface( pVeh )
00311 #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
00312                 && pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
00313 #endif
00314                 && (pVeh->m_ucmd.forwardmove < 0||pVeh->m_ucmd.upmove<0) //decelerating or holding crouch button
00315                 && parentPS->speed <= MIN_LANDING_SPEED )//going slow enough to start landing - was using pVeh->m_pVehicleInfo->speedIdle, but that's still too fast
00316         {
00317                 return qtrue;
00318         }
00319         return qfalse;
00320 }
00321 
00322 qboolean FighterIsLaunching( Vehicle_t *pVeh, playerState_t *parentPS )
00323 {
00324 
00325         if ( FighterOverValidLandingSurface( pVeh )
00326 #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
00327                 && pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
00328 #endif
00329                 && pVeh->m_ucmd.upmove > 0 //trying to take off
00330                 && parentPS->speed <= 200.0f )//going slow enough to start landing - was using pVeh->m_pVehicleInfo->speedIdle, but that's still too fast
00331         {
00332                 return qtrue;
00333         }
00334         return qfalse;
00335 }
00336 
00337 qboolean FighterSuspended( Vehicle_t *pVeh, playerState_t *parentPS )
00338 {
00339 #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
00340         if (!pVeh->m_pPilot//empty
00341                 && !parentPS->speed//not moving
00342                 && pVeh->m_ucmd.forwardmove <= 0//not trying to go forward for whatever reason
00343                 && pVeh->m_pParentEntity != NULL
00344                 && (((gentity_t *)pVeh->m_pParentEntity)->spawnflags&2) )//SUSPENDED spawnflag is on
00345         {
00346                 return qtrue;
00347         }
00348         return qfalse;
00349 #elif CGAME
00350         return qfalse;
00351 #endif
00352 }
00353 
00354 #ifdef CGAME
00355 extern void trap_S_StartSound( vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); //cg_syscalls.c
00356 extern sfxHandle_t trap_S_RegisterSound( const char *sample); //cg_syscalls.c
00357 #endif
00358 
00359 //MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
00360 //If you really need to violate this rule for SP, then use ifdefs.
00361 //By BG-compatible, I mean no use of game-specific data - ONLY use
00362 //stuff available in the MP bgEntity (in SP, the bgEntity is #defined
00363 //as a gentity, but the MP-compatible access restrictions are based
00364 //on the bgEntity structure in the MP codebase) -rww
00365 // ProcessMoveCommands the Vehicle.
00366 #define FIGHTER_MIN_TAKEOFF_FRACTION 0.7f
00367 static void ProcessMoveCommands( Vehicle_t *pVeh )
00368 {
00369         /************************************************************************************/
00370         /*      BEGIN   Here is where we move the vehicle (forward or back or whatever). BEGIN  */
00371         /************************************************************************************/
00372 
00373         //Client sets ucmds and such for speed alterations
00374         float speedInc, speedIdleDec, speedIdle, speedIdleAccel, speedMin, speedMax;
00375         bgEntity_t *parent = pVeh->m_pParentEntity;
00376         qboolean isLandingOrLaunching = qfalse;
00377         /*
00378 #ifndef _JK2MP//SP
00379         int curTime = level.time;
00380 #elif QAGAME//MP GAME
00381         int curTime = level.time;
00382 #elif CGAME//MP CGAME
00383         //FIXME: pass in ucmd?  Not sure if this is reliable...
00384         int curTime = pm->cmd.serverTime;
00385 #endif
00386         */
00387         //this function should only be called from pmove.. if it gets called elsehwere,
00388         //obviously this will explode.
00389         int curTime = pm->cmd.serverTime;
00390 
00391 #ifdef _JK2MP
00392         playerState_t *parentPS = parent->playerState;
00393 #else
00394         playerState_t *parentPS = &parent->client->ps;
00395 #endif
00396 
00397 #ifdef _JK2MP
00398         if ( parentPS->hyperSpaceTime
00399                 && curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME )
00400         {//Going to Hyperspace
00401                 //totally override movement
00402                 float timeFrac = ((float)(curTime-parentPS->hyperSpaceTime))/HYPERSPACE_TIME;
00403                 if ( timeFrac < HYPERSPACE_TELEPORT_FRAC )
00404                 {//for first half, instantly jump to top speed!
00405                         if ( !(parentPS->eFlags2&EF2_HYPERSPACE) )
00406                         {//waiting to face the right direction, do nothing
00407                                 parentPS->speed = 0.0f;
00408                         }
00409                         else
00410                         {
00411                                 if ( parentPS->speed < HYPERSPACE_SPEED )
00412                                 {//just started hyperspace
00413 //MIKE: This is going to play the sound twice for the predicting client, I suggest using
00414 //a predicted event or only doing it game-side. -rich
00415 #ifdef QAGAME//MP GAME-side
00416                                         //G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_LOCAL, pVeh->m_pVehicleInfo->soundHyper );
00417 #elif CGAME//MP CGAME-side
00418                                         trap_S_StartSound( NULL, pm->ps->clientNum, CHAN_LOCAL, pVeh->m_pVehicleInfo->soundHyper );
00419 #endif                  
00420                                 }
00421 
00422                                 parentPS->speed = HYPERSPACE_SPEED;
00423                         }
00424                 }
00425                 else
00426                 {//slow from top speed to 200...
00427                         parentPS->speed = 200.0f + ((1.0f-timeFrac)*(1.0f/HYPERSPACE_TELEPORT_FRAC)*(HYPERSPACE_SPEED-200.0f));
00428                         //don't mess with acceleration, just pop to the high velocity
00429                         if ( VectorLength( parentPS->velocity ) < parentPS->speed )
00430                         {
00431                                 VectorScale( parentPS->moveDir, parentPS->speed, parentPS->velocity );
00432                         }
00433                 }
00434                 return;
00435         }
00436 #endif
00437 
00438         if ( pVeh->m_iDropTime >= curTime )
00439         {//no speed, just drop
00440                 parentPS->speed = 0.0f;
00441                 parentPS->gravity = 800;
00442                 return;
00443         }
00444 
00445         isLandingOrLaunching = (FighterIsLanding( pVeh, parentPS )||FighterIsLaunching( pVeh, parentPS ));
00446 
00447         // If we are hitting the ground, just allow the fighter to go up and down.
00448         if ( isLandingOrLaunching//going slow enough to start landing
00449                 && (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)
00450         {//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...
00451                 //float fInvFrac = 1.0f - pVeh->m_LandTrace.fraction; 
00452 
00453                 if ( pVeh->m_ucmd.upmove > 0 )
00454                 {
00455 #ifdef _JK2MP
00456                         if ( parentPS->velocity[2] <= 0 
00457                                 && pVeh->m_pVehicleInfo->soundTakeOff )
00458                         {//taking off for the first time
00459 #ifdef QAGAME//MP GAME-side
00460                                 G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundTakeOff );
00461 #endif
00462                         }
00463 #endif
00464                         parentPS->velocity[2] += pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;// * ( /*fInvFrac **/ 1.5f );
00465                 }
00466                 else if ( pVeh->m_ucmd.upmove < 0 )
00467                 {
00468                         parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;// * ( /*fInvFrac **/ 1.8f );
00469                 }
00470                 else if ( pVeh->m_ucmd.forwardmove < 0 )
00471                 {
00472                         if ( pVeh->m_LandTrace.fraction != 0.0f ) 
00473                         {
00474                                 parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
00475                         }
00476 
00477                         if ( pVeh->m_LandTrace.fraction <= FIGHTER_MIN_TAKEOFF_FRACTION )
00478                         {
00479                                 //pVeh->m_pParentEntity->client->ps.velocity[0] *= pVeh->m_LandTrace.fraction;
00480                                 //pVeh->m_pParentEntity->client->ps.velocity[1] *= pVeh->m_LandTrace.fraction;
00481 
00482                                 //remember to always base this stuff on the time modifier! otherwise, you create
00483                                 //framerate-dependancy issues and break prediction in MP -rww
00484                                 //parentPS->velocity[2] *= pVeh->m_LandTrace.fraction;
00485                                 //it's not an angle, but hey
00486                                 parentPS->velocity[2] = PredictedAngularDecrement(pVeh->m_LandTrace.fraction, pVeh->m_fTimeModifier*5.0f, parentPS->velocity[2]);
00487                         
00488                                 parentPS->speed = 0;
00489                         }
00490                 }
00491 
00492                 // Make sure they don't pitch as they near the ground.
00493                 //pVeh->m_vOrientation[PITCH] *= 0.7f;
00494                 pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.7f, pVeh->m_fTimeModifier*10.0f, pVeh->m_vOrientation[PITCH]);
00495 
00496                 return;
00497         }
00498 
00499         if ( (pVeh->m_ucmd.upmove > 0) && pVeh->m_pVehicleInfo->turboSpeed )
00500         {
00501                 if ((curTime - pVeh->m_iTurboTime)>pVeh->m_pVehicleInfo->turboRecharge)
00502                 {
00503                         pVeh->m_iTurboTime = (curTime + pVeh->m_pVehicleInfo->turboDuration);
00504 
00505 #ifdef QAGAME//MP GAME-side
00506                         //NOTE: turbo sound can't be part of effect if effect is played on every muzzle!
00507                         if ( pVeh->m_pVehicleInfo->soundTurbo )
00508                         {
00509                                 G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo );
00510                         }
00511 #endif
00512                 }
00513         }
00514         speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
00515         if ( curTime < pVeh->m_iTurboTime )
00516         {//going turbo speed
00517                 speedMax = pVeh->m_pVehicleInfo->turboSpeed;
00518                 //double our acceleration
00519                 //speedInc *= 2.0f;
00520                 //no no no! this would el breako el predictiono! we want the following... -rww
00521         speedInc = (pVeh->m_pVehicleInfo->acceleration*2.0f) * pVeh->m_fTimeModifier;
00522 
00523                 //force us to move forward
00524                 pVeh->m_ucmd.forwardmove = 127;
00525 #ifdef _JK2MP//SP can cheat and just check m_iTurboTime directly... :)
00526                 //add flag to let cgame know to draw the iTurboFX effect
00527                 parentPS->eFlags |= EF_JETPACK_ACTIVE;
00528 #endif
00529         }
00530         /*
00531         //FIXME: if turbotime is up and we're waiting for it to recharge, should our max speed drop while we recharge?
00532         else if ( (curTime - pVeh->m_iTurboTime)<3000 )
00533         {//still waiting for the recharge
00534                 speedMax = pVeh->m_pVehicleInfo->speedMax*0.75;
00535         }
00536         */
00537         else
00538         {//normal max speed
00539                 speedMax = pVeh->m_pVehicleInfo->speedMax;
00540 #ifdef _JK2MP//SP can cheat and just check m_iTurboTime directly... :)
00541                 if ( (parentPS->eFlags&EF_JETPACK_ACTIVE) )
00542                 {//stop cgame from playing the turbo exhaust effect
00543                         parentPS->eFlags &= ~EF_JETPACK_ACTIVE;
00544                 }
00545 #endif
00546         }
00547         speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier;
00548         speedIdle = pVeh->m_pVehicleInfo->speedIdle;
00549         speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier;
00550         speedMin = pVeh->m_pVehicleInfo->speedMin;
00551 
00552         if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_HEAVY)) )
00553         {//engine has taken heavy damage
00554                 speedMax *= 0.8f;//at 80% speed
00555         }
00556         else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_BACK_LIGHT)) )
00557         {//engine has taken light damage
00558                 speedMax *= 0.6f;//at 60% speed
00559         }
00560 
00561         if (pVeh->m_iRemovedSurfaces
00562                 || parentPS->electrifyTime>=curTime)
00563         { //go out of control
00564                 parentPS->speed += speedInc;
00565                 //Why set forwardmove?  PMove code doesn't use it... does it?
00566                 pVeh->m_ucmd.forwardmove = 127;
00567         }
00568 #ifdef QAGAME //well, the thing is always going to be inhabited if it's being predicted!
00569         else if ( FighterSuspended( pVeh, parentPS ) )
00570         {
00571                 parentPS->speed = 0;
00572                 pVeh->m_ucmd.forwardmove = 0;
00573         }
00574         else if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) 
00575                 && parentPS->speed > 0 )
00576         {//pilot jumped out while we were moving forward (not landing or landed) so just keep the throttle locked
00577                 //Why set forwardmove?  PMove code doesn't use it... does it?
00578                 pVeh->m_ucmd.forwardmove = 127;
00579         }
00580 #endif
00581         else if ( ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE  ||
00582                  pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 ) && pVeh->m_LandTrace.fraction >= 0.05f ) 
00583         {
00584                 if ( pVeh->m_ucmd.forwardmove > 0 && speedInc )
00585                 {
00586                         parentPS->speed += speedInc;
00587                         pVeh->m_ucmd.forwardmove = 127;
00588                 }
00589                 else if ( pVeh->m_ucmd.forwardmove < 0 
00590                         || pVeh->m_ucmd.upmove < 0 )
00591                 {//decelerating or braking
00592                         if ( pVeh->m_ucmd.upmove < 0 )
00593                         {//braking (trying to land?), slow down faster
00594                                 if ( pVeh->m_ucmd.forwardmove )
00595                                 {//decelerator + brakes
00596                                         speedInc += pVeh->m_pVehicleInfo->braking;
00597                                         speedIdleDec += pVeh->m_pVehicleInfo->braking;
00598                                 }
00599                                 else
00600                                 {//just brakes
00601                                         speedInc = speedIdleDec = pVeh->m_pVehicleInfo->braking;
00602                                 }
00603                         }
00604                         if ( parentPS->speed > speedIdle )
00605                         {
00606                                 parentPS->speed -= speedInc;
00607                         }
00608                         else if ( parentPS->speed > speedMin )
00609                         {
00610                                 if ( FighterOverValidLandingSurface( pVeh ) )
00611                                 {//there's ground below us and we're trying to slow down, slow down faster
00612                                         parentPS->speed -= speedInc;
00613                                 }
00614                                 else
00615                                 {
00616                                         parentPS->speed -= speedIdleDec;
00617                                         if ( parentPS->speed < MIN_LANDING_SPEED )
00618                                         {//unless you can land, don't drop below the landing speed!!!  This way you can't come to a dead stop in mid-air
00619                                                 parentPS->speed = MIN_LANDING_SPEED;
00620                                         }
00621                                 }
00622                         }
00623                         if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
00624                         {
00625                                 pVeh->m_ucmd.forwardmove = 127;
00626                         }
00627                         else if ( speedMin >= 0 )
00628                         {
00629                                 pVeh->m_ucmd.forwardmove = 0;
00630                         }
00631                 }
00632                 //else not accel, decel or braking
00633                 else if ( pVeh->m_pVehicleInfo->throttleSticks )
00634                 {//we're using a throttle that sticks at current speed
00635                         if ( parentPS->speed <= MIN_LANDING_SPEED )
00636                         {//going less than landing speed
00637                                 if ( FighterOverValidLandingSurface( pVeh ) )
00638                                 {//close to ground and not going very fast
00639                                         //slow to a stop if within landing height and not accel/decel/braking
00640                                         if ( parentPS->speed > 0 )
00641                                         {//slow down
00642                                                 parentPS->speed -= speedIdleDec;
00643                                         }
00644                                         else if ( parentPS->speed < 0 )
00645                                         {//going backwards, slow down
00646                                                 parentPS->speed += speedIdleDec;
00647                                         }
00648                                 }
00649                                 else
00650                                 {//not over a valid landing surf, but going too slow
00651                                         //speed up to idle speed if not over a valid landing surf and not accel/decel/braking
00652                                         if ( parentPS->speed < speedIdle )
00653                                         {
00654                                                 parentPS->speed += speedIdleAccel;
00655                                                 if ( parentPS->speed > speedIdle )
00656                                                 {
00657                                                         parentPS->speed = speedIdle;
00658                                                 }
00659                                         }
00660                                 }
00661                         }
00662                 }
00663                 else
00664                 {//then speed up or slow down to idle speed
00665                         //accelerate to cruising speed only, otherwise, just coast
00666                         // If they've launched, apply some constant motion.
00667                         if ( (pVeh->m_LandTrace.fraction >= 1.0f //no ground
00668                                         || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )//or can't land on ground below us
00669                                 && speedIdle > 0 ) 
00670                         {//not above ground and have an idle speed
00671                                 //float fSpeed = pVeh->m_pParentEntity->client->ps.speed;
00672                                 if ( parentPS->speed < speedIdle )
00673                                 {
00674                                         parentPS->speed += speedIdleAccel;
00675                                         if ( parentPS->speed > speedIdle )
00676                                         {
00677                                                 parentPS->speed = speedIdle;
00678                                         }
00679                                 }
00680                                 else if ( parentPS->speed > 0 )
00681                                 {//slow down
00682                                         parentPS->speed -= speedIdleDec;
00683 
00684                                         if ( parentPS->speed < speedIdle )
00685                                         {
00686                                                 parentPS->speed = speedIdle;
00687                                         }
00688                                 }
00689                         }
00690                         else//either close to ground or no idle speed
00691                         {//slow to a stop if no idle speed or within landing height and not accel/decel/braking
00692                                 if ( parentPS->speed > 0 )
00693                                 {//slow down
00694                                         parentPS->speed -= speedIdleDec;
00695                                 }
00696                                 else if ( parentPS->speed < 0 )
00697                                 {//going backwards, slow down
00698                                         parentPS->speed += speedIdleDec;
00699                                 }
00700                         }
00701                 }
00702         }
00703         else
00704         {
00705                 if ( pVeh->m_ucmd.forwardmove < 0 )
00706                 {
00707                         pVeh->m_ucmd.forwardmove = 0;
00708                 }
00709                 if ( pVeh->m_ucmd.upmove < 0 )
00710                 {
00711                         pVeh->m_ucmd.upmove = 0;
00712                 }
00713 
00714 #ifndef _JK2MP
00715                 if ( !pVeh->m_pVehicleInfo->strafePerc || (!g_speederControlScheme->value && !pVeh->m_pParentEntity->s.number) )
00716                 {//if in a strafe-capable vehicle, clear strafing unless using alternate control scheme
00717                         pVeh->m_ucmd.rightmove = 0;
00718                 }
00719 #endif
00720         }
00721 
00722 #if 1//This is working now, but there are some transitional jitters... Rich?
00723 //STRAFING==============================================================================
00724         if ( pVeh->m_pVehicleInfo->strafePerc 
00725 #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
00726                 && pVeh->m_pVehicleInfo->Inhabited( pVeh )//has to have a driver in order to be capable of landing
00727 #endif
00728                 && !pVeh->m_iRemovedSurfaces
00729                 && parentPS->electrifyTime<curTime
00730                 && (pVeh->m_LandTrace.fraction >= 1.0f//no grounf
00731                         ||pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE//can't land here
00732                         ||parentPS->speed>MIN_LANDING_SPEED)//going too fast to land
00733                 && pVeh->m_ucmd.rightmove )
00734         {//strafe
00735                 vec3_t vAngles, vRight;
00736                 float strafeSpeed = (pVeh->m_pVehicleInfo->strafePerc*speedMax)*5.0f;
00737                 VectorCopy( pVeh->m_vOrientation, vAngles );
00738                 vAngles[PITCH] = vAngles[ROLL] = 0;
00739                 AngleVectors( vAngles, NULL, vRight, NULL );
00740 
00741                 if ( pVeh->m_ucmd.rightmove > 0 )
00742                 {//strafe right
00743                         //FIXME: this will probably make it possible to cheat and
00744                         //              go faster than max speed if you keep turning and strafing...
00745                         if ( parentPS->hackingTime > -MAX_STRAFE_TIME )
00746                         {//can strafe right for 2 seconds
00747                                 float curStrafeSpeed = DotProduct( parentPS->velocity, vRight );
00748                                 if ( curStrafeSpeed > 0.0f )
00749                                 {//if > 0, already strafing right
00750                                         strafeSpeed -= curStrafeSpeed;//so it doesn't add up
00751                                 }
00752                                 if ( strafeSpeed > 0 )
00753                                 {
00754                                         VectorMA( parentPS->velocity, strafeSpeed*pVeh->m_fTimeModifier, vRight, parentPS->velocity );
00755                                 }
00756                                 parentPS->hackingTime -= 50*pVeh->m_fTimeModifier;
00757                         }
00758                 }
00759                 else
00760                 {//strafe left
00761                         if ( parentPS->hackingTime < MAX_STRAFE_TIME )
00762                         {//can strafe left for 2 seconds
00763                                 float curStrafeSpeed = DotProduct( parentPS->velocity, vRight );
00764                                 if ( curStrafeSpeed < 0.0f )
00765                                 {//if < 0, already strafing left
00766                                         strafeSpeed += curStrafeSpeed;//so it doesn't add up
00767                                 }
00768                                 if ( strafeSpeed > 0 )
00769                                 {
00770                                         VectorMA( parentPS->velocity, -strafeSpeed*pVeh->m_fTimeModifier, vRight, parentPS->velocity );
00771                                 }
00772                                 parentPS->hackingTime += 50*pVeh->m_fTimeModifier;
00773                         }
00774                 }
00775                 //strafing takes away from forward speed?  If so, strafePerc above should use speedMax
00776                 //parentPS->speed *= (1.0f-pVeh->m_pVehicleInfo->strafePerc);
00777         }
00778         else//if ( parentPS->hackingTimef )
00779         {
00780                 if ( parentPS->hackingTime > 0 )
00781                 {
00782                         parentPS->hackingTime -= 50*pVeh->m_fTimeModifier;
00783                         if ( parentPS->hackingTime < 0 )
00784                         {
00785                                 parentPS->hackingTime = 0.0f;
00786                         }
00787                 }
00788                 else if ( parentPS->hackingTime < 0 )
00789                 {
00790                         parentPS->hackingTime += 50*pVeh->m_fTimeModifier;
00791                         if ( parentPS->hackingTime > 0 )
00792                         {
00793                                 parentPS->hackingTime = 0.0f;
00794                         }
00795                 }
00796         }
00797 //STRAFING==============================================================================
00798 #endif
00799 
00800         if ( parentPS->speed > speedMax )
00801         {
00802                 parentPS->speed = speedMax;
00803         }
00804         else if ( parentPS->speed < speedMin )
00805         {
00806                 parentPS->speed = speedMin;
00807         }
00808 
00809 #ifdef QAGAME//FIXME: get working in GAME and CGAME
00810         if ((pVeh->m_vOrientation[PITCH]*0.1f) > 10.0f)
00811         { //pitched downward, increase speed more and more based on our tilt
00812                 if ( FighterIsInSpace( (gentity_t *)parent ) )
00813                 {//in space, do nothing with speed base on pitch...
00814                 }
00815                 else
00816                 {
00817                         //really should only do this when on a planet
00818                         float mult = pVeh->m_vOrientation[PITCH]*0.1f;
00819                         if (mult < 1.0f)
00820                         {
00821                                 mult = 1.0f;
00822                         }
00823                         parentPS->speed = PredictedAngularDecrement(mult, pVeh->m_fTimeModifier*10.0f, parentPS->speed);
00824                 }
00825         }
00826 
00827         if (pVeh->m_iRemovedSurfaces
00828                 || parentPS->electrifyTime>=curTime)
00829         { //going down
00830                 if ( FighterIsInSpace( (gentity_t *)parent ) )
00831                 {//we're in a valid trigger_space brush
00832                         //simulate randomness
00833                         if ( !(parent->s.number&3) )
00834                         {//even multiple of 3, don't do anything
00835                                 parentPS->gravity = 0;
00836                         }
00837                         else if ( !(parent->s.number&2) )
00838                         {//even multiple of 2, go up
00839                                 parentPS->gravity = -500.0f;
00840                                 parentPS->velocity[2] = 80.0f;
00841                         }
00842                         else
00843                         {//odd number, go down
00844                                 parentPS->gravity = 500.0f;
00845                                 parentPS->velocity[2] = -80.0f;
00846                         }
00847                 }
00848                 else
00849                 {//over a planet
00850                         parentPS->gravity = 500.0f;
00851                         parentPS->velocity[2] = -80.0f;
00852                 }
00853         }
00854         else if ( FighterSuspended( pVeh, parentPS ) )
00855         {
00856                 parentPS->gravity = 0;
00857         }
00858         else if ( (!parentPS->speed||parentPS->speed < speedIdle) && pVeh->m_ucmd.upmove <= 0 )
00859         {//slowing down or stopped and not trying to take off
00860                 if ( FighterIsInSpace( (gentity_t *)parent ) )
00861                 {//we're in space, stopping doesn't make us drift downward
00862                         if ( FighterOverValidLandingSurface( pVeh ) )
00863                         {//well, there's something below us to land on, so go ahead and lower us down to it
00864                                 parentPS->gravity = (speedIdle - parentPS->speed)/4;
00865                         }
00866                 }
00867                 else
00868                 {//over a planet
00869                         parentPS->gravity = (speedIdle - parentPS->speed)/4;
00870                 }
00871         }
00872         else
00873         {
00874                 parentPS->gravity = 0;
00875         }
00876 #else//FIXME: get above checks working in GAME and CGAME
00877         parentPS->gravity = 0;
00878 #endif
00879 
00880         /********************************************************************************/
00881         /*      END Here is where we move the vehicle (forward or back or whatever). END        */
00882         /********************************************************************************/
00883 }
00884 
00885 extern void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride );
00886 static void FighterWingMalfunctionCheck( Vehicle_t *pVeh, playerState_t *parentPS )
00887 {
00888         float mPitchOverride = 1.0f;
00889         float mYawOverride = 1.0f;
00890         BG_VehicleTurnRateForSpeed( pVeh, parentPS->speed, &mPitchOverride, &mYawOverride );
00891         //check right wing damage
00892         if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_RIGHT_HEAVY)) )
00893         {//right wing has taken heavy damage
00894                 pVeh->m_vOrientation[ROLL] += (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*50.0f;
00895         }
00896         else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_RIGHT_LIGHT)) )
00897         {//right wing has taken light damage
00898                 pVeh->m_vOrientation[ROLL] += (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*12.5f;
00899         }
00900 
00901         //check left wing damage
00902         if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_LEFT_HEAVY)) )
00903         {//left wing has taken heavy damage
00904                 pVeh->m_vOrientation[ROLL] -= (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*50.0f;
00905         }
00906         else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_LEFT_LIGHT)) )
00907         {//left wing has taken light damage
00908                 pVeh->m_vOrientation[ROLL] -= (sin( pVeh->m_ucmd.serverTime*0.001 )+1.0f)*pVeh->m_fTimeModifier*mYawOverride*12.5f;
00909         }
00910 
00911 }
00912 
00913 static void FighterNoseMalfunctionCheck( Vehicle_t *pVeh, playerState_t *parentPS )
00914 {
00915         float mPitchOverride = 1.0f;
00916         float mYawOverride = 1.0f;
00917         BG_VehicleTurnRateForSpeed( pVeh, parentPS->speed, &mPitchOverride, &mYawOverride );
00918         //check nose damage
00919         if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_FRONT_HEAVY)) )
00920         {//nose has taken heavy damage
00921                 //pitch up and down over time
00922                 pVeh->m_vOrientation[PITCH] += sin( pVeh->m_ucmd.serverTime*0.001 )*pVeh->m_fTimeModifier*mPitchOverride*50.0f;
00923         }
00924         else if ( (parentPS->brokenLimbs&(1<<SHIPSURF_DAMAGE_FRONT_LIGHT)) )
00925         {//nose has taken heavy damage
00926                 //pitch up and down over time
00927                 pVeh->m_vOrientation[PITCH] += sin( pVeh->m_ucmd.serverTime*0.001 )*pVeh->m_fTimeModifier*mPitchOverride*20.0f;
00928         }
00929 }
00930 
00931 static void FighterDamageRoutine( Vehicle_t *pVeh, bgEntity_t *parent, playerState_t *parentPS, playerState_t *riderPS, qboolean isDead )
00932 {
00933         if ( !pVeh->m_iRemovedSurfaces )
00934         {//still in one piece
00935                 if ( pVeh->m_pParentEntity && isDead )
00936                 {//death spiral
00937                         pVeh->m_ucmd.upmove = 0;
00938                         //FIXME: don't bias toward pitching down when not in space
00939                         /*
00940                         if ( FighterIsInSpace( pVeh->m_pParentEntity ) )
00941                         {
00942                         }
00943                         else
00944                         */
00945                         if ( !(pVeh->m_pParentEntity->s.number%3) )
00946                         {//NOT everyone should do this
00947                                 pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier; 
00948                                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
00949                                 {
00950                                         if ( pVeh->m_vOrientation[PITCH] > 60.0f )  
00951                                         {
00952                                                 pVeh->m_vOrientation[PITCH] = 60.0f;
00953                                         }
00954                                 }
00955                         }
00956                         else if ( !(pVeh->m_pParentEntity->s.number%2) )
00957                         {
00958                                 pVeh->m_vOrientation[PITCH] -= pVeh->m_fTimeModifier; 
00959                                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
00960                                 {
00961                                         if ( pVeh->m_vOrientation[PITCH] > -60.0f )  
00962                                         {
00963                                                 pVeh->m_vOrientation[PITCH] = -60.0f;
00964                                         }
00965                                 }
00966                         }
00967                         if ( (pVeh->m_pParentEntity->s.number%2) )
00968                         {
00969                                 pVeh->m_vOrientation[YAW] += pVeh->m_fTimeModifier;
00970                                 pVeh->m_vOrientation[ROLL] += pVeh->m_fTimeModifier*4.0f;
00971                         }
00972                         else
00973                         {
00974                                 pVeh->m_vOrientation[YAW] -= pVeh->m_fTimeModifier;
00975                                 pVeh->m_vOrientation[ROLL] -= pVeh->m_fTimeModifier*4.0f;
00976                         }
00977                 }
00978                 return;
00979         }
00980 
00981         //if we get into here we have at least one broken piece
00982         pVeh->m_ucmd.upmove = 0;
00983         
00984         //if you're off the ground and not suspended, pitch down
00985         //FIXME: not in space!
00986         if ( pVeh->m_LandTrace.fraction >= 0.1f )
00987         {
00988                 if ( !FighterSuspended( pVeh, parentPS ) )
00989                 {
00990                         //pVeh->m_ucmd.forwardmove = 0;
00991                         //FIXME: don't bias towards pitching down when in space...
00992                         if ( !(pVeh->m_pParentEntity->s.number%2) )
00993                         {//NOT everyone should do this
00994                                 pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier; 
00995                                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
00996                                 {
00997                                         if ( pVeh->m_vOrientation[PITCH] > 60.0f )  
00998                                         {
00999                                                 pVeh->m_vOrientation[PITCH] = 60.0f;
01000                                         }
01001                                 }
01002                         }
01003                         else if ( !(pVeh->m_pParentEntity->s.number%3) )
01004                         {
01005                                 pVeh->m_vOrientation[PITCH] -= pVeh->m_fTimeModifier; 
01006                                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01007                                 {
01008                                         if ( pVeh->m_vOrientation[PITCH] > -60.0f )  
01009                                         {
01010                                                 pVeh->m_vOrientation[PITCH] = -60.0f;
01011                                         }
01012                                 }
01013                         }
01014                         //else: just keep going forward
01015                 }
01016         }
01017 #ifdef QAGAME
01018         if ( pVeh->m_LandTrace.fraction < 1.0f )
01019         { //if you land at all when pieces of your ship are missing, then die
01020                 gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
01021                 gentity_t *killer = parent;
01022 #ifdef _JK2MP//only have this info in MP...
01023                 if (parent->client->ps.otherKiller < ENTITYNUM_WORLD &&
01024                         parent->client->ps.otherKillerTime > level.time)
01025                 {
01026                         gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller];
01027 
01028                         if (potentialKiller->inuse && potentialKiller->client)
01029                         { //he's valid I guess
01030                                 killer = potentialKiller;
01031                         }
01032                 }
01033 #endif
01034                 G_Damage(parent, killer, killer, vec3_origin, parent->client->ps.origin, 99999, DAMAGE_NO_ARMOR, MOD_SUICIDE);
01035         }
01036 #endif
01037 
01038         if ( ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
01039                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D)) &&
01040                 ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
01041                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F)) )
01042         { //wings on both side broken
01043                 float factor = 2.0f;
01044                 if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
01045                         (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F) &&
01046                         (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
01047                         (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
01048                 { //all wings broken
01049                         factor *= 2.0f;
01050                 }
01051 
01052                 if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
01053                 {//won't yaw, so increase roll factor
01054                         factor *= 4.0f;
01055                 }
01056 
01057                 pVeh->m_vOrientation[ROLL] += (pVeh->m_fTimeModifier*factor); //do some spiralling
01058         }
01059         else if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
01060                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
01061         { //left wing broken
01062                 float factor = 2.0f;
01063                 if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
01064                         (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
01065                 { //if both are broken..
01066                         factor *= 2.0f;
01067                 }
01068 
01069                 if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
01070                 {//won't yaw, so increase roll factor
01071                         factor *= 4.0f;
01072                 }
01073 
01074                 pVeh->m_vOrientation[ROLL] += factor*pVeh->m_fTimeModifier;
01075         }
01076         else if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
01077                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
01078         { //right wing broken
01079                 float factor = 2.0f;
01080                 if ((pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
01081                         (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
01082                 { //if both are broken..
01083                         factor *= 2.0f;
01084                 }
01085 
01086                 if ( !(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5) )
01087                 {//won't yaw, so increase roll factor
01088                         factor *= 4.0f;
01089                 }
01090 
01091                 pVeh->m_vOrientation[ROLL] -= factor*pVeh->m_fTimeModifier;
01092         }
01093 }
01094 
01095 #ifdef _JK2MP
01096 
01097 #ifdef VEH_CONTROL_SCHEME_4
01098 
01099 #define FIGHTER_TURNING_MULTIPLIER 0.8f//was 1.6f //magic number hackery
01100 #define FIGHTER_TURNING_DEADZONE 0.25f//no turning if offset is this much
01101 void FighterRollAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
01102 {
01103 /*
01104         float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
01105 */
01106         float angDif = AngleSubtract(pVeh->m_vPrevRiderViewAngles[YAW],riderPS->viewangles[YAW]);
01107         /*
01108         if ( fabs( angDif ) < FIGHTER_TURNING_DEADZONE )
01109         {
01110                 angDif = 0.0f;
01111         }
01112         else if ( angDif >= FIGHTER_TURNING_DEADZONE )
01113         {
01114                 angDif -= FIGHTER_TURNING_DEADZONE;
01115         }
01116         else if ( angDif <= -FIGHTER_TURNING_DEADZONE )
01117         {
01118                 angDif += FIGHTER_TURNING_DEADZONE;
01119         }
01120         */
01121         
01122         angDif *= 0.5f;
01123         if ( angDif > 0.0f )
01124         {
01125                 angDif *= angDif;
01126         }
01127         else if ( angDif < 0.0f )
01128         {
01129                 angDif *= -angDif;
01130         }
01131 
01132         if (parentPS && parentPS->speed)
01133         {
01134                 float maxDif = pVeh->m_pVehicleInfo->turningSpeed*FIGHTER_TURNING_MULTIPLIER;
01135 
01136                 if ( pVeh->m_pVehicleInfo->speedDependantTurning )
01137                 {
01138                         float speedFrac = 1.0f;
01139                         if ( pVeh->m_LandTrace.fraction >= 1.0f 
01140                                 || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
01141                         {
01142                                 float s = parentPS->speed;
01143                                 if (s < 0.0f)
01144                                 {
01145                                         s = -s;
01146                                 }
01147                                 speedFrac = (s/(pVeh->m_pVehicleInfo->speedMax*0.75f));
01148                                 if ( speedFrac < 0.25f )
01149                                 {
01150                                         speedFrac = 0.25f;
01151                                 }
01152                                 else if ( speedFrac > 1.0f )
01153                                 {
01154                                         speedFrac = 1.0f;
01155                                 }
01156                         }
01157                         angDif *= speedFrac;
01158                 }
01159                 if (angDif > maxDif)
01160                 {
01161                         angDif = maxDif;
01162                 }
01163                 else if (angDif < -maxDif)
01164                 {
01165                         angDif = -maxDif;
01166                 }
01167                 pVeh->m_vOrientation[ROLL] = AngleNormalize180(pVeh->m_vOrientation[ROLL] + angDif*(pVeh->m_fTimeModifier*0.2f));
01168         }
01169 }
01170 
01171 void FighterYawAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
01172 {
01173         float angDif = AngleSubtract(pVeh->m_vPrevRiderViewAngles[YAW],riderPS->viewangles[YAW]);
01174         if ( fabs( angDif ) < FIGHTER_TURNING_DEADZONE )
01175         {
01176                 angDif = 0.0f;
01177         }
01178         else if ( angDif >= FIGHTER_TURNING_DEADZONE )
01179         {
01180                 angDif -= FIGHTER_TURNING_DEADZONE;
01181         }
01182         else if ( angDif <= -FIGHTER_TURNING_DEADZONE )
01183         {
01184                 angDif += FIGHTER_TURNING_DEADZONE;
01185         }
01186         
01187         angDif *= 0.5f;
01188         if ( angDif > 0.0f )
01189         {
01190                 angDif *= angDif;
01191         }
01192         else if ( angDif < 0.0f )
01193         {
01194                 angDif *= -angDif;
01195         }
01196 
01197         if (parentPS && parentPS->speed)
01198         {
01199                 float maxDif = pVeh->m_pVehicleInfo->turningSpeed*FIGHTER_TURNING_MULTIPLIER;
01200 
01201                 if ( pVeh->m_pVehicleInfo->speedDependantTurning )
01202                 {
01203                         float speedFrac = 1.0f;
01204                         if ( pVeh->m_LandTrace.fraction >= 1.0f 
01205                                 || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
01206                         {
01207                                 float s = parentPS->speed;
01208                                 if (s < 0.0f)
01209                                 {
01210                                         s = -s;
01211                                 }
01212                                 speedFrac = (s/(pVeh->m_pVehicleInfo->speedMax*0.75f));
01213                                 if ( speedFrac < 0.25f )
01214                                 {
01215                                         speedFrac = 0.25f;
01216                                 }
01217                                 else if ( speedFrac > 1.0f )
01218                                 {
01219                                         speedFrac = 1.0f;
01220                                 }
01221                         }
01222                         angDif *= speedFrac;
01223                 }
01224                 if (angDif > maxDif)
01225                 {
01226                         angDif = maxDif;
01227                 }
01228                 else if (angDif < -maxDif)
01229                 {
01230                         angDif = -maxDif;
01231                 }
01232                 pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - (angDif*(pVeh->m_fTimeModifier*0.2f)) );
01233         }
01234 }
01235 
01236 void FighterPitchAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
01237 {
01238         float angDif = AngleSubtract(0,riderPS->viewangles[PITCH]);//AngleSubtract(pVeh->m_vPrevRiderViewAngles[PITCH], riderPS->viewangles[PITCH]);
01239         if ( fabs( angDif ) < FIGHTER_TURNING_DEADZONE )
01240         {
01241                 angDif = 0.0f;
01242         }
01243         else if ( angDif >= FIGHTER_TURNING_DEADZONE )
01244         {
01245                 angDif -= FIGHTER_TURNING_DEADZONE;
01246         }
01247         else if ( angDif <= -FIGHTER_TURNING_DEADZONE )
01248         {
01249                 angDif += FIGHTER_TURNING_DEADZONE;
01250         }
01251 
01252         angDif *= 0.5f;
01253         if ( angDif > 0.0f )
01254         {
01255                 angDif *= angDif;
01256         }
01257         else if ( angDif < 0.0f )
01258         {
01259                 angDif *= -angDif;
01260         }
01261 
01262         if (parentPS && parentPS->speed)
01263         {
01264                 float maxDif = pVeh->m_pVehicleInfo->turningSpeed*FIGHTER_TURNING_MULTIPLIER;
01265 
01266                 if ( pVeh->m_pVehicleInfo->speedDependantTurning )
01267                 {
01268                         float speedFrac = 1.0f;
01269                         if ( pVeh->m_LandTrace.fraction >= 1.0f 
01270                                 || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE  )
01271                         {
01272                                 float s = parentPS->speed;
01273                                 if (s < 0.0f)
01274                                 {
01275                                         s = -s;
01276                                 }
01277                                 speedFrac = (s/(pVeh->m_pVehicleInfo->speedMax*0.75f));
01278                                 if ( speedFrac < 0.25f )
01279                                 {
01280                                         speedFrac = 0.25f;
01281                                 }
01282                                 else if ( speedFrac > 1.0f )
01283                                 {
01284                                         speedFrac = 1.0f;
01285                                 }
01286                         }
01287                         angDif *= speedFrac;
01288                 }
01289                 if (angDif > maxDif)
01290                 {
01291                         angDif = maxDif;
01292                 }
01293                 else if (angDif < -maxDif)
01294                 {
01295                         angDif = -maxDif;
01296                 }
01297                 pVeh->m_vOrientation[PITCH] = AngleNormalize180(pVeh->m_vOrientation[PITCH] - (angDif*(pVeh->m_fTimeModifier*0.2f)) );
01298         }
01299 }
01300 
01301 #else// VEH_CONTROL_SCHEME_4
01302 
01303 void FighterYawAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
01304 {
01305         float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
01306 
01307         if (parentPS && parentPS->speed)
01308         {
01309                 float s = parentPS->speed;
01310                 float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
01311 
01312                 if (s < 0.0f)
01313                 {
01314                         s = -s;
01315                 }
01316                 angDif *= s/pVeh->m_pVehicleInfo->speedMax;
01317                 if (angDif > maxDif)
01318                 {
01319                         angDif = maxDif;
01320                 }
01321                 else if (angDif < -maxDif)
01322                 {
01323                         angDif = -maxDif;
01324                 }
01325                 pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f));
01326         }
01327 }
01328 
01329 void FighterPitchAdjust(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS)
01330 {
01331         float angDif = AngleSubtract(pVeh->m_vOrientation[PITCH], riderPS->viewangles[PITCH]);
01332 
01333         if (parentPS && parentPS->speed)
01334         {
01335                 float s = parentPS->speed;
01336                 float maxDif = pVeh->m_pVehicleInfo->turningSpeed*0.8f; //magic number hackery
01337 
01338                 if (s < 0.0f)
01339                 {
01340                         s = -s;
01341                 }
01342                 angDif *= s/pVeh->m_pVehicleInfo->speedMax;
01343                 if (angDif > maxDif)
01344                 {
01345                         angDif = maxDif;
01346                 }
01347                 else if (angDif < -maxDif)
01348                 {
01349                         angDif = -maxDif;
01350                 }
01351                 pVeh->m_vOrientation[PITCH] = AngleNormalize360(pVeh->m_vOrientation[PITCH] - angDif*(pVeh->m_fTimeModifier*0.2f));
01352         }
01353 }
01354 #endif// VEH_CONTROL_SCHEME_4
01355 
01356 void FighterPitchClamp(Vehicle_t *pVeh, playerState_t *riderPS, playerState_t *parentPS, int curTime )
01357 {
01358         if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01359         {//cap pitch reasonably
01360                 if ( pVeh->m_pVehicleInfo->pitchLimit != -1 
01361                         && !pVeh->m_iRemovedSurfaces 
01362                         && parentPS->electrifyTime < curTime )
01363                 {
01364                         if (pVeh->m_vOrientation[PITCH] > pVeh->m_pVehicleInfo->pitchLimit )
01365                         {
01366                                 pVeh->m_vOrientation[PITCH] = pVeh->m_pVehicleInfo->pitchLimit;
01367                         }
01368                         else if (pVeh->m_vOrientation[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit)
01369                         {
01370                                 pVeh->m_vOrientation[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit;
01371                         }
01372                 }
01373         }
01374 }
01375 
01376 #endif// _JK2MP
01377 
01378 //MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
01379 //If you really need to violate this rule for SP, then use ifdefs.
01380 //By BG-compatible, I mean no use of game-specific data - ONLY use
01381 //stuff available in the MP bgEntity (in SP, the bgEntity is #defined
01382 //as a gentity, but the MP-compatible access restrictions are based
01383 //on the bgEntity structure in the MP codebase) -rww
01384 // ProcessOrientCommands the Vehicle.
01385 static void ProcessOrientCommands( Vehicle_t *pVeh )
01386 {
01387         /********************************************************************************/
01388         /*      BEGIN   Here is where make sure the vehicle is properly oriented.       BEGIN   */
01389         /********************************************************************************/
01390 
01391         bgEntity_t *parent = pVeh->m_pParentEntity;
01392         playerState_t *parentPS, *riderPS;
01393         float angleTimeMod;
01394 #ifdef QAGAME
01395         const float groundFraction = 0.1f;
01396 #endif
01397         float   curRoll = 0.0f;
01398         qboolean isDead = qfalse;
01399         qboolean isLandingOrLanded = qfalse;
01400 #ifndef _JK2MP//SP
01401         int curTime = level.time;
01402 #elif QAGAME//MP GAME
01403         int curTime = level.time;
01404 #elif CGAME//MP CGAME
01405         //FIXME: pass in ucmd?  Not sure if this is reliable...
01406         int curTime = pm->cmd.serverTime;
01407 #endif
01408 
01409 #ifdef _JK2MP
01410         bgEntity_t *rider = NULL;
01411         if (parent->s.owner != ENTITYNUM_NONE)
01412         {
01413                 rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
01414         }
01415 #else
01416         gentity_t *rider = parent->owner;
01417 #endif
01418 
01419 #ifdef _JK2MP
01420         if ( !rider )
01421 #else
01422         if ( !rider || !rider->client )
01423 #endif
01424         {
01425                 rider = parent;
01426         }
01427 
01428 #ifdef _JK2MP
01429         parentPS = parent->playerState;
01430         riderPS = rider->playerState;
01431         isDead = (qboolean)((parentPS->eFlags&EF_DEAD)!=0);
01432 #else
01433         parentPS = &parent->client->ps;
01434         riderPS = &rider->client->ps;
01435         isDead = (parentPS->stats[STAT_HEALTH] <= 0 );
01436 #endif
01437 
01438 #ifdef _JK2MP
01439 
01440 #ifdef VEH_CONTROL_SCHEME_4
01441         if ( parentPS->hyperSpaceTime
01442                 && (curTime - parentPS->hyperSpaceTime) < HYPERSPACE_TIME )
01443         {//Going to Hyperspace
01444                 //VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
01445                 //VectorCopy( riderPS->viewangles, parentPS->viewangles );
01446                 VectorCopy( parentPS->viewangles, pVeh->m_vOrientation );
01447                 VectorClear( pVeh->m_vPrevRiderViewAngles );
01448                 pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
01449                 FighterPitchClamp( pVeh, riderPS, parentPS, curTime );
01450                 return;
01451         }
01452         else if ( parentPS->vehTurnaroundIndex
01453                 && parentPS->vehTurnaroundTime > curTime )
01454         {//in turn-around brush
01455                 //VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
01456                 //VectorCopy( riderPS->viewangles, parentPS->viewangles );
01457                 VectorCopy( parentPS->viewangles, pVeh->m_vOrientation );
01458                 VectorClear( pVeh->m_vPrevRiderViewAngles );
01459                 pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
01460                 FighterPitchClamp( pVeh, riderPS, parentPS, curTime );
01461                 return;
01462         }
01463 
01464 #else// VEH_CONTROL_SCHEME_4
01465 
01466         if ( parentPS->hyperSpaceTime
01467                 && (curTime - parentPS->hyperSpaceTime) < HYPERSPACE_TIME )
01468         {//Going to Hyperspace
01469                 VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
01470                 VectorCopy( riderPS->viewangles, parentPS->viewangles );
01471                 return;
01472         }
01473 #endif// VEH_CONTROL_SCHEME_4
01474 
01475 #endif//_JK2MP
01476         
01477         if ( pVeh->m_iDropTime >= curTime )
01478         {//you can only YAW during this
01479                 parentPS->viewangles[YAW] = pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
01480 #ifdef VEH_CONTROL_SCHEME_4
01481                 VectorClear( pVeh->m_vPrevRiderViewAngles );
01482                 pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
01483 #endif// VEH_CONTROL_SCHEME_4
01484                 return;
01485         }
01486 
01487         angleTimeMod = pVeh->m_fTimeModifier;
01488 
01489         if ( isDead || parentPS->electrifyTime>=curTime ||
01490                 (pVeh->m_pVehicleInfo->surfDestruction &&
01491                 pVeh->m_iRemovedSurfaces &&
01492                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) &&
01493                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D) &&
01494                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) &&
01495                 (pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F)) )
01496         { //do some special stuff for when all the wings are torn off
01497                 FighterDamageRoutine(pVeh, parent, parentPS, riderPS, isDead);
01498                 pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
01499                 return;
01500         }
01501 
01502         if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01503         {
01504                 pVeh->m_vOrientation[ROLL] = PredictedAngularDecrement(0.95f, angleTimeMod*2.0f, pVeh->m_vOrientation[ROLL]);
01505         }
01506 
01507         isLandingOrLanded = (FighterIsLanding( pVeh, parentPS )||FighterIsLanded( pVeh, parentPS ));
01508 
01509         if (!isLandingOrLanded)
01510         { //don't do this stuff while landed.. I guess. I don't want ships spinning in place, looks silly.
01511                 int m = 0;
01512                 float aVelDif;
01513                 float dForVel;
01514 
01515                 FighterWingMalfunctionCheck( pVeh, parentPS );
01516 
01517                 while (m < 3)
01518                 {
01519                         aVelDif = pVeh->m_vFullAngleVelocity[m];
01520 
01521                         if (aVelDif != 0.0f)
01522                         {
01523                                 dForVel = (aVelDif*0.1f)*pVeh->m_fTimeModifier;
01524                                 if (dForVel > 1.0f || dForVel < -1.0f)
01525                                 {
01526                                         pVeh->m_vOrientation[m] += dForVel;
01527                                         pVeh->m_vOrientation[m] = AngleNormalize180(pVeh->m_vOrientation[m]);
01528                                         if (m == PITCH)
01529                                         { //don't pitch downward into ground even more.
01530                                                 if (pVeh->m_vOrientation[m] > 90.0f && (pVeh->m_vOrientation[m]-dForVel) < 90.0f)
01531                                                 {
01532                                                         pVeh->m_vOrientation[m] = 90.0f;
01533                                                         pVeh->m_vFullAngleVelocity[m] = -pVeh->m_vFullAngleVelocity[m];
01534                                                 }
01535                                         }
01536                                         pVeh->m_vFullAngleVelocity[m] -= dForVel;
01537                                 }
01538                                 else
01539                                 {
01540                                         pVeh->m_vFullAngleVelocity[m] = 0.0f;
01541                                 }
01542                         }
01543 
01544                         m++;
01545                 }
01546         }
01547         else
01548         { //clear decr/incr angles once landed.
01549                 VectorClear(pVeh->m_vFullAngleVelocity);
01550         }
01551 
01552         curRoll = pVeh->m_vOrientation[ROLL];
01553 
01554         // If we're landed, we shouldn't be able to do anything but take off.
01555         if ( isLandingOrLanded //going slow enough to start landing
01556                 && !pVeh->m_iRemovedSurfaces 
01557                 && parentPS->electrifyTime<curTime)//not spiraling out of control
01558         {
01559                 if ( parentPS->speed > 0.0f )
01560                 {//Uh... what?  Why?
01561                         if ( pVeh->m_LandTrace.fraction < 0.3f )
01562                         {
01563                                 pVeh->m_vOrientation[PITCH] = 0.0f;
01564                         }
01565                         else
01566                         {
01567                                 pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.83f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
01568                         }
01569                 }
01570                 if ( pVeh->m_LandTrace.fraction > 0.1f 
01571                         || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE )
01572                 {//off the ground, at least (or not on a valid landing surf)
01573                         // Dampen the turn rate based on the current height.
01574 #ifdef _JK2MP
01575                         FighterYawAdjust(pVeh, riderPS, parentPS);
01576 #else
01577                         pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];//*pVeh->m_LandTrace.fraction;
01578 #endif
01579                 }
01580 #ifdef VEH_CONTROL_SCHEME_4
01581                 else
01582                 {
01583                         VectorClear( pVeh->m_vPrevRiderViewAngles );
01584                         pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
01585                 }
01586 #endif// VEH_CONTROL_SCHEME_4
01587         }
01588         else if ( (pVeh->m_iRemovedSurfaces||parentPS->electrifyTime>=curTime)//spiralling out of control
01589                 && (!(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5)) )
01590         {//no yaw control
01591         }
01592         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 ) )   
01593         {
01594 #ifdef VEH_CONTROL_SCHEME_4
01595                 if ( 0  )
01596 #else// VEH_CONTROL_SCHEME_4
01597                 if ( BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01598 #endif// VEH_CONTROL_SCHEME_4
01599                 {
01600                         VectorCopy( riderPS->viewangles, pVeh->m_vOrientation );
01601                         VectorCopy( riderPS->viewangles, parentPS->viewangles );
01602 #ifdef _JK2MP
01603                         //BG_ExternThisSoICanRecompileInDebug( pVeh, riderPS );
01604 #endif
01605 
01606                         curRoll = pVeh->m_vOrientation[ROLL];
01607 
01608                         FighterNoseMalfunctionCheck( pVeh, parentPS );
01609 
01610                         //VectorCopy( pVeh->m_vOrientation, parentPS->viewangles );
01611                 }
01612                 else
01613                 {
01614                         /*
01615                         float fTurnAmt[3];
01616                         //PITCH
01617                         fTurnAmt[PITCH] = riderPS->viewangles[PITCH] * 0.08f;
01618                         //YAW
01619                         fTurnAmt[YAW] = riderPS->viewangles[YAW] * 0.065f;
01620                         fTurnAmt[YAW] *= fTurnAmt[YAW];
01621                         // Dampen the turn rate based on the current height.
01622                         if ( riderPS->viewangles[YAW] < 0 )
01623                         {//must keep it negative because squaring a negative makes it positive
01624                                 fTurnAmt[YAW] = -fTurnAmt[YAW];
01625                         }
01626                         fTurnAmt[YAW] *= pVeh->m_LandTrace.fraction;
01627                         //ROLL
01628                         fTurnAmt[2] = 0.0f;
01629                         */
01630 
01631                         //Actal YAW
01632 #ifdef _JK2MP
01633                         /*
01634                         pVeh->m_vOrientation[ROLL] = curRoll;
01635                         FighterRollAdjust(pVeh, riderPS, parentPS);
01636                         curRoll = pVeh->m_vOrientation[ROLL];
01637                         */
01638                         FighterYawAdjust(pVeh, riderPS, parentPS);
01639 #else
01640                         pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
01641 #endif
01642 
01643                         // If we are not hitting the ground, allow the fighter to pitch up and down.
01644                         if ( !FighterOverValidLandingSurface( pVeh ) 
01645                                 || parentPS->speed > MIN_LANDING_SPEED )
01646                         //if ( ( pVeh->m_LandTrace.fraction >= 1.0f || pVeh->m_ucmd.forwardmove != 0 ) && pVeh->m_LandTrace.fraction >= 0.0f )
01647                         {
01648                                 float fYawDelta;
01649 
01650 #ifdef _JK2MP
01651                                 FighterPitchAdjust(pVeh, riderPS, parentPS);
01652 #ifdef VEH_CONTROL_SCHEME_4
01653                                 FighterPitchClamp( pVeh, riderPS, parentPS, curTime );
01654 #endif// VEH_CONTROL_SCHEME_4
01655 #else
01656                                 pVeh->m_vOrientation[PITCH] = riderPS->viewangles[PITCH]; 
01657 #endif
01658 
01659                                 FighterNoseMalfunctionCheck( pVeh, parentPS );
01660 
01661                                 // Adjust the roll based on the turn amount and dampen it a little.
01662                                 fYawDelta = AngleSubtract(pVeh->m_vOrientation[YAW], pVeh->m_vPrevOrientation[YAW]); //pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW];
01663                                 if ( fYawDelta > 8.0f ) 
01664                                 {
01665                                         fYawDelta = 8.0f;
01666                                 }
01667                                 else if ( fYawDelta < -8.0f )
01668                                 {
01669                                         fYawDelta = -8.0f;
01670                                 }
01671                                 curRoll -= fYawDelta;
01672                                 curRoll = PredictedAngularDecrement(0.93f, angleTimeMod*2.0f, curRoll);
01673 
01674                                 //cap it reasonably
01675                                 //NOTE: was hardcoded to 40.0f, now using extern data
01676 #ifdef VEH_CONTROL_SCHEME_4
01677                                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01678                                 {
01679                                         if ( pVeh->m_pVehicleInfo->rollLimit != -1 )
01680                                         {
01681                                                 if (curRoll > pVeh->m_pVehicleInfo->rollLimit )
01682                                                 {
01683                                                         curRoll = pVeh->m_pVehicleInfo->rollLimit;
01684                                                 }
01685                                                 else if (curRoll < -pVeh->m_pVehicleInfo->rollLimit)
01686                                                 {
01687                                                         curRoll = -pVeh->m_pVehicleInfo->rollLimit;
01688                                                 }
01689                                         }
01690                                 }
01691 #else// VEH_CONTROL_SCHEME_4
01692                                 if ( pVeh->m_pVehicleInfo->rollLimit != -1 )
01693                                 {
01694                                         if (curRoll > pVeh->m_pVehicleInfo->rollLimit )
01695                                         {
01696                                                 curRoll = pVeh->m_pVehicleInfo->rollLimit;
01697                                         }
01698                                         else if (curRoll < -pVeh->m_pVehicleInfo->rollLimit)
01699                                         {
01700                                                 curRoll = -pVeh->m_pVehicleInfo->rollLimit;
01701                                         }
01702                                 }
01703 #endif// VEH_CONTROL_SCHEME_4
01704                         }
01705                 }
01706         }
01707 
01708         // If you are directly impacting the ground, even out your pitch.
01709         if ( isLandingOrLanded )    
01710         {//only if capable of landing
01711                 if ( !isDead 
01712                         && parentPS->electrifyTime<curTime
01713                         && (!pVeh->m_pVehicleInfo->surfDestruction || !pVeh->m_iRemovedSurfaces ) )
01714                 {//not crashing or spiralling out of control...
01715                         if ( pVeh->m_vOrientation[PITCH] > 0 )
01716                         {
01717                                 pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.2f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
01718                         }
01719                         else
01720                         {
01721                                 pVeh->m_vOrientation[PITCH] = PredictedAngularDecrement(0.75f, angleTimeMod*10.0f, pVeh->m_vOrientation[PITCH]);
01722                         }
01723                 }
01724         }
01725 
01726 
01727 /*
01728 //NOTE: all this is redundant now since we have the FighterDamageRoutine func...
01729 #ifdef _JK2MP //...yeah. Need to send armor across net for prediction to work.
01730         if ( isDead )
01731 #else
01732         if ( pVeh->m_iArmor <= 0 )
01733 #endif
01734         {//going to explode
01735                 //FIXME: maybe make it erratically jerk or spin or start and stop?
01736 #ifndef _JK2MP
01737                 if ( g_speederControlScheme->value > 0 || !rider || rider->s.number )
01738 #else
01739                 if (1)
01740 #endif
01741                 {
01742                         pVeh->m_ucmd.rightmove = Q_irand( -64, 64 );
01743                 }
01744                 else
01745                 {
01746                         pVeh->m_ucmd.rightmove = 0;
01747                 }
01748                 pVeh->m_ucmd.forwardmove = Q_irand( -32, 127 );
01749                 pVeh->m_ucmd.upmove = Q_irand( -127, 127 );
01750                 pVeh->m_vOrientation[YAW] += Q_flrand( -10, 10 );
01751                 pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier; 
01752                 if ( pVeh->m_vOrientation[PITCH] > 60.0f )  
01753                 {
01754                         pVeh->m_vOrientation[PITCH] = 60.0f;
01755                 }
01756                 if ( pVeh->m_LandTrace.fraction != 0.0f )
01757                 {
01758                         parentPS->velocity[2] -= pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
01759                 }
01760         }
01761 */
01762         // If no one is in this vehicle and it's up in the sky, pitch it forward as it comes tumbling down.
01763 #ifdef QAGAME //never gonna happen on client anyway, we can't be getting predicted unless the predicting client is boarded
01764         if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) 
01765                 && pVeh->m_LandTrace.fraction >= groundFraction 
01766                 && !FighterIsInSpace( (gentity_t *)parent ) 
01767                 && !FighterSuspended( pVeh, parentPS ) )
01768         {
01769                 pVeh->m_ucmd.upmove = 0;
01770                 //pVeh->m_ucmd.forwardmove = 0;
01771                 pVeh->m_vOrientation[PITCH] += pVeh->m_fTimeModifier; 
01772                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01773                 {
01774                         if ( pVeh->m_vOrientation[PITCH] > 60.0f )  
01775                         {
01776                                 pVeh->m_vOrientation[PITCH] = 60.0f;
01777                         }
01778                 }
01779         }
01780 #endif
01781 
01782         if ( !parentPS->hackingTime )
01783         {//use that roll
01784                 pVeh->m_vOrientation[ROLL] = curRoll;
01785                 //NOTE: this seems really backwards...
01786                 if ( pVeh->m_vOrientation[ROLL] )
01787                 { //continually adjust the yaw based on the roll..
01788                         if ( (pVeh->m_iRemovedSurfaces||parentPS->electrifyTime>=curTime)//spiralling out of control
01789                                 && (!(pVeh->m_pParentEntity->s.number%4)||!(pVeh->m_pParentEntity->s.number%5)) )
01790                         {//leave YAW alone
01791                         }
01792                         else
01793                         {
01794                                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01795                                 {
01796                                         pVeh->m_vOrientation[YAW] -= ((pVeh->m_vOrientation[ROLL])*0.05f)*pVeh->m_fTimeModifier;
01797                                 }
01798                         }
01799                 }
01800         }
01801         else
01802         {//add in strafing roll
01803                 float strafeRoll = (parentPS->hackingTime/MAX_STRAFE_TIME)*pVeh->m_pVehicleInfo->rollLimit;//pVeh->m_pVehicleInfo->bankingSpeed*
01804                 float strafeDif = AngleSubtract(strafeRoll, pVeh->m_vOrientation[ROLL]);
01805                 pVeh->m_vOrientation[ROLL] += (strafeDif*0.1f)*pVeh->m_fTimeModifier;
01806                 if ( !BG_UnrestrainedPitchRoll( riderPS, pVeh ) )
01807                 {//cap it reasonably
01808                         if ( pVeh->m_pVehicleInfo->rollLimit != -1 
01809                                 && !pVeh->m_iRemovedSurfaces 
01810                                 && parentPS->electrifyTime<curTime)
01811                         {
01812                                 if (pVeh->m_vOrientation[ROLL] > pVeh->m_pVehicleInfo->rollLimit )
01813                                 {
01814                                         pVeh->m_vOrientation[ROLL] = pVeh->m_pVehicleInfo->rollLimit;
01815                                 }
01816                                 else if (pVeh->m_vOrientation[ROLL] < -pVeh->m_pVehicleInfo->rollLimit)
01817                                 {
01818                                         pVeh->m_vOrientation[ROLL] = -pVeh->m_pVehicleInfo->rollLimit;
01819                                 }
01820                         }
01821                 }
01822         }
01823 
01824         if (pVeh->m_pVehicleInfo->surfDestruction)
01825         {
01826                 FighterDamageRoutine(pVeh, parent, parentPS, riderPS, isDead);
01827         }
01828         pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] );
01829 
01830         /********************************************************************************/
01831         /*      END     Here is where make sure the vehicle is properly oriented.       END                     */
01832         /********************************************************************************/
01833 }
01834 
01835 #ifdef QAGAME //ONLY in SP or on server, not cgame
01836 
01837 extern void PM_SetAnim(pmove_t  *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
01838 
01839 // This function makes sure that the vehicle is properly animated.
01840 static void AnimateVehicle( Vehicle_t *pVeh )
01841 {
01842         int Anim = -1; 
01843         int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300;
01844         qboolean isLanding = qfalse, isLanded = qfalse;
01845 #ifdef _JK2MP
01846         playerState_t *parentPS = pVeh->m_pParentEntity->playerState;
01847 #else
01848         playerState_t *parentPS = &pVeh->m_pParentEntity->client->ps;
01849 #endif
01850 #ifndef _JK2MP//SP
01851         //nothing
01852 #elif QAGAME//MP GAME
01853         int curTime = level.time;
01854 #elif CGAME//MP CGAME
01855         //FIXME: pass in ucmd?  Not sure if this is reliable...
01856         int curTime = pm->cmd.serverTime;
01857 #endif
01858 
01859         if ( parentPS->hyperSpaceTime
01860                 && curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME )
01861         {//Going to Hyperspace
01862                 //close the wings (FIXME: makes sense on X-Wing, not Shuttle?)
01863                 if ( pVeh->m_ulFlags & VEH_WINGSOPEN )
01864                 {
01865                         pVeh->m_ulFlags &= ~VEH_WINGSOPEN;
01866                         Anim = BOTH_WINGS_CLOSE;
01867                 }
01868         }
01869         else
01870         {
01871                 isLanding = FighterIsLanding( pVeh, parentPS );
01872                 isLanded = FighterIsLanded( pVeh, parentPS );
01873 
01874                 // if we're above launch height (way up in the air)... 
01875                 if ( !isLanding && !isLanded ) 
01876                 {
01877                         if ( !( pVeh->m_ulFlags & VEH_WINGSOPEN ) )
01878                         {
01879                                 pVeh->m_ulFlags |= VEH_WINGSOPEN;
01880                                 pVeh->m_ulFlags &= ~VEH_GEARSOPEN;
01881                                 Anim = BOTH_WINGS_OPEN;
01882                         }
01883                 }
01884                 // otherwise we're below launch height and still taking off.
01885                 else
01886                 {
01887                         if ( (pVeh->m_ucmd.forwardmove < 0 || pVeh->m_ucmd.upmove < 0||isLanded)
01888                                 && pVeh->m_LandTrace.fraction <= 0.4f
01889                                 && pVeh->m_LandTrace.plane.normal[2] >= MIN_LANDING_SLOPE )
01890                         {//already landed or trying to land and close to ground
01891                                 // Open gears.
01892                                 if ( !( pVeh->m_ulFlags & VEH_GEARSOPEN ) )
01893                                 {
01894 #ifdef _JK2MP
01895                                         if ( pVeh->m_pVehicleInfo->soundLand )
01896                                         {//just landed?
01897 #ifdef QAGAME//MP GAME-side
01898                                                 G_EntitySound( ((gentity_t *)(pVeh->m_pParentEntity)), CHAN_AUTO, pVeh->m_pVehicleInfo->soundLand );
01899 #elif CGAME//MP CGAME-side
01900                                                 //trap_S_StartSound( NULL, pVeh->m_pParentEntity->s.number, CHAN_AUTO, pVeh->m_pVehicleInfo->soundLand );
01901 #endif
01902                                         }
01903 #endif
01904                                         pVeh->m_ulFlags |= VEH_GEARSOPEN;
01905                                         Anim = BOTH_GEARS_OPEN;
01906                                 }
01907                         }
01908                         else
01909                         {//trying to take off and almost halfway off the ground
01910                                 // Close gears (if they're open).
01911                                 if ( pVeh->m_ulFlags & VEH_GEARSOPEN )
01912                                 {
01913                                         pVeh->m_ulFlags &= ~VEH_GEARSOPEN;
01914                                         Anim = BOTH_GEARS_CLOSE;
01915                                         //iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD; 
01916                                 }
01917                                 // If gears are closed, and we are below launch height, close the wings.
01918                                 else
01919                                 {
01920                                         if ( pVeh->m_ulFlags & VEH_WINGSOPEN )
01921                                         {
01922                                                 pVeh->m_ulFlags &= ~VEH_WINGSOPEN;
01923                                                 Anim = BOTH_WINGS_CLOSE;
01924                                         }
01925                                 }
01926                         }
01927                 }
01928         }
01929 
01930         if ( Anim != -1 )
01931         {
01932                 #ifdef _JK2MP
01933                         BG_SetAnim(pVeh->m_pParentEntity->playerState, bgAllAnims[pVeh->m_pParentEntity->localAnimIndex].anims,
01934                                 SETANIM_BOTH, Anim, iFlags, iBlend);
01935                 #else
01936                         NPC_SetAnim( pVeh->m_pParentEntity, SETANIM_BOTH, Anim, iFlags, iBlend );
01937                 #endif
01938         }
01939 }
01940 
01941 // This function makes sure that the rider's in this vehicle are properly animated.
01942 static void AnimateRiders( Vehicle_t *pVeh )
01943 {
01944 }
01945 
01946 #endif //game-only
01947 
01948 #ifndef QAGAME
01949 void AttachRidersGeneric( Vehicle_t *pVeh );
01950 #endif
01951 
01952 void G_SetFighterVehicleFunctions( vehicleInfo_t *pVehInfo )
01953 {
01954 #ifdef QAGAME //ONLY in SP or on server, not cgame
01955         pVehInfo->AnimateVehicle                        =               AnimateVehicle;
01956         pVehInfo->AnimateRiders                         =               AnimateRiders;
01957 //      pVehInfo->ValidateBoard                         =               ValidateBoard;
01958 //      pVehInfo->SetParent                                     =               SetParent;
01959 //      pVehInfo->SetPilot                                      =               SetPilot;
01960 //      pVehInfo->AddPassenger                          =               AddPassenger;
01961 //      pVehInfo->Animate                                       =               Animate;
01962         pVehInfo->Board                                         =               Board;
01963         pVehInfo->Eject                                         =               Eject;
01964 //      pVehInfo->EjectAll                                      =               EjectAll;
01965 //      pVehInfo->StartDeathDelay                       =               StartDeathDelay;
01966 //      pVehInfo->DeathUpdate                           =               DeathUpdate;
01967 //      pVehInfo->RegisterAssets                        =               RegisterAssets;
01968 //      pVehInfo->Initialize                            =               Initialize;
01969         pVehInfo->Update                                        =               Update;
01970 //      pVehInfo->UpdateRider                           =               UpdateRider;
01971 #endif //game-only
01972         pVehInfo->ProcessMoveCommands           =               ProcessMoveCommands;
01973         pVehInfo->ProcessOrientCommands         =               ProcessOrientCommands;
01974 
01975 #ifndef QAGAME //cgame prediction attachment func
01976         pVehInfo->AttachRiders                          =               AttachRidersGeneric;
01977 #endif
01978 //      pVehInfo->AttachRiders                          =               AttachRiders;
01979 //      pVehInfo->Ghost                                         =               Ghost;
01980 //      pVehInfo->UnGhost                                       =               UnGhost;
01981 //      pVehInfo->Inhabited                                     =               Inhabited;
01982 }
01983 
01984 // Following is only in game, not in namespace
01985 #ifdef _JK2MP
01986 #include "../namespace_end.h"
01987 #endif
01988 
01989 #ifdef QAGAME
01990 extern void G_AllocateVehicleObject(Vehicle_t **pVeh);
01991 #endif
01992 
01993 #ifdef _JK2MP
01994 #include "../namespace_begin.h"
01995 #endif
01996 
01997 // Create/Allocate a new Animal Vehicle (initializing it as well).
01998 void G_CreateFighterNPC( Vehicle_t **pVeh, const char *strType )
01999 {
02000         // Allocate the Vehicle.
02001 #ifdef _JK2MP
02002 #ifdef QAGAME
02003         //these will remain on entities on the client once allocated because the pointer is
02004         //never stomped. on the server, however, when an ent is freed, the entity struct is
02005         //memset to 0, so this memory would be lost..
02006     G_AllocateVehicleObject(pVeh);
02007 #else
02008         if (!*pVeh)
02009         { //only allocate a new one if we really have to
02010                 (*pVeh) = (Vehicle_t *) BG_Alloc( sizeof(Vehicle_t) );
02011         }
02012 #endif
02013         memset(*pVeh, 0, sizeof(Vehicle_t));
02014 #else
02015         (*pVeh) = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qtrue );
02016 #endif
02017         (*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strType )];
02018 }
02019 
02020 #ifdef _JK2MP
02021 
02022 #include "../namespace_end.h"
02023 
02024 //get rid of all the crazy defs we added for this file
02025 #undef currentAngles
02026 #undef currentOrigin
02027 #undef mins
02028 #undef maxs
02029 #undef legsAnimTimer
02030 #undef torsoAnimTimer
02031 #undef bool
02032 #undef false
02033 #undef true
02034 
02035 #undef sqrtf
02036 #undef Q_flrand
02037 
02038 #undef MOD_EXPLOSIVE
02039 #endif