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.