00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "cg_local.h"
00029
00030 static pmove_t cg_pmove;
00031
00032 static int cg_numSolidEntities;
00033 static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
00034 static int cg_numTriggerEntities;
00035 static centity_t *cg_triggerEntities[MAX_ENTITIES_IN_SNAPSHOT];
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046 void CG_BuildSolidList( void ) {
00047 int i;
00048 centity_t *cent;
00049 snapshot_t *snap;
00050 entityState_t *ent;
00051
00052 cg_numSolidEntities = 0;
00053 cg_numTriggerEntities = 0;
00054
00055 if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
00056 snap = cg.nextSnap;
00057 } else {
00058 snap = cg.snap;
00059 }
00060
00061 for ( i = 0 ; i < snap->numEntities ; i++ ) {
00062 cent = &cg_entities[ snap->entities[ i ].number ];
00063 ent = ¢->currentState;
00064
00065 if ( ent->eType == ET_ITEM || ent->eType == ET_PUSH_TRIGGER || ent->eType == ET_TELEPORT_TRIGGER ) {
00066 cg_triggerEntities[cg_numTriggerEntities] = cent;
00067 cg_numTriggerEntities++;
00068 continue;
00069 }
00070
00071 if ( cent->nextState.solid ) {
00072 cg_solidEntities[cg_numSolidEntities] = cent;
00073 cg_numSolidEntities++;
00074 continue;
00075 }
00076 }
00077 }
00078
00079
00080
00081
00082
00083
00084
00085 static void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
00086 int skipNumber, int mask, trace_t *tr ) {
00087 int i, x, zd, zu;
00088 trace_t trace;
00089 entityState_t *ent;
00090 clipHandle_t cmodel;
00091 vec3_t bmins, bmaxs;
00092 vec3_t origin, angles;
00093 centity_t *cent;
00094
00095 for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
00096 cent = cg_solidEntities[ i ];
00097 ent = ¢->currentState;
00098
00099 if ( ent->number == skipNumber ) {
00100 continue;
00101 }
00102
00103 if ( ent->solid == SOLID_BMODEL ) {
00104
00105 cmodel = trap_CM_InlineModel( ent->modelindex );
00106 VectorCopy( cent->lerpAngles, angles );
00107 BG_EvaluateTrajectory( ¢->currentState.pos, cg.physicsTime, origin );
00108 } else {
00109
00110 x = (ent->solid & 255);
00111 zd = ((ent->solid>>8) & 255);
00112 zu = ((ent->solid>>16) & 255) - 32;
00113
00114 bmins[0] = bmins[1] = -x;
00115 bmaxs[0] = bmaxs[1] = x;
00116 bmins[2] = -zd;
00117 bmaxs[2] = zu;
00118
00119 cmodel = trap_CM_TempBoxModel( bmins, bmaxs );
00120 VectorCopy( vec3_origin, angles );
00121 VectorCopy( cent->lerpOrigin, origin );
00122 }
00123
00124
00125 trap_CM_TransformedBoxTrace ( &trace, start, end,
00126 mins, maxs, cmodel, mask, origin, angles);
00127
00128 if (trace.allsolid || trace.fraction < tr->fraction) {
00129 trace.entityNum = ent->number;
00130 *tr = trace;
00131 } else if (trace.startsolid) {
00132 tr->startsolid = qtrue;
00133 }
00134 if ( tr->allsolid ) {
00135 return;
00136 }
00137 }
00138 }
00139
00140
00141
00142
00143
00144
00145 void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
00146 int skipNumber, int mask ) {
00147 trace_t t;
00148
00149 trap_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
00150 t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
00151
00152 CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t);
00153
00154 *result = t;
00155 }
00156
00157
00158
00159
00160
00161
00162 int CG_PointContents( const vec3_t point, int passEntityNum ) {
00163 int i;
00164 entityState_t *ent;
00165 centity_t *cent;
00166 clipHandle_t cmodel;
00167 int contents;
00168
00169 contents = trap_CM_PointContents (point, 0);
00170
00171 for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
00172 cent = cg_solidEntities[ i ];
00173
00174 ent = ¢->currentState;
00175
00176 if ( ent->number == passEntityNum ) {
00177 continue;
00178 }
00179
00180 if (ent->solid != SOLID_BMODEL) {
00181 continue;
00182 }
00183
00184 cmodel = trap_CM_InlineModel( ent->modelindex );
00185 if ( !cmodel ) {
00186 continue;
00187 }
00188
00189 contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
00190 }
00191
00192 return contents;
00193 }
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204 static void CG_InterpolatePlayerState( qboolean grabAngles ) {
00205 float f;
00206 int i;
00207 playerState_t *out;
00208 snapshot_t *prev, *next;
00209
00210 out = &cg.predictedPlayerState;
00211 prev = cg.snap;
00212 next = cg.nextSnap;
00213
00214 *out = cg.snap->ps;
00215
00216
00217 if ( grabAngles ) {
00218 usercmd_t cmd;
00219 int cmdNum;
00220
00221 cmdNum = trap_GetCurrentCmdNumber();
00222 trap_GetUserCmd( cmdNum, &cmd );
00223
00224 PM_UpdateViewAngles( out, &cmd );
00225 }
00226
00227
00228 if ( cg.nextFrameTeleport ) {
00229 return;
00230 }
00231
00232 if ( !next || next->serverTime <= prev->serverTime ) {
00233 return;
00234 }
00235
00236 f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
00237
00238 i = next->ps.bobCycle;
00239 if ( i < prev->ps.bobCycle ) {
00240 i += 256;
00241 }
00242 out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
00243
00244 for ( i = 0 ; i < 3 ; i++ ) {
00245 out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
00246 if ( !grabAngles ) {
00247 out->viewangles[i] = LerpAngle(
00248 prev->ps.viewangles[i], next->ps.viewangles[i], f );
00249 }
00250 out->velocity[i] = prev->ps.velocity[i] +
00251 f * (next->ps.velocity[i] - prev->ps.velocity[i] );
00252 }
00253
00254 }
00255
00256
00257
00258
00259
00260
00261 static void CG_TouchItem( centity_t *cent ) {
00262 gitem_t *item;
00263
00264 if ( !cg_predictItems.integer ) {
00265 return;
00266 }
00267 if ( !BG_PlayerTouchesItem( &cg.predictedPlayerState, ¢->currentState, cg.time ) ) {
00268 return;
00269 }
00270
00271
00272 if ( cent->miscTime == cg.time ) {
00273 return;
00274 }
00275
00276 if ( !BG_CanItemBeGrabbed( cgs.gametype, ¢->currentState, &cg.predictedPlayerState ) ) {
00277 return;
00278 }
00279
00280 item = &bg_itemlist[ cent->currentState.modelindex ];
00281
00282
00283
00284 #ifdef MISSIONPACK
00285 if( cgs.gametype == GT_1FCTF ) {
00286 if( item->giTag != PW_NEUTRALFLAG ) {
00287 return;
00288 }
00289 }
00290 if( cgs.gametype == GT_CTF || cgs.gametype == GT_HARVESTER ) {
00291 #else
00292 if( cgs.gametype == GT_CTF ) {
00293 #endif
00294 if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_RED &&
00295 item->giTag == PW_REDFLAG)
00296 return;
00297 if (cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_BLUE &&
00298 item->giTag == PW_BLUEFLAG)
00299 return;
00300 }
00301
00302
00303 BG_AddPredictableEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predictedPlayerState);
00304
00305
00306 cent->currentState.eFlags |= EF_NODRAW;
00307
00308
00309 cent->miscTime = cg.time;
00310
00311
00312 if ( item->giType == IT_WEAPON ) {
00313 cg.predictedPlayerState.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
00314 if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) {
00315 cg.predictedPlayerState.ammo[ item->giTag ] = 1;
00316 }
00317 }
00318 }
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328 static void CG_TouchTriggerPrediction( void ) {
00329 int i;
00330 trace_t trace;
00331 entityState_t *ent;
00332 clipHandle_t cmodel;
00333 centity_t *cent;
00334 qboolean spectator;
00335
00336
00337 if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
00338 return;
00339 }
00340
00341 spectator = ( cg.predictedPlayerState.pm_type == PM_SPECTATOR );
00342
00343 if ( cg.predictedPlayerState.pm_type != PM_NORMAL && !spectator ) {
00344 return;
00345 }
00346
00347 for ( i = 0 ; i < cg_numTriggerEntities ; i++ ) {
00348 cent = cg_triggerEntities[ i ];
00349 ent = ¢->currentState;
00350
00351 if ( ent->eType == ET_ITEM && !spectator ) {
00352 CG_TouchItem( cent );
00353 continue;
00354 }
00355
00356 if ( ent->solid != SOLID_BMODEL ) {
00357 continue;
00358 }
00359
00360 cmodel = trap_CM_InlineModel( ent->modelindex );
00361 if ( !cmodel ) {
00362 continue;
00363 }
00364
00365 trap_CM_BoxTrace( &trace, cg.predictedPlayerState.origin, cg.predictedPlayerState.origin,
00366 cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
00367
00368 if ( !trace.startsolid ) {
00369 continue;
00370 }
00371
00372 if ( ent->eType == ET_TELEPORT_TRIGGER ) {
00373 cg.hyperspace = qtrue;
00374 } else if ( ent->eType == ET_PUSH_TRIGGER ) {
00375 BG_TouchJumpPad( &cg.predictedPlayerState, ent );
00376 }
00377 }
00378
00379
00380 if ( cg.predictedPlayerState.jumppad_frame != cg.predictedPlayerState.pmove_framecount ) {
00381 cg.predictedPlayerState.jumppad_frame = 0;
00382 cg.predictedPlayerState.jumppad_ent = 0;
00383 }
00384 }
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414 void CG_PredictPlayerState( void ) {
00415 int cmdNum, current;
00416 playerState_t oldPlayerState;
00417 qboolean moved;
00418 usercmd_t oldestCmd;
00419 usercmd_t latestCmd;
00420
00421 cg.hyperspace = qfalse;
00422
00423
00424
00425
00426 if ( !cg.validPPS ) {
00427 cg.validPPS = qtrue;
00428 cg.predictedPlayerState = cg.snap->ps;
00429 }
00430
00431
00432
00433 if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
00434 CG_InterpolatePlayerState( qfalse );
00435 return;
00436 }
00437
00438
00439 if ( cg_nopredict.integer || cg_synchronousClients.integer ) {
00440 CG_InterpolatePlayerState( qtrue );
00441 return;
00442 }
00443
00444
00445 cg_pmove.ps = &cg.predictedPlayerState;
00446 cg_pmove.trace = CG_Trace;
00447 cg_pmove.pointcontents = CG_PointContents;
00448 if ( cg_pmove.ps->pm_type == PM_DEAD ) {
00449 cg_pmove.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
00450 }
00451 else {
00452 cg_pmove.tracemask = MASK_PLAYERSOLID;
00453 }
00454 if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR ) {
00455 cg_pmove.tracemask &= ~CONTENTS_BODY;
00456 }
00457 cg_pmove.noFootsteps = ( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
00458
00459
00460 oldPlayerState = cg.predictedPlayerState;
00461
00462 current = trap_GetCurrentCmdNumber();
00463
00464
00465
00466
00467 cmdNum = current - CMD_BACKUP + 1;
00468 trap_GetUserCmd( cmdNum, &oldestCmd );
00469 if ( oldestCmd.serverTime > cg.snap->ps.commandTime
00470 && oldestCmd.serverTime < cg.time ) {
00471 if ( cg_showmiss.integer ) {
00472 CG_Printf ("exceeded PACKET_BACKUP on commands\n");
00473 }
00474 return;
00475 }
00476
00477
00478 trap_GetUserCmd( current, &latestCmd );
00479
00480
00481
00482
00483
00484 if ( cg.nextSnap && !cg.nextFrameTeleport && !cg.thisFrameTeleport ) {
00485 cg.predictedPlayerState = cg.nextSnap->ps;
00486 cg.physicsTime = cg.nextSnap->serverTime;
00487 } else {
00488 cg.predictedPlayerState = cg.snap->ps;
00489 cg.physicsTime = cg.snap->serverTime;
00490 }
00491
00492 if ( pmove_msec.integer < 8 ) {
00493 trap_Cvar_Set("pmove_msec", "8");
00494 }
00495 else if (pmove_msec.integer > 33) {
00496 trap_Cvar_Set("pmove_msec", "33");
00497 }
00498
00499 cg_pmove.pmove_fixed = pmove_fixed.integer;
00500 cg_pmove.pmove_msec = pmove_msec.integer;
00501
00502
00503 moved = qfalse;
00504 for ( cmdNum = current - CMD_BACKUP + 1 ; cmdNum <= current ; cmdNum++ ) {
00505
00506 trap_GetUserCmd( cmdNum, &cg_pmove.cmd );
00507
00508 if ( cg_pmove.pmove_fixed ) {
00509 PM_UpdateViewAngles( cg_pmove.ps, &cg_pmove.cmd );
00510 }
00511
00512
00513 if ( cg_pmove.cmd.serverTime <= cg.predictedPlayerState.commandTime ) {
00514 continue;
00515 }
00516
00517
00518 if ( cg_pmove.cmd.serverTime > latestCmd.serverTime ) {
00519 continue;
00520 }
00521
00522
00523
00524
00525
00526
00527 if ( cg.predictedPlayerState.commandTime == oldPlayerState.commandTime ) {
00528 vec3_t delta;
00529 float len;
00530
00531 if ( cg.thisFrameTeleport ) {
00532
00533 VectorClear( cg.predictedError );
00534 if ( cg_showmiss.integer ) {
00535 CG_Printf( "PredictionTeleport\n" );
00536 }
00537 cg.thisFrameTeleport = qfalse;
00538 } else {
00539 vec3_t adjusted;
00540 CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
00541 cg.predictedPlayerState.groundEntityNum, cg.physicsTime, cg.oldTime, adjusted );
00542
00543 if ( cg_showmiss.integer ) {
00544 if (!VectorCompare( oldPlayerState.origin, adjusted )) {
00545 CG_Printf("prediction error\n");
00546 }
00547 }
00548 VectorSubtract( oldPlayerState.origin, adjusted, delta );
00549 len = VectorLength( delta );
00550 if ( len > 0.1 ) {
00551 if ( cg_showmiss.integer ) {
00552 CG_Printf("Prediction miss: %f\n", len);
00553 }
00554 if ( cg_errorDecay.integer ) {
00555 int t;
00556 float f;
00557
00558 t = cg.time - cg.predictedErrorTime;
00559 f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
00560 if ( f < 0 ) {
00561 f = 0;
00562 }
00563 if ( f > 0 && cg_showmiss.integer ) {
00564 CG_Printf("Double prediction decay: %f\n", f);
00565 }
00566 VectorScale( cg.predictedError, f, cg.predictedError );
00567 } else {
00568 VectorClear( cg.predictedError );
00569 }
00570 VectorAdd( delta, cg.predictedError, cg.predictedError );
00571 cg.predictedErrorTime = cg.oldTime;
00572 }
00573 }
00574 }
00575
00576
00577
00578 cg_pmove.gauntletHit = qfalse;
00579
00580 if ( cg_pmove.pmove_fixed ) {
00581 cg_pmove.cmd.serverTime = ((cg_pmove.cmd.serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
00582 }
00583
00584 Pmove (&cg_pmove);
00585
00586 moved = qtrue;
00587
00588
00589 CG_TouchTriggerPrediction();
00590
00591
00592
00593 }
00594
00595 if ( cg_showmiss.integer > 1 ) {
00596 CG_Printf( "[%i : %i] ", cg_pmove.cmd.serverTime, cg.time );
00597 }
00598
00599 if ( !moved ) {
00600 if ( cg_showmiss.integer ) {
00601 CG_Printf( "not moved\n" );
00602 }
00603 return;
00604 }
00605
00606
00607 CG_AdjustPositionForMover( cg.predictedPlayerState.origin,
00608 cg.predictedPlayerState.groundEntityNum,
00609 cg.physicsTime, cg.time, cg.predictedPlayerState.origin );
00610
00611 if ( cg_showmiss.integer ) {
00612 if (cg.predictedPlayerState.eventSequence > oldPlayerState.eventSequence + MAX_PS_EVENTS) {
00613 CG_Printf("WARNING: dropped event\n");
00614 }
00615 }
00616
00617
00618 CG_TransitionPlayerState( &cg.predictedPlayerState, &oldPlayerState );
00619
00620 if ( cg_showmiss.integer ) {
00621 if (cg.eventSequence > cg.predictedPlayerState.eventSequence) {
00622 CG_Printf("WARNING: double event\n");
00623 cg.eventSequence = cg.predictedPlayerState.eventSequence;
00624 }
00625 }
00626 }
00627
00628