codemp/game/g_vehicles.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 #include "q_shared.h"
00005 #include "g_local.h"
00006 
00007 #ifdef _JK2 //SP does not have this preprocessor for game like MP does
00008 #ifndef _JK2MP
00009 #define _JK2MP
00010 #endif
00011 #endif
00012 
00013 #ifndef _JK2MP
00014 #include "g_functions.h"
00015 #include "g_vehicles.h"
00016 #include "../CGame/cg_Local.h"
00017 #else
00018 #include "bg_vehicles.h"
00019 #endif
00020 
00021 #ifdef _JK2MP
00022 //this is really horrible, but it works! just be sure not to use any locals or anything
00023 //with these names (exluding bool, false, true). -rww
00024 #define currentAngles r.currentAngles
00025 #define currentOrigin r.currentOrigin
00026 #define mins r.mins
00027 #define maxs r.maxs
00028 #define legsAnimTimer legsTimer
00029 #define torsoAnimTimer torsoTimer
00030 #define bool qboolean
00031 #define false qfalse
00032 #define true qtrue
00033 
00034 #define sqrtf sqrt
00035 
00036 #define MOD_EXPLOSIVE MOD_SUICIDE
00037 #endif
00038 
00039 #ifndef _JK2MP
00040 #define GAME_INLINE inline
00041 #define bgEntity_t gentity_t
00042 #endif
00043 
00044 #ifdef _JK2MP
00045 extern gentity_t *NPC_Spawn_Do( gentity_t *ent );
00046 extern void NPC_SetAnim(gentity_t       *ent,int setAnimParts,int anim,int setAnimFlags);
00047 #else
00048 extern gentity_t *NPC_Spawn_Do( gentity_t *pEnt, qboolean fullSpawnNow );
00049 extern qboolean G_ClearLineOfSight(const vec3_t point1, const vec3_t point2, int ignore, int clipmask);
00050 
00051 extern qboolean G_SetG2PlayerModelInfo( gentity_t *pEnt, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn );
00052 extern void G_RemovePlayerModel( gentity_t *pEnt );
00053 extern void G_ChangePlayerModel( gentity_t *pEnt, const char *newModel );
00054 extern void G_RemoveWeaponModels( gentity_t *pEnt );
00055 extern void CG_ChangeWeapon( int num );
00056 extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
00057 extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType );
00058 extern void SetClientViewAngle( gentity_t *ent, vec3_t angle );
00059 
00060 extern vmCvar_t cg_thirdPersonAlpha;
00061 extern vec3_t playerMins;
00062 extern vec3_t playerMaxs;
00063 extern cvar_t   *g_speederControlScheme;
00064 extern cvar_t *in_joystick;
00065 extern void PM_SetAnim(pmove_t  *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
00066 extern int PM_AnimLength( int index, animNumber_t anim );
00067 extern void NPC_SetAnim(gentity_t       *ent,int setAnimParts,int anim,int setAnimFlags, int iBlend);
00068 extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
00069 #endif
00070 
00071 #ifdef _JK2MP
00072 #include "../namespace_begin.h"
00073 extern void BG_SetAnim(playerState_t *ps, animation_t *animations, int setAnimParts,int anim,int setAnimFlags, int blendTime);
00074 extern void BG_SetLegsAnimTimer(playerState_t *ps, int time );
00075 extern void BG_SetTorsoAnimTimer(playerState_t *ps, int time );
00076 #include "../namespace_end.h"
00077 void G_VehUpdateShields( gentity_t *targ );
00078 #ifdef QAGAME
00079 extern void VEH_TurretThink( Vehicle_t *pVeh, gentity_t *parent, int turretNum );
00080 #endif
00081 #else
00082 extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time );
00083 extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
00084 #endif
00085 
00086 extern qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh );
00087 
00088 void Vehicle_SetAnim(gentity_t *ent,int setAnimParts,int anim,int setAnimFlags, int iBlend)
00089 {
00090 #ifdef _JK2MP
00091         assert(ent->client);
00092         BG_SetAnim(&ent->client->ps, bgAllAnims[ent->localAnimIndex].anims, setAnimParts, anim, setAnimFlags, iBlend);
00093         ent->s.legsAnim = ent->client->ps.legsAnim;
00094 #else
00095         NPC_SetAnim(ent, setAnimParts, anim, setAnimFlags, iBlend);
00096 #endif
00097 }
00098 
00099 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 )
00100 {
00101 #ifdef _JK2MP
00102         trap_Trace(results, start, tMins, tMaxs, end, passEntityNum, contentmask);
00103 #else
00104         gi.trace( results, start, tMins, tMaxs, end, passEntityNum, contentmask );
00105 #endif
00106 }
00107 
00108 Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt )
00109 {
00110         gentity_t *ent = (gentity_t *)pEnt;
00111 
00112         if ( ent && ent->client && ent->client->NPC_class != CLASS_VEHICLE && ent->s.m_iVehicleNum != 0 ) //ent->client && ( ent->client->ps.eFlags & EF_IN_VEHICLE ) && ent->owner )
00113         {
00114                 return g_entities[ent->s.m_iVehicleNum].m_pVehicle;
00115         }
00116         return NULL;
00117 }
00118 
00119 
00120 
00121 float   G_CanJumpToEnemyVeh(Vehicle_t *pVeh, const usercmd_t *pUcmd )
00122 {
00123 #ifndef _JK2MP
00124         gentity_t*      rider = pVeh->m_pPilot;
00125 
00126         // If There Is An Enemy And We Are At The Same Z Height
00127         //------------------------------------------------------
00128         if (rider && 
00129                 rider->enemy && 
00130                 pUcmd->rightmove && 
00131                 fabsf(rider->enemy->currentOrigin[2] - rider->currentOrigin[2])<50.0f)
00132         {
00133                 if (level.time<pVeh->m_safeJumpMountTime)
00134                 {
00135                         return pVeh->m_safeJumpMountRightDot;
00136                 }
00137 
00138 
00139                 // If The Enemy Is Riding Another Vehicle
00140                 //----------------------------------------
00141                 Vehicle_t*      enemyVeh = G_IsRidingVehicle(rider->enemy);
00142                 if (enemyVeh)
00143                 {
00144                         vec3_t  enemyFwd;
00145                         vec3_t  toEnemy;
00146                         float   toEnemyDistance;
00147                         vec3_t  riderFwd;
00148                         vec3_t  riderRight;
00149                         float   riderRightDot;
00150 
00151                         // If He Is Close Enough And Going The Same Speed
00152                         //------------------------------------------------
00153                         VectorSubtract(rider->enemy->currentOrigin, rider->currentOrigin, toEnemy);
00154                         toEnemyDistance = VectorNormalize(toEnemy);
00155                         if (toEnemyDistance<70.0f && 
00156                                 pVeh->m_pParentEntity->resultspeed>100.0f &&
00157                                 fabsf(pVeh->m_pParentEntity->resultspeed - enemyVeh->m_pParentEntity->resultspeed)<100.0f)
00158                         {
00159                                 // If He Is Either To The Left Or Right Of Me
00160                                 //--------------------------------------------
00161                                 AngleVectors(rider->currentAngles, riderFwd, riderRight, 0);
00162                                 riderRightDot = DotProduct(riderRight, toEnemy);
00163                                 if ((pUcmd->rightmove>0 && riderRightDot>0.2) || (pUcmd->rightmove<0 &&riderRightDot<-0.2))
00164                                 {
00165                                         // If We Are Both Going About The Same Direction
00166                                         //-----------------------------------------------
00167                                         AngleVectors(rider->enemy->currentAngles, enemyFwd, 0, 0);
00168                                         if (DotProduct(enemyFwd, riderFwd)>0.2f)
00169                                         {
00170                                                 pVeh->m_safeJumpMountTime = level.time + Q_irand(3000, 4000);   // Ok, now you get a 3 sec window
00171                                                 pVeh->m_safeJumpMountRightDot = riderRightDot;
00172                                                 return riderRightDot;
00173                                         }// Same Direction?
00174                                 }// To Left Or Right?
00175                         }// Close Enough & Same Speed?
00176                 }// Enemy Riding A Vehicle?
00177         }// Has Enemy And On Same Z-Height
00178 #endif
00179         return 0.0f;
00180 }
00181 
00182 // Spawn this vehicle into the world.
00183 void G_VehicleSpawn( gentity_t *self )
00184 {
00185         float yaw;
00186         gentity_t *vehEnt;
00187 
00188         VectorCopy( self->currentOrigin, self->s.origin );
00189 
00190 #ifdef _JK2MP
00191         trap_LinkEntity( self );
00192 #else
00193         gi.linkentity( self );
00194 #endif
00195 
00196         if ( !self->count )
00197         {
00198                 self->count = 1;
00199         }
00200 
00201         //save this because self gets removed in next func
00202         yaw = self->s.angles[YAW];
00203         
00204 #ifdef _JK2MP
00205         vehEnt = NPC_Spawn_Do( self );
00206 #else
00207         vehEnt = NPC_Spawn_Do( self, qtrue );
00208 #endif
00209         
00210         if ( !vehEnt )
00211         {
00212                 return;//return NULL;
00213         }
00214         
00215         vehEnt->s.angles[YAW] = yaw;
00216         if ( vehEnt->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL )
00217         {
00218                 vehEnt->NPC->behaviorState = BS_CINEMATIC;
00219         }
00220 
00221 #ifdef _JK2MP //special check in case someone disconnects/dies while boarding
00222         if (vehEnt->spawnflags & 1)
00223         { //die without pilot
00224                 if (!vehEnt->damage)
00225                 { //default 10 sec
00226                         vehEnt->damage = 10000;
00227                 }
00228                 if (!vehEnt->speed)
00229                 { //default 512 units
00230                         vehEnt->speed = 512.0f;
00231                 }
00232                 vehEnt->m_pVehicle->m_iPilotTime = level.time + vehEnt->damage;
00233         }
00234 #else
00235         if (vehEnt->spawnflags & 1)
00236         { //die without pilot
00237                 vehEnt->m_pVehicle->m_iPilotTime = level.time + vehEnt->endFrame;
00238         }
00239 #endif
00240         //return vehEnt;
00241 }
00242 
00243 // Attachs an entity to the vehicle it's riding (it's owner).
00244 void G_AttachToVehicle( gentity_t *pEnt, usercmd_t **ucmd )
00245 {
00246         gentity_t               *vehEnt;
00247         mdxaBone_t              boltMatrix;
00248         gentity_t               *ent;
00249 #ifdef _JK2MP
00250         int                             crotchBolt;
00251 #endif
00252 
00253         if ( !pEnt || !ucmd )
00254                 return;
00255 
00256         ent = (gentity_t *)pEnt;
00257 
00258 #ifdef _JK2MP
00259         vehEnt = &g_entities[ent->r.ownerNum];
00260 #else
00261         vehEnt = ent->owner;
00262 #endif
00263         ent->waypoint = vehEnt->waypoint; // take the veh's waypoint as your own
00264 
00265         if ( !vehEnt->m_pVehicle )
00266                 return;
00267 
00268 #ifdef _JK2MP
00269         crotchBolt = trap_G2API_AddBolt(vehEnt->ghoul2, 0, "*driver");
00270 
00271         // Get the driver tag.
00272         trap_G2API_GetBoltMatrix( vehEnt->ghoul2, 0, crotchBolt, &boltMatrix,
00273                                                         vehEnt->m_pVehicle->m_vOrientation, vehEnt->currentOrigin,
00274                                                         level.time, NULL, vehEnt->modelScale );
00275         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, ent->client->ps.origin );
00276         G_SetOrigin(ent, ent->client->ps.origin);
00277         trap_LinkEntity( ent );
00278 #else
00279         // Get the driver tag.
00280         gi.G2API_GetBoltMatrix( vehEnt->ghoul2, vehEnt->playerModel, vehEnt->crotchBolt, &boltMatrix,
00281                                                         vehEnt->m_pVehicle->m_vOrientation, vehEnt->currentOrigin,
00282                                                         (cg.time?cg.time:level.time), NULL, vehEnt->s.modelScale );
00283         gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->client->ps.origin );
00284         gi.linkentity( ent );
00285 #endif
00286 }
00287 
00288 #ifndef _JK2MP
00289 void G_KnockOffVehicle( gentity_t *pRider, gentity_t *self, qboolean bPull )
00290 {
00291         Vehicle_t *pVeh = NULL;
00292         vec3_t riderAngles, fDir, rDir, dir2Me;
00293         float   fDot, rDot;
00294 
00295         if ( !pRider || !pRider->client )
00296         {
00297                 return;
00298         }
00299         
00300         pVeh = G_IsRidingVehicle( pRider );
00301 
00302         if ( !pVeh || !pVeh->m_pVehicleInfo )
00303         {
00304                 return;
00305         }
00306 
00307         VectorCopy( pRider->currentAngles, riderAngles );
00308         riderAngles[0] = 0;
00309         AngleVectors( riderAngles, fDir, rDir, NULL );
00310         VectorSubtract( self->currentOrigin, pRider->currentOrigin, dir2Me );
00311         dir2Me[2] = 0;
00312         VectorNormalize( dir2Me );
00313         fDot = DotProduct( fDir, dir2Me );
00314         if ( fDot >= 0.5f )
00315         {//I'm in front of them
00316                 if ( bPull )
00317                 {//pull them foward
00318                         pVeh->m_EjectDir = VEH_EJECT_FRONT;
00319                 }
00320                 else
00321                 {//push them back
00322                         pVeh->m_EjectDir = VEH_EJECT_REAR;
00323                 }
00324         }
00325         else if ( fDot <= -0.5f )
00326         {//I'm behind them
00327                 if ( bPull )
00328                 {//pull them back
00329                         pVeh->m_EjectDir = VEH_EJECT_REAR;
00330                 }
00331                 else
00332                 {//push them forward
00333                         pVeh->m_EjectDir = VEH_EJECT_FRONT;
00334                 }
00335         }
00336         else
00337         {//to the side of them
00338                 rDot = DotProduct( fDir, dir2Me );
00339                 if ( rDot >= 0.0f )
00340                 {//to the right
00341                         if ( bPull )
00342                         {//pull them right
00343                                 pVeh->m_EjectDir = VEH_EJECT_RIGHT;
00344                         }
00345                         else
00346                         {//push them left
00347                                 pVeh->m_EjectDir = VEH_EJECT_LEFT;
00348                         }
00349                 }
00350                 else
00351                 {//to the left
00352                         if ( bPull )
00353                         {//pull them left
00354                                 pVeh->m_EjectDir = VEH_EJECT_LEFT;
00355                         }
00356                         else
00357                         {//push them right
00358                                 pVeh->m_EjectDir = VEH_EJECT_RIGHT;
00359                         }
00360                 }
00361         }
00362         //now forcibly eject them
00363         pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qtrue );
00364 }
00365 #endif
00366 
00367 #ifndef _JK2MP //don't want this in mp at least for now
00368 void G_DrivableATSTDie( gentity_t *self )
00369 {
00370 }
00371 
00372 void G_DriveATST( gentity_t *pEnt, gentity_t *atst )
00373 {
00374         if ( pEnt->NPC_type && pEnt->client && (pEnt->client->NPC_class == CLASS_ATST) )
00375         {//already an atst, switch back
00376                 //open hatch
00377                 G_RemovePlayerModel( pEnt );
00378                 pEnt->NPC_type = "player";
00379                 pEnt->client->NPC_class = CLASS_PLAYER;
00380                 pEnt->flags &= ~FL_SHIELDED;
00381                 pEnt->client->ps.eFlags &= ~EF_IN_ATST;
00382                 //size
00383                 VectorCopy( playerMins, pEnt->mins );
00384                 VectorCopy( playerMaxs, pEnt->maxs );
00385                 pEnt->client->crouchheight = CROUCH_MAXS_2;
00386                 pEnt->client->standheight = DEFAULT_MAXS_2;
00387                 G_ChangePlayerModel( pEnt, pEnt->NPC_type );
00388                 //G_SetG2PlayerModel( pEnt, pEnt->NPC_type, NULL, NULL, NULL );
00389 
00390                 //FIXME: reset/4 their weapon
00391                 pEnt->client->ps.stats[STAT_WEAPONS] &= ~(( 1 << WP_ATST_MAIN )|( 1 << WP_ATST_SIDE ));
00392                 pEnt->client->ps.ammo[weaponData[WP_ATST_MAIN].ammoIndex] = 0;
00393                 pEnt->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = 0;
00394                 CG_ChangeWeapon( WP_BLASTER );
00395                 //camera
00396                 //if ( pEnt->client->ps.weapon != WP_SABER )
00397                 {
00398                         gi.cvar_set( "cg_thirdperson", "0" );
00399                 }
00400                 cg.overrides.active &= ~(CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_VOF|CG_OVERRIDE_3RD_PERSON_POF|CG_OVERRIDE_3RD_PERSON_APH);
00401                 cg.overrides.thirdPersonRange = cg.overrides.thirdPersonVertOffset = cg.overrides.thirdPersonPitchOffset = 0;
00402                 cg.overrides.thirdPersonAlpha = cg_thirdPersonAlpha.value;
00403                 pEnt->client->ps.viewheight = pEnt->maxs[2] + STANDARD_VIEWHEIGHT_OFFSET;
00404                 //pEnt->mass = 10;
00405         }
00406         else
00407         {//become an atst
00408                 pEnt->NPC_type = "atst";
00409                 pEnt->client->NPC_class = CLASS_ATST;
00410                 pEnt->client->ps.eFlags |= EF_IN_ATST;
00411                 pEnt->flags |= FL_SHIELDED;
00412                 //size
00413                 VectorSet( pEnt->mins, ATST_MINS0, ATST_MINS1, ATST_MINS2 );
00414                 VectorSet( pEnt->maxs, ATST_MAXS0, ATST_MAXS1, ATST_MAXS2 );
00415                 pEnt->client->crouchheight = ATST_MAXS2;
00416                 pEnt->client->standheight = ATST_MAXS2;
00417                 if ( !atst )
00418                 {//no pEnt to copy from
00419                         G_ChangePlayerModel( pEnt, "atst" );
00420                         //G_SetG2PlayerModel( pEnt, "atst", NULL, NULL, NULL );
00421                         NPC_SetAnim( pEnt, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE, 200 );
00422                 }
00423                 else
00424                 {
00425                         G_RemovePlayerModel( pEnt );
00426                         G_RemoveWeaponModels( pEnt );
00427                         gi.G2API_CopyGhoul2Instance( atst->ghoul2, pEnt->ghoul2 );
00428                         pEnt->playerModel = 0;
00429                         G_SetG2PlayerModelInfo( pEnt, "atst", NULL, NULL, NULL );
00430                         //turn off hatch underside
00431                         gi.G2API_SetSurfaceOnOff( &pEnt->ghoul2[pEnt->playerModel], "head_hatchcover", 0x00000002/*G2SURFACEFLAG_OFF*/ );
00432                         G_Sound( pEnt, G_SoundIndex( "sound/chars/atst/atst_hatch_close" ));
00433                 }
00434                 pEnt->s.radius = 320;
00435                 //weapon
00436                 gitem_t *item = FindItemForWeapon( WP_ATST_MAIN );      //precache the weapon
00437                 CG_RegisterItemSounds( (item-bg_itemlist) );
00438                 CG_RegisterItemVisuals( (item-bg_itemlist) );
00439                 item = FindItemForWeapon( WP_ATST_SIDE );       //precache the weapon
00440                 CG_RegisterItemSounds( (item-bg_itemlist) );
00441                 CG_RegisterItemVisuals( (item-bg_itemlist) );
00442                 pEnt->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ATST_MAIN )|( 1 << WP_ATST_SIDE );
00443                 pEnt->client->ps.ammo[weaponData[WP_ATST_MAIN].ammoIndex] = ammoData[weaponData[WP_ATST_MAIN].ammoIndex].max;
00444                 pEnt->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = ammoData[weaponData[WP_ATST_SIDE].ammoIndex].max;
00445                 CG_ChangeWeapon( WP_ATST_MAIN );
00446                 //HACKHACKHACKTEMP
00447                 item = FindItemForWeapon( WP_EMPLACED_GUN );
00448                 CG_RegisterItemSounds( (item-bg_itemlist) );
00449                 CG_RegisterItemVisuals( (item-bg_itemlist) );
00450                 item = FindItemForWeapon( WP_ROCKET_LAUNCHER );
00451                 CG_RegisterItemSounds( (item-bg_itemlist) );
00452                 CG_RegisterItemVisuals( (item-bg_itemlist) );
00453                 item = FindItemForWeapon( WP_BOWCASTER );
00454                 CG_RegisterItemSounds( (item-bg_itemlist) );
00455                 CG_RegisterItemVisuals( (item-bg_itemlist) );
00456                 //HACKHACKHACKTEMP
00457                 //FIXME: these get lost in load/save!  Must use variables that are set every frame or saved/loaded
00458                 //camera
00459                 gi.cvar_set( "cg_thirdperson", "1" );
00460                 cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG;
00461                 cg.overrides.thirdPersonRange = 240;
00462                 //cg.overrides.thirdPersonVertOffset = 100;
00463                 //cg.overrides.thirdPersonPitchOffset = -30;
00464                 //FIXME: this gets stomped in pmove?
00465                 pEnt->client->ps.viewheight = 120;
00466                 //FIXME: setting these broke things very badly...?
00467                 //pEnt->client->standheight = 200;
00468                 //pEnt->client->crouchheight = 200;
00469                 //pEnt->mass = 300;
00470                 //movement
00471                 //pEnt->client->ps.speed = 0;//FIXME: override speed?
00472                 //FIXME: slow turn turning/can't turn if not moving?
00473         }
00474 }
00475 #endif //_JK2MP
00476 
00477 // Animate the vehicle and it's riders.
00478 void Animate( Vehicle_t *pVeh )
00479 {
00480         // Validate a pilot rider.
00481         if ( pVeh->m_pPilot )
00482         {
00483                 if (pVeh->m_pVehicleInfo->AnimateRiders)
00484                 {
00485                         pVeh->m_pVehicleInfo->AnimateRiders( pVeh );
00486                 }
00487         }
00488 
00489         pVeh->m_pVehicleInfo->AnimateVehicle( pVeh );
00490 }
00491 
00492 // Determine whether this entity is able to board this vehicle or not.
00493 bool ValidateBoard( Vehicle_t *pVeh, bgEntity_t *pEnt )
00494 {
00495         // Determine where the entity is entering the vehicle from (left, right, or back).
00496         vec3_t vVehToEnt;
00497         vec3_t vVehDir;
00498         const gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
00499         const gentity_t *ent = (gentity_t *)pEnt;
00500         vec3_t vVehAngles;
00501         float fDot;
00502 
00503         if ( pVeh->m_iDieTime>0)
00504         {
00505                 return false;
00506         }
00507 
00508         if ( pVeh->m_pPilot != NULL )
00509         {//already have a driver!
00510                 if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
00511                 {//I know, I know, this should by in the fighters's validateboard()
00512                         //can never steal a fighter from it's pilot
00513                         if ( pVeh->m_iNumPassengers < pVeh->m_pVehicleInfo->maxPassengers )
00514                         {
00515                                 return true;
00516                         }
00517                         else
00518                         {
00519                                 return false;
00520                         }
00521                 }
00522                 else if ( pVeh->m_pVehicleInfo->type == VH_WALKER )
00523                 {//I know, I know, this should by in the walker's validateboard()
00524                         if ( !ent->client || ent->client->ps.groundEntityNum != parent->s.number )
00525                         {//can only steal an occupied AT-ST if you're on top (by the hatch)
00526                                 return false;
00527                         }
00528                 }
00529                 else if (pVeh->m_pVehicleInfo->type == VH_SPEEDER)
00530                 {//you can only steal the bike from the driver if you landed on the driver or bike
00531                         return (pVeh->m_iBoarding==VEH_MOUNT_THROW_LEFT || pVeh->m_iBoarding==VEH_MOUNT_THROW_RIGHT);
00532                 }
00533         }
00534         // Yes, you shouldn't have put this here (you 'should' have made an 'overriden' ValidateBoard func), but in this
00535         // instance it's more than adequate (which is why I do it too :-). Making a whole other function for this is silly.
00536         else if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
00537         {
00538                 // If you're a fighter, you allow everyone to enter you from all directions.
00539                 return true;
00540         }
00541 
00542         // Clear out all orientation axis except for the yaw.
00543         VectorSet(vVehAngles, 0, parent->currentAngles[YAW], 0);
00544 
00545         // Vector from Entity to Vehicle.
00546         VectorSubtract( ent->currentOrigin, parent->currentOrigin, vVehToEnt );
00547         vVehToEnt[2] = 0;
00548         VectorNormalize( vVehToEnt );
00549 
00550         // Get the right vector.
00551         AngleVectors( vVehAngles, NULL, vVehDir, NULL ); 
00552         VectorNormalize( vVehDir );
00553 
00554         // Find the angle between the vehicle right vector and the vehicle to entity vector.
00555         fDot = DotProduct( vVehToEnt, vVehDir );
00556 
00557         // If the entity is within a certain angle to the left of the vehicle...
00558         if ( fDot >= 0.5f )
00559         {
00560                 // Right board.
00561                 pVeh->m_iBoarding = -2;
00562         }
00563         else if ( fDot <= -0.5f )
00564         {
00565                 // Left board.
00566                 pVeh->m_iBoarding = -1;
00567         }
00568         // Maybe they're trying to board from the back...
00569         else
00570         {
00571                 // The forward vector of the vehicle.
00572         //      AngleVectors( vVehAngles, vVehDir, NULL, NULL ); 
00573         //      VectorNormalize( vVehDir );
00574 
00575                 // Find the angle between the vehicle forward and the vehicle to entity vector.
00576         //      fDot = DotProduct( vVehToEnt, vVehDir );
00577 
00578                 // If the entity is within a certain angle behind the vehicle...
00579                 //if ( fDot <= -0.85f )
00580                 {
00581                         // Jump board.
00582                         pVeh->m_iBoarding = -3;
00583                 }
00584         }
00585 
00586         // If for some reason we couldn't board, leave...
00587         if ( pVeh->m_iBoarding > -1 )
00588                 return false;
00589 
00590         return true;
00591 }
00592 
00593 #ifdef VEH_CONTROL_SCHEME_4
00594 void FighterStorePilotViewAngles( Vehicle_t *pVeh, bgEntity_t *parent )
00595 {
00596         playerState_t *riderPS;
00597 #ifdef _JK2MP
00598         bgEntity_t *rider = NULL;
00599         if (parent->s.owner != ENTITYNUM_NONE)
00600         {
00601                 rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
00602         }
00603 #else
00604         gentity_t *rider = parent->owner;
00605 #endif
00606 
00607 #ifdef _JK2MP
00608         if ( !rider )
00609 #else
00610         if ( !rider || !rider->client )
00611 #endif
00612         {
00613                 rider = parent;
00614         }
00615 
00616 #ifdef _JK2MP
00617         riderPS = rider->playerState;
00618 #else
00619         riderPS = &rider->client->ps;
00620 #endif
00621         VectorClear( pVeh->m_vPrevRiderViewAngles );
00622         pVeh->m_vPrevRiderViewAngles[YAW] = AngleNormalize180(riderPS->viewangles[YAW]);
00623 }
00624 #endif// VEH_CONTROL_SCHEME_4
00625 
00626 // Board this Vehicle (get on). The first entity to board an empty vehicle becomes the Pilot.
00627 bool Board( Vehicle_t *pVeh, bgEntity_t *pEnt )
00628 {
00629         vec3_t vPlayerDir;
00630         gentity_t *ent = (gentity_t *)pEnt;
00631         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
00632 
00633         // If it's not a valid entity, OR if the vehicle is blowing up (it's dead), OR it's not
00634         // empty, OR we're already being boarded, OR the person trying to get on us is already
00635         // in a vehicle (that was a fun bug :-), leave!
00636         if ( !ent || parent->health <= 0 /*|| !( parent->client->ps.eFlags & EF_EMPTY_VEHICLE )*/ || (pVeh->m_iBoarding > 0) ||
00637 #ifdef _JK2MP
00638                  ( ent->client->ps.m_iVehicleNum ) )
00639 #else
00640                  ( ent->s.m_iVehicleNum != 0 ) )
00641 #endif
00642                 return false;
00643 
00644         // Bucking so we can't do anything (NOTE: Should probably be a better name since fighters don't buck...).
00645         if ( pVeh->m_ulFlags & VEH_BUCKING )
00646                 return false;
00647 
00648         // Validate the entity's ability to board this vehicle.
00649         if ( !pVeh->m_pVehicleInfo->ValidateBoard( pVeh, pEnt ) )
00650                 return false;
00651 
00652         // FIXME FIXME!!! Ask Mike monday where ent->client->ps.eFlags might be getting changed!!! It is always 0 (when it should
00653         // be 1024) so a person riding a vehicle is able to ride another vehicle!!!!!!!!
00654 
00655         // Tell everybody their status.
00656         // ALWAYS let the player be the pilot.
00657         if ( ent->s.number < MAX_CLIENTS )
00658         {
00659                 pVeh->m_pOldPilot = pVeh->m_pPilot;
00660 
00661 
00662 #ifdef _JK2MP
00663                 if ( !pVeh->m_pPilot )
00664                 { //become the pilot, if there isn't one now
00665                         pVeh->m_pVehicleInfo->SetPilot( pVeh, (bgEntity_t *)ent );
00666                 }
00667                 // If we're not yet full...
00668                 else if ( pVeh->m_iNumPassengers < pVeh->m_pVehicleInfo->maxPassengers )
00669                 {
00670                         int i;
00671                         // Find an empty slot and put that passenger here.
00672                         for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
00673                         {
00674                                 if ( pVeh->m_ppPassengers[i] == NULL )
00675                                 {
00676                                         pVeh->m_ppPassengers[i] = (bgEntity_t *)ent;
00677 #ifdef QAGAME
00678                                         //Server just needs to tell client which passengernum he is
00679                                         if ( ent->client )
00680                                         {
00681                                                 ent->client->ps.generic1 = i+1;
00682                                         }
00683 #endif
00684                                         break;
00685                                 }
00686                         }
00687                         pVeh->m_iNumPassengers++;
00688                 }
00689                 // We're full, sorry...
00690                 else
00691                 {
00692                         return false;
00693                 }
00694                 ent->s.m_iVehicleNum = parent->s.number;
00695                 if (ent->client)
00696                 {
00697                         ent->client->ps.m_iVehicleNum = ent->s.m_iVehicleNum;
00698                 }
00699                 if ( pVeh->m_pPilot == (bgEntity_t *)ent )
00700                 {
00701                         parent->r.ownerNum = ent->s.number;
00702                         parent->s.owner = parent->r.ownerNum; //for prediction
00703                 }
00704 #else
00705                 pVeh->m_pVehicleInfo->SetPilot( pVeh, ent );
00706                 ent->s.m_iVehicleNum = parent->s.number;
00707                 parent->owner = ent;
00708 #endif
00709 
00710 #ifdef QAGAME           
00711                 {
00712                         gentity_t *gParent = (gentity_t *)parent;
00713                         if ( (gParent->spawnflags&2) )
00714                         {//was being suspended
00715                                 gParent->spawnflags &= ~2;//SUSPENDED - clear this spawnflag, no longer docked, okay to free-fall if not in space
00716                                 //gParent->client->ps.eFlags &= ~EF_RADAROBJECT;
00717                                 G_Sound( gParent, CHAN_AUTO, G_SoundIndex( "sound/vehicles/common/release.wav" ) );
00718                                 if ( gParent->fly_sound_debounce_time )
00719                                 {//we should drop like a rock for a few seconds
00720                                         pVeh->m_iDropTime = level.time + gParent->fly_sound_debounce_time;
00721                                 }
00722                         }
00723                 }
00724 #endif
00725 
00726 #ifndef _JK2MP
00727                 gi.cvar_set( "cg_thirdperson", "1" );                                                           //go to third person
00728                 CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 );         //tell them how to get out!
00729 #endif
00730 
00731                 //FIXME: rider needs to look in vehicle's direction when he gets in
00732                 // Clear these since they're used to turn the vehicle now.
00733                 /*SetClientViewAngle( ent, pVeh->m_vOrientation );
00734                 memset( &parent->client->usercmd, 0, sizeof( usercmd_t ) );
00735                 memset( &pVeh->m_ucmd, 0, sizeof( usercmd_t ) );
00736                 VectorClear( parent->client->ps.viewangles );
00737                 VectorClear( parent->client->ps.delta_angles );*/
00738 
00739                 // Set the looping sound only when there is a pilot (when the vehicle is "on").
00740                 if ( pVeh->m_pVehicleInfo->soundLoop )
00741                 {
00742 #ifdef _JK2MP
00743                         parent->client->ps.loopSound = parent->s.loopSound = pVeh->m_pVehicleInfo->soundLoop;
00744 #else
00745                         parent->s.loopSound = pVeh->m_pVehicleInfo->soundLoop;
00746 #endif
00747                 }
00748         }
00749         else
00750         {
00751                 // If there's no pilot, try to drive this vehicle.
00752                 if ( pVeh->m_pPilot == NULL )
00753                 {
00754 #ifdef _JK2MP
00755                         pVeh->m_pVehicleInfo->SetPilot( pVeh, (bgEntity_t *)ent );
00756                         // TODO: Set pilot should do all this stuff....
00757                         parent->r.ownerNum = ent->s.number;
00758                         parent->s.owner = parent->r.ownerNum; //for prediction
00759 #else
00760                         pVeh->m_pVehicleInfo->SetPilot( pVeh, ent );
00761                         // TODO: Set pilot should do all this stuff....
00762                         parent->owner = ent;
00763 #endif
00764                         // Set the looping sound only when there is a pilot (when the vehicle is "on").
00765                         if ( pVeh->m_pVehicleInfo->soundLoop )
00766                         {
00767 #ifdef _JK2MP
00768                                 parent->client->ps.loopSound = parent->s.loopSound = pVeh->m_pVehicleInfo->soundLoop;
00769 #else
00770                                 parent->s.loopSound = pVeh->m_pVehicleInfo->soundLoop;
00771 #endif
00772                         }
00773 
00774                         parent->client->ps.speed = 0;
00775                         memset( &pVeh->m_ucmd, 0, sizeof( usercmd_t ) );
00776                 }
00777                 // If we're not yet full...
00778                 else if ( pVeh->m_iNumPassengers < pVeh->m_pVehicleInfo->maxPassengers )
00779                 {
00780                         int i;
00781                         // Find an empty slot and put that passenger here.
00782                         for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
00783                         {
00784                                 if ( pVeh->m_ppPassengers[i] == NULL )
00785                                 {
00786 #ifdef _JK2MP
00787                                         pVeh->m_ppPassengers[i] = (bgEntity_t *)ent;
00788 #ifdef QAGAME
00789                                         //Server just needs to tell client which passengernum he is
00790                                         if ( ent->client )
00791                                         {
00792                                                 ent->client->ps.generic1 = i+1;
00793                                         }
00794 #endif
00795 #else
00796                                         pVeh->m_ppPassengers[i] = ent;
00797 #endif
00798                                         break;
00799                                 }
00800                         }
00801                         pVeh->m_iNumPassengers++;
00802                 }
00803                 // We're full, sorry...
00804                 else
00805                 {
00806                         return false;
00807                 }
00808         }
00809         
00810         // Make sure the entity knows it's in a vehicle.
00811 #ifdef _JK2MP
00812         ent->client->ps.m_iVehicleNum = parent->s.number;
00813         ent->r.ownerNum = parent->s.number;
00814         ent->s.owner = ent->r.ownerNum; //for prediction
00815         if (pVeh->m_pPilot == (bgEntity_t *)ent)
00816         {
00817                 parent->client->ps.m_iVehicleNum = ent->s.number+1; //always gonna be under MAX_CLIENTS so no worries about 1 byte overflow
00818         }
00819 #else
00820         ent->s.m_iVehicleNum = parent->s.number;
00821         ent->owner = parent;
00822         parent->s.m_iVehicleNum = ent->s.number+1;
00823 #endif
00824 
00825         //memset( &ent->client->usercmd, 0, sizeof( usercmd_t ) );
00826 
00827         //FIXME: no saber or weapons if numHands = 2, should switch to speeder weapon, no attack anim on player
00828         if ( pVeh->m_pVehicleInfo->numHands == 2 )
00829         {//switch to vehicle weapon
00830 #ifndef _JK2MP //rwwFIXMEFIXMEFIXME
00831                 if (ent->s.number<MAX_CLIENTS)
00832                 {
00833                         CG_ChangeWeapon(WP_NONE);
00834                 }
00835 
00836                 ent->client->ps.weapon = WP_NONE;
00837                 G_RemoveWeaponModels( ent );
00838 #endif
00839         }
00840 
00841         if ( pVeh->m_pVehicleInfo->hideRider )
00842         {//hide the rider
00843                 pVeh->m_pVehicleInfo->Ghost( pVeh, (bgEntity_t *)ent );
00844         }
00845 
00846         // Play the start sounds
00847         if ( pVeh->m_pVehicleInfo->soundOn )
00848         {
00849 #ifdef _JK2MP
00850                 G_Sound( parent, CHAN_AUTO, pVeh->m_pVehicleInfo->soundOn );
00851 #else
00852                 // NOTE: Use this type so it's spatialized and updates play origin as bike moves - MCG
00853                 G_SoundIndexOnEnt( parent, CHAN_AUTO, pVeh->m_pVehicleInfo->soundOn );
00854 #endif
00855         }
00856 
00857 #ifdef VEH_CONTROL_SCHEME_4
00858         if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
00859         {//clear their angles
00860                 FighterStorePilotViewAngles( pVeh, (bgEntity_t *)parent );
00861         }
00862 #endif //VEH_CONTROL_SCHEME_4
00863 
00864         VectorCopy( pVeh->m_vOrientation, vPlayerDir );
00865         vPlayerDir[ROLL] = 0;
00866         SetClientViewAngle( ent, vPlayerDir );
00867 
00868         return true;
00869 }
00870 
00871 bool VEH_TryEject( Vehicle_t *pVeh, 
00872                                   gentity_t *parent, 
00873                                   gentity_t *ent, 
00874                                   int ejectDir, 
00875                                   vec3_t vExitPos )
00876 {
00877         float           fBias;
00878         float           fVehDiag;
00879         float           fEntDiag;
00880         int                     oldOwner;
00881         vec3_t          vEntMins, vEntMaxs, vVehLeaveDir, vVehAngles;
00882         trace_t         m_ExitTrace;
00883 
00884         // Make sure that the entity is not 'stuck' inside the vehicle (since their bboxes will now intersect).
00885         // This makes the entity leave the vehicle from the right side.
00886         VectorSet(vVehAngles, 0, parent->currentAngles[YAW], 0);
00887         switch ( ejectDir )
00888         {
00889                 // Left.
00890                 case VEH_EJECT_LEFT:
00891                         AngleVectors( vVehAngles, NULL, vVehLeaveDir, NULL ); 
00892                         vVehLeaveDir[0] = -vVehLeaveDir[0];
00893                         vVehLeaveDir[1] = -vVehLeaveDir[1];
00894                         vVehLeaveDir[2] = -vVehLeaveDir[2];
00895                         break;
00896                 // Right.
00897                 case VEH_EJECT_RIGHT:
00898                         AngleVectors( vVehAngles, NULL, vVehLeaveDir, NULL ); 
00899                         break;
00900                 // Front.
00901                 case VEH_EJECT_FRONT:
00902                         AngleVectors( vVehAngles, vVehLeaveDir, NULL, NULL ); 
00903                         break;
00904                 // Rear.
00905                 case VEH_EJECT_REAR:
00906                         AngleVectors( vVehAngles, vVehLeaveDir, NULL, NULL ); 
00907                         vVehLeaveDir[0] = -vVehLeaveDir[0];
00908                         vVehLeaveDir[1] = -vVehLeaveDir[1];
00909                         vVehLeaveDir[2] = -vVehLeaveDir[2];
00910                         break;
00911                 // Top.
00912                 case VEH_EJECT_TOP:
00913                         AngleVectors( vVehAngles, NULL, NULL, vVehLeaveDir ); 
00914                         break;
00915                 // Bottom?.
00916                 case VEH_EJECT_BOTTOM:
00917                         break;
00918         }
00919         VectorNormalize( vVehLeaveDir );
00920         //NOTE: not sure why following line was needed - MCG
00921         //pVeh->m_EjectDir = VEH_EJECT_LEFT;
00922 
00923         // Since (as of this time) the collidable geometry of the entity is just an axis 
00924         // aligned box, we need to get the diagonal length of it in case we come out on that side.
00925         // Diagonal Length == squareroot( squared( Sidex / 2 ) + squared( Sidey / 2 ) );
00926 
00927         // TODO: DO diagonal for entity.
00928 
00929         fBias = 1.0f;
00930         if (pVeh->m_pVehicleInfo->type == VH_WALKER)
00931         { //hacktastic!
00932                 fBias += 0.2f;
00933         }
00934         VectorCopy( ent->currentOrigin, vExitPos );
00935         fVehDiag = sqrtf( ( parent->maxs[0] * parent->maxs[0] ) + ( parent->maxs[1] * parent->maxs[1] ) );
00936         VectorCopy( ent->maxs, vEntMaxs );
00937 #ifdef _JK2MP
00938         if ( ent->s.number < MAX_CLIENTS )
00939         {//for some reason, in MP, player client mins and maxs are never stored permanently, just set to these hardcoded numbers in PMove
00940                 vEntMaxs[0] = 15;
00941                 vEntMaxs[1] = 15;
00942         }
00943 #endif
00944         fEntDiag = sqrtf( ( vEntMaxs[0] * vEntMaxs[0] ) + ( vEntMaxs[1] * vEntMaxs[1] ) );
00945         vVehLeaveDir[0] *= ( fVehDiag + fEntDiag ) * fBias;     // x
00946         vVehLeaveDir[1] *= ( fVehDiag + fEntDiag ) * fBias;     // y
00947         vVehLeaveDir[2] *= ( fVehDiag + fEntDiag ) * fBias;
00948         VectorAdd( vExitPos, vVehLeaveDir, vExitPos );
00949 
00950         //we actually could end up *not* getting off if the trace fails...
00951         // Check to see if this new position is a valid place for our entity to go.
00952 #ifdef _JK2MP
00953         VectorSet(vEntMins, -15.0f, -15.0f, DEFAULT_MINS_2);
00954         VectorSet(vEntMaxs, 15.0f, 15.0f, DEFAULT_MAXS_2);
00955 #else
00956         VectorCopy(ent->mins, vEntMins);
00957         VectorCopy(ent->maxs, vEntMaxs);
00958 #endif
00959         oldOwner = ent->r.ownerNum;
00960         ent->r.ownerNum = ENTITYNUM_NONE;
00961         G_VehicleTrace( &m_ExitTrace, ent->currentOrigin, vEntMins, vEntMaxs, vExitPos, ent->s.number, ent->clipmask );
00962         ent->r.ownerNum = oldOwner;
00963 
00964         if ( m_ExitTrace.allsolid//in solid
00965                 || m_ExitTrace.startsolid)
00966         {
00967                 return false;
00968         }
00969         // If the trace hit something, we can't go there!
00970         if ( m_ExitTrace.fraction < 1.0f )
00971         {//not totally clear
00972 #ifdef _JK2MP
00973 //              if ( (parent->clipmask&ent->r.contents) )//vehicle could actually get stuck on body
00974 #else
00975                 if ( (parent->clipmask&ent->contents) )//vehicle could actually get stuck on body
00976 #endif
00977                 {//the trace hit the vehicle, don't let them get out, just in case
00978                         return false;
00979                 }
00980                 //otherwise, use the trace.endpos
00981                 VectorCopy( m_ExitTrace.endpos, vExitPos );
00982         }
00983         return true;
00984 }
00985 
00986 void G_EjectDroidUnit( Vehicle_t *pVeh, qboolean kill )
00987 {
00988         pVeh->m_pDroidUnit->s.m_iVehicleNum = ENTITYNUM_NONE;
00989 #ifdef _JK2MP
00990         pVeh->m_pDroidUnit->s.owner = ENTITYNUM_NONE;
00991 #else
00992         pVeh->m_pDroidUnit->owner = NULL;
00993 #endif
00994 //      pVeh->m_pDroidUnit->s.otherEntityNum2 = ENTITYNUM_NONE;
00995 #ifdef QAGAME
00996         {
00997                 gentity_t *droidEnt = (gentity_t *)pVeh->m_pDroidUnit;
00998                 droidEnt->flags &= ~FL_UNDYING;
00999                 droidEnt->r.ownerNum = ENTITYNUM_NONE;
01000                 if ( droidEnt->client )
01001                 {
01002                         droidEnt->client->ps.m_iVehicleNum = ENTITYNUM_NONE;
01003                 }
01004                 if ( kill )
01005                 {//Kill them, too
01006                         //FIXME: proper origin, MOD and attacker (for credit/death message)?  Get from vehicle?
01007                         G_MuteSound(droidEnt->s.number, CHAN_VOICE);
01008                         G_Damage( droidEnt, NULL, NULL, NULL, droidEnt->s.origin, 10000, 0, MOD_SUICIDE );//FIXME: proper MOD?  Get from vehicle?
01009                 }
01010         }
01011 #endif
01012         pVeh->m_pDroidUnit = NULL;
01013 }
01014 
01015 // Eject the pilot from the vehicle.
01016 bool Eject( Vehicle_t *pVeh, bgEntity_t *pEnt, qboolean forceEject )
01017 {
01018         gentity_t       *parent;
01019         vec3_t          vExitPos;
01020 #ifndef _JK2MP
01021         vec3_t          vPlayerDir;
01022 #endif
01023         gentity_t       *ent = (gentity_t *)pEnt;
01024         int                     firstEjectDir;
01025 
01026 #ifdef _JK2MP
01027         qboolean        taintedRider = qfalse;
01028         qboolean        deadRider = qfalse;
01029 
01030         if ( pEnt == pVeh->m_pDroidUnit )
01031         {
01032                 G_EjectDroidUnit( pVeh, qfalse );
01033                 return true;
01034         }
01035 
01036         if (ent)
01037         {
01038                 if (!ent->inuse || !ent->client || ent->client->pers.connected != CON_CONNECTED)
01039                 {
01040                         taintedRider = qtrue;
01041                         parent = (gentity_t *)pVeh->m_pParentEntity;
01042                         goto getItOutOfMe;
01043                 }
01044                 else if (ent->health < 1)
01045                 {
01046                         deadRider = qtrue;
01047                 }
01048         }
01049 #endif
01050 
01051         // Validate.
01052         if ( !ent )
01053         {
01054                 return false;
01055         }
01056         if ( !forceEject )
01057         {
01058                 if ( !( pVeh->m_iBoarding == 0 || pVeh->m_iBoarding == -999 || ( pVeh->m_iBoarding < -3 && pVeh->m_iBoarding >= -9 ) ) )
01059                 {
01060 #ifdef _JK2MP //I don't care, if he's dead get him off even if he died while boarding
01061                         deadRider = qtrue;
01062                         pVeh->m_iBoarding = 0;
01063                         pVeh->m_bWasBoarding = false;
01064 #else
01065                         return false;
01066 #endif
01067                 }
01068         }
01069 
01070 #ifndef _JK2MP //rwwFIXMEFIXMEFIXME
01071         if (ent->s.number<MAX_CLIENTS)
01072         {
01073                 CG_ChangeWeapon(WP_NONE);
01074         }
01075         ent->client->ps.weapon = WP_NONE;
01076         G_RemoveWeaponModels( ent );
01077 #endif
01078 
01079         parent = (gentity_t *)pVeh->m_pParentEntity;
01080 
01081         //Try ejecting in every direction
01082         if ( pVeh->m_EjectDir < VEH_EJECT_LEFT )
01083         {
01084                 pVeh->m_EjectDir = VEH_EJECT_LEFT;
01085         }
01086         else if ( pVeh->m_EjectDir > VEH_EJECT_BOTTOM )
01087         {
01088                 pVeh->m_EjectDir = VEH_EJECT_BOTTOM;
01089         }
01090         firstEjectDir = pVeh->m_EjectDir;
01091         while ( !VEH_TryEject( pVeh, parent, ent, pVeh->m_EjectDir, vExitPos ) )
01092         {
01093                 pVeh->m_EjectDir++;
01094                 if ( pVeh->m_EjectDir > VEH_EJECT_BOTTOM )
01095                 {
01096                         pVeh->m_EjectDir = VEH_EJECT_LEFT;
01097                 }
01098                 if ( pVeh->m_EjectDir == firstEjectDir )
01099                 {//they all failed
01100 #ifdef _JK2MP
01101                         if (!deadRider)
01102                         { //if he's dead.. just shove him in solid, who cares.
01103                                 return false;
01104                         }
01105 #endif
01106                         if ( forceEject )
01107                         {//we want to always get out, just eject him here
01108                                 VectorCopy( ent->currentOrigin, vExitPos );
01109                                 break;
01110                         }
01111                         else
01112                         {//can't eject
01113                                 return false;
01114                         }
01115                 }
01116         }
01117 
01118         // Move them to the exit position.
01119         G_SetOrigin( ent, vExitPos );
01120 #ifdef _JK2MP
01121         VectorCopy(ent->currentOrigin, ent->client->ps.origin);
01122         trap_LinkEntity( ent );
01123 #else
01124         gi.linkentity( ent );
01125 #endif
01126 
01127         // If it's the player, stop overrides.
01128         if ( ent->s.number < MAX_CLIENTS )
01129         {
01130 #ifndef _JK2MP
01131                 cg.overrides.active = 0;
01132 #else
01133 
01134 #endif
01135         }
01136 
01137 #ifdef _JK2MP //in MP if someone disconnects on us, we still have to clear our owner
01138 getItOutOfMe:
01139 #endif
01140 
01141         // If he's the pilot...
01142         if ( (gentity_t *)pVeh->m_pPilot == ent )
01143         {
01144 #ifdef _JK2MP
01145                 int j = 0;
01146 #endif
01147 
01148                 pVeh->m_pPilot = NULL;
01149 #ifdef _JK2MP
01150                 parent->r.ownerNum = ENTITYNUM_NONE;
01151                 parent->s.owner = parent->r.ownerNum; //for prediction
01152 
01153                 //keep these current angles
01154                 //SetClientViewAngle( parent, pVeh->m_vOrientation );
01155                 memset( &parent->client->pers.cmd, 0, sizeof( usercmd_t ) );
01156 #else
01157                 parent->owner = NULL;
01158 
01159                 //keep these current angles
01160                 //SetClientViewAngle( parent, pVeh->m_vOrientation );
01161                 memset( &parent->client->usercmd, 0, sizeof( usercmd_t ) );
01162 #endif
01163                 memset( &pVeh->m_ucmd, 0, sizeof( usercmd_t ) );
01164 
01165 #ifdef _JK2MP //if there are some passengers, promote the first passenger to pilot
01166                 while (j < pVeh->m_iNumPassengers)
01167                 {
01168                         if (pVeh->m_ppPassengers[j])
01169                         {
01170                                 int k = 1;
01171                                 pVeh->m_pVehicleInfo->SetPilot( pVeh, pVeh->m_ppPassengers[j] );
01172                                 parent->r.ownerNum = pVeh->m_ppPassengers[j]->s.number;
01173                                 parent->s.owner = parent->r.ownerNum; //for prediction
01174                                 parent->client->ps.m_iVehicleNum = pVeh->m_ppPassengers[j]->s.number+1;
01175 
01176                                 //rearrange the passenger slots now..
01177 #ifdef QAGAME
01178                                 //Server just needs to tell client he's not a passenger anymore
01179                                 if ( ((gentity_t *)pVeh->m_ppPassengers[j])->client )
01180                                 {
01181                                         ((gentity_t *)pVeh->m_ppPassengers[j])->client->ps.generic1 = 0;
01182                                 }
01183 #endif
01184                                 pVeh->m_ppPassengers[j] = NULL;
01185                                 while (k < pVeh->m_iNumPassengers)
01186                                 {
01187                                         if (!pVeh->m_ppPassengers[k-1])
01188                                         { //move down
01189                                                 pVeh->m_ppPassengers[k-1] = pVeh->m_ppPassengers[k];
01190                                                 pVeh->m_ppPassengers[k] = NULL;
01191 #ifdef QAGAME
01192                                                 //Server just needs to tell client which passenger he is
01193                                                 if ( ((gentity_t *)pVeh->m_ppPassengers[k-1])->client )
01194                                                 {
01195                                                         ((gentity_t *)pVeh->m_ppPassengers[k-1])->client->ps.generic1 = k;
01196                                                 }
01197 #endif
01198                                         }
01199                                         k++;
01200                                 }
01201                                 pVeh->m_iNumPassengers--;
01202 
01203                                 break;
01204                         }
01205                         j++;
01206                 }
01207 #endif
01208         }
01209         else if (ent==(gentity_t *)pVeh->m_pOldPilot)
01210         {
01211                 pVeh->m_pOldPilot = 0;
01212         }
01213         else
01214         {
01215                 int i;
01216                 // Look for this guy in the passenger list.
01217                 for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
01218                 {
01219                         // If we found him...
01220                         if ( (gentity_t *)pVeh->m_ppPassengers[i] == ent )
01221                         {
01222 #ifdef QAGAME
01223                                 //Server just needs to tell client he's not a passenger anymore
01224                                 if ( ((gentity_t *)pVeh->m_ppPassengers[i])->client )
01225                                 {
01226                                         ((gentity_t *)pVeh->m_ppPassengers[i])->client->ps.generic1 = 0;
01227                                 }
01228 #endif
01229                                 pVeh->m_ppPassengers[i] = NULL;
01230                                 pVeh->m_iNumPassengers--;
01231                                 break;
01232                         }
01233                 }
01234 
01235                 // Didn't find him, can't eject because they aren't in the vehicle (hopefully)!
01236                 if ( i == pVeh->m_pVehicleInfo->maxPassengers )
01237                 {
01238                         return false;
01239                 }
01240         }
01241 
01242 #ifdef _JK2MP //I hate adding these!
01243         if (!taintedRider)
01244         {
01245 #endif
01246                 if ( pVeh->m_pVehicleInfo->hideRider )
01247                 {
01248                         pVeh->m_pVehicleInfo->UnGhost( pVeh, (bgEntity_t *)ent );
01249                 }
01250 #ifdef _JK2MP
01251         }
01252 #endif
01253 
01254         // If the vehicle now has no pilot...
01255         if ( pVeh->m_pPilot == NULL  )
01256         {
01257 #ifdef _JK2MP
01258                 parent->client->ps.loopSound = parent->s.loopSound = 0;
01259 #else
01260                 parent->s.loopSound = 0;
01261 #endif
01262                 // Completely empty vehicle...?
01263                 if ( pVeh->m_iNumPassengers == 0 )
01264                 {
01265 #ifdef _JK2MP
01266                         parent->client->ps.m_iVehicleNum = 0;
01267 #else
01268                         parent->s.m_iVehicleNum = 0;
01269 #endif
01270                 }
01271         }
01272 
01273 #ifdef _JK2MP
01274         if (taintedRider)
01275         { //you can go now
01276                 pVeh->m_iBoarding = level.time + 1000;
01277                 return true;
01278         }
01279 #endif
01280 
01281         // Client not in a vehicle.
01282 #ifdef _JK2MP
01283         ent->client->ps.m_iVehicleNum = 0;
01284         ent->r.ownerNum = ENTITYNUM_NONE;
01285         ent->s.owner = ent->r.ownerNum; //for prediction
01286 
01287         ent->client->ps.viewangles[PITCH] = 0.0f;
01288         ent->client->ps.viewangles[ROLL] = 0.0f;
01289         ent->client->ps.viewangles[YAW] = pVeh->m_vOrientation[YAW];
01290         SetClientViewAngle(ent, ent->client->ps.viewangles);
01291 
01292         if (ent->client->solidHack)
01293         {
01294                 ent->client->solidHack = 0;
01295                 ent->r.contents = CONTENTS_BODY;
01296         }
01297 #else
01298         ent->owner = NULL;      
01299 #endif
01300         ent->s.m_iVehicleNum = 0;
01301 
01302         // Jump out.
01303 /*      if ( ent->client->ps.velocity[2] < JUMP_VELOCITY )
01304         {
01305                 ent->client->ps.velocity[2] = JUMP_VELOCITY;
01306         }
01307         else
01308         {
01309                 ent->client->ps.velocity[2] += JUMP_VELOCITY;
01310         }*/
01311 
01312         // Make sure entity is facing the direction it got off at.
01313 #ifndef _JK2MP
01314         VectorCopy( pVeh->m_vOrientation, vPlayerDir );
01315         vPlayerDir[ROLL] = 0;
01316         SetClientViewAngle( ent, vPlayerDir );
01317 #endif
01318 
01319         //if was using vehicle weapon, remove it and switch to normal weapon when hop out...
01320         if ( ent->client->ps.weapon == WP_NONE )
01321         {//FIXME: check against this vehicle's gun from the g_vehicleInfo table
01322                 //remove the vehicle's weapon from me
01323                 //ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_EMPLACED_GUN );
01324                 //ent->client->ps.ammo[weaponData[WP_EMPLACED_GUN].ammoIndex] = 0;//maybe store this ammo on the vehicle before clearing it?
01325                 //switch back to a normal weapon we're carrying
01326                 
01327                 //FIXME: store the weapon we were using when we got on and restore that when hop off
01328 /*              if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<<WP_SABER)) )
01329                 {
01330                         CG_ChangeWeapon( WP_SABER );
01331                 }
01332                 else
01333                 {//go through all weapons and switch to highest held
01334                         for ( int checkWp = WP_NUM_WEAPONS-1; checkWp > WP_NONE; checkWp-- )
01335                         {
01336                                 if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<<checkWp)) )
01337                                 {
01338                                         CG_ChangeWeapon( checkWp );
01339                                 }
01340                         }
01341                         if ( checkWp == WP_NONE )
01342                         {
01343                                 CG_ChangeWeapon( WP_NONE );
01344                         }
01345                 }*/
01346         }
01347         else
01348         {//FIXME: if they have their saber out:
01349                 //if dualSabers, add the second saber into the left hand
01350                 //saber[0] has more than one blade, turn them all on
01351                 //NOTE: this is because you're only allowed to use your first saber's first blade on a vehicle
01352         }
01353 
01354 /*      if ( !ent->s.number && ent->client->ps.weapon != WP_SABER 
01355                 && cg_gunAutoFirst.value )
01356         {
01357                 gi.cvar_set( "cg_thirdperson", "0" );
01358         }*/
01359 #ifdef _JK2MP
01360         BG_SetLegsAnimTimer( &ent->client->ps, 0 );
01361         BG_SetTorsoAnimTimer( &ent->client->ps, 0 );
01362 #else
01363         PM_SetLegsAnimTimer( ent, &ent->client->ps.legsAnimTimer, 0 );
01364         PM_SetTorsoAnimTimer( ent, &ent->client->ps.torsoAnimTimer, 0 );
01365 #endif
01366 
01367         // Set how long until this vehicle can be boarded again.
01368         pVeh->m_iBoarding = level.time + 1000;
01369 
01370         return true;
01371 }
01372 
01373 // Eject all the inhabitants of this vehicle.
01374 bool EjectAll( Vehicle_t *pVeh )
01375 {
01376         // TODO: Setup a default escape for ever vehicle type.
01377 
01378         pVeh->m_EjectDir = VEH_EJECT_TOP;
01379         // Make sure no other boarding calls exist. We MUST exit.
01380         pVeh->m_iBoarding = 0;
01381         pVeh->m_bWasBoarding = false;
01382 
01383         // Throw them off.
01384         if ( pVeh->m_pPilot )
01385         {
01386 #ifdef QAGAME
01387                 gentity_t *pilot = (gentity_t*)pVeh->m_pPilot;
01388 #endif
01389                 pVeh->m_pVehicleInfo->Eject( pVeh, pVeh->m_pPilot, qtrue );
01390 #ifdef QAGAME
01391                 if ( pVeh->m_pVehicleInfo->killRiderOnDeath && pilot )
01392                 {//Kill them, too
01393                         //FIXME: proper origin, MOD and attacker (for credit/death message)?  Get from vehicle?
01394                         G_MuteSound(pilot->s.number, CHAN_VOICE);
01395                         G_Damage( pilot, NULL, NULL, NULL, pilot->s.origin, 10000, 0, MOD_SUICIDE );
01396                 }
01397 #endif
01398         }
01399         if ( pVeh->m_pOldPilot )
01400         {
01401 #ifdef QAGAME
01402                 gentity_t *pilot = (gentity_t*)pVeh->m_pOldPilot;
01403 #endif
01404                 pVeh->m_pVehicleInfo->Eject( pVeh, pVeh->m_pOldPilot, qtrue );
01405 #ifdef QAGAME
01406                 if ( pVeh->m_pVehicleInfo->killRiderOnDeath && pilot )
01407                 {//Kill them, too
01408                         //FIXME: proper origin, MOD and attacker (for credit/death message)?  Get from vehicle?
01409                         G_MuteSound(pilot->s.number, CHAN_VOICE);
01410                         G_Damage( pilot, NULL, NULL, NULL, pilot->s.origin, 10000, 0, MOD_SUICIDE );
01411                 }
01412 #endif
01413         }
01414         if ( pVeh->m_iNumPassengers )
01415         {
01416                 int i;
01417 
01418                 for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
01419                 {
01420                         if ( pVeh->m_ppPassengers[i] )
01421                         {
01422 #ifdef QAGAME
01423                                 gentity_t *rider = (gentity_t*)pVeh->m_ppPassengers[i];
01424 #endif
01425                                 pVeh->m_pVehicleInfo->Eject( pVeh, pVeh->m_ppPassengers[i], qtrue );
01426 #ifdef QAGAME
01427                                 if ( pVeh->m_pVehicleInfo->killRiderOnDeath && rider )
01428                                 {//Kill them, too
01429                                         //FIXME: proper origin, MOD and attacker (for credit/death message)?  Get from vehicle?
01430                                         G_MuteSound(rider->s.number, CHAN_VOICE);
01431                                         G_Damage( rider, NULL, NULL, NULL, rider->s.origin, 10000, 0, MOD_SUICIDE );//FIXME: proper MOD?  Get from vehicle?
01432                                 }
01433 #endif
01434                         }
01435                 }
01436                 pVeh->m_iNumPassengers = 0;
01437         }
01438         
01439         if ( pVeh->m_pDroidUnit )
01440         {
01441                 G_EjectDroidUnit( pVeh, pVeh->m_pVehicleInfo->killRiderOnDeath );
01442         }
01443 
01444         return true;
01445 }
01446 
01447 // Start a delay until the vehicle explodes.
01448 static void StartDeathDelay( Vehicle_t *pVeh, int iDelayTimeOverride )
01449 {
01450         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
01451 
01452         if ( iDelayTimeOverride )
01453         {
01454                 pVeh->m_iDieTime = level.time + iDelayTimeOverride;
01455         }
01456         else
01457         {
01458                 pVeh->m_iDieTime = level.time + pVeh->m_pVehicleInfo->explosionDelay;
01459         }
01460 
01461 #ifdef _JK2MP
01462         if ( pVeh->m_pVehicleInfo->flammable )
01463         {
01464                 parent->client->ps.loopSound = parent->s.loopSound = G_SoundIndex( "sound/vehicles/common/fire_lp.wav" );
01465         }
01466 #else
01467         // Armor Gone Effects (Fire)
01468         //---------------------------
01469         if (pVeh->m_pVehicleInfo->iArmorGoneFX)
01470         {
01471                 if (!(pVeh->m_ulFlags&VEH_ARMORGONE) && (pVeh->m_iArmor <= 0))
01472                 {
01473                         pVeh->m_ulFlags |= VEH_ARMORGONE;
01474                         G_PlayEffect(pVeh->m_pVehicleInfo->iArmorGoneFX, parent->playerModel, parent->crotchBolt, parent->s.number, parent->currentOrigin, 1, qtrue);
01475                         parent->s.loopSound = G_SoundIndex( "sound/vehicles/common/fire_lp.wav" );
01476                 }
01477         }
01478 #endif
01479 }
01480 
01481 // Decide whether to explode the vehicle or not.
01482 static void DeathUpdate( Vehicle_t *pVeh )
01483 {
01484         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
01485 
01486         if ( level.time >= pVeh->m_iDieTime )
01487         {
01488                 // If the vehicle is not empty.
01489                 if ( pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
01490                 {
01491 #ifndef _JK2MP
01492                         if (pVeh->m_pPilot)
01493                         {
01494                                 pVeh->m_pPilot->client->noRagTime = -1;         // no ragdoll for you
01495                         }
01496 #endif
01497 
01498                         pVeh->m_pVehicleInfo->EjectAll( pVeh );
01499 #ifdef _JK2MP
01500                         if ( pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
01501                         { //if we've still got people in us, just kill the bastards
01502                                 if ( pVeh->m_pPilot )
01503                                 {
01504                                         //FIXME: does this give proper credit to the enemy who shot you down?
01505                                         G_Damage((gentity_t *)pVeh->m_pPilot, (gentity_t *)pVeh->m_pParentEntity, (gentity_t *)pVeh->m_pParentEntity,
01506                                                 NULL, pVeh->m_pParentEntity->playerState->origin, 999, DAMAGE_NO_PROTECTION, MOD_EXPLOSIVE);
01507                                 }
01508                                 if ( pVeh->m_iNumPassengers )
01509                                 {
01510                                         int i;
01511 
01512                                         for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
01513                                         {
01514                                                 if ( pVeh->m_ppPassengers[i] )
01515                                                 {
01516                                                         //FIXME: does this give proper credit to the enemy who shot you down?
01517                                                         G_Damage((gentity_t *)pVeh->m_ppPassengers[i], (gentity_t *)pVeh->m_pParentEntity, (gentity_t *)pVeh->m_pParentEntity,
01518                                                                 NULL, pVeh->m_pParentEntity->playerState->origin, 999, DAMAGE_NO_PROTECTION, MOD_EXPLOSIVE);
01519                                                 }
01520                                         }
01521                                 }                               
01522                         }
01523 #endif
01524                 }
01525 
01526                 if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
01527                 { //explode now as long as we managed to kick everyone out
01528                         vec3_t  lMins, lMaxs, bottom;
01529                         trace_t trace;
01530 
01531 
01532 #ifndef _JK2MP
01533                         // Kill All Client Side Looping Effects
01534                         //--------------------------------------
01535                         if (pVeh->m_pVehicleInfo->iExhaustFX)
01536                         {
01537                                 for (int i=0; (i<MAX_VEHICLE_EXHAUSTS && pVeh->m_iExhaustTag[i]!=-1); i++)
01538                                 {
01539                                         G_StopEffect(pVeh->m_pVehicleInfo->iExhaustFX, parent->playerModel, pVeh->m_iExhaustTag[i], parent->s.number);
01540                                 }
01541                         }
01542                         if (pVeh->m_pVehicleInfo->iArmorLowFX)
01543                         {
01544                                 G_StopEffect(pVeh->m_pVehicleInfo->iArmorLowFX,  parent->playerModel, parent->crotchBolt, parent->s.number);
01545                         }
01546                         if (pVeh->m_pVehicleInfo->iArmorGoneFX)
01547                         {
01548                                 G_StopEffect(pVeh->m_pVehicleInfo->iArmorGoneFX, parent->playerModel, parent->crotchBolt, parent->s.number);
01549                         }
01550                         //--------------------------------------
01551 #endif
01552                         if ( pVeh->m_pVehicleInfo->iExplodeFX )
01553                         {
01554 #ifdef _JK2MP
01555                                 vec3_t fxAng;
01556 
01557                                 VectorSet(fxAng, -90.0f, 0.0f, 0.0f);
01558                                 G_PlayEffectID( pVeh->m_pVehicleInfo->iExplodeFX, parent->currentOrigin, fxAng );
01559 #else
01560                                 G_PlayEffect( pVeh->m_pVehicleInfo->iExplodeFX, parent->currentOrigin, vec3_origin );
01561 #endif
01562                                 //trace down and place mark
01563                                 VectorCopy( parent->currentOrigin, bottom );
01564                                 bottom[2] -= 80;
01565                                 G_VehicleTrace( &trace, parent->currentOrigin, vec3_origin, vec3_origin, bottom, parent->s.number, CONTENTS_SOLID );
01566                                 if ( trace.fraction < 1.0f )
01567                                 {
01568                                         VectorCopy( trace.endpos, bottom );
01569                                         bottom[2] += 2;
01570 #ifdef _JK2MP
01571                                         VectorSet(fxAng, -90.0f, 0.0f, 0.0f);
01572                                         G_PlayEffectID( G_EffectIndex("ships/ship_explosion_mark"), trace.endpos, fxAng );
01573 #else
01574                                         G_PlayEffect( "ships/ship_explosion_mark", trace.endpos );
01575 #endif
01576                                 }
01577                         }
01578 
01579                         parent->takedamage = qfalse;//so we don't recursively damage ourselves
01580                         if ( pVeh->m_pVehicleInfo->explosionRadius > 0 && pVeh->m_pVehicleInfo->explosionDamage > 0 )
01581                         {
01582                                 VectorCopy( parent->mins, lMins );
01583                                 lMins[2] = -4;//to keep it off the ground a *little*
01584                                 VectorCopy( parent->maxs, lMaxs );
01585                                 VectorCopy( parent->currentOrigin, bottom );
01586                                 bottom[2] += parent->mins[2] - 32;
01587                                 G_VehicleTrace( &trace, parent->currentOrigin, lMins, lMaxs, bottom, parent->s.number, CONTENTS_SOLID );
01588 #ifdef _JK2MP
01589                                 G_RadiusDamage( trace.endpos, NULL, pVeh->m_pVehicleInfo->explosionDamage, pVeh->m_pVehicleInfo->explosionRadius, NULL, NULL, MOD_EXPLOSIVE );//FIXME: extern damage and radius or base on fuel
01590 #else
01591                                 G_RadiusDamage( trace.endpos, NULL, pVeh->m_pVehicleInfo->explosionDamage, pVeh->m_pVehicleInfo->explosionRadius, NULL, MOD_EXPLOSIVE );//FIXME: extern damage and radius or base on fuel
01592 #endif
01593                         }
01594 
01595 #ifdef _JK2MP
01596                         parent->think = G_FreeEntity;
01597 #else
01598                         parent->e_ThinkFunc = thinkF_G_FreeEntity;
01599 #endif
01600                         parent->nextthink = level.time + FRAMETIME;
01601                 }
01602         }
01603 #ifndef _JK2MP
01604         else
01605         {//let everyone around me know I'm gonna blow!
01606                 if ( !Q_irand( 0, 10 ) )
01607                 {//not so often...
01608                         AddSoundEvent( parent, parent->currentOrigin, 512, AEL_DANGER );
01609                         AddSightEvent( parent, parent->currentOrigin, 512, AEL_DANGER, 100 );
01610                 }
01611         }
01612 #endif
01613 }
01614 
01615 // Register all the assets used by this vehicle.
01616 void RegisterAssets( Vehicle_t *pVeh )
01617 {
01618 }
01619 
01620 extern void ChangeWeapon( gentity_t *ent, int newWeapon );
01621 
01622 // Initialize the vehicle.
01623 bool Initialize( Vehicle_t *pVeh )
01624 {
01625         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
01626         int i = 0;
01627 
01628         if ( !parent || !parent->client )
01629                 return false;
01630 
01631 #ifdef _JK2MP
01632         parent->client->ps.m_iVehicleNum = 0;
01633 #endif
01634         parent->s.m_iVehicleNum = 0;
01635         {
01636         pVeh->m_iArmor = pVeh->m_pVehicleInfo->armor;
01637         parent->client->pers.maxHealth = parent->client->ps.stats[STAT_MAX_HEALTH] = parent->NPC->stats.health = parent->health = parent->client->ps.stats[STAT_HEALTH] = pVeh->m_iArmor;
01638         pVeh->m_iShields = pVeh->m_pVehicleInfo->shields;
01639 #ifdef _JK2MP
01640         G_VehUpdateShields( parent );
01641 #endif
01642         parent->client->ps.stats[STAT_ARMOR] = pVeh->m_iShields;
01643         }
01644         parent->mass = pVeh->m_pVehicleInfo->mass;
01645         //initialize the ammo to max
01646         for ( i = 0; i < MAX_VEHICLE_WEAPONS; i++ )
01647         {
01648                 parent->client->ps.ammo[i] = pVeh->weaponStatus[i].ammo = pVeh->m_pVehicleInfo->weapon[i].ammoMax;
01649         }
01650         for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
01651         {
01652                 pVeh->turretStatus[i].nextMuzzle = (pVeh->m_pVehicleInfo->turret[i].iMuzzle[i]-1);
01653                 parent->client->ps.ammo[MAX_VEHICLE_WEAPONS+i] = pVeh->turretStatus[i].ammo = pVeh->m_pVehicleInfo->turret[i].iAmmoMax;
01654                 if ( pVeh->m_pVehicleInfo->turret[i].bAI )
01655                 {//they're going to be finding enemies, init this to NONE
01656                         pVeh->turretStatus[i].enemyEntNum = ENTITYNUM_NONE;
01657                 }
01658         }
01659         //begin stopped...?
01660         parent->client->ps.speed = 0; 
01661         
01662         VectorClear( pVeh->m_vOrientation );
01663         pVeh->m_vOrientation[YAW] = parent->s.angles[YAW];
01664 
01665 #ifdef _JK2MP
01666         if ( pVeh->m_pVehicleInfo->gravity &&
01667                 pVeh->m_pVehicleInfo->gravity != g_gravity.value )
01668         {//not normal gravity
01669                 if ( parent->NPC )
01670                 {
01671                         parent->NPC->aiFlags |= NPCAI_CUSTOM_GRAVITY;
01672                 }
01673                 parent->client->ps.gravity = pVeh->m_pVehicleInfo->gravity;
01674         }
01675 #else
01676         if ( pVeh->m_pVehicleInfo->gravity &&
01677                 pVeh->m_pVehicleInfo->gravity != g_gravity->value )
01678         {//not normal gravity
01679                 parent->svFlags |= SVF_CUSTOM_GRAVITY;
01680                 parent->client->ps.gravity = pVeh->m_pVehicleInfo->gravity;
01681         }
01682 #endif
01683 
01684         if ( pVeh->m_pVehicleInfo->maxPassengers > 0 )
01685         {
01686                 int i;
01687 
01688                 // Allocate an array of entity pointers.
01689 #ifndef _JK2MP //this is kind of silly if you ask me, I'm just using a static pointer array
01690                 pVeh->m_ppPassengers = (gentity_t**)G_Alloc ( sizeof(gentity_t*) * pVeh->m_pVehicleInfo->maxPassengers );
01691 #endif
01692                 for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
01693                 {
01694                         pVeh->m_ppPassengers[i] = NULL;
01695                 }
01696         }
01697 
01698         pVeh->m_iNumPassengers = 0; 
01699         /*
01700         if ( pVeh->m_iVehicleTypeID == VH_FIGHTER )
01701         {
01702                 pVeh->m_ulFlags = VEH_GEARSOPEN;
01703         }
01704         else
01705         */
01706         //why?! -rww
01707         {
01708                 pVeh->m_ulFlags = 0;
01709         }
01710         pVeh->m_fTimeModifier = 1.0f;
01711         pVeh->m_iBoarding = 0;
01712         pVeh->m_bWasBoarding = false;
01713         pVeh->m_pOldPilot = NULL;
01714         VectorClear(pVeh->m_vBoardingVelocity);
01715         pVeh->m_pPilot = NULL;
01716         memset( &pVeh->m_ucmd, 0, sizeof( usercmd_t ) );
01717         pVeh->m_iDieTime = 0;
01718         pVeh->m_EjectDir = VEH_EJECT_LEFT;
01719 
01720         //pVeh->m_iDriverTag = -1;
01721         //pVeh->m_iLeftExhaustTag = -1;
01722         //pVeh->m_iRightExhaustTag = -1;
01723         //pVeh->m_iGun1Tag = -1;
01724         //pVeh->m_iGun1Bone = -1;
01725         //pVeh->m_iLeftWingBone = -1;
01726         //pVeh->m_iRightWingBone = -1;
01727         memset( pVeh->m_iExhaustTag, -1, sizeof( int ) * MAX_VEHICLE_EXHAUSTS );
01728         memset( pVeh->m_iMuzzleTag, -1, sizeof( int ) * MAX_VEHICLE_MUZZLES );
01729         // FIXME! Use external values read from the vehicle data file!
01730 #ifndef _JK2MP //blargh, fixme
01731         memset( pVeh->m_Muzzles, 0, sizeof( Muzzle ) * MAX_VEHICLE_MUZZLES );
01732 #endif
01733         pVeh->m_iDroidUnitTag = -1;
01734 
01735         //initialize to blaster, just since it's a basic weapon and there's no lightsaber crap...?
01736         parent->client->ps.weapon = WP_BLASTER;
01737         parent->client->ps.weaponstate = WEAPON_READY;
01738         parent->client->ps.stats[STAT_WEAPONS] |= (1<<WP_BLASTER);
01739 
01740         //Initialize to landed (wings closed, gears down) animation
01741         {
01742                 int iFlags = SETANIM_FLAG_NORMAL, iBlend = 300;
01743 #ifdef _JK2MP
01744                 pVeh->m_ulFlags |= VEH_GEARSOPEN;
01745                 BG_SetAnim(pVeh->m_pParentEntity->playerState, 
01746                         bgAllAnims[pVeh->m_pParentEntity->localAnimIndex].anims,
01747                         SETANIM_BOTH, BOTH_VS_IDLE, iFlags, iBlend);
01748 #else
01749                 NPC_SetAnim( pVeh->m_pParentEntity, SETANIM_BOTH, BOTH_VS_IDLE, iFlags, iBlend );
01750 #endif
01751         }
01752 
01753         return true;
01754 }
01755 
01756 // Like a think or move command, this updates various vehicle properties.
01757 #ifdef _JK2MP
01758 void G_VehicleDamageBoxSizing(Vehicle_t *pVeh); //declared below
01759 #endif
01760 static bool Update( Vehicle_t *pVeh, const usercmd_t *pUmcd )
01761 {
01762         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
01763         gentity_t *pilotEnt;
01764         //static float fMod = 1000.0f / 60.0f;
01765         vec3_t vVehAngles;
01766         int i;
01767         int     prevSpeed;
01768         int     nextSpeed;
01769         int     curTime;
01770         int halfMaxSpeed;
01771         playerState_t *parentPS;
01772         qboolean linkHeld = qfalse;
01773 
01774 
01775 #ifdef _JK2MP
01776         parentPS =  pVeh->m_pParentEntity->playerState;
01777 #else
01778         parentPS = &pVeh->m_pParentEntity->client->ps;
01779 #endif
01780 
01781 #ifndef _JK2MP//SP
01782         curTime = level.time;
01783 #elif QAGAME//MP GAME
01784         curTime = level.time;
01785 #elif CGAME//MP CGAME
01786         //FIXME: pass in ucmd?  Not sure if this is reliable...
01787         curTime = pm->cmd.serverTime;
01788 #endif
01789 
01790         //increment the ammo for all rechargeable weapons
01791         for ( i = 0; i < MAX_VEHICLE_WEAPONS; i++ )
01792         {
01793                 if ( pVeh->m_pVehicleInfo->weapon[i].ID > VEH_WEAPON_BASE//have a weapon in this slot
01794                         && pVeh->m_pVehicleInfo->weapon[i].ammoRechargeMS//its ammo is rechargable
01795                         && pVeh->weaponStatus[i].ammo < pVeh->m_pVehicleInfo->weapon[i].ammoMax//its ammo is below max
01796                         && pUmcd->serverTime-pVeh->weaponStatus[i].lastAmmoInc >= pVeh->m_pVehicleInfo->weapon[i].ammoRechargeMS )//enough time has passed
01797                 {//add 1 to the ammo
01798                         pVeh->weaponStatus[i].lastAmmoInc = pUmcd->serverTime;
01799                         pVeh->weaponStatus[i].ammo++;
01800                         //NOTE: in order to send the vehicle's ammo info to the client, we copy the ammo into the first 2 ammo slots on the vehicle NPC's client->ps.ammo array
01801                         if ( parent && parent->client )
01802                         {
01803                                 parent->client->ps.ammo[i] = pVeh->weaponStatus[i].ammo;
01804                         }
01805                 }
01806         }
01807         for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
01808         {
01809                 if ( pVeh->m_pVehicleInfo->turret[i].iWeapon > VEH_WEAPON_BASE//have a weapon in this slot
01810                         && pVeh->m_pVehicleInfo->turret[i].iAmmoRechargeMS//its ammo is rechargable
01811                         && pVeh->turretStatus[i].ammo < pVeh->m_pVehicleInfo->turret[i].iAmmoMax//its ammo is below max
01812                         && pUmcd->serverTime-pVeh->turretStatus[i].lastAmmoInc >= pVeh->m_pVehicleInfo->turret[i].iAmmoRechargeMS )//enough time has passed
01813                 {//add 1 to the ammo
01814                         pVeh->turretStatus[i].lastAmmoInc = pUmcd->serverTime;
01815                         pVeh->turretStatus[i].ammo++;
01816                         //NOTE: in order to send the vehicle's ammo info to the client, we copy the ammo into the first 2 ammo slots on the vehicle NPC's client->ps.ammo array
01817                         if ( parent && parent->client )
01818                         {
01819                                 parent->client->ps.ammo[MAX_VEHICLE_WEAPONS+i] = pVeh->turretStatus[i].ammo;
01820                         }
01821                 }
01822         }
01823 
01824         //increment shields for rechargable shields
01825         if ( pVeh->m_pVehicleInfo->shieldRechargeMS
01826                 && parentPS->stats[STAT_ARMOR] > 0 //still have some shields left
01827                 && parentPS->stats[STAT_ARMOR] < pVeh->m_pVehicleInfo->shields//its below max
01828                 && pUmcd->serverTime-pVeh->lastShieldInc >= pVeh->m_pVehicleInfo->shieldRechargeMS )//enough time has passed
01829         {
01830                 parentPS->stats[STAT_ARMOR]++;
01831                 if ( parentPS->stats[STAT_ARMOR] > pVeh->m_pVehicleInfo->shields )
01832                 {
01833                         parentPS->stats[STAT_ARMOR] = pVeh->m_pVehicleInfo->shields;
01834                 }
01835                 pVeh->m_iShields = parentPS->stats[STAT_ARMOR];
01836 #ifdef _JK2MP
01837                 G_VehUpdateShields( parent );
01838 #endif
01839         }
01840 
01841 #ifdef _JK2MP //sometimes this gets out of whack, probably init'ing
01842         if (parent && parent->r.ownerNum != parent->s.owner)
01843         {
01844                 parent->s.owner = parent->r.ownerNum;
01845         }
01846 
01847         //keep the PS value in sync. set it up here in case we return below at some point.
01848         if (pVeh->m_iBoarding)
01849         {
01850                 parent->client->ps.vehBoarding = qtrue;
01851         }
01852         else
01853         {
01854                 parent->client->ps.vehBoarding = qfalse;
01855         }
01856 #endif
01857 
01858         // See whether this vehicle should be dieing or dead.
01859         if ( pVeh->m_iDieTime != 0 
01860 #ifndef _JK2MP //sometimes this gets out of whack, probably init'ing
01861                 || (parent->health <= 0)
01862 #endif
01863                 )
01864         {//NOTE!!!: This HAS to be consistent with cgame!!!
01865                 // Keep track of the old orientation.
01866                 VectorCopy( pVeh->m_vOrientation, pVeh->m_vPrevOrientation );
01867 
01868                 // Process the orient commands.
01869                 pVeh->m_pVehicleInfo->ProcessOrientCommands( pVeh );
01870                 // Need to copy orientation to our entity's viewangles so that it renders at the proper angle and currentAngles is correct.
01871                 SetClientViewAngle( parent, pVeh->m_vOrientation ); 
01872                 if ( pVeh->m_pPilot )
01873                 {
01874                         SetClientViewAngle( (gentity_t *)pVeh->m_pPilot, pVeh->m_vOrientation );
01875                 }
01876                 /*
01877                 for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
01878                 {
01879                         if ( pVeh->m_ppPassengers[i] )
01880                         {
01881                                 SetClientViewAngle( (gentity_t *)pVeh->m_ppPassengers[i], pVeh->m_vOrientation );
01882                         }
01883                 }
01884                 */
01885 
01886                 // Process the move commands.
01887                 pVeh->m_pVehicleInfo->ProcessMoveCommands( pVeh );
01888 
01889                 // Setup the move direction.
01890                 if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
01891                 {
01892                         AngleVectors( pVeh->m_vOrientation, parent->client->ps.moveDir, NULL, NULL ); 
01893                 }
01894                 else
01895                 {
01896                         VectorSet(vVehAngles, 0, pVeh->m_vOrientation[YAW], 0);
01897                         AngleVectors( vVehAngles, parent->client->ps.moveDir, NULL, NULL ); 
01898                 }
01899                 pVeh->m_pVehicleInfo->DeathUpdate( pVeh );
01900                 return false;
01901         }
01902         // Vehicle dead!
01903 
01904 #ifdef _JK2MP
01905         else if ( parent->health <= 0 )
01906         {
01907                 // Instant kill.
01908                 if (pVeh->m_pVehicleInfo->type == VH_FIGHTER &&
01909                         pVeh->m_iLastImpactDmg > 500)
01910                 { //explode instantly in inferno-y death
01911                         pVeh->m_pVehicleInfo->StartDeathDelay( pVeh, -1/* -1 causes instant death */);
01912                 }
01913                 else
01914                 {
01915                         pVeh->m_pVehicleInfo->StartDeathDelay( pVeh, 0 );
01916                 }
01917                 pVeh->m_pVehicleInfo->DeathUpdate( pVeh );
01918                 return false;
01919         }
01920 #endif
01921         
01922 #ifdef _JK2MP //special check in case someone disconnects/dies while boarding
01923 #ifdef QAGAME
01924         if (parent->spawnflags & 1)
01925         {
01926                 if (pVeh->m_pPilot || !pVeh->m_bHasHadPilot)
01927                 {
01928                         if (pVeh->m_pPilot && !pVeh->m_bHasHadPilot)
01929                         {
01930                                 pVeh->m_bHasHadPilot = qtrue;
01931                                 pVeh->m_iPilotLastIndex = pVeh->m_pPilot->s.number;
01932                         }
01933                         pVeh->m_iPilotTime = level.time + parent->damage;
01934                 }
01935                 else if (pVeh->m_iPilotTime)
01936                 { //die
01937                         gentity_t *oldPilot = &g_entities[pVeh->m_iPilotLastIndex];
01938 
01939                         if (!oldPilot->inuse || !oldPilot->client ||
01940                                 oldPilot->client->pers.connected != CON_CONNECTED)
01941                         { //no longer in the game?
01942                                 G_Damage(parent, parent, parent, NULL, parent->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
01943                         }
01944                         else
01945                         {
01946                                 vec3_t v;
01947                                 VectorSubtract(parent->client->ps.origin, oldPilot->client->ps.origin, v);
01948 
01949                                 if (VectorLength(v) < parent->speed)
01950                                 { //they are still within the minimum distance to their vehicle
01951                                         pVeh->m_iPilotTime = level.time + parent->damage;
01952                                 }
01953                                 else if (pVeh->m_iPilotTime < level.time)
01954                                 { //dying time
01955                                         G_Damage(parent, parent, parent, NULL, parent->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
01956                                 }
01957                         }
01958                 }
01959         }
01960 #endif
01961 #else
01962         if (parent->spawnflags & 1)
01963         {//NOTE: in SP, this actually just checks LOS to the Player
01964                 if (pVeh->m_iPilotTime < level.time)
01965                 {//do another check?
01966                         if ( !player || G_ClearLineOfSight(pVeh->m_pParentEntity->currentOrigin, player->currentOrigin, pVeh->m_pParentEntity->s.number, MASK_OPAQUE ) )
01967                         {
01968                                 pVeh->m_iPilotTime = level.time + pVeh->m_pParentEntity->endFrame;
01969                         }
01970                 }
01971                 if (pVeh->m_iPilotTime && pVeh->m_iPilotTime < level.time)
01972                 { //die
01973                         //FIXME: does this give proper credit to the enemy who shot you down?
01974             G_Damage(parent, parent, parent, NULL, parent->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
01975                 }
01976         }
01977 #endif
01978 
01979 #ifndef _JK2MP
01980 //      if (level.time<pVeh->m_iTurboTime || pVeh->m_pVehicleInfo->type==VH_ANIMAL)
01981         // always knock guys around now...
01982         {
01983                 vec3_t  dir;                                            
01984                 vec3_t  projectedPosition;
01985                 VectorCopy(parent->client->ps.velocity, dir);
01986                 VectorMA(parent->currentOrigin, 0.1f, dir, projectedPosition);
01987 
01988                 float   force = VectorNormalize(dir);
01989                 force /= 10.0f;
01990                 if (force>30.0f)
01991                 {
01992                         trace_t tr;
01993                         G_VehicleTrace(&tr, parent->currentOrigin, parent->mins, parent->maxs, projectedPosition, parent->s.number, CONTENTS_BODY);
01994                         if (tr.fraction<1.0f && 
01995                                 !tr.allsolid && 
01996                                 !tr.startsolid && 
01997                                 tr.entityNum!=ENTITYNUM_NONE && 
01998                                 tr.entityNum!=ENTITYNUM_WORLD &&
01999                                 (level.time<pVeh->m_iTurboTime || Q_irand(0,3)==0))
02000                         {
02001                                 gentity_t*      other = &g_entities[tr.entityNum];
02002                                 if (other && other->client && !other->s.m_iVehicleNum)
02003                                 {
02004                                         G_Throw( other, dir, force/10.0f );
02005                                         G_Knockdown( other, parent, dir, force, qtrue );
02006                                         G_Damage( other, parent, parent, parent->client->ps.velocity, parent->currentOrigin, force, DAMAGE_NO_ARMOR|DAMAGE_EXTRA_KNOCKBACK, MOD_IMPACT);
02007                                 }
02008                         }
02009                 }
02010         }
02011 #endif
02012 
02013 #ifdef _JK2MP //special check in case someone disconnects/dies while boarding
02014         if (pVeh->m_iBoarding != 0)
02015         {
02016                 pilotEnt = (gentity_t *)pVeh->m_pPilot;
02017                 if (pilotEnt)
02018                 {
02019                         if (!pilotEnt->inuse || !pilotEnt->client || pilotEnt->health <= 0 ||
02020                                 pilotEnt->client->pers.connected != CON_CONNECTED)
02021                         {
02022                                 pVeh->m_pVehicleInfo->Eject( pVeh, pVeh->m_pPilot, qtrue );
02023                                 return false;
02024                         }
02025                 }
02026         }
02027 #endif
02028 
02029         // If we're not done mounting, can't do anything.
02030         if ( pVeh->m_iBoarding != 0 )
02031         {
02032                 if (!pVeh->m_bWasBoarding)
02033                 {
02034                         VectorCopy(parentPS->velocity, pVeh->m_vBoardingVelocity);
02035                         pVeh->m_bWasBoarding = true;
02036                 }
02037 
02038                 // See if we're done boarding.
02039                 if ( pVeh->m_iBoarding > -1 && pVeh->m_iBoarding <= level.time )
02040                 {
02041                         pVeh->m_bWasBoarding = false;
02042                         pVeh->m_iBoarding = 0;
02043                 }
02044                 else
02045                 {
02046 #ifdef _JK2MP
02047                         goto maintainSelfDuringBoarding;
02048 #else
02049                         return false;
02050 #endif
02051                 }
02052         }
02053 
02054         parent = (gentity_t *)pVeh->m_pParentEntity;
02055 
02056         // Validate vehicle.
02057         if ( !parent || !parent->client || parent->health <= 0 )
02058                 return false;
02059 
02060         // See if any of the riders are dead and if so kick em off.
02061         if ( pVeh->m_pPilot )
02062         {
02063                 pilotEnt = (gentity_t *)pVeh->m_pPilot;
02064                 
02065 #ifdef _JK2MP
02066                 if (!pilotEnt->inuse || !pilotEnt->client || pilotEnt->health <= 0 ||
02067                         pilotEnt->client->pers.connected != CON_CONNECTED)
02068 #else
02069                 if (pilotEnt->health <= 0)
02070 #endif
02071                 {
02072                         pVeh->m_pVehicleInfo->Eject( pVeh, pVeh->m_pPilot, qtrue );
02073                 }
02074         }
02075         // If we're not empty...
02076         if ( pVeh->m_iNumPassengers > 0 )
02077         {
02078                 gentity_t *psngr;
02079                 // See if any of these suckers are dead.
02080                 for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
02081                 {
02082                         psngr = (gentity_t *)pVeh->m_ppPassengers[i];
02083 
02084 #ifdef _JK2MP
02085                         if ( psngr &&
02086                                 (!psngr->inuse || !psngr->client || psngr->health <= 0 || psngr->client->pers.connected != CON_CONNECTED) )
02087 #else
02088                         if ( psngr && psngr->health <= 0 )
02089 #endif
02090                         {
02091                                 pVeh->m_pVehicleInfo->Eject( pVeh, pVeh->m_ppPassengers[i], qtrue );
02092                                 pVeh->m_iNumPassengers--;
02093                         }
02094                 }
02095         }
02096 
02097 #ifdef _JK2MP
02098         // Copy over the commands for local storage.
02099         memcpy( &parent->client->pers.cmd, &pVeh->m_ucmd, sizeof( usercmd_t ) );
02100         pVeh->m_ucmd.buttons &= ~(BUTTON_TALK);//|BUTTON_GESTURE); //don't want some of these buttons
02101 #else
02102         // Copy over the commands for local storage.
02103         memcpy( &pVeh->m_ucmd, pUmcd, sizeof( usercmd_t ) );
02104         memcpy( &parent->client->pers.lastCommand, pUmcd, sizeof( usercmd_t ) );
02105 #endif
02106 
02107         /*
02108         // Update time modifier.
02109         pVeh->m_fTimeModifier = pVeh->m_ucmd.serverTime - parent->client->ps.commandTime;
02110         //sanity check
02111         if ( pVeh->m_fTimeModifier < 1 ) 
02112         {
02113                 pVeh->m_fTimeModifier = 1;
02114         } 
02115         else if ( pVeh->m_fTimeModifier > 200 ) 
02116         {
02117                 pVeh->m_fTimeModifier = 200;
02118         }
02119         //normalize to 1.0f at 20fps
02120         pVeh->m_fTimeModifier = pVeh->m_fTimeModifier / fMod;
02121         */
02122         
02123         //check for weapon linking/unlinking command
02124         for ( i = 0; i < MAX_VEHICLE_WEAPONS; i++ )
02125         {//HMM... can't get a seperate command for each weapon, so do them all...?
02126                 if ( pVeh->m_pVehicleInfo->weapon[i].linkable == 2 )
02127                 {//always linked
02128                         //FIXME: just set this once, on Initialize...?
02129                         if ( !pVeh->weaponStatus[i].linked )
02130                         {
02131                                 pVeh->weaponStatus[i].linked = qtrue;
02132                         }
02133                 }
02134 #ifdef _JK2MP
02135                 else if ( (pVeh->m_ucmd.buttons&BUTTON_USE_HOLDABLE) ) 
02136 #else
02137                 //FIXME: implement... just a console command bound to a key?
02138                 else if ( 0 )
02139 #endif  
02140                 {//pilot pressed the "weapon link" toggle button
02141                         playerState_t *pilotPS;
02142 #ifdef _JK2MP
02143                         bgEntity_t *rider = NULL;
02144                         if (parent->s.owner != ENTITYNUM_NONE)
02145                         {
02146                                 rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
02147                         }
02148                         pilotPS = rider->playerState;
02149 #else
02150                         gentity_t *rider = parent->owner;
02151                         pilotPS = &rider->client->ps;
02152 #endif
02153                         if ( !pVeh->linkWeaponToggleHeld )//so we don't hold it down and toggle it back and forth
02154                         {//okay to toggle
02155                                 if ( pVeh->m_pVehicleInfo->weapon[i].linkable == 1 )
02156                                 {//link-toggleable
02157                                         pVeh->weaponStatus[i].linked = !pVeh->weaponStatus[i].linked;
02158                                 }
02159                         }
02160                         linkHeld = qtrue;
02161                 }
02162         }
02163         if ( linkHeld )
02164         {
02165                 //so we don't hold it down and toggle it back and forth
02166                 pVeh->linkWeaponToggleHeld = qtrue;
02167         }
02168         else
02169         {
02170                 //so we don't hold it down and toggle it back and forth
02171                 pVeh->linkWeaponToggleHeld = qfalse;
02172         }
02173 #ifdef _JK2MP
02174         //now pass it over the network so cgame knows about it
02175         //NOTE: SP can just cheat and check directly
02176         parentPS->vehWeaponsLinked = qfalse;
02177         for ( i = 0; i < MAX_VEHICLE_WEAPONS; i++ )
02178         {//HMM... can't get a seperate command for each weapon, so do them all...?
02179                 if ( pVeh->weaponStatus[i].linked )
02180                 {
02181                         parentPS->vehWeaponsLinked = qtrue;
02182                 }
02183         }
02184 #endif
02185 
02186 #ifdef QAGAME
02187         for ( i = 0; i < MAX_VEHICLE_TURRETS; i++ )
02188         {//HMM... can't get a seperate command for each weapon, so do them all...?
02189                 VEH_TurretThink( pVeh, parent, i );
02190         }
02191 #endif
02192 
02193 #ifdef _JK2MP
02194 maintainSelfDuringBoarding:
02195 
02196         if (pVeh->m_pPilot && pVeh->m_pPilot->playerState && pVeh->m_iBoarding != 0)
02197         {
02198         VectorCopy(pVeh->m_vOrientation, pVeh->m_pPilot->playerState->viewangles);
02199                 pVeh->m_ucmd.buttons = 0;
02200                 pVeh->m_ucmd.forwardmove = 0;
02201                 pVeh->m_ucmd.rightmove = 0;
02202                 pVeh->m_ucmd.upmove = 0;
02203         }
02204 #endif
02205 
02206         // Keep track of the old orientation.
02207         VectorCopy( pVeh->m_vOrientation, pVeh->m_vPrevOrientation );
02208 
02209         // Process the orient commands.
02210         pVeh->m_pVehicleInfo->ProcessOrientCommands( pVeh );
02211         // Need to copy orientation to our entity's viewangles so that it renders at the proper angle and currentAngles is correct.
02212         SetClientViewAngle( parent, pVeh->m_vOrientation ); 
02213         if ( pVeh->m_pPilot )
02214         {
02215 #ifdef _JK2MP
02216                 if ( !BG_UnrestrainedPitchRoll( pVeh->m_pPilot->playerState, pVeh ) )
02217                 {
02218                         vec3_t newVAngle;
02219                         newVAngle[PITCH] = pVeh->m_pPilot->playerState->viewangles[PITCH];
02220                         newVAngle[YAW] = pVeh->m_pPilot->playerState->viewangles[YAW];
02221                         newVAngle[ROLL] = pVeh->m_vOrientation[ROLL];
02222                         SetClientViewAngle( (gentity_t *)pVeh->m_pPilot, newVAngle );
02223                 }
02224 #else
02225                 if ( !BG_UnrestrainedPitchRoll( &pVeh->m_pPilot->client->ps, pVeh ) )
02226                 {
02227                         SetClientViewAngle( (gentity_t *)pVeh->m_pPilot, pVeh->m_vOrientation );
02228                 }
02229 #endif
02230         }
02231         /*
02232         for ( i = 0; i < pVeh->m_pVehicleInfo->maxPassengers; i++ )
02233         {
02234                 if ( pVeh->m_ppPassengers[i] )
02235                 {
02236                         SetClientViewAngle( (gentity_t *)pVeh->m_ppPassengers[i], pVeh->m_vOrientation );
02237                 }
02238         }
02239         */
02240 
02241         // Process the move commands.
02242         prevSpeed = parentPS->speed;
02243         pVeh->m_pVehicleInfo->ProcessMoveCommands( pVeh );
02244         nextSpeed = parentPS->speed;
02245         halfMaxSpeed = pVeh->m_pVehicleInfo->speedMax*0.5f;
02246         
02247 
02248 // Shifting Sounds
02249 //=====================================================================
02250         if (pVeh->m_iTurboTime<curTime && 
02251                 pVeh->m_iSoundDebounceTimer<curTime && 
02252                 ((nextSpeed>prevSpeed && nextSpeed>halfMaxSpeed &&      prevSpeed<halfMaxSpeed) || (nextSpeed>halfMaxSpeed && !Q_irand(0,1000)))
02253                 )
02254         {
02255                 int     shiftSound = Q_irand(1,4);
02256                 switch (shiftSound)
02257                 {
02258                 case 1: shiftSound=pVeh->m_pVehicleInfo->soundShift1; break;
02259                 case 2: shiftSound=pVeh->m_pVehicleInfo->soundShift2; break;
02260                 case 3: shiftSound=pVeh->m_pVehicleInfo->soundShift3; break;
02261                 case 4: shiftSound=pVeh->m_pVehicleInfo->soundShift4; break;
02262                 }
02263                 if (shiftSound)
02264                 {
02265                         pVeh->m_iSoundDebounceTimer = curTime + Q_irand(1000, 4000);
02266 #ifdef _JK2MP
02267                         // TODO: MP Shift Sound Playback
02268 #else
02269                         // NOTE: Use this type so it's spatialized and updates play origin as bike moves - MCG
02270                         G_SoundIndexOnEnt( pVeh->m_pParentEntity, CHAN_AUTO, shiftSound);
02271 #endif
02272                 }
02273         }
02274 //=====================================================================
02275 
02276 
02277         // Setup the move direction.
02278         if ( pVeh->m_pVehicleInfo->type == VH_FIGHTER )
02279         {
02280                 AngleVectors( pVeh->m_vOrientation, parent->client->ps.moveDir, NULL, NULL ); 
02281         }
02282         else
02283         {
02284                 VectorSet(vVehAngles, 0, pVeh->m_vOrientation[YAW], 0);
02285                 AngleVectors( vVehAngles, parent->client->ps.moveDir, NULL, NULL ); 
02286         }
02287 
02288 #ifdef _JK2MP
02289         if (pVeh->m_pVehicleInfo->surfDestruction)
02290         {
02291                 if (pVeh->m_iRemovedSurfaces)
02292                 {
02293                         gentity_t *killer = parent;
02294                         float      dmg;
02295                         G_VehicleDamageBoxSizing(pVeh);
02296 
02297                         //damage him constantly if any chunks are currently taken off
02298                         if (parent->client->ps.otherKiller < ENTITYNUM_WORLD &&
02299                                 parent->client->ps.otherKillerTime > level.time)
02300                         {
02301                                 gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller];
02302 
02303                                 if (potentialKiller->inuse && potentialKiller->client)
02304                                 { //he's valid I guess
02305                                         killer = potentialKiller;
02306                                 }
02307                         }
02308                         //FIXME: aside from bypassing shields, maybe set m_iShields to 0, too... ?
02309                         
02310                         // 3 seconds max on death.
02311                         dmg = (float)parent->client->ps.stats[STAT_MAX_HEALTH] * pVeh->m_fTimeModifier / 180.0f;                        
02312                         G_Damage(parent, killer, killer, NULL, parent->client->ps.origin, dmg, DAMAGE_NO_SELF_PROTECTION|DAMAGE_NO_HIT_LOC|DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR, MOD_SUICIDE);
02313                 }
02314                 
02315                 //make sure playerstate value stays in sync
02316                 parent->client->ps.vehSurfaces = pVeh->m_iRemovedSurfaces;
02317         }
02318 #endif
02319 
02320 #ifdef _JK2MP
02321         //keep the PS value in sync
02322         if (pVeh->m_iBoarding)
02323         {
02324                 parent->client->ps.vehBoarding = qtrue;
02325         }
02326         else
02327         {
02328                 parent->client->ps.vehBoarding = qfalse;
02329         }
02330 #endif
02331 
02332 #ifndef _JK2MP
02333         // Make sure the vehicle takes on the enemy of it's rider (for homing missles for instance).
02334         if ( pVeh->m_pPilot )
02335         {
02336                 parent->enemy = pVeh->m_pPilot->enemy;
02337         }
02338 #endif
02339         
02340 
02341         return true;
02342 }
02343 
02344 
02345 // Update the properties of a Rider (that may reflect what happens to the vehicle).
02346 static bool UpdateRider( Vehicle_t *pVeh, bgEntity_t *pRider, usercmd_t *pUmcd )
02347 {
02348         gentity_t *parent;
02349         gentity_t *rider;
02350 
02351         if ( pVeh->m_iBoarding != 0 && pVeh->m_iDieTime==0)
02352                 return true;
02353 
02354         parent = (gentity_t *)pVeh->m_pParentEntity;
02355         rider = (gentity_t *)pRider;
02356 #ifdef _JK2MP
02357         //MG FIXME !! Single player needs update!
02358         if ( rider && rider->client 
02359                 && parent && parent->client )
02360         {//so they know who we're locking onto with our rockets, if anyone
02361                 rider->client->ps.rocketLockIndex = parent->client->ps.rocketLockIndex;
02362                 rider->client->ps.rocketLockTime = parent->client->ps.rocketLockTime;
02363                 rider->client->ps.rocketTargetTime = parent->client->ps.rocketTargetTime;
02364         }
02365 #endif
02366         // Regular exit.
02367         if ( pUmcd->buttons & BUTTON_USE && pVeh->m_pVehicleInfo->type!=VH_SPEEDER)
02368         {
02369                 if ( pVeh->m_pVehicleInfo->type == VH_WALKER )
02370                 {//just get the fuck out
02371                         pVeh->m_EjectDir = VEH_EJECT_REAR;
02372                         if ( pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qfalse ) )
02373                                 return false;
02374                 }
02375                 else if ( !(pVeh->m_ulFlags & VEH_FLYING))
02376                 {
02377                         // If going too fast, roll off.
02378                         if ((parent->client->ps.speed<=600) && pUmcd->rightmove!=0)
02379                         {
02380                                 if ( pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qfalse ) )
02381                                 {
02382                                         animNumber_t Anim;
02383                                         int iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS, iBlend = 300;
02384                                         if ( pUmcd->rightmove > 0 )
02385                                         {
02386                                                 Anim = BOTH_ROLL_R;
02387                                                 pVeh->m_EjectDir = VEH_EJECT_RIGHT;
02388                                         }
02389                                         else
02390                                         {
02391                                                 Anim = BOTH_ROLL_L;
02392                                                 pVeh->m_EjectDir = VEH_EJECT_LEFT;
02393                                         }
02394                                         VectorScale( parent->client->ps.velocity, 0.25f, rider->client->ps.velocity );
02395 #if 1
02396                                         Vehicle_SetAnim( rider, SETANIM_BOTH, Anim, iFlags, iBlend );
02397 #else
02398 
02399 #endif
02400                                         //PM_SetAnim(pm,SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS);
02401                                         rider->client->ps.weaponTime = rider->client->ps.torsoAnimTimer - 200;//just to make sure it's cleared when roll is done
02402                                         G_AddEvent( rider, EV_ROLL, 0 );
02403                                         return false;
02404                                 }
02405                         }
02406                         else
02407                         {
02408                                 // FIXME: Check trace to see if we should start playing the animation.
02409                                 animNumber_t Anim;
02410                                 int iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, iBlend = 500;
02411                                 if ( pUmcd->rightmove > 0 )
02412                                 {
02413                                         Anim = BOTH_VS_DISMOUNT_R;
02414                                         pVeh->m_EjectDir = VEH_EJECT_RIGHT;
02415                                 }
02416                                 else
02417                                 {
02418                                         Anim = BOTH_VS_DISMOUNT_L;
02419                                         pVeh->m_EjectDir = VEH_EJECT_LEFT;
02420                                 }
02421                                 
02422                                 if ( pVeh->m_iBoarding <= 1 )
02423                                 {
02424                                         int iAnimLen;
02425                                         // NOTE: I know I shouldn't reuse pVeh->m_iBoarding so many times for so many different
02426                                         // purposes, but it's not used anywhere else right here so why waste memory???
02427 #ifdef _JK2MP
02428                                         iAnimLen = BG_AnimLength( rider->localAnimIndex, Anim );
02429 #else
02430                                         iAnimLen = PM_AnimLength( pRider->client->clientInfo.animFileIndex, Anim );
02431 #endif
02432                                         pVeh->m_iBoarding = level.time + iAnimLen;
02433                                         // Weird huh? Well I wanted to reuse flags and this should never be set in an
02434                                         // entity, so what the heck.
02435 #ifdef _JK2MP
02436                                         rider->flags |= FL_VEH_BOARDING;
02437 #else
02438                                         rider->client->ps.eFlags |= EF_VEH_BOARDING;
02439 #endif
02440 
02441                                         // Make sure they can't fire when leaving.
02442                                         rider->client->ps.weaponTime = iAnimLen;
02443                                 }
02444 
02445                                 VectorScale( parent->client->ps.velocity, 0.25f, rider->client->ps.velocity );
02446 
02447                                 Vehicle_SetAnim( rider, SETANIM_BOTH, Anim, iFlags, iBlend );
02448                         }
02449                 }
02450                 // Flying, so just fall off.
02451                 else
02452                 {
02453                         pVeh->m_EjectDir = VEH_EJECT_LEFT;
02454                         if ( pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qfalse ) )
02455                                 return false;
02456                 }
02457         }
02458 
02459         // Getting off animation complete (if we had one going)?
02460 #ifdef _JK2MP
02461         if ( pVeh->m_iBoarding < level.time && (rider->flags & FL_VEH_BOARDING) )
02462         {
02463                 rider->flags &= ~FL_VEH_BOARDING;
02464 #else
02465         if ( pVeh->m_iBoarding < level.time && (rider->client->ps.eFlags & EF_VEH_BOARDING) )
02466         {
02467                 rider->client->ps.eFlags &= ~EF_VEH_BOARDING;
02468 #endif
02469                 // Eject this guy now.
02470                 if ( pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qfalse ) )
02471                 {
02472                         return false;
02473                 }
02474         }
02475 
02476         if ( pVeh->m_pVehicleInfo->type != VH_FIGHTER 
02477                 && pVeh->m_pVehicleInfo->type != VH_WALKER  )
02478         {
02479                 // Jump off.
02480                 if ( pUmcd->upmove > 0 )
02481                 {
02482 
02483 // NOT IN MULTI PLAYER!
02484 //===================================================================
02485 #ifndef _JK2MP
02486                         float riderRightDot = G_CanJumpToEnemyVeh(pVeh, pUmcd);
02487                         if (riderRightDot!=0.0f)
02488                         {
02489                                 // Eject Player From Current Vehicle
02490                                 //-----------------------------------
02491                                 pVeh->m_EjectDir = VEH_EJECT_TOP;
02492                                 pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qtrue );
02493 
02494                                 // Send Current Vehicle Spinning Out Of Control
02495                                 //----------------------------------------------
02496                                 pVeh->m_pVehicleInfo->StartDeathDelay(pVeh, 10000);
02497                                 pVeh->m_ulFlags |= (VEH_OUTOFCONTROL);
02498                                 VectorScale(pVeh->m_pParentEntity->client->ps.velocity, 1.0f, pVeh->m_pParentEntity->pos3);
02499 
02500                                 // Todo: Throw Old Vehicle Away From The New Vehicle Some
02501                                 //-------------------------------------------------------
02502                                 vec3_t  toEnemy;
02503                                 VectorSubtract(pVeh->m_pParentEntity->currentOrigin, rider->enemy->currentOrigin, toEnemy);
02504                                 VectorNormalize(toEnemy);
02505                                 G_Throw(pVeh->m_pParentEntity, toEnemy, 50);
02506 
02507                                 // Start Boarding On Enemy's Vehicle
02508                                 //-----------------------------------
02509                                 Vehicle_t*      enemyVeh = G_IsRidingVehicle(rider->enemy);
02510                                 enemyVeh->m_iBoarding = (riderRightDot>0)?(VEH_MOUNT_THROW_RIGHT):(VEH_MOUNT_THROW_LEFT);
02511                                 enemyVeh->m_pVehicleInfo->Board(enemyVeh, rider);
02512                         }
02513 
02514                         // Don't Jump Off If Holding Strafe Key and Moving Fast
02515                         else if (pUmcd->rightmove && (parent->client->ps.speed>=10))
02516                         {
02517                                 return true;
02518                         }
02519 #endif
02520 //===================================================================
02521 
02522                         if ( pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qfalse ) )
02523                         {
02524                                 // Allow them to force jump off.
02525                                 VectorScale( parent->client->ps.velocity, 0.5f, rider->client->ps.velocity );
02526                                 rider->client->ps.velocity[2] += JUMP_VELOCITY;
02527 #ifdef _JK2MP
02528                                 rider->client->ps.fd.forceJumpZStart = rider->client->ps.origin[2];
02529 
02530                                 if (!trap_ICARUS_TaskIDPending(rider, TID_CHAN_VOICE))
02531 #else
02532                                 rider->client->ps.pm_flags |= ( PMF_JUMPING | PMF_JUMP_HELD );
02533                                 rider->client->ps.forceJumpZStart = rider->client->ps.origin[2];
02534 
02535                                 if ( !Q3_TaskIDPending( rider, TID_CHAN_VOICE ) )
02536 #endif
02537                                 {
02538                                         G_AddEvent( rider, EV_JUMP, 0 );
02539                                 }
02540 #if 1
02541                                 Vehicle_SetAnim( rider, SETANIM_BOTH, BOTH_JUMP1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 300 );
02542 #else
02543 
02544 #endif
02545                                 return false;
02546                         }
02547                 }
02548 
02549                 // Roll off.
02550                 if ( pUmcd->upmove < 0 )
02551                 {
02552                         animNumber_t Anim = BOTH_ROLL_B;
02553                         pVeh->m_EjectDir = VEH_EJECT_REAR;
02554                         if ( pUmcd->rightmove > 0 )
02555                         {
02556                                 Anim = BOTH_ROLL_R;
02557                                 pVeh->m_EjectDir = VEH_EJECT_RIGHT;
02558                         }
02559                         else if ( pUmcd->rightmove < 0 )
02560                         {
02561                                 Anim = BOTH_ROLL_L;
02562                                 pVeh->m_EjectDir = VEH_EJECT_LEFT;
02563                         }
02564                         else if ( pUmcd->forwardmove < 0 )
02565                         {
02566                                 Anim = BOTH_ROLL_B;
02567                                 pVeh->m_EjectDir = VEH_EJECT_REAR;
02568                         }
02569                         else if ( pUmcd->forwardmove > 0 )
02570                         {
02571                                 Anim = BOTH_ROLL_F;
02572                                 pVeh->m_EjectDir = VEH_EJECT_FRONT;
02573                         }
02574 
02575                         if ( pVeh->m_pVehicleInfo->Eject( pVeh, pRider, qfalse ) )
02576                         {
02577                                 if ( !(pVeh->m_ulFlags & VEH_FLYING) )
02578                                 {
02579                                         VectorScale( parent->client->ps.velocity, 0.25f, rider->client->ps.velocity );
02580 #if 1
02581                                         Vehicle_SetAnim( rider, SETANIM_BOTH, Anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS, 300 );
02582 #else
02583 
02584 #endif
02585                                         //PM_SetAnim(pm,SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS);
02586                                         rider->client->ps.weaponTime = rider->client->ps.torsoAnimTimer - 200;//just to make sure it's cleared when roll is done
02587                                         G_AddEvent( rider, EV_ROLL, 0 );
02588                                 }
02589                                 return false;
02590                         }
02591 
02592                 }
02593         }
02594 
02595         return true;
02596 }
02597 
02598 #ifdef _JK2MP //we want access to this one clientside, but it's the only
02599 //generic vehicle function we care about over there
02600 #include "../namespace_begin.h"
02601 extern void AttachRidersGeneric( Vehicle_t *pVeh );
02602 #include "../namespace_end.h"
02603 #endif
02604 
02605 // Attachs all the riders of this vehicle to their appropriate tag (*driver, *pass1, *pass2, whatever...).
02606 static void AttachRiders( Vehicle_t *pVeh )
02607 {
02608 #ifdef _JK2MP
02609         int i = 0;
02610 
02611         AttachRidersGeneric(pVeh);
02612 
02613         if (pVeh->m_pPilot)
02614         {
02615                 gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
02616                 gentity_t *pilot = (gentity_t *)pVeh->m_pPilot;
02617                 pilot->waypoint = parent->waypoint; // take the veh's waypoint as your own
02618 
02619                 //assuming we updated him relative to the bolt in AttachRidersGeneric
02620                 G_SetOrigin( pilot, pilot->client->ps.origin );
02621                 trap_LinkEntity( pilot );
02622         }
02623 
02624         if (pVeh->m_pOldPilot)
02625         {
02626                 gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
02627                 gentity_t *oldpilot = (gentity_t *)pVeh->m_pOldPilot;
02628                 oldpilot->waypoint = parent->waypoint; // take the veh's waypoint as your own
02629 
02630                 //assuming we updated him relative to the bolt in AttachRidersGeneric
02631                 G_SetOrigin( oldpilot, oldpilot->client->ps.origin );
02632                 trap_LinkEntity( oldpilot );
02633         }
02634 
02635         //attach passengers
02636         while (i < pVeh->m_iNumPassengers)
02637         {
02638                 if (pVeh->m_ppPassengers[i])
02639                 {
02640                         mdxaBone_t boltMatrix;
02641                         vec3_t  yawOnlyAngles;
02642                         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
02643                         gentity_t *pilot = (gentity_t *)pVeh->m_ppPassengers[i];
02644                         int crotchBolt;
02645 
02646                         assert(parent->ghoul2);
02647                         crotchBolt = trap_G2API_AddBolt(parent->ghoul2, 0, "*driver");
02648                         assert(parent->client);
02649                         assert(pilot->client);
02650 
02651                         VectorSet(yawOnlyAngles, 0, parent->client->ps.viewangles[YAW], 0);
02652 
02653                         // Get the driver tag.
02654                         trap_G2API_GetBoltMatrix( parent->ghoul2, 0, crotchBolt, &boltMatrix,
02655                                                                         yawOnlyAngles, parent->client->ps.origin,
02656                                                                         level.time, NULL, parent->modelScale );
02657                         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, pilot->client->ps.origin );
02658 
02659                         G_SetOrigin( pilot, pilot->client->ps.origin );
02660                         trap_LinkEntity( pilot );
02661                 }
02662                 i++;
02663         }
02664 
02665         //attach droid
02666         if (pVeh->m_pDroidUnit
02667                 && pVeh->m_iDroidUnitTag != -1)
02668         {
02669                 mdxaBone_t boltMatrix;
02670                 vec3_t  yawOnlyAngles, fwd;
02671                 gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
02672                 gentity_t *droid = (gentity_t *)pVeh->m_pDroidUnit;
02673 
02674                 assert(parent->ghoul2);
02675                 assert(parent->client);
02676                 //assert(droid->client);
02677 
02678                 if ( droid->client )
02679                 {
02680                         VectorSet(yawOnlyAngles, 0, parent->client->ps.viewangles[YAW], 0);
02681 
02682                         // Get the droid tag.
02683                         trap_G2API_GetBoltMatrix( parent->ghoul2, 0, pVeh->m_iDroidUnitTag, &boltMatrix,
02684                                                                         yawOnlyAngles, parent->currentOrigin,
02685                                                                         level.time, NULL, parent->modelScale );
02686                         BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, droid->client->ps.origin );
02687                         BG_GiveMeVectorFromMatrix( &boltMatrix, NEGATIVE_Y, fwd );
02688                         vectoangles( fwd, droid->client->ps.viewangles );
02689 
02690                         G_SetOrigin( droid, droid->client->ps.origin );
02691                         G_SetAngles( droid, droid->client->ps.viewangles);
02692                         SetClientViewAngle( droid, droid->client->ps.viewangles );
02693                         trap_LinkEntity( droid );
02694 
02695                         if ( droid->NPC )
02696                         {
02697                                 NPC_SetAnim( droid, SETANIM_BOTH, BOTH_STAND2, (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD) );
02698                                 droid->client->ps.legsTimer = 500;
02699                                 droid->client->ps.torsoTimer = 500;
02700                         }
02701                 }
02702         }
02703 #else
02704         // If we have a pilot, attach him to the driver tag.
02705         if ( pVeh->m_pPilot )
02706         {
02707                 gentity_t * const parent = pVeh->m_pParentEntity;
02708                 gentity_t * const pilot = pVeh->m_pPilot;
02709                 mdxaBone_t      boltMatrix;
02710 
02711                 pilot->waypoint = parent->waypoint; // take the veh's waypoint as your own
02712 
02713                 // Get the driver tag.
02714                 gi.G2API_GetBoltMatrix( parent->ghoul2, parent->playerModel, parent->crotchBolt, &boltMatrix,
02715                                                                 pVeh->m_vOrientation, parent->currentOrigin,
02716                                                                 (cg.time?cg.time:level.time), NULL, parent->s.modelScale );
02717                 gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pilot->client->ps.origin );
02718                 G_SetOrigin( pilot, pilot->client->ps.origin );
02719                 gi.linkentity( pilot );
02720         }
02721         // If we have a pilot, attach him to the driver tag.
02722         if ( pVeh->m_pOldPilot )
02723         {
02724                 gentity_t * const parent = pVeh->m_pParentEntity;
02725                 gentity_t * const pilot = pVeh->m_pOldPilot;
02726                 mdxaBone_t      boltMatrix;
02727 
02728                 pilot->waypoint = parent->waypoint; // take the veh's waypoint as your own
02729 
02730                 // Get the driver tag.
02731                 gi.G2API_GetBoltMatrix( parent->ghoul2, parent->playerModel, parent->crotchBolt, &boltMatrix,
02732                                                                 pVeh->m_vOrientation, parent->currentOrigin,
02733                                                                 (cg.time?cg.time:level.time), NULL, parent->s.modelScale );
02734                 gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pilot->client->ps.origin );
02735                 G_SetOrigin( pilot, pilot->client->ps.origin );
02736                 gi.linkentity( pilot );
02737         }
02738 #endif
02739 }
02740 
02741 // Make someone invisible and un-collidable.
02742 static void Ghost( Vehicle_t *pVeh, bgEntity_t *pEnt )
02743 {
02744         gentity_t *ent;
02745 
02746         if ( !pEnt )
02747                 return;
02748 
02749         ent = (gentity_t *)pEnt;
02750         
02751         // This was introduced to prevent one extra entity from being sent to the clients
02752         ent->r.svFlags |= SVF_NOCLIENT;
02753 
02754         ent->s.eFlags |= EF_NODRAW;
02755         if ( ent->client )
02756         {
02757                 ent->client->ps.eFlags |= EF_NODRAW;
02758         }
02759 #ifdef _JK2MP
02760         ent->r.contents = 0;
02761 #else
02762         ent->contents = 0;
02763 #endif
02764 }
02765 
02766 // Make someone visible and collidable.
02767 static void UnGhost( Vehicle_t *pVeh, bgEntity_t *pEnt )
02768 {
02769         gentity_t *ent;
02770 
02771         if ( !pEnt )
02772                 return;
02773 
02774         ent = (gentity_t *)pEnt;
02775 
02776         // make sure the client is sent again
02777         ent->r.svFlags &= ~SVF_NOCLIENT;
02778 
02779         ent->s.eFlags &= ~EF_NODRAW;
02780         if ( ent->client )
02781         {
02782                 ent->client->ps.eFlags &= ~EF_NODRAW;
02783         }
02784 #ifdef _JK2MP
02785         ent->r.contents = CONTENTS_BODY;
02786 #else
02787         ent->contents = CONTENTS_BODY;
02788 #endif
02789 }
02790 
02791 #ifdef _JK2MP
02792 //try to resize the bounding box around a torn apart ship
02793 void G_VehicleDamageBoxSizing(Vehicle_t *pVeh)
02794 {
02795         vec3_t fwd, right, up;
02796         vec3_t nose; //maxs
02797         vec3_t back; //mins
02798         trace_t trace;
02799         const float fDist = 256.0f; //estimated distance to nose from origin
02800         const float bDist = 256.0f; //estimated distance to back from origin
02801         const float wDist = 32.0f; //width on each side from origin
02802         const float hDist = 32.0f; //height on each side from origin
02803         gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
02804 
02805         if (!parent->ghoul2 || !parent->m_pVehicle || !parent->client)
02806         { //shouldn't have gotten in here then
02807                 return;
02808         }
02809 
02810         //for now, let's only do anything if all wings are stripped off.
02811         //this is because I want to be able to tear my wings off and fling
02812         //myself down narrow hallways to my death. Because it's fun! -rww
02813         if (!(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
02814                 !(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D) ||
02815                 !(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
02816                 !(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F) )
02817         {
02818                 return;
02819         }
02820 
02821         //get directions based on orientation
02822         AngleVectors(pVeh->m_vOrientation, fwd, right, up);
02823 
02824         //get the nose and back positions (relative to 0, they're gonna be mins/maxs)
02825         VectorMA(vec3_origin, fDist, fwd, nose);
02826         VectorMA(vec3_origin, -bDist, fwd, back);
02827 
02828         //move the nose and back to opposite right/left, they will end up as our relative mins and maxs
02829         VectorMA(nose, wDist, right, nose);
02830         VectorMA(nose, -wDist, right, back);
02831 
02832         //use the same concept for up/down now
02833         VectorMA(nose, hDist, up, nose);
02834         VectorMA(nose, -hDist, up, back);
02835 
02836         //and now, let's trace and see if our new mins/maxs are safe..
02837         trap_Trace(&trace, parent->client->ps.origin, back, nose, parent->client->ps.origin, parent->s.number, parent->clipmask);
02838         if (!trace.allsolid && !trace.startsolid && trace.fraction == 1.0f)
02839         { //all clear!
02840                 VectorCopy(nose, parent->maxs);
02841                 VectorCopy(back, parent->mins);
02842         }
02843         else
02844         { //oh well, DIE!
02845                 //FIXME: does this give proper credit to the enemy who shot you down?
02846                 G_Damage(parent, parent, parent, NULL, parent->client->ps.origin, 9999, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
02847         }
02848 }
02849 
02850 //get one of 4 possible impact locations based on the trace direction
02851 int G_FlyVehicleImpactDir(gentity_t *veh, trace_t *trace)
02852 {
02853         float impactAngle;
02854         float relativeAngle;
02855         trace_t localTrace;
02856         vec3_t testMins, testMaxs;
02857         vec3_t rWing, lWing;
02858         vec3_t fwd, right;
02859         vec3_t fPos;
02860         Vehicle_t *pVeh = veh->m_pVehicle;
02861         qboolean noseClear = qfalse;
02862 
02863         if (!trace || !pVeh || !veh->client)
02864         {
02865                 return -1;
02866         }
02867 
02868         AngleVectors(veh->client->ps.viewangles, fwd, right, 0);
02869         VectorSet(testMins, -24.0f, -24.0f, -24.0f);
02870         VectorSet(testMaxs, 24.0f, 24.0f, 24.0f);
02871 
02872         //do a trace to determine if the nose is clear
02873         VectorMA(veh->client->ps.origin, 256.0f, fwd, fPos);
02874         trap_Trace(&localTrace, veh->client->ps.origin, testMins, testMaxs, fPos, veh->s.number, veh->clipmask);
02875         if (!localTrace.startsolid && !localTrace.allsolid && localTrace.fraction == 1.0f)
02876         { //otherwise I guess it's not clear..
02877                 noseClear = qtrue;
02878         }
02879 
02880         if (noseClear)
02881         { //if nose is clear check for tearing the wings off
02882                 //sadly, the trace endpos given always matches the vehicle origin, so we
02883                 //can't get a real impact direction. First we'll trace forward and see if the wings are colliding
02884                 //with anything, and if not, we'll fall back to checking the trace plane normal.
02885                 VectorMA(veh->client->ps.origin, 128.0f, right, rWing);
02886                 VectorMA(veh->client->ps.origin, -128.0f, right, lWing);
02887 
02888                 //test the right wing - unless it's already removed
02889                 if (!(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_E) ||
02890                         !(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_F))
02891                 {
02892                         VectorMA(rWing, 256.0f, fwd, fPos);
02893                         trap_Trace(&localTrace, rWing, testMins, testMaxs, fPos, veh->s.number, veh->clipmask);
02894                         if (localTrace.startsolid || localTrace.allsolid || localTrace.fraction != 1.0f)
02895                         { //impact
02896                                 return SHIPSURF_RIGHT;
02897                         }
02898                 }
02899 
02900                 //test the left wing - unless it's already removed
02901                 if (!(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_C) ||
02902                         !(pVeh->m_iRemovedSurfaces & SHIPSURF_BROKEN_D))
02903                 {
02904                         VectorMA(lWing, 256.0f, fwd, fPos);
02905                         trap_Trace(&localTrace, lWing, testMins, testMaxs, fPos, veh->s.number, veh->clipmask);
02906                         if (localTrace.startsolid || localTrace.allsolid || localTrace.fraction != 1.0f)
02907                         { //impact
02908                                 return SHIPSURF_LEFT;
02909                         }
02910                 }
02911         }
02912 
02913         //try to use the trace plane normal
02914         impactAngle = vectoyaw(trace->plane.normal);
02915         relativeAngle = AngleSubtract(impactAngle, veh->client->ps.viewangles[YAW]);
02916 
02917         if (relativeAngle > 130 ||
02918                 relativeAngle < -130)
02919         { //consider this front
02920                 return SHIPSURF_FRONT;
02921         }
02922         else if (relativeAngle > 0)
02923         {
02924                 return SHIPSURF_RIGHT;
02925         }
02926         else if (relativeAngle < 0)
02927         {
02928                 return SHIPSURF_LEFT;
02929         }
02930 
02931         return SHIPSURF_BACK;
02932 }
02933 
02934 //try to break surfaces off the ship on impact
02935 #define TURN_ON                         0x00000000
02936 #define TURN_OFF                        0x00000100
02937 extern void NPC_SetSurfaceOnOff(gentity_t *ent, const char *surfaceName, int surfaceFlags); //NPC_utils.c
02938 int G_ShipSurfaceForSurfName( const char *surfaceName )
02939 {
02940         if ( !surfaceName )
02941         {
02942                 return -1;
02943         }
02944         if ( !Q_strncmp( "nose", surfaceName, 4 )
02945                 || !Q_strncmp( "f_gear", surfaceName, 6 )
02946                 || !Q_strncmp( "glass", surfaceName, 5 ) )
02947         {
02948                 return SHIPSURF_FRONT;
02949         }
02950         if ( !Q_strncmp( "body", surfaceName, 4 ) )
02951         {
02952                 return SHIPSURF_BACK;
02953         }
02954         if ( !Q_strncmp( "r_wing1", surfaceName, 7 )
02955                 || !Q_strncmp( "r_wing2", surfaceName, 7 )
02956                 || !Q_strncmp( "r_gear", surfaceName, 6 ) )
02957         {
02958                 return SHIPSURF_RIGHT;
02959         }
02960         if ( !Q_strncmp( "l_wing1", surfaceName, 7 )
02961                 || !Q_strncmp( "l_wing2", surfaceName, 7 )
02962                 || !Q_strncmp( "l_gear", surfaceName, 6 ) )
02963         {
02964                 return SHIPSURF_LEFT;
02965         }
02966         return -1;
02967 }
02968 
02969 void G_SetVehDamageFlags( gentity_t *veh, int shipSurf, int damageLevel )
02970 {
02971         int dmgFlag;
02972         switch ( damageLevel )
02973         {
02974         case 3://destroyed
02975                 //add both flags so cgame side knows this surf is GONE
02976                 //add heavy
02977                 dmgFlag = SHIPSURF_DAMAGE_FRONT_HEAVY+(shipSurf-SHIPSURF_FRONT);
02978                 veh->client->ps.brokenLimbs |= (1<<dmgFlag);
02979                 //add light
02980                 dmgFlag = SHIPSURF_DAMAGE_FRONT_LIGHT+(shipSurf-SHIPSURF_FRONT);
02981                 veh->client->ps.brokenLimbs |= (1<<dmgFlag);
02982                 //copy down
02983                 veh->s.brokenLimbs = veh->client->ps.brokenLimbs;
02984                 //check droid
02985                 if ( shipSurf == SHIPSURF_BACK )
02986                 {//destroy the droid if we have one
02987                         if ( veh->m_pVehicle
02988                                 && veh->m_pVehicle->m_pDroidUnit )
02989                         {//we have one
02990                                 gentity_t *droidEnt = (gentity_t *)veh->m_pVehicle->m_pDroidUnit;
02991                                 if ( droidEnt 
02992                                         && ((droidEnt->flags&FL_UNDYING) || droidEnt->health > 0) )
02993                                 {//boom
02994                                         //make it vulnerable
02995                                         droidEnt->flags &= ~FL_UNDYING;
02996                                         //blow it up
02997                                         G_Damage( droidEnt, veh->enemy, veh->enemy, NULL, NULL, 99999, 0, MOD_UNKNOWN );
02998                                 }
02999                         }
03000                 }
03001                 break;
03002         case 2://heavy only
03003                 dmgFlag = SHIPSURF_DAMAGE_FRONT_HEAVY+(shipSurf-SHIPSURF_FRONT);
03004                 veh->client->ps.brokenLimbs |= (1<<dmgFlag);
03005                 //remove light
03006                 dmgFlag = SHIPSURF_DAMAGE_FRONT_LIGHT+(shipSurf-SHIPSURF_FRONT);
03007                 veh->client->ps.brokenLimbs &= ~(1<<dmgFlag);
03008                 //copy down
03009                 veh->s.brokenLimbs = veh->client->ps.brokenLimbs;
03010                 //check droid
03011                 if ( shipSurf == SHIPSURF_BACK )
03012                 {//make the droid vulnerable if we have one
03013                         if ( veh->m_pVehicle
03014                                 && veh->m_pVehicle->m_pDroidUnit )
03015                         {//we have one
03016                                 gentity_t *droidEnt = (gentity_t *)veh->m_pVehicle->m_pDroidUnit;
03017                                 if ( droidEnt 
03018                                         && (droidEnt->flags&FL_UNDYING) )
03019                                 {//make it vulnerab;e
03020                                         droidEnt->flags &= ~FL_UNDYING;
03021                                 }
03022                         }
03023                 }
03024                 break;
03025         case 1://light only
03026                 //add light
03027                 dmgFlag = SHIPSURF_DAMAGE_FRONT_LIGHT+(shipSurf-SHIPSURF_FRONT);
03028                 veh->client->ps.brokenLimbs |= (1<<dmgFlag);
03029                 //remove heavy (shouldn't have to do this, but...
03030                 dmgFlag = SHIPSURF_DAMAGE_FRONT_HEAVY+(shipSurf-SHIPSURF_FRONT);
03031                 veh->client->ps.brokenLimbs &= ~(1<<dmgFlag);
03032                 //copy down
03033                 veh->s.brokenLimbs = veh->client->ps.brokenLimbs;
03034                 break;
03035         case 0://no damage
03036         default:
03037                 //remove heavy
03038                 dmgFlag = SHIPSURF_DAMAGE_FRONT_HEAVY+(shipSurf-SHIPSURF_FRONT);
03039                 veh->client->ps.brokenLimbs &= ~(1<<dmgFlag);
03040                 //remove light
03041                 dmgFlag = SHIPSURF_DAMAGE_FRONT_LIGHT+(shipSurf-SHIPSURF_FRONT);
03042                 veh->client->ps.brokenLimbs &= ~(1<<dmgFlag);
03043                 //copy down
03044                 veh->s.brokenLimbs = veh->client->ps.brokenLimbs;
03045                 break;
03046         }
03047 }
03048 
03049 void G_VehicleSetDamageLocFlags( gentity_t *veh, int impactDir, int deathPoint )
03050 {
03051         if ( !veh->client )
03052         {
03053                 return;
03054         }
03055         else
03056         {
03057                 int     deathPoint, heavyDamagePoint, lightDamagePoint;
03058                 switch(impactDir)
03059                 {
03060                 case SHIPSURF_FRONT:
03061                         deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_front;
03062                         break;
03063                 case SHIPSURF_BACK:
03064                         deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_back;
03065                         break;
03066                 case SHIPSURF_RIGHT:
03067                         deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_right;
03068                         break;
03069                 case SHIPSURF_LEFT:
03070                         deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_left;
03071                         break;
03072                 default:
03073                         return;
03074                         break;
03075                 }
03076                 if ( veh->m_pVehicle
03077                         && veh->m_pVehicle->m_pVehicleInfo
03078                         && veh->m_pVehicle->m_pVehicleInfo->malfunctionArmorLevel 
03079                         && veh->m_pVehicle->m_pVehicleInfo->armor )
03080                 {
03081                         float perc = ((float)veh->m_pVehicle->m_pVehicleInfo->malfunctionArmorLevel/(float)veh->m_pVehicle->m_pVehicleInfo->armor);
03082                         if ( perc > 0.99f )
03083                         {
03084                                 perc = 0.99f;
03085                         }
03086                         lightDamagePoint = ceil( deathPoint*perc*0.25f );
03087                         heavyDamagePoint = ceil( deathPoint*perc );
03088                 }
03089                 else
03090                 {
03091                         heavyDamagePoint = ceil( deathPoint*0.66f );
03092                         lightDamagePoint = ceil( deathPoint*0.14f );
03093                 }
03094 
03095                 if ( veh->locationDamage[impactDir] >= deathPoint)
03096                 {//destroyed
03097                         G_SetVehDamageFlags( veh, impactDir, 3 );
03098                 }
03099                 else if ( veh->locationDamage[impactDir] <= lightDamagePoint )
03100                 {//light only
03101                         G_SetVehDamageFlags( veh, impactDir, 1 );
03102                 }
03103                 else if ( veh->locationDamage[impactDir] <= heavyDamagePoint )
03104                 {//heavy only
03105                         G_SetVehDamageFlags( veh, impactDir, 2 );
03106                 }
03107         }
03108 }
03109 
03110 qboolean G_FlyVehicleDestroySurface( gentity_t *veh, int surface )
03111 {
03112         char *surfName[4]; //up to 4 surfs at once
03113         int numSurfs = 0;
03114         int smashedBits = 0;
03115 
03116         if (surface == -1)
03117         { //not valid?
03118                 return qfalse;
03119         }
03120 
03121         switch(surface)
03122         {
03123         case SHIPSURF_FRONT: //break the nose off
03124                 surfName[0] = "nose";
03125 
03126                 smashedBits = (SHIPSURF_BROKEN_G);
03127 
03128                 numSurfs = 1;
03129                 break;
03130         case SHIPSURF_BACK: //break both the bottom wings off for a backward impact I guess
03131                 surfName[0] = "r_wing2";
03132                 surfName[1] = "l_wing2";
03133 
03134                 //get rid of the landing gear
03135                 surfName[2] = "r_gear";
03136                 surfName[3] = "l_gear";
03137 
03138                 smashedBits = (SHIPSURF_BROKEN_A|SHIPSURF_BROKEN_B|SHIPSURF_BROKEN_D|SHIPSURF_BROKEN_F);
03139 
03140                 numSurfs = 4;
03141                 break;
03142         case SHIPSURF_RIGHT: //break both right wings off
03143                 surfName[0] = "r_wing1";
03144                 surfName[1] = "r_wing2";
03145 
03146                 //get rid of the landing gear
03147                 surfName[2] = "r_gear";
03148 
03149                 smashedBits = (SHIPSURF_BROKEN_B|SHIPSURF_BROKEN_E|SHIPSURF_BROKEN_F);
03150 
03151                 numSurfs = 3;
03152                 break;
03153         case SHIPSURF_LEFT: //break both left wings off
03154                 surfName[0] = "l_wing1";
03155                 surfName[1] = "l_wing2";
03156 
03157                 //get rid of the landing gear
03158                 surfName[2] = "l_gear";
03159 
03160                 smashedBits = (SHIPSURF_BROKEN_A|SHIPSURF_BROKEN_C|SHIPSURF_BROKEN_D);
03161 
03162                 numSurfs = 3;
03163                 break;
03164         default:
03165                 break;
03166         }
03167 
03168         if (numSurfs < 1)
03169         { //didn't get any valid surfs..
03170                 return qfalse;
03171         }
03172 
03173         while (numSurfs > 0)
03174         { //use my silly system of automatically managing surf status on both client and server
03175                 numSurfs--;
03176                 NPC_SetSurfaceOnOff(veh, surfName[numSurfs], TURN_OFF);
03177         }
03178 
03179         if ( !veh->m_pVehicle->m_iRemovedSurfaces )
03180         {//first time something got blown off
03181                 if ( veh->m_pVehicle->m_pPilot )
03182                 {//make the pilot scream to his death
03183                         G_EntitySound((gentity_t*)veh->m_pVehicle->m_pPilot, CHAN_VOICE, G_SoundIndex("*falling1.wav"));
03184                 }
03185         }
03186         //so we can check what's broken
03187         veh->m_pVehicle->m_iRemovedSurfaces |= smashedBits;
03188 
03189         //do some explosive damage, but don't damage this ship with it
03190         G_RadiusDamage(veh->client->ps.origin, veh, 100, 500, veh, NULL, MOD_SUICIDE);
03191 
03192         //when spiraling to your death, do the electical shader
03193         veh->client->ps.electrifyTime = level.time + 10000;
03194 
03195         return qtrue;
03196 }
03197 
03198 void G_FlyVehicleSurfaceDestruction(gentity_t *veh, trace_t *trace, int magnitude, qboolean force)
03199 {
03200         int impactDir;
03201         int secondImpact;
03202         int deathPoint = -1;
03203         qboolean alreadyRebroken = qfalse;
03204 
03205         if (!veh->ghoul2 || !veh->m_pVehicle)
03206         { //no g2 instance.. or no vehicle instance
03207                 return;
03208         }
03209 
03210     impactDir = G_FlyVehicleImpactDir(veh, trace);
03211 
03212 anotherImpact:
03213         if (impactDir == -1)
03214         { //not valid?
03215                 return;
03216         }
03217 
03218         veh->locationDamage[impactDir] += magnitude*7;
03219 
03220         switch(impactDir)
03221         {
03222         case SHIPSURF_FRONT:
03223                 deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_front;
03224                 break;
03225         case SHIPSURF_BACK:
03226                 deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_back;
03227                 break;
03228         case SHIPSURF_RIGHT:
03229                 deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_right;
03230                 break;
03231         case SHIPSURF_LEFT:
03232                 deathPoint = veh->m_pVehicle->m_pVehicleInfo->health_left;
03233                 break;
03234         default:
03235                 break;
03236         }
03237 
03238         if ( deathPoint != -1 )
03239         {//got a valid health value
03240                 if ( force && veh->locationDamage[impactDir] < deathPoint )
03241                 {//force that surf to be destroyed
03242                         veh->locationDamage[impactDir] = deathPoint;
03243                 }
03244                 if ( veh->locationDamage[impactDir] >= deathPoint)
03245                 { //do it
03246                         if ( G_FlyVehicleDestroySurface( veh, impactDir ) )
03247                         {//actually took off a surface
03248                                 G_VehicleSetDamageLocFlags( veh, impactDir, deathPoint );
03249                         }
03250                 }
03251                 else
03252                 {
03253                         G_VehicleSetDamageLocFlags( veh, impactDir, deathPoint );
03254                 }
03255         }
03256 
03257         if (!alreadyRebroken)
03258         {
03259                 secondImpact = G_FlyVehicleImpactDir(veh, trace);
03260                 if (impactDir != secondImpact)
03261                 { //can break off another piece in this same impact.. but only break off up to 2 at once
03262                         alreadyRebroken = qtrue;
03263                         impactDir = secondImpact;
03264                         goto anotherImpact;
03265                 }
03266         }
03267 }
03268 
03269 void G_VehUpdateShields( gentity_t *targ )
03270 {
03271         if ( !targ || !targ->client 
03272                 || !targ->m_pVehicle || !targ->m_pVehicle->m_pVehicleInfo )
03273         {
03274                 return;
03275         }
03276         if ( targ->m_pVehicle->m_pVehicleInfo->shields <= 0 )
03277         {//doesn't have shields, so don't have to send it
03278                 return;
03279         }
03280         targ->client->ps.activeForcePass = floor(((float)targ->m_pVehicle->m_iShields/(float)targ->m_pVehicle->m_pVehicleInfo->shields)*10.0f);
03281 }
03282 #endif
03283 
03284 // Set the parent entity of this Vehicle NPC.
03285 GAME_INLINE void SetParent( Vehicle_t *pVeh, bgEntity_t *pParentEntity ) { pVeh->m_pParentEntity = pParentEntity; }
03286 
03287 // Add a pilot to the vehicle.
03288 GAME_INLINE void SetPilot( Vehicle_t *pVeh, bgEntity_t *pPilot ) { pVeh->m_pPilot = pPilot; }
03289 
03290 // Add a passenger to the vehicle (false if we're full).
03291 GAME_INLINE bool AddPassenger( Vehicle_t *pVeh ) { return false; }
03292 
03293 // Whether this vehicle is currently inhabited (by anyone) or not.
03294 GAME_INLINE bool Inhabited( Vehicle_t *pVeh ) { return ( pVeh->m_pPilot || pVeh->m_iNumPassengers ) ? true : false; }
03295 
03296 
03297 // Setup the shared functions (one's that all vehicles would generally use).
03298 void G_SetSharedVehicleFunctions( vehicleInfo_t *pVehInfo )
03299 {
03300 //      pVehInfo->AnimateVehicle                                =               AnimateVehicle;
03301 //      pVehInfo->AnimateRiders                                 =               AnimateRiders;
03302         pVehInfo->ValidateBoard                                 =               ValidateBoard;
03303         pVehInfo->SetParent                                             =               SetParent;
03304         pVehInfo->SetPilot                                              =               SetPilot;
03305         pVehInfo->AddPassenger                                  =               AddPassenger;
03306         pVehInfo->Animate                                               =               Animate;
03307         pVehInfo->Board                                                 =               Board;
03308         pVehInfo->Eject                                                 =               Eject;
03309         pVehInfo->EjectAll                                              =               EjectAll;
03310         pVehInfo->StartDeathDelay                               =               StartDeathDelay;
03311         pVehInfo->DeathUpdate                                   =               DeathUpdate;
03312         pVehInfo->RegisterAssets                                =               RegisterAssets;
03313         pVehInfo->Initialize                                    =               Initialize;
03314         pVehInfo->Update                                                =               Update;
03315         pVehInfo->UpdateRider                                   =               UpdateRider;
03316 //      pVehInfo->ProcessMoveCommands                   =               ProcessMoveCommands;
03317 //      pVehInfo->ProcessOrientCommands                 =               ProcessOrientCommands;
03318         pVehInfo->AttachRiders                                  =               AttachRiders;
03319         pVehInfo->Ghost                                                 =               Ghost;
03320         pVehInfo->UnGhost                                               =               UnGhost;
03321         pVehInfo->Inhabited                                             =               Inhabited;
03322 }
03323 
03324 #ifdef _JK2MP
03325 //get rid of all the crazy defs we added for this file
03326 #undef currentAngles
03327 #undef currentOrigin
03328 #undef mins
03329 #undef maxs
03330 #undef legsAnimTimer
03331 #undef torsoAnimTimer
03332 #undef bool
03333 #undef false
03334 #undef true
03335 
03336 #undef sqrtf
03337 
03338 #undef MOD_EXPLOSIVE
03339 #endif