codemp/cgame/cg_predict.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 // cg_predict.c -- this file generates cg.predictedPlayerState by either
00004 // interpolating between snapshots from the server or locally predicting
00005 // ahead the client's movement.
00006 // It also handles local physics interaction, like fragments bouncing off walls
00007 
00008 #include "cg_local.h"
00009 
00010 static  pmove_t         cg_pmove;
00011 
00012 static  int                     cg_numSolidEntities;
00013 static  centity_t       *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
00014 static  int                     cg_numTriggerEntities;
00015 static  centity_t       *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT];
00016 
00017 //is this client piloting this veh?
00018 static CGAME_INLINE qboolean CG_Piloting(int vehNum)
00019 {
00020         centity_t *veh;
00021 
00022         if (!vehNum)
00023         {
00024                 return qfalse;
00025         }
00026 
00027         veh = &cg_entities[vehNum];
00028 
00029         if (veh->currentState.owner != cg.predictedPlayerState.clientNum)
00030         { //the owner should be the current pilot
00031                 return qfalse;
00032         }
00033 
00034         return qtrue;
00035 }
00036 
00037 /*
00038 ====================
00039 CG_BuildSolidList
00040 
00041 When a new cg.snap has been set, this function builds a sublist
00042 of the entities that are actually solid, to make for more
00043 efficient collision detection
00044 ====================
00045 */
00046 void CG_BuildSolidList( void ) {
00047         int                             i;
00048         centity_t               *cent;
00049         snapshot_t              *snap;
00050         entityState_t   *ent;
00051         vec3_t                  difference;
00052         float                   dsquared;
00053 
00054         cg_numSolidEntities = 0;
00055         cg_numTriggerEntities = 0;
00056 
00057         if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
00058                 snap = cg.nextSnap;
00059         } else {
00060                 snap = cg.snap;
00061         }
00062 
00063         for ( i = 0 ; i < snap->numEntities ; i++ ) {
00064                 cent = &cg_entities[ snap->entities[ i ].number ];
00065                 ent = &cent->currentState;
00066 
00067                 if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) {
00068                         cg_triggerEntities[cg_numTriggerEntities] = cent;
00069                         cg_numTriggerEntities++;
00070                         continue;
00071                 }
00072 
00073                 if ( cent->nextState.solid ) {
00074                         cg_solidEntities[cg_numSolidEntities] = cent;
00075                         cg_numSolidEntities++;
00076                         continue;
00077                 }
00078         }
00079 
00080         //rww - Horrible, terrible, awful hack.
00081         //We don't send your client entity from the server,
00082         //so it isn't added into the solid list from the snapshot,
00083         //and in addition, it has no solid data. So we will force
00084         //adding it in based on a hardcoded player bbox size.
00085         //This will cause issues if the player box size is ever
00086         //changed..
00087         if (cg_numSolidEntities < MAX_ENTITIES_IN_SNAPSHOT)
00088         {
00089                 vec3_t  playerMins = {-15, -15, DEFAULT_MINS_2};
00090                 vec3_t  playerMaxs = {15, 15, DEFAULT_MAXS_2};
00091                 int i, j, k;
00092 
00093                 i = playerMaxs[0];
00094                 if (i<1)
00095                         i = 1;
00096                 if (i>255)
00097                         i = 255;
00098 
00099                 // z is not symetric
00100                 j = (-playerMins[2]);
00101                 if (j<1)
00102                         j = 1;
00103                 if (j>255)
00104                         j = 255;
00105 
00106                 // and z playerMaxs can be negative...
00107                 k = (playerMaxs[2]+32);
00108                 if (k<1)
00109                         k = 1;
00110                 if (k>255)
00111                         k = 255;
00112 
00113                 cg_solidEntities[cg_numSolidEntities] = &cg_entities[cg.predictedPlayerState.clientNum];
00114                 cg_solidEntities[cg_numSolidEntities]->currentState.solid = (k<<16) | (j<<8) | i;
00115 
00116                 cg_numSolidEntities++;
00117         }
00118 
00119         dsquared = /*RMG_distancecull.value*/5000+500;
00120         dsquared *= dsquared;
00121 
00122         for(i=0;i<cg_numpermanents;i++)
00123         {
00124                 cent = cg_permanents[i];
00125                 VectorSubtract(cent->lerpOrigin, snap->ps.origin, difference);
00126                 if (cent->currentState.eType == ET_TERRAIN ||
00127                         ((difference[0]*difference[0]) + (difference[1]*difference[1]) + (difference[2]*difference[2])) <= dsquared)
00128                 {
00129                         cent->currentValid = qtrue;
00130                         if ( cent->nextState.solid ) 
00131                         {
00132                                 cg_solidEntities[cg_numSolidEntities] = cent;
00133                                 cg_numSolidEntities++;
00134                         }
00135                 }
00136                 else
00137                 {
00138                         cent->currentValid = qfalse;
00139                 }
00140         }
00141 }
00142 
00143 static CGAME_INLINE qboolean CG_VehicleClipCheck(centity_t *ignored, trace_t *trace)
00144 {
00145         if (!trace || trace->entityNum < 0 || trace->entityNum >= ENTITYNUM_WORLD)
00146         { //it's alright then
00147                 return qtrue;
00148         }
00149 
00150         if (ignored->currentState.eType != ET_PLAYER &&
00151                 ignored->currentState.eType != ET_NPC)
00152         { //can't possibly be valid then
00153                 return qtrue;
00154         }
00155 
00156         if (ignored->currentState.m_iVehicleNum)
00157         { //see if the ignore ent is a vehicle/rider - if so, see if the ent we supposedly hit is a vehicle/rider.
00158                 //if they belong to each other, we don't want to collide them.
00159                 centity_t *otherguy = &cg_entities[trace->entityNum];
00160 
00161                 if (otherguy->currentState.eType != ET_PLAYER &&
00162                         otherguy->currentState.eType != ET_NPC)
00163                 { //can't possibly be valid then
00164                         return qtrue;
00165                 }
00166 
00167                 if (otherguy->currentState.m_iVehicleNum)
00168                 { //alright, both of these are either a vehicle or a player who is on a vehicle
00169                         int index;
00170 
00171                         if (ignored->currentState.eType == ET_PLAYER
00172                                 || (ignored->currentState.eType == ET_NPC && ignored->currentState.NPC_class != CLASS_VEHICLE) )
00173                         { //must be a player or NPC riding a vehicle
00174                                 index = ignored->currentState.m_iVehicleNum;
00175                         }
00176                         else
00177                         { //a vehicle
00178                                 index = ignored->currentState.m_iVehicleNum-1;
00179                         }
00180 
00181                         if (index == otherguy->currentState.number)
00182                         { //this means we're riding or being ridden by this guy, so don't collide
00183                                 return qfalse;
00184                         }
00185                         else 
00186                         {//see if I'm hitting one of my own passengers
00187                                 if (otherguy->currentState.eType == ET_PLAYER
00188                                         || (otherguy->currentState.eType == ET_NPC && otherguy->currentState.NPC_class != CLASS_VEHICLE) )
00189                                 { //must be a player or NPC riding a vehicle
00190                                         if (otherguy->currentState.m_iVehicleNum==ignored->currentState.number)
00191                                         { //this means we're other guy is riding the ignored ent
00192                                                 return qfalse;
00193                                         }
00194                                 }
00195                         }
00196                 }
00197         }
00198 
00199         return qtrue;
00200 }
00201 
00202 //rww - I'm disabling this warning for this function. It complains about oldTrace but as you can see it
00203 //always gets set before use, and I am not wasting CPU memsetting it to shut the compiler up.
00204 #pragma warning(disable : 4701) //local variable may be used without having been initialized
00205 /*
00206 ====================
00207 CG_ClipMoveToEntities
00208 
00209 ====================
00210 */
00211 #include "../namespace_begin.h"
00212 extern void BG_VehicleAdjustBBoxForOrientation( Vehicle_t *veh, vec3_t origin, vec3_t mins, vec3_t maxs,
00213                                                                                 int clientNum, int tracemask,
00214                                                                                 void (*localTrace)(trace_t *results, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int passEntityNum, int contentMask)); // bg_pmove.c
00215 #include "../namespace_end.h"
00216 static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
00217                                                         int skipNumber, int mask, trace_t *tr, qboolean g2Check ) {
00218         int                     i, x, zd, zu;
00219         trace_t         trace, oldTrace;
00220         entityState_t   *ent;
00221         clipHandle_t    cmodel;
00222         vec3_t          bmins, bmaxs;
00223         vec3_t          origin, angles;
00224         centity_t       *cent;
00225         centity_t       *ignored = NULL;
00226 
00227         if (skipNumber != -1 && skipNumber != ENTITYNUM_NONE)
00228         {
00229                 ignored = &cg_entities[skipNumber];
00230         }
00231 
00232         for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
00233                 cent = cg_solidEntities[ i ];
00234                 ent = &cent->currentState;
00235 
00236                 if ( ent->number == skipNumber ) {
00237                         continue;
00238                 }
00239 
00240                 if ( ent->number > MAX_CLIENTS && 
00241                          (ent->genericenemyindex-MAX_GENTITIES==cg.predictedPlayerState.clientNum || ent->genericenemyindex-MAX_GENTITIES==cg.predictedVehicleState.clientNum) )
00242 //              if (ent->number > MAX_CLIENTS && cg.snap && ent->genericenemyindex && (ent->genericenemyindex-MAX_GENTITIES) == cg.snap->ps.clientNum)
00243                 { //rww - method of keeping objects from colliding in client-prediction (in case of ownership)
00244                         continue;
00245                 }
00246 
00247                 if ( ent->solid == SOLID_BMODEL ) {
00248                         // special value for bmodel
00249                         cmodel = trap_CM_InlineModel( ent->modelindex );
00250                         VectorCopy( cent->lerpAngles, angles );
00251                         BG_EvaluateTrajectory( &cent->currentState.pos, cg.physicsTime, origin );
00252                 } else {
00253                         // encoded bbox
00254                         x = (ent->solid & 255);
00255                         zd = ((ent->solid>>8) & 255);
00256                         zu = ((ent->solid>>16) & 255) - 32;
00257 
00258                         bmins[0] = bmins[1] = -x;
00259                         bmaxs[0] = bmaxs[1] = x;
00260                         bmins[2] = -zd;
00261                         bmaxs[2] = zu;
00262 
00263                         if (ent->eType == ET_NPC && ent->NPC_class == CLASS_VEHICLE &&
00264                                 cent->m_pVehicle)
00265                         { //try to dynamically adjust his bbox dynamically, if possible
00266                                 float *old = cent->m_pVehicle->m_vOrientation;
00267                                 cent->m_pVehicle->m_vOrientation = &cent->lerpAngles[0];
00268                                 BG_VehicleAdjustBBoxForOrientation(cent->m_pVehicle, cent->lerpOrigin, bmins, bmaxs,
00269                                                                                         cent->currentState.number, MASK_PLAYERSOLID, NULL);
00270                                 cent->m_pVehicle->m_vOrientation = old;
00271                         }
00272 
00273                         cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
00274                         VectorCopy( vec3_origin, angles );
00275                         
00276                         VectorCopy( cent->lerpOrigin, origin );
00277                 }
00278 
00279 
00280                 trap_CM_TransformedBoxTrace ( &trace, start, end,
00281                         mins, maxs, cmodel,  mask, origin, angles);
00282                 trace.entityNum = trace.fraction != 1.0 ? ent->number : ENTITYNUM_NONE;
00283 
00284                 if (g2Check || (ignored && ignored->currentState.m_iVehicleNum))
00285                 {
00286                         //keep these older variables around for a bit, incase we need to replace them in the Ghoul2 Collision check
00287                         //or in the vehicle owner trace check
00288                         oldTrace = *tr;
00289                 }
00290 
00291                 if (trace.allsolid || trace.fraction < tr->fraction) {
00292                         trace.entityNum = ent->number;
00293                         *tr = trace;
00294                 } else if (trace.startsolid) {
00295                         tr->startsolid = qtrue;
00296 
00297                         //rww 12-02-02
00298                         tr->entityNum = trace.entityNum = ent->number;
00299                 }
00300                 if ( tr->allsolid )
00301                 {
00302                         if (ignored && ignored->currentState.m_iVehicleNum)
00303                         {
00304                                 trace.entityNum = ent->number;
00305                 if (CG_VehicleClipCheck(ignored, &trace))
00306                                 { //this isn't our vehicle, we're really stuck
00307                                         return;
00308                                 }
00309                                 else
00310                                 { //it's alright, keep going
00311                                         trace = oldTrace;
00312                                         *tr = trace;
00313                                 }
00314                         }
00315                         else
00316                         {
00317                                 return;
00318                         }
00319                 }
00320 
00321                 if (g2Check)
00322                 {
00323                         if (trace.entityNum == ent->number && cent->ghoul2)
00324                         {
00325                                 CG_G2TraceCollide(&trace, mins, maxs, start, end);
00326 
00327                                 if (trace.entityNum == ENTITYNUM_NONE)
00328                                 { //g2 trace failed, so put it back where it was.
00329                                         trace = oldTrace;
00330                                         *tr = trace;
00331                                 }
00332                         }
00333                 }
00334 
00335                 if (ignored && ignored->currentState.m_iVehicleNum)
00336                 { //see if this is the vehicle we hit
00337                         centity_t *hit = &cg_entities[trace.entityNum];
00338             if (!CG_VehicleClipCheck(ignored, &trace))
00339                         { //looks like it
00340                                 trace = oldTrace;
00341                                 *tr = trace;
00342                         }
00343                         else if (hit->currentState.eType == ET_MISSILE &&
00344                                 hit->currentState.owner == ignored->currentState.number)
00345                         { //hack, don't hit own missiles
00346                                 trace = oldTrace;
00347                                 *tr = trace;
00348                         }
00349                 }
00350         }
00351 }
00352 #pragma warning(default : 4701) //local variable may be used without having been initialized
00353 
00354 /*
00355 ================
00356 CG_Trace
00357 ================
00358 */
00359 void    CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, 
00360                                          int skipNumber, int mask ) {
00361         trace_t t;
00362 
00363         trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
00364         t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
00365         // check all other solid models
00366         CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t, qfalse);
00367 
00368         *result = t;
00369 }
00370 
00371 /*
00372 ================
00373 CG_G2Trace
00374 ================
00375 */
00376 void    CG_G2Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, 
00377                                          int skipNumber, int mask ) {
00378         trace_t t;
00379 
00380         trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
00381         t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
00382         // check all other solid models
00383         CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t, qtrue);
00384 
00385         *result = t;
00386 }
00387 
00388 /*
00389 ================
00390 CG_PointContents
00391 ================
00392 */
00393 int             CG_PointContents( const vec3_t point, int passEntityNum ) {
00394         int                     i;
00395         entityState_t   *ent;
00396         centity_t       *cent;
00397         clipHandle_t cmodel;
00398         int                     contents;
00399 
00400         contents = trap_CM_PointContents (point, 0);
00401 
00402         for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
00403                 cent = cg_solidEntities[ i ];
00404 
00405                 ent = &cent->currentState;
00406 
00407                 if ( ent->number == passEntityNum ) {
00408                         continue;
00409                 }
00410 
00411                 if (ent->solid != SOLID_BMODEL) { // special value for bmodel
00412                         continue;
00413                 }
00414 
00415                 cmodel = trap_CM_InlineModel( ent->modelindex );
00416                 if ( !cmodel ) {
00417                         continue;
00418                 }
00419 
00420                 contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
00421         }
00422 
00423         return contents;
00424 }
00425 
00426 
00427 /*
00428 ========================
00429 CG_InterpolatePlayerState
00430 
00431 Generates cg.predictedPlayerState by interpolating between
00432 cg.snap->player_state and cg.nextFrame->player_state
00433 ========================
00434 */
00435 static void CG_InterpolatePlayerState( qboolean grabAngles ) {
00436         float                   f;
00437         int                             i;
00438         playerState_t   *out;
00439         snapshot_t              *prev, *next;
00440 
00441         out = &cg.predictedPlayerState;
00442         prev = cg.snap;
00443         next = cg.nextSnap;
00444 
00445         *out = cg.snap->ps;
00446 
00447         // if we are still allowing local input, short circuit the view angles
00448         if ( grabAngles ) {
00449                 usercmd_t       cmd;
00450                 int                     cmdNum;
00451 
00452                 cmdNum = trap_GetCurrentCmdNumber();
00453                 trap_GetUserCmd( cmdNum, &cmd );
00454 
00455                 PM_UpdateViewAngles( out, &cmd );
00456         }
00457 
00458         // if the next frame is a teleport, we can't lerp to it
00459         if ( cg.nextFrameTeleport ) {
00460                 return;
00461         }
00462 
00463         if ( !next || next->serverTime <= prev->serverTime ) {
00464                 return;
00465         }
00466 
00467         f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
00468 
00469         i = next->ps.bobCycle;
00470         if ( i < prev->ps.bobCycle ) {
00471                 i += 256;               // handle wraparound
00472         }
00473         out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
00474 
00475         for ( i = 0 ; i < 3 ; i++ ) {
00476                 out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
00477                 if ( !grabAngles ) {
00478                         out->viewangles[i] = LerpAngle( 
00479                                 prev->ps.viewangles[i], next->ps.viewangles[i], f );
00480                 }
00481                 out->velocity[i] = prev->ps.velocity[i] + 
00482                         f * (next->ps.velocity[i] - prev->ps.velocity[i] );
00483         }
00484 
00485 }
00486 
00487 static void CG_InterpolateVehiclePlayerState( qboolean grabAngles ) {
00488         float                   f;
00489         int                             i;
00490         playerState_t   *out;
00491         snapshot_t              *prev, *next;
00492 
00493         out = &cg.predictedVehicleState;
00494         prev = cg.snap;
00495         next = cg.nextSnap;
00496 
00497         *out = cg.snap->vps;
00498 
00499         // if we are still allowing local input, short circuit the view angles
00500         if ( grabAngles ) {
00501                 usercmd_t       cmd;
00502                 int                     cmdNum;
00503 
00504                 cmdNum = trap_GetCurrentCmdNumber();
00505                 trap_GetUserCmd( cmdNum, &cmd );
00506 
00507                 PM_UpdateViewAngles( out, &cmd );
00508         }
00509 
00510         // if the next frame is a teleport, we can't lerp to it
00511         if ( cg.nextFrameTeleport ) {
00512                 return;
00513         }
00514 
00515         if ( !next || next->serverTime <= prev->serverTime ) {
00516                 return;
00517         }
00518 
00519         f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
00520 
00521         i = next->vps.bobCycle;
00522         if ( i < prev->vps.bobCycle ) {
00523                 i += 256;               // handle wraparound
00524         }
00525         out->bobCycle = prev->vps.bobCycle + f * ( i - prev->vps.bobCycle );
00526 
00527         for ( i = 0 ; i < 3 ; i++ ) {
00528                 out->origin[i] = prev->vps.origin[i] + f * (next->vps.origin[i] - prev->vps.origin[i] );
00529                 if ( !grabAngles ) {
00530                         out->viewangles[i] = LerpAngle( 
00531                                 prev->vps.viewangles[i], next->vps.viewangles[i], f );
00532                 }
00533                 out->velocity[i] = prev->vps.velocity[i] + 
00534                         f * (next->vps.velocity[i] - prev->vps.velocity[i] );
00535         }
00536 
00537 }
00538 
00539 /*
00540 ===================
00541 CG_TouchItem
00542 ===================
00543 */
00544 static void CG_TouchItem( centity_t *cent ) {
00545         gitem_t         *item;
00546 
00547         if ( !cg_predictItems.integer ) {
00548                 return;
00549         }
00550         if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, &cent->currentState, cg.time ) ) {
00551                 return;
00552         }
00553 
00554         if (cent->currentState.brokenLimbs)
00555         { //dropped item
00556                 return;
00557         }
00558 
00559         if (cent->currentState.eFlags & EF_ITEMPLACEHOLDER)
00560         {
00561                 return;
00562         }
00563 
00564         if (cent->currentState.eFlags & EF_NODRAW)
00565         {
00566                 return;
00567         }
00568 
00569         // never pick an item up twice in a prediction
00570         if ( cent->miscTime == cg.time ) {
00571                 return;
00572         }
00573 
00574         if ( !BG_CanItemBeGrabbed( cgs.gametype, &cent->currentState, &cg.predictedPlayerState ) ) {
00575                 return;         // can't hold it
00576         }
00577 
00578         item = &bg_itemlist[ cent->currentState.modelindex ];
00579 
00580         //Currently there is no reliable way of knowing if the client has touched a certain item before another if they are next to each other, or rather
00581         //if the server has touched them in the same order. This results often in grabbing an item in the prediction and the server giving you the other
00582         //item. So for now prediction of armor, health, and ammo is disabled.
00583 /*
00584         if (item->giType == IT_ARMOR)
00585         { //rww - this will be stomped next update, but it's set so that we don't try to pick up two shields in one prediction and have the server cancel one
00586         //      cg.predictedPlayerState.stats[STAT_ARMOR] += item->quantity;
00587 
00588                 //FIXME: This can't be predicted properly at the moment
00589                 return;
00590         }
00591 
00592         if (item->giType == IT_HEALTH)
00593         { //same as above, for health
00594         //      cg.predictedPlayerState.stats[STAT_HEALTH] += item->quantity;
00595 
00596                 //FIXME: This can't be predicted properly at the moment
00597                 return;
00598         }
00599 
00600         if (item->giType == IT_AMMO)
00601         { //same as above, for ammo
00602         //      cg.predictedPlayerState.ammo[item->giTag] += item->quantity;
00603 
00604                 //FIXME: This can't be predicted properly at the moment
00605                 return;
00606         }
00607 
00608         if (item->giType == IT_HOLDABLE)
00609         { //same as above, for holdables
00610         //      cg.predictedPlayerState.stats[STAT_HOLDABLE_ITEMS] |= (1 << item->giTag);
00611         }
00612 */
00613         // Special case for flags.  
00614         // We don't predict touching our own flag
00615         if( cgs.gametype == GT_CTF || cgs.gametype == GT_CTY ) {
00616                 if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED &&
00617                         item->giTag == PW_REDFLAG)
00618                         return;
00619                 if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE &&
00620                         item->giTag == PW_BLUEFLAG)
00621                         return;
00622         }
00623 
00624         if (item->giType == IT_POWERUP &&
00625                 (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT || item->giTag == PW_FORCE_ENLIGHTENED_DARK))
00626         {
00627                 if (item->giTag == PW_FORCE_ENLIGHTENED_LIGHT)
00628                 {
00629                         if (cg.predictedPlayerState.fd.forceSide != FORCE_LIGHTSIDE)
00630                         {
00631                                 return;
00632                         }
00633                 }
00634                 else
00635                 {
00636                         if (cg.predictedPlayerState.fd.forceSide != FORCE_DARKSIDE)
00637                         {
00638                                 return;
00639                         }
00640                 }
00641         }
00642 
00643 
00644         // grab it
00645         BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.number , &cg.predictedPlayerState);
00646 
00647         // remove it from the frame so it won't be drawn
00648         cent->currentState.eFlags |= EF_NODRAW;
00649 
00650         // don't touch it again this prediction
00651         cent->miscTime = cg.time;
00652 
00653         // if its a weapon, give them some predicted ammo so the autoswitch will work
00654         if ( item->giType == IT_WEAPON ) {
00655                 cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
00656                 if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) {
00657                         cg.predictedPlayerState.ammo[ item->giTag ] = 1;
00658                 }
00659         }
00660 }
00661 
00662 
00663 /*
00664 =========================
00665 CG_TouchTriggerPrediction
00666 
00667 Predict push triggers and items
00668 =========================
00669 */
00670 static void CG_TouchTriggerPrediction( void ) {
00671         int                     i;
00672         trace_t         trace;
00673         entityState_t   *ent;
00674         clipHandle_t cmodel;
00675         centity_t       *cent;
00676         qboolean        spectator;
00677 
00678         // dead clients don't activate triggers
00679         if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
00680                 return;
00681         }
00682 
00683         spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR );
00684 
00685         if ( cg.predictedPlayerState.pm_type != PM_NORMAL && cg.predictedPlayerState.pm_type != PM_JETPACK && cg.predictedPlayerState.pm_type != PM_FLOAT && !spectator ) {
00686                 return;
00687         }
00688 
00689         for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) {
00690                 cent = cg_triggerEntities[ i ];
00691                 ent = &cent->currentState;
00692 
00693                 if ( ent->eType == ET_ITEM && !spectator ) {
00694                         CG_TouchItem( cent );
00695                         continue;
00696                 }
00697 
00698                 if ( ent->solid != SOLID_BMODEL ) {
00699                         continue;
00700                 }
00701 
00702                 cmodel = trap_CM_InlineModel( ent->modelindex );
00703                 if ( !cmodel ) {
00704                         continue;
00705                 }
00706 
00707                 trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin, 
00708                         cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
00709 
00710                 if ( !trace.startsolid ) {
00711                         continue;
00712                 }
00713 
00714                 if ( ent->eType == ET_TELEPORT_TRIGGER ) {
00715                         cg.hyperspace = qtrue;
00716                 } else if ( ent->eType == ET_PUSH_TRIGGER ) {
00717                         BG_TouchJumpPad( &cg.predictedPlayerState, ent );
00718                 }
00719         }
00720 
00721         // if we didn't touch a jump pad this pmove frame
00722         if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) {
00723                 cg.predictedPlayerState.jumppad_frame = 0;
00724                 cg.predictedPlayerState.jumppad_ent = 0;
00725         }
00726 }
00727 
00728 #if 0
00729 static ID_INLINE void CG_EntityStateToPlayerState( entityState_t *s, playerState_t *ps )
00730 {
00731         //currently unused vars commented out for speed.. only uncomment if you need them.
00732         ps->clientNum = s->number;
00733         VectorCopy( s->pos.trBase, ps->origin );
00734         VectorCopy( s->pos.trDelta, ps->velocity );
00735         ps->saberLockFrame = s->forceFrame;
00736         ps->legsAnim = s->legsAnim;
00737         ps->torsoAnim = s->torsoAnim;
00738         ps->legsFlip = s->legsFlip;
00739         ps->torsoFlip = s->torsoFlip;
00740         ps->clientNum = s->clientNum;
00741         ps->saberMove = s->saberMove;
00742 
00743         /*
00744         VectorCopy( s->apos.trBase, ps->viewangles );
00745 
00746         ps->fd.forceMindtrickTargetIndex = s->trickedentindex;
00747         ps->fd.forceMindtrickTargetIndex2 = s->trickedentindex2;
00748         ps->fd.forceMindtrickTargetIndex3 = s->trickedentindex3;
00749         ps->fd.forceMindtrickTargetIndex4 = s->trickedentindex4;
00750 
00751         ps->electrifyTime = s->emplacedOwner;
00752 
00753         ps->speed = s->speed;
00754 
00755         ps->genericEnemyIndex = s->genericenemyindex;
00756 
00757         ps->activeForcePass = s->activeForcePass;
00758 
00759         ps->movementDir = s->angles2[YAW];
00760 
00761         ps->eFlags = s->eFlags;
00762 
00763         ps->saberInFlight = s->saberInFlight;
00764         ps->saberEntityNum = s->saberEntityNum;
00765 
00766         ps->fd.forcePowersActive = s->forcePowersActive;
00767 
00768         if (s->bolt1)
00769         {
00770                 ps->duelInProgress = qtrue;
00771         }
00772         else
00773         {
00774                 ps->duelInProgress = qfalse;
00775         }
00776 
00777         if (s->bolt2)
00778         {
00779                 ps->dualBlade = qtrue;
00780         }
00781         else
00782         {
00783                 ps->dualBlade = qfalse;
00784         }
00785 
00786         ps->emplacedIndex = s->otherEntityNum2;
00787 
00788         ps->saberHolstered = s->saberHolstered; //reuse bool in entitystate for players differently
00789 
00790         ps->genericEnemyIndex = -1; //no real option for this
00791 
00792         //The client has no knowledge of health levels (except for the client entity)
00793         if (s->eFlags & EF_DEAD)
00794         {
00795                 ps->stats[STAT_HEALTH] = 0;
00796         }
00797         else
00798         {
00799                 ps->stats[STAT_HEALTH] = 100;
00800         }
00801 
00802         if ( ps->externalEvent ) {
00803                 s->event = ps->externalEvent;
00804                 s->eventParm = ps->externalEventParm;
00805         } else if ( ps->entityEventSequence < ps->eventSequence ) {
00806                 int             seq;
00807 
00808                 if ( ps->entityEventSequence < ps->eventSequence - MAX_PS_EVENTS) {
00809                         ps->entityEventSequence = ps->eventSequence - MAX_PS_EVENTS;
00810                 }
00811                 seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
00812                 s->event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
00813                 s->eventParm = ps->eventParms[ seq ];
00814                 ps->entityEventSequence++;
00815         }
00816 
00817         ps->weapon = s->weapon;
00818         ps->groundEntityNum = s->groundEntityNum;
00819 
00820         for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
00821                 if (s->powerups & (1 << i))
00822                 {
00823                         ps->powerups[i] = 30;
00824                 }
00825                 else
00826                 {
00827                         ps->powerups[i] = 0;
00828                 }
00829         }
00830 
00831         ps->loopSound = s->loopSound;
00832         ps->generic1 = s->generic1;
00833         */
00834 }
00835 #endif
00836 
00837 // This many playerState_t structures is painfully large. And we
00838 // don't need that many. So we just use a small pool of them.
00839 // PC gets to keep one per entity, just in case.
00840 #ifdef _XBOX
00841 
00842 struct psLinkedNode_t
00843 {
00844         playerState_t   ps;
00845         psLinkedNode_t  *next;
00846 };
00847 
00848 #define CG_SEND_PS_POOL_SIZE    64
00849 psLinkedNode_t cgSendPSPool[ CG_SEND_PS_POOL_SIZE ];
00850 psLinkedNode_t *cgSendPSFreeList;
00851 
00852 #else
00853 playerState_t cgSendPSPool[ MAX_GENTITIES ];
00854 #endif
00855 
00856 playerState_t *cgSendPS[MAX_GENTITIES];
00857 
00858 #ifdef _XBOX
00859 void AllocSendPlayerstate(int entNum)
00860 {
00861         if (cgSendPS[entNum])
00862         {
00863                 //Com_Printf( S_COLOR_RED "ERROR: Entity %d already has a playerstate!\n", entNum );
00864                 return;
00865         }
00866 
00867         if (!cgSendPSFreeList)
00868                 Com_Error( ERR_DROP, "ERROR: No free playerstates! Increase CG_SEND_PS_POOL_SIZE\n" );
00869 
00870         cgSendPS[entNum] = &cgSendPSFreeList->ps;
00871         cgSendPSFreeList = cgSendPSFreeList->next;
00872 }
00873 #endif
00874 
00875 //#define _PROFILE_ES_TO_PS
00876 
00877 #ifdef _PROFILE_ES_TO_PS
00878 int g_cgEStoPSTime = 0;
00879 #endif
00880 
00881 //Assign all the entity playerstate pointers to the corresponding one
00882 //so that we can access playerstate stuff in bg code (and then translate
00883 //it back to entitystate data)
00884 void CG_PmoveClientPointerUpdate()
00885 {
00886         int i;
00887 
00888         memset(&cgSendPSPool[0], 0, sizeof(cgSendPSPool));
00889 
00890         for ( i = 0 ; i < MAX_GENTITIES ; i++ )
00891         {
00892 #ifdef _XBOX
00893                 cgSendPS[i] = NULL;
00894 #else
00895                 cgSendPS[i] = &cgSendPSPool[i];
00896 #endif
00897 
00898                 // These will be invalid at this point on Xbox
00899                 cg_entities[i].playerState = cgSendPS[i];
00900         }
00901 
00902 #ifdef _XBOX
00903         for ( i = 0; i < CG_SEND_PS_POOL_SIZE - 1; i++ )
00904         {
00905                 cgSendPSPool[i].next = &cgSendPSPool[i+1];
00906         }
00907         
00908         // Last .next is already NULL from memset above
00909         cgSendPSFreeList = &cgSendPSPool[0];
00910 #endif
00911 
00912         //Set up bg entity data
00913         cg_pmove.baseEnt = (bgEntity_t *)cg_entities;
00914         cg_pmove.entSize = sizeof(centity_t);
00915 
00916         cg_pmove.ghoul2 = NULL;
00917 }
00918 
00919 //check if local client is on an eweb
00920 qboolean CG_UsingEWeb(void)
00921 {
00922         if (cg.predictedPlayerState.weapon == WP_EMPLACED_GUN && cg.predictedPlayerState.emplacedIndex &&
00923                 cg_entities[cg.predictedPlayerState.emplacedIndex].currentState.weapon == WP_NONE)
00924         {
00925                 return qtrue;
00926         }
00927 
00928         return qfalse;
00929 }
00930 
00931 /*
00932 =================
00933 CG_PredictPlayerState
00934 
00935 Generates cg.predictedPlayerState for the current cg.time
00936 cg.predictedPlayerState is guaranteed to be valid after exiting.
00937 
00938 For demo playback, this will be an interpolation between two valid
00939 playerState_t.
00940 
00941 For normal gameplay, it will be the result of predicted usercmd_t on
00942 top of the most recent playerState_t received from the server.
00943 
00944 Each new snapshot will usually have one or more new usercmd over the last,
00945 but we simulate all unacknowledged commands each time, not just the new ones.
00946 This means that on an internet connection, quite a few pmoves may be issued
00947 each frame.
00948 
00949 OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
00950 differs from the predicted one.  Would require saving all intermediate
00951 playerState_t during prediction.
00952 
00953 We detect prediction errors and allow them to be decayed off over several frames
00954 to ease the jerk.
00955 =================
00956 */
00957 extern void CG_Cube( vec3_t mins, vec3_t maxs, vec3_t color, float alpha );
00958 extern  vmCvar_t                cg_showVehBounds;
00959 pmove_t cg_vehPmove;
00960 qboolean cg_vehPmoveSet = qfalse;
00961 
00962 #pragma warning(disable : 4701) //local variable may be used without having been initialized
00963 void CG_PredictPlayerState( void ) {
00964         int                     cmdNum, current, i;
00965         playerState_t   oldPlayerState;
00966         playerState_t   oldVehicleState;
00967         qboolean        moved;
00968         usercmd_t       oldestCmd;
00969         usercmd_t       latestCmd;
00970         centity_t *pEnt;
00971         clientInfo_t *ci;
00972 
00973         cg.hyperspace = qfalse; // will be set if touching a trigger_teleport
00974 
00975         // if this is the first frame we must guarantee
00976         // predictedPlayerState is valid even if there is some
00977         // other error condition
00978         if ( !cg.validPPS ) {
00979                 cg.validPPS = qtrue;
00980                 cg.predictedPlayerState = cg.snap->ps;
00981                 if (CG_Piloting(cg.snap->ps.m_iVehicleNum))
00982                 {
00983                         cg.predictedVehicleState = cg.snap->vps;
00984                 }
00985         }
00986 
00987         // demo playback just copies the moves
00988         if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
00989                 CG_InterpolatePlayerState( qfalse );
00990                 if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
00991                 {
00992                         CG_InterpolateVehiclePlayerState(qfalse);
00993                 }
00994                 return;
00995         }
00996 
00997         // non-predicting local movement will grab the latest angles
00998         if ( cg_nopredict.integer || cg_synchronousClients.integer || CG_UsingEWeb() ) {
00999                 CG_InterpolatePlayerState( qtrue );
01000                 if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
01001                 {
01002                         CG_InterpolateVehiclePlayerState(qtrue);
01003                 }
01004                 return;
01005         }
01006 
01007         // prepare for pmove
01008         cg_pmove.ps = &cg.predictedPlayerState;
01009         cg_pmove.trace = CG_Trace;
01010         cg_pmove.pointcontents = CG_PointContents;
01011 
01012         pEnt = &cg_entities[cg.predictedPlayerState.clientNum];
01013         //rww - bgghoul2
01014         if (cg_pmove.ghoul2 != pEnt->ghoul2) //only update it if the g2 instance has changed
01015         {
01016                 if (cg.snap &&
01017                         pEnt->ghoul2 &&
01018                         !(cg.snap->ps.pm_flags & PMF_FOLLOW) &&
01019                         cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR)
01020                 {
01021                         cg_pmove.ghoul2 = pEnt->ghoul2;
01022                         cg_pmove.g2Bolts_LFoot = trap_G2API_AddBolt(pEnt->ghoul2, 0, "*l_leg_foot");
01023                         cg_pmove.g2Bolts_RFoot = trap_G2API_AddBolt(pEnt->ghoul2, 0, "*r_leg_foot");
01024                 }
01025                 else
01026                 {
01027                         cg_pmove.ghoul2 = NULL;
01028                 }
01029         }
01030 
01031         ci = &cgs.clientinfo[cg.predictedPlayerState.clientNum];
01032 
01033         //I'll just do this every frame in case the scale changes in realtime (don't need to update the g2 inst for that)
01034         VectorCopy(pEnt->modelScale, cg_pmove.modelScale);
01035         //rww end bgghoul2
01036 
01037         if ( cg_pmove.ps->pm_type == PM_DEAD ) {
01038                 cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
01039         }
01040         else {
01041                 cg_pmove.tracemask = MASK_PLAYERSOLID;
01042         }
01043         if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
01044                 cg_pmove.tracemask &= ~CONTENTS_BODY;   // spectators can fly through bodies
01045         }
01046         cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
01047 
01048         // save the state before the pmove so we can detect transitions
01049         oldPlayerState = cg.predictedPlayerState;
01050         if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
01051         {
01052                 oldVehicleState = cg.predictedVehicleState;
01053         }
01054 
01055         current = trap_GetCurrentCmdNumber();
01056 
01057         // if we don't have the commands right after the snapshot, we
01058         // can't accurately predict a current position, so just freeze at
01059         // the last good position we had
01060         cmdNum = current - CMD_BACKUP + 1;
01061         trap_GetUserCmd( cmdNum, &oldestCmd );
01062         if ( oldestCmd.serverTime > cg.snap->ps.commandTime 
01063                 && oldestCmd.serverTime < cg.time ) {   // special check for map_restart
01064                 if ( cg_showmiss.integer ) {
01065                         CG_Printf ("exceeded PACKET_BACKUP on commands\n");
01066                 }
01067                 return;
01068         }
01069 
01070         // get the latest command so we can know which commands are from previous map_restarts
01071         trap_GetUserCmd( current, &latestCmd );
01072 
01073         // get the most recent information we have, even if
01074         // the server time is beyond our current cg.time,
01075         // because predicted player positions are going to 
01076         // be ahead of everything else anyway
01077         if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
01078                 cg.nextSnap->ps.slopeRecalcTime = cg.predictedPlayerState.slopeRecalcTime; //this is the only value we want to maintain seperately on server/client
01079                 cg.predictedPlayerState = cg.nextSnap->ps;
01080                 if (CG_Piloting(cg.nextSnap->ps.m_iVehicleNum))
01081                 {
01082                         cg.predictedVehicleState = cg.nextSnap->vps;
01083                 }
01084                 cg.physicsTime = cg.nextSnap->serverTime;
01085         } else {
01086                 cg.snap->ps.slopeRecalcTime = cg.predictedPlayerState.slopeRecalcTime; //this is the only value we want to maintain seperately on server/client
01087                 cg.predictedPlayerState = cg.snap->ps;
01088                 if (CG_Piloting(cg.snap->ps.m_iVehicleNum))
01089                 {
01090                         cg.predictedVehicleState = cg.snap->vps;
01091                 }
01092                 cg.physicsTime = cg.snap->serverTime;
01093         }
01094 
01095         if ( pmove_msec.integer < 8 ) {
01096                 trap_Cvar_Set("pmove_msec", "8");
01097         }
01098         else if (pmove_msec.integer > 33) {
01099                 trap_Cvar_Set("pmove_msec", "33");
01100         }
01101 
01102         cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer;
01103         cg_pmove.pmove_msec = pmove_msec.integer;
01104 
01105         for ( i = 0 ; i < MAX_GENTITIES ; i++ )
01106         {
01107                 //Written this way for optimal speed, even though it doesn't look pretty.
01108                 //(we don't want to spend the time assigning pointers as it does take
01109                 //a small precious fraction of time and adds up in the loop.. so says
01110                 //the precision timer!)
01111 
01112                 if (cg_entities[i].currentState.eType == ET_PLAYER ||
01113                         cg_entities[i].currentState.eType == ET_NPC)
01114                 {
01115                         // Need a new playerState_t on Xbox
01116 #ifdef _XBOX
01117                         AllocSendPlayerstate(i);
01118 #endif
01119                         VectorCopy( cg_entities[i].currentState.pos.trBase, cgSendPS[i]->origin );
01120                         VectorCopy( cg_entities[i].currentState.pos.trDelta, cgSendPS[i]->velocity );
01121                         cgSendPS[i]->saberLockFrame = cg_entities[i].currentState.forceFrame;
01122                         cgSendPS[i]->legsAnim = cg_entities[i].currentState.legsAnim;
01123                         cgSendPS[i]->torsoAnim = cg_entities[i].currentState.torsoAnim;
01124                         cgSendPS[i]->legsFlip = cg_entities[i].currentState.legsFlip;
01125                         cgSendPS[i]->torsoFlip = cg_entities[i].currentState.torsoFlip;
01126                         cgSendPS[i]->clientNum = cg_entities[i].currentState.clientNum;
01127                         cgSendPS[i]->saberMove = cg_entities[i].currentState.saberMove;
01128                 }
01129         }
01130 
01131         if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
01132         {
01133                 cg_entities[cg.predictedPlayerState.clientNum].playerState = &cg.predictedPlayerState;
01134                 cg_entities[cg.predictedPlayerState.m_iVehicleNum].playerState = &cg.predictedVehicleState;
01135 
01136                 //use the player command time, because we are running with the player cmds (this is even the case
01137                 //on the server)
01138                 cg.predictedVehicleState.commandTime = cg.predictedPlayerState.commandTime;
01139         }
01140 
01141         // run cmds
01142         moved = qfalse;
01143         for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) {
01144                 // get the command
01145                 trap_GetUserCmd( cmdNum, &cg_pmove.cmd );
01146 
01147                 if ( cg_pmove.pmove_fixed ) {
01148                         PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd );
01149                 }
01150 
01151                 // don't do anything if the time is before the snapshot player time
01152                 if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime )
01153                 {
01154                         continue;
01155                 }
01156 
01157                 // don't do anything if the command was from a previous map_restart
01158                 if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) {
01159                         continue;
01160                 }
01161 
01162                 // check for a prediction error from last frame
01163                 // on a lan, this will often be the exact value
01164                 // from the snapshot, but on a wan we will have
01165                 // to predict several commands to get to the point
01166                 // we want to compare
01167                 if ( CG_Piloting(oldPlayerState.m_iVehicleNum) &&
01168                         cg.predictedVehicleState.commandTime == oldVehicleState.commandTime )
01169                 {
01170                         vec3_t  delta;
01171                         float   len;
01172 
01173                         if ( cg.thisFrameTeleport ) {
01174                                 // a teleport will not cause an error decay
01175                                 VectorClear( cg.predictedError );
01176                                 if ( cg_showVehMiss.integer ) {
01177                                         CG_Printf( "VEH PredictionTeleport\n" );
01178                                 }
01179                                 cg.thisFrameTeleport = qfalse;
01180                         } else {
01181                                 vec3_t  adjusted;
01182                                 CG_AdjustPositionForMover( cg.predictedVehicleState.origin, 
01183                                         cg.predictedVehicleState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );
01184 
01185                                 if ( cg_showVehMiss.integer ) {
01186                                         if (!VectorCompare( oldVehicleState.origin, adjusted )) {
01187                                                 CG_Printf("VEH prediction error\n");
01188                                         }
01189                                 }
01190                                 VectorSubtract( oldVehicleState.origin, adjusted, delta );
01191                                 len = VectorLength( delta );
01192                                 if ( len > 0.1 ) {
01193                                         if ( cg_showVehMiss.integer ) {
01194                                                 CG_Printf("VEH Prediction miss: %f\n", len);
01195                                         }
01196                                         if ( cg_errorDecay.integer ) {
01197                                                 int             t;
01198                                                 float   f;
01199 
01200                                                 t = cg.time - cg.predictedErrorTime;
01201                                                 f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
01202                                                 if ( f < 0 ) {
01203                                                         f = 0;
01204                                                 }
01205                                                 if ( f > 0 && cg_showVehMiss.integer ) {
01206                                                         CG_Printf("VEH Double prediction decay: %f\n", f);
01207                                                 }
01208                                                 VectorScale( cg.predictedError, f, cg.predictedError );
01209                                         } else {
01210                                                 VectorClear( cg.predictedError );
01211                                         }
01212                                         VectorAdd( delta, cg.predictedError, cg.predictedError );
01213                                         cg.predictedErrorTime = cg.oldTime;
01214                                 }
01215                                 //
01216                                 if ( cg_showVehMiss.integer ) {
01217                                         if (!VectorCompare( oldVehicleState.vehOrientation, cg.predictedVehicleState.vehOrientation )) {
01218                                                 CG_Printf("VEH orient prediction error\n");
01219                                                 CG_Printf("VEH pitch prediction miss: %f\n", AngleSubtract( oldVehicleState.vehOrientation[0], cg.predictedVehicleState.vehOrientation[0] ) );
01220                                                 CG_Printf("VEH yaw prediction miss: %f\n", AngleSubtract( oldVehicleState.vehOrientation[1], cg.predictedVehicleState.vehOrientation[1] ) );
01221                                                 CG_Printf("VEH roll prediction miss: %f\n", AngleSubtract( oldVehicleState.vehOrientation[2], cg.predictedVehicleState.vehOrientation[2] ) );
01222                                         }
01223                                 }
01224                         }
01225                 }
01226                 else if ( !oldPlayerState.m_iVehicleNum && //don't do pred err on ps while riding veh
01227                         cg.predictedPlayerState.commandTime == oldPlayerState.commandTime )
01228                 {
01229                         vec3_t  delta;
01230                         float   len;
01231 
01232                         if ( cg.thisFrameTeleport ) {
01233                                 // a teleport will not cause an error decay
01234                                 VectorClear( cg.predictedError );
01235                                 if ( cg_showmiss.integer ) {
01236                                         CG_Printf( "PredictionTeleport\n" );
01237                                 }
01238                                 cg.thisFrameTeleport = qfalse;
01239                         } else {
01240                                 vec3_t  adjusted;
01241                                 CG_AdjustPositionForMover( cg.predictedPlayerState.origin, 
01242                                         cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );
01243 
01244                                 if ( cg_showmiss.integer ) {
01245                                         if (!VectorCompare( oldPlayerState.origin, adjusted )) {
01246                                                 CG_Printf("prediction error\n");
01247                                         }
01248                                 }
01249                                 VectorSubtract( oldPlayerState.origin, adjusted, delta );
01250                                 len = VectorLength( delta );
01251                                 if ( len > 0.1 ) {
01252                                         if ( cg_showmiss.integer ) {
01253                                                 CG_Printf("Prediction miss: %f\n", len);
01254                                         }
01255                                         if ( cg_errorDecay.integer ) {
01256                                                 int             t;
01257                                                 float   f;
01258 
01259                                                 t = cg.time - cg.predictedErrorTime;
01260                                                 f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
01261                                                 if ( f < 0 ) {
01262                                                         f = 0;
01263                                                 }
01264                                                 if ( f > 0 && cg_showmiss.integer ) {
01265                                                         CG_Printf("Double prediction decay: %f\n", f);
01266                                                 }
01267                                                 VectorScale( cg.predictedError, f, cg.predictedError );
01268                                         } else {
01269                                                 VectorClear( cg.predictedError );
01270                                         }
01271                                         VectorAdd( delta, cg.predictedError, cg.predictedError );
01272                                         cg.predictedErrorTime = cg.oldTime;
01273                                 }
01274                         }
01275                 }
01276 
01277                 if ( cg_pmove.pmove_fixed ) {
01278                         cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
01279                 }
01280 
01281                 cg_pmove.animations = bgAllAnims[pEnt->localAnimIndex].anims;
01282                 cg_pmove.gametype = cgs.gametype;
01283 
01284                 cg_pmove.debugMelee = cgs.debugMelee;
01285                 cg_pmove.stepSlideFix = cgs.stepSlideFix;
01286                 cg_pmove.noSpecMove = cgs.noSpecMove;
01287 
01288                 cg_pmove.nonHumanoid = (pEnt->localAnimIndex > 0);
01289 
01290                 if (cg.snap && cg.snap->ps.saberLockTime > cg.time)
01291                 {
01292                         centity_t *blockOpp = &cg_entities[cg.snap->ps.saberLockEnemy];
01293 
01294                         if (blockOpp)
01295                         {
01296                                 vec3_t lockDir, lockAng;
01297 
01298                                 VectorSubtract( blockOpp->lerpOrigin, cg.snap->ps.origin, lockDir );
01299                                 vectoangles(lockDir, lockAng);
01300 
01301                                 VectorCopy(lockAng, cg_pmove.ps->viewangles);
01302                         }
01303                 }
01304 
01305                 //THIS is pretty much bad, but...
01306                 cg_pmove.ps->fd.saberAnimLevelBase = cg_pmove.ps->fd.saberAnimLevel;
01307                 if ( cg_pmove.ps->saberHolstered == 1 )
01308                 {
01309                         if ( ci->saber[0].numBlades > 0 )
01310                         {
01311                                 cg_pmove.ps->fd.saberAnimLevelBase = SS_STAFF;
01312                         }
01313                         else if ( ci->saber[1].model[0] )
01314                         {
01315                                 cg_pmove.ps->fd.saberAnimLevelBase = SS_DUAL;
01316                         }
01317                 }
01318         
01319                 Pmove (&cg_pmove);
01320 
01321                 if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum) &&
01322                         cg.predictedPlayerState.pm_type != PM_INTERMISSION)
01323                 { //we're riding a vehicle, let's predict it
01324                         centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
01325                         int x, zd, zu;
01326 
01327                         if (veh->m_pVehicle)
01328                         { //make sure pointer is set up to go to our predicted state
01329                                 veh->m_pVehicle->m_vOrientation = &cg.predictedVehicleState.vehOrientation[0];
01330 
01331                                 //keep this updated based on what the playerstate says
01332                                 veh->m_pVehicle->m_iRemovedSurfaces = cg.predictedVehicleState.vehSurfaces;
01333 
01334                                 trap_GetUserCmd( cmdNum, &veh->m_pVehicle->m_ucmd );
01335 
01336                                 if ( veh->m_pVehicle->m_ucmd.buttons & BUTTON_TALK )
01337                                 { //forced input if "chat bubble" is up
01338                                         veh->m_pVehicle->m_ucmd.buttons = BUTTON_TALK;
01339                                         veh->m_pVehicle->m_ucmd.forwardmove = 0;
01340                                         veh->m_pVehicle->m_ucmd.rightmove = 0;
01341                                         veh->m_pVehicle->m_ucmd.upmove = 0;
01342                                 }
01343                                 cg_vehPmove.ps = &cg.predictedVehicleState;
01344                                 cg_vehPmove.animations = bgAllAnims[veh->localAnimIndex].anims;
01345 
01346                                 memcpy(&cg_vehPmove.cmd, &veh->m_pVehicle->m_ucmd, sizeof(usercmd_t));
01347                                 /*
01348                                 cg_vehPmove.cmd.rightmove = 0; //no vehicle can move right/left
01349                                 cg_vehPmove.cmd.upmove = 0; //no vehicle can move up/down
01350                                 */
01351 
01352                                 cg_vehPmove.gametype = cgs.gametype;
01353                                 cg_vehPmove.ghoul2 = veh->ghoul2;
01354 
01355                                 cg_vehPmove.nonHumanoid = (veh->localAnimIndex > 0);
01356 
01357                                 /*
01358                                 x = (veh->currentState.solid & 255);
01359                                 zd = (veh->currentState.solid & 255);
01360                                 zu = (veh->currentState.solid & 255) - 32;
01361 
01362                                 cg_vehPmove.mins[0] = cg_vehPmove.mins[1] = -x;
01363                                 cg_vehPmove.maxs[0] = cg_vehPmove.maxs[1] = x;
01364                                 cg_vehPmove.mins[2] = -zd;
01365                                 cg_vehPmove.maxs[2] = zu;
01366                                 */
01367                                 //I think this was actually wrong.. just copy-pasted from id code. Oh well.
01368                                 x = (veh->currentState.solid)&255;
01369                                 zd = (veh->currentState.solid>>8)&255;
01370                                 zu = (veh->currentState.solid>>15)&255;
01371                                 
01372                                 zu -= 32; //I don't quite get the reason for this.
01373                                 zd = -zd;
01374 
01375                                 //z/y must be symmetrical (blah)
01376                                 cg_vehPmove.mins[0] = cg_vehPmove.mins[1] = -x;
01377                                 cg_vehPmove.maxs[0] = cg_vehPmove.maxs[1] = x;
01378                                 cg_vehPmove.mins[2] = zd;
01379                                 cg_vehPmove.maxs[2] = zu;
01380 
01381                                 VectorCopy(veh->modelScale, cg_vehPmove.modelScale);
01382 
01383                                 if (!cg_vehPmoveSet)
01384                                 { //do all the one-time things
01385                                         cg_vehPmove.trace = CG_Trace;
01386                                         cg_vehPmove.pointcontents = CG_PointContents;
01387                                         cg_vehPmove.tracemask = MASK_PLAYERSOLID;
01388                                         cg_vehPmove.debugLevel = 0;
01389                                         cg_vehPmove.g2Bolts_LFoot = -1;
01390                                         cg_vehPmove.g2Bolts_RFoot = -1;
01391 
01392                                         cg_vehPmove.baseEnt = (bgEntity_t *)cg_entities;
01393                                         cg_vehPmove.entSize = sizeof(centity_t);
01394 
01395                                         cg_vehPmoveSet = qtrue;
01396                                 }
01397                                 
01398                                 cg_vehPmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
01399                                 cg_vehPmove.pmove_fixed = pmove_fixed.integer;
01400                                 cg_vehPmove.pmove_msec = pmove_msec.integer;
01401 
01402                                 cg_entities[cg.predictedPlayerState.clientNum].playerState = &cg.predictedPlayerState;
01403                                 veh->playerState = &cg.predictedVehicleState;
01404 
01405                                 //update boarding value sent from server. boarding is not predicted, but no big deal
01406                                 veh->m_pVehicle->m_iBoarding = cg.predictedVehicleState.vehBoarding;
01407 
01408                                 Pmove(&cg_vehPmove);
01409                                 /*
01410                                 if ( !cg_paused.integer )
01411                                 {
01412                                         Com_Printf( "%d - PITCH change %4.2f\n", cg.time, AngleSubtract(veh->m_pVehicle->m_vOrientation[0],veh->m_pVehicle->m_vPrevOrientation[0]) );
01413                                 }
01414                                 */
01415                                 if ( cg_showVehBounds.integer )
01416                                 {
01417                                         vec3_t NPCDEBUG_RED = {1.0, 0.0, 0.0};
01418                                         vec3_t absmin, absmax;
01419                                         VectorAdd( cg_vehPmove.ps->origin, cg_vehPmove.mins, absmin );
01420                                         VectorAdd( cg_vehPmove.ps->origin, cg_vehPmove.maxs, absmax );
01421                                         CG_Cube( absmin, absmax, NPCDEBUG_RED, 0.25 );
01422                                 }
01423                         }
01424                 }
01425 
01426                 moved = qtrue;
01427 
01428                 // add push trigger movement effects
01429                 CG_TouchTriggerPrediction();
01430 
01431                 // check for predictable events that changed from previous predictions
01432                 //CG_CheckChangedPredictableEvents(&cg.predictedPlayerState);
01433         }
01434 
01435         if ( cg_showmiss.integer > 1 ) {
01436                 CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time );
01437         }
01438 
01439         if ( !moved ) {
01440                 if ( cg_showmiss.integer ) {
01441                         CG_Printf( "not moved\n" );
01442                 }
01443                 goto revertES;
01444         }
01445 
01446         if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
01447         {
01448                 CG_AdjustPositionForMover( cg.predictedVehicleState.origin, 
01449                         cg.predictedVehicleState.groundEntityNum, 
01450                         cg.physicsTime, cg.time, cg.predictedVehicleState.origin );
01451         }
01452         else
01453         {
01454                 // adjust for the movement of the groundentity
01455                 CG_AdjustPositionForMover( cg.predictedPlayerState.origin, 
01456                         cg.predictedPlayerState.groundEntityNum, 
01457                         cg.physicsTime, cg.time, cg.predictedPlayerState.origin );
01458         }
01459 
01460         if ( cg_showmiss.integer ) {
01461                 if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) {
01462                         CG_Printf("WARNING: dropped event\n");
01463                 }
01464         }
01465 
01466         // fire events and other transition triggered things
01467         CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState );
01468 
01469         if ( cg_showmiss.integer ) {
01470                 if (cg.eventSequence > cg.predictedPlayerState.eventSequence) {
01471                         CG_Printf("WARNING: double event\n");
01472                         cg.eventSequence = cg.predictedPlayerState.eventSequence;
01473                 }
01474         }
01475 
01476         if (cg.predictedPlayerState.m_iVehicleNum &&
01477                 !CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
01478         { //a passenger on this vehicle, bolt them in
01479                 centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
01480                 VectorCopy(veh->lerpAngles, cg.predictedPlayerState.viewangles);
01481                 VectorCopy(veh->lerpOrigin, cg.predictedPlayerState.origin);
01482         }
01483 
01484 revertES:
01485         if (CG_Piloting(cg.predictedPlayerState.m_iVehicleNum))
01486         {
01487                 centity_t *veh = &cg_entities[cg.predictedPlayerState.m_iVehicleNum];
01488 
01489                 if (veh->m_pVehicle)
01490                 {
01491                         //switch ptr back for this ent in case we stop riding it
01492                         veh->m_pVehicle->m_vOrientation = &cgSendPS[veh->currentState.number]->vehOrientation[0];
01493                 }
01494 
01495                 cg_entities[cg.predictedPlayerState.clientNum].playerState = cgSendPS[cg.predictedPlayerState.clientNum];
01496                 veh->playerState = cgSendPS[veh->currentState.number];
01497         }
01498 
01499         //copy some stuff back into the entstates to help actually "predict" them if applicable
01500         for ( i = 0 ; i < MAX_GENTITIES ; i++ )
01501         {
01502                 if (cg_entities[i].currentState.eType == ET_PLAYER ||
01503                         cg_entities[i].currentState.eType == ET_NPC)
01504                 {
01505                         cg_entities[i].currentState.torsoAnim = cgSendPS[i]->torsoAnim;
01506                         cg_entities[i].currentState.legsAnim = cgSendPS[i]->legsAnim;
01507                         cg_entities[i].currentState.forceFrame = cgSendPS[i]->saberLockFrame;
01508                         cg_entities[i].currentState.saberMove = cgSendPS[i]->saberMove;
01509                 }
01510         }
01511 }
01512 #pragma warning(default : 4701) //local variable may be used without having been initialized