From 0a620bae55008e91d7de3d4e2dd0f697f8e649ca Mon Sep 17 00:00:00 2001 From: "Tony J. White" Date: Sat, 9 Dec 2006 19:23:19 +0000 Subject: * (bug 2903) Added g_unlagged. This is based on Neil Toronto's unlagged project. Thanks to kevlarman and WolfWings for help with this implementation for Tremulous. --- src/game/g_active.c | 303 ++++++++++++++++++++++++++++++++++++++++++++++++- src/game/g_buildable.c | 2 + src/game/g_client.c | 1 + src/game/g_combat.c | 10 +- src/game/g_local.h | 24 ++++ src/game/g_main.c | 29 ++++- src/game/g_misc.c | 1 + src/game/g_weapon.c | 34 +++++- 8 files changed, 391 insertions(+), 13 deletions(-) diff --git a/src/game/g_active.c b/src/game/g_active.c index a08d9e48..f4bad98c 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -267,6 +267,13 @@ void ClientImpacts( gentity_t *ent, pmove_t *pm ) other = &g_entities[ pm->touchents[ i ] ]; + // see G_UnlaggedDetectCollisions(), this is the inverse of that. + // if our movement is blocked by another player's real position, + // don't use the unlagged position for them because they are + // blocking or server-side Pmove() from reaching it + if( other->client && other->client->unlaggedCalc.used ) + other->client->unlaggedCalc.used = qfalse; + //charge attack if( ent->client->ps.weapon == WP_ALEVEL4 && ent->client->ps.stats[ STAT_MISC ] > 0 && @@ -952,6 +959,288 @@ void SendPendingPredictableEvents( playerState_t *ps ) } } +/* +============== + G_UnlaggedStore + + Called on every server frame. Stores position data for the client at that + into client->unlaggedHist[] and the time into level.unlaggedTimes[]. + This data is used by G_UnlaggedCalc() +============== +*/ +void G_UnlaggedStore( void ) +{ + int i = 0; + gentity_t *ent; + unlagged_t *save; + + if( !g_unlagged.integer ) + return; + level.unlaggedIndex++; + if( level.unlaggedIndex >= MAX_UNLAGGED_MARKERS ) + level.unlaggedIndex = 0; + + level.unlaggedTimes[ level.unlaggedIndex ] = level.time; + + for( i = 0; i < level.maxclients; i++ ) + { + ent = &g_entities[ i ]; + save = &ent->client->unlaggedHist[ level.unlaggedIndex ]; + save->used = qfalse; + if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) ) + continue; + if( ent->client->pers.connected != CON_CONNECTED ) + continue; + VectorCopy( ent->r.mins, save->mins ); + VectorCopy( ent->r.maxs, save->maxs ); + VectorCopy( ent->s.pos.trBase, save->origin ); + save->used = qtrue; + } +} + +/* +============== + G_UnlaggedClear + + Mark all unlaggedHist[] markers for this client invalid. Useful for + preventing teleporting and death. +============== +*/ +void G_UnlaggedClear( gentity_t *ent ) +{ + int i; + + for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ ) + ent->client->unlaggedHist[ i ].used = qfalse; +} + +/* +============== + G_UnlaggedCalc + + Loops through all active clients and calculates their predicted position + for time then stores it in client->unlaggedCalc +============== +*/ +void G_UnlaggedCalc( int time, gentity_t *rewindEnt ) +{ + int i = 0; + gentity_t *ent; + int startIndex = level.unlaggedIndex; + int stopIndex = -1; + int frameMsec = 0; + float lerp = 0.5f; + + if( !g_unlagged.integer ) + return; + + // clear any calculated values from a previous run + for( i = 0; i < level.maxclients; i++ ) + { + ent = &g_entities[ i ]; + ent->client->unlaggedCalc.used = qfalse; + } + + for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ ) + { + if( level.unlaggedTimes[ startIndex ] <= time ) + break; + stopIndex = startIndex; + if( --startIndex < 0 ) + startIndex = MAX_UNLAGGED_MARKERS - 1; + } + if( i == MAX_UNLAGGED_MARKERS ) + { + // if we searched all markers and the oldest one still isn't old enough + // just use the oldest marker with no lerping + lerp = 0.0f; + } + + // client is on the current frame, no need for unlagged + if( stopIndex == -1 ) + return; + + // lerp between two markers + frameMsec = level.unlaggedTimes[ stopIndex ] - + level.unlaggedTimes[ startIndex ]; + if( frameMsec > 0 ) + { + lerp = ( float )( time - level.unlaggedTimes[ startIndex ] ) + / ( float )frameMsec; + } + + for( i = 0; i < level.maxclients; i++ ) + { + ent = &g_entities[ i ]; + if( ent->s.number == rewindEnt->s.number ) + continue; + if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) ) + continue; + if( ent->client->pers.connected != CON_CONNECTED ) + continue; + if( !ent->client->unlaggedHist[ startIndex ].used ) + continue; + if( !ent->client->unlaggedHist[ stopIndex ].used ) + continue; + + // between two unlagged markers + VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].mins, + ent->client->unlaggedHist[ stopIndex ].mins, + ent->client->unlaggedCalc.mins ); + VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].maxs, + ent->client->unlaggedHist[ stopIndex ].maxs, + ent->client->unlaggedCalc.maxs ); + VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].origin, + ent->client->unlaggedHist[ stopIndex ].origin, + ent->client->unlaggedCalc.origin ); + + ent->client->unlaggedCalc.used = qtrue; + } +} + +/* +============== + G_UnlaggedOff + + Reverses the changes made to all active clients by G_UnlaggedOn() +============== +*/ +void G_UnlaggedOff( void ) +{ + int i = 0; + gentity_t *ent; + + if( !g_unlagged.integer ) + return; + + for( i = 0; i < level.maxclients; i++ ) + { + ent = &g_entities[ i ]; + if( !ent->client->unlaggedBackup.used ) + continue; + VectorCopy( ent->client->unlaggedBackup.mins, ent->r.mins ); + VectorCopy( ent->client->unlaggedBackup.maxs, ent->r.maxs ); + VectorCopy( ent->client->unlaggedBackup.origin, ent->r.currentOrigin ); + ent->client->unlaggedBackup.used = qfalse; + trap_LinkEntity( ent ); + } +} + +/* +============== + G_UnlaggedOn + + Called after G_UnlaggedCalc() to apply the calculated values to all active + clients. Once finished tracing, G_UnlaggedOff() must be called to restore + the clients' position data + + As an optimization, all clients that have an unlagged position that is + not touchable at "range" from "muzzle" will be ignored. This is required + to prevent a huge amount of trap_LinkEntity() calls per user cmd. +============== +*/ + +void G_UnlaggedOn( vec3_t muzzle, float range ) +{ + int i = 0; + gentity_t *ent; + unlagged_t *calc; + + if( !g_unlagged.integer ) + return; + + for( i = 0; i < level.maxclients; i++ ) + { + ent = &g_entities[ i ]; + calc = &ent->client->unlaggedCalc; + + if( !calc->used ) + continue; + if( ent->client->unlaggedBackup.used ) + continue; + if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) ) + continue; + if( VectorCompare( ent->r.currentOrigin, calc->origin ) ) + continue; + if( muzzle ) + { + float r1 = Distance( calc->origin, calc->maxs ); + float r2 = Distance( calc->origin, calc->mins ); + float maxRadius = ( r1 > r2 ) ? r1 : r2; + + if( Distance( muzzle, calc->origin ) > range + maxRadius ) + continue; + } + + // create a backup of the real positions + VectorCopy( ent->r.mins, ent->client->unlaggedBackup.mins ); + VectorCopy( ent->r.maxs, ent->client->unlaggedBackup.maxs ); + VectorCopy( ent->r.currentOrigin, ent->client->unlaggedBackup.origin ); + ent->client->unlaggedBackup.used = qtrue; + + // move the client to the calculated unlagged position + VectorCopy( calc->mins, ent->r.mins ); + VectorCopy( calc->maxs, ent->r.maxs ); + VectorCopy( calc->origin, ent->r.currentOrigin ); + trap_LinkEntity( ent ); + } +} +/* +============== + G_UnlaggedDetectCollisions + + cgame prediction will predict a client's own position all the way up to + the current time, but only updates other player's positions up to the + postition sent in the most recent snapshot. + + This allows player X to essentially "move through" the position of player Y + when player X's cmd is processed with Pmove() on the server. This is because + player Y was clipping player X's Pmove() on his client, but when the same + cmd is processed with Pmove on the server it is not clipped. + + Long story short (too late): don't use unlagged positions for players who + were blocking this player X's client-side Pmove(). This makes the assumption + that if player X's movement was blocked in the client he's going to still + be up against player Y when the Pmove() is run on the server with the + same cmd. + + NOTE: this must be called after Pmove() and G_UnlaggedCalc() +============== +*/ +static void G_UnlaggedDetectCollisions( gentity_t *ent ) +{ + unlagged_t *calc; + trace_t tr; + float r1, r2; + float range; + + if( !g_unlagged.integer ) + return; + + calc = &ent->client->unlaggedCalc; + + // if the client isn't moving, this is not necessary + if( VectorCompare( ent->client->oldOrigin, ent->client->ps.origin ) ) + return; + + range = Distance( ent->client->oldOrigin, ent->client->ps.origin ); + + // increase the range by the player's largest possible radius since it's + // the players bounding box that collides, not their origin + r1 = Distance( calc->origin, calc->mins ); + r2 = Distance( calc->origin, calc->maxs ); + range += ( r1 > r2 ) ? r1 : r2; + + G_UnlaggedOn( ent->client->oldOrigin, range ); + + trap_Trace(&tr, ent->client->oldOrigin, ent->r.mins, ent->r.maxs, + ent->client->ps.origin, ent->s.number, MASK_PLAYERSOLID ); + if( tr.entityNum >= 0 && tr.entityNum < MAX_CLIENTS ) + g_entities[ tr.entityNum ].client->unlaggedCalc.used = qfalse; + + G_UnlaggedOff( ); +} + /* ============== ClientThink @@ -1002,6 +1291,8 @@ void ClientThink_real( gentity_t *ent ) if( msec > 200 ) msec = 200; + client->unlaggedTime = ucmd->serverTime; + if( pmove_msec.integer < 8 ) trap_Cvar_Set( "pmove_msec", "8" ); else if( pmove_msec.integer > 33 ) @@ -1039,6 +1330,9 @@ void ClientThink_real( gentity_t *ent ) if( !ClientInactivityTimer( client ) ) return; + // calculate where ent is currently seeing all the other active clients + G_UnlaggedCalc( ent->client->unlaggedTime, ent ); + if( client->noclip ) client->ps.pm_type = PM_NOCLIP; else if( client->ps.stats[ STAT_HEALTH ] <= 0 ) @@ -1214,6 +1508,8 @@ void ClientThink_real( gentity_t *ent ) Pmove( &pm ); + G_UnlaggedDetectCollisions( ent ); + // save results of pmove if( ent->client->ps.eventSequence != oldEventSequence ) ent->eventTime = level.time; @@ -1239,6 +1535,9 @@ void ClientThink_real( gentity_t *ent ) ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; + // touch other objects + ClientImpacts( ent, &pm ); + // execute client events ClientEvents( ent, oldEventSequence ); @@ -1249,9 +1548,6 @@ void ClientThink_real( gentity_t *ent ) VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); VectorCopy( ent->client->ps.origin, ent->s.origin ); - // touch other objects - ClientImpacts( ent, &pm ); - // save results of triggers and client events if( ent->client->ps.eventSequence != oldEventSequence ) ent->eventTime = level.time; @@ -1278,6 +1574,7 @@ void ClientThink_real( gentity_t *ent ) //prevent lerping client->ps.eFlags ^= EF_TELEPORT_BIT; client->ps.eFlags &= ~EF_NODRAW; + G_UnlaggedClear( ent ); //client leaves hovel client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 87f8574a..b7abfbe6 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -1190,6 +1190,7 @@ void AHovel_Use( gentity_t *self, gentity_t *other, gentity_t *activator ) //prevent lerping activator->client->ps.eFlags ^= EF_TELEPORT_BIT; activator->client->ps.eFlags |= EF_NODRAW; + G_UnlaggedClear( activator ); activator->client->ps.stats[ STAT_STATE ] |= SS_HOVELING; activator->client->hovel = self; @@ -1276,6 +1277,7 @@ void AHovel_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int //prevent lerping builder->client->ps.eFlags ^= EF_TELEPORT_BIT; builder->client->ps.eFlags &= ~EF_NODRAW; + G_UnlaggedClear( builder ); G_SetOrigin( builder, newOrigin ); VectorCopy( newOrigin, builder->client->ps.origin ); diff --git a/src/game/g_client.c b/src/game/g_client.c index 7141f222..aa39c90c 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1412,6 +1412,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & ( EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED ); flags ^= EF_TELEPORT_BIT; + G_UnlaggedClear( ent ); // clear everything but the persistant data diff --git a/src/game/g_combat.c b/src/game/g_combat.c index 95d88412..7fc5a02a 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -684,6 +684,7 @@ G_CalcDamageModifier */ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *attacker, int class, int dflags ) { + vec3_t targOrigin; vec3_t bulletPath; vec3_t bulletAngle; vec3_t pMINUSfloor, floor, normal; @@ -696,6 +697,11 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att if( point == NULL ) return 1.0f; + if( g_unlagged.integer && targ->client && targ->client->unlaggedCalc.used ) + VectorCopy( targ->client->unlaggedCalc.origin, targOrigin ); + else + VectorCopy( targ->r.currentOrigin, targOrigin ); + clientHeight = targ->r.maxs[ 2 ] - targ->r.mins[ 2 ]; if( targ->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING ) @@ -703,7 +709,7 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att else VectorSet( normal, 0, 0, 1 ); - VectorMA( targ->r.currentOrigin, targ->r.mins[ 2 ], normal, floor ); + VectorMA( targOrigin, targ->r.mins[ 2 ], normal, floor ); VectorSubtract( point, floor, pMINUSfloor ); hitRelative = DotProduct( normal, pMINUSfloor ) / VectorLength( normal ); @@ -716,7 +722,7 @@ static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *att hitRatio = hitRelative / clientHeight; - VectorSubtract( targ->r.currentOrigin, point, bulletPath ); + VectorSubtract( targOrigin, point, bulletPath ); vectoangles( bulletPath, bulletAngle ); clientRotation = targ->client->ps.viewangles[ YAW ]; diff --git a/src/game/g_local.h b/src/game/g_local.h index a4fe225b..6cf5db75 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -351,6 +351,14 @@ typedef struct int adminLevel; } clientPersistant_t; +#define MAX_UNLAGGED_MARKERS 10 +typedef struct unlagged_s { + vec3_t origin; + vec3_t mins; + vec3_t maxs; + qboolean used; +} unlagged_t; + // this structure is cleared on each ClientSpawn(), // except for 'client->pers' and 'client->sess' struct gclient_s @@ -442,6 +450,12 @@ struct gclient_s #define RAM_FRAMES 1 // number of frames to wait before retriggering int retriggerArmouryMenu; // frame number to retrigger the armoury menu + + unlagged_t unlaggedHist[ MAX_UNLAGGED_MARKERS ]; + unlagged_t unlaggedBackup; + unlagged_t unlaggedCalc; + int unlaggedTime; + }; @@ -628,6 +642,9 @@ typedef struct qboolean uncondHumanWin; qboolean alienTeamLocked; qboolean humanTeamLocked; + + int unlaggedIndex; + int unlaggedTimes[ MAX_UNLAGGED_MARKERS ]; } level_locals_t; // @@ -914,6 +931,11 @@ void ClientCommand( int clientNum ); // // g_active.c // +void G_UnlaggedStore( void ); +void G_UnlaggedClear( gentity_t *ent ); +void G_UnlaggedCalc( int time, gentity_t *skipEnt ); +void G_UnlaggedOn( vec3_t muzzle, float range ); +void G_UnlaggedOff( void ); void ClientThink( int clientNum ); void ClientEndFrame( gentity_t *ent ); void G_RunClient( gentity_t *ent ); @@ -1100,6 +1122,8 @@ extern vmCvar_t g_alienMaxStage; extern vmCvar_t g_alienStage2Threshold; extern vmCvar_t g_alienStage3Threshold; +extern vmCvar_t g_unlagged; + extern vmCvar_t g_disabledEquipment; extern vmCvar_t g_disabledClasses; extern vmCvar_t g_disabledBuildables; diff --git a/src/game/g_main.c b/src/game/g_main.c index c2a4d66a..ab10f15b 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -103,6 +103,8 @@ vmCvar_t g_alienMaxStage; vmCvar_t g_alienStage2Threshold; vmCvar_t g_alienStage3Threshold; +vmCvar_t g_unlagged; + vmCvar_t g_disabledEquipment; vmCvar_t g_disabledClasses; vmCvar_t g_disabledBuildables; @@ -210,6 +212,8 @@ static cvarTable_t gameCvarTable[ ] = { &g_alienMaxStage, "g_alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, 0, 0, qfalse }, { &g_alienStage2Threshold, "g_alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, 0, 0, qfalse }, { &g_alienStage3Threshold, "g_alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, 0, 0, qfalse }, + + { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, { &g_disabledEquipment, "g_disabledEquipment", "", CVAR_ROM, 0, qfalse }, { &g_disabledClasses, "g_disabledClasses", "", CVAR_ROM, 0, qfalse }, @@ -2138,12 +2142,6 @@ void G_RunFrame( int levelTime ) if( !ent->r.linked && ent->neverFree ) continue; - if( ent->s.eType == ET_MISSILE ) - { - G_RunMissile( ent ); - continue; - } - if( ent->s.eType == ET_BUILDABLE ) { G_BuildableThink( ent, msec ); @@ -2183,6 +2181,25 @@ void G_RunFrame( int levelTime ) ClientEndFrame( ent ); } + // save position information for all active clients + G_UnlaggedStore( ); + + // for missle impacts, move every active client one server frame time back + // to compensate for built-in 50ms lag + G_UnlaggedCalc( level.previousTime, NULL ); + G_UnlaggedOn( NULL, 0.0f ); + for( i = MAX_CLIENTS; i < level.num_entities ; i++) + { + ent = &g_entities[ i ]; + if( !ent->inuse ) + continue; + if( ent->freeAfterEvent ) + continue; + if( ent->s.eType == ET_MISSILE ) + G_RunMissile( ent ); + } + G_UnlaggedOff( ); + end = trap_Milliseconds(); //TA: diff --git a/src/game/g_misc.c b/src/game/g_misc.c index 23a46a3b..9d54033e 100644 --- a/src/game/g_misc.c +++ b/src/game/g_misc.c @@ -86,6 +86,7 @@ void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) // toggle the teleport bit so the client knows to not lerp player->client->ps.eFlags ^= EF_TELEPORT_BIT; + G_UnlaggedClear( player ); // set angles SetClientViewAngle( player, angles ); diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index 2afa24b7..4496d18a 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -179,7 +179,10 @@ void meleeAttack( gentity_t *ent, float range, float width, int damage, meansOfD VectorMA( muzzle, range, forward, end ); + G_UnlaggedOn( muzzle, range ); trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); + if( tr.surfaceFlags & SURF_NOIMPACT ) return; @@ -223,7 +226,16 @@ void bulletFire( gentity_t *ent, float spread, int damage, int mod ) VectorMA( end, r, right, end ); VectorMA( end, u, up, end ); - trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); + // don't use unlagged if this is not a client (e.g. turret) + if( ent->client ) + { + G_UnlaggedOn( muzzle, 8192 * 16 ); + trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); + } + else + trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); + if( tr.surfaceFlags & SURF_NOIMPACT ) return; @@ -308,8 +320,9 @@ void shotgunFire( gentity_t *ent ) SnapVector( tent->s.origin2 ); tent->s.eventParm = rand() & 255; // seed for spread pattern tent->s.otherEntityNum = ent->s.number; - + G_UnlaggedOn( muzzle, 8192 * 16 ); ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent ); + G_UnlaggedOff(); } /* @@ -329,7 +342,10 @@ void massDriverFire( gentity_t *ent ) VectorMA( muzzle, 8192 * 16, forward, end ); + G_UnlaggedOn( muzzle, 8192 * 16 ); trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); + if( tr.surfaceFlags & SURF_NOIMPACT ) return; @@ -482,7 +498,10 @@ void lasGunFire( gentity_t *ent ) VectorMA( muzzle, 8192 * 16, forward, end ); + G_UnlaggedOn( muzzle, 8192 * 16 ); trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); + if( tr.surfaceFlags & SURF_NOIMPACT ) return; @@ -534,7 +553,10 @@ void painSawFire( gentity_t *ent ) VectorMA( muzzle, PAINSAW_RANGE, forward, end ); + G_UnlaggedOn( muzzle, PAINSAW_RANGE ); trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); + if( tr.surfaceFlags & SURF_NOIMPACT ) return; @@ -805,7 +827,9 @@ qboolean CheckVenomAttack( gentity_t *ent ) VectorMA( muzzle, LEVEL0_BITE_RANGE, forward, end ); + G_UnlaggedOn( muzzle, LEVEL0_BITE_RANGE ); trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); if( tr.surfaceFlags & SURF_NOIMPACT ) return qfalse; @@ -939,6 +963,7 @@ void poisonCloud( gentity_t *ent ) VectorAdd( ent->client->ps.origin, range, maxs ); VectorSubtract( ent->client->ps.origin, range, mins ); + G_UnlaggedOn( ent->client->ps.origin, LEVEL1_PCLOUD_RANGE ); num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { @@ -967,6 +992,7 @@ void poisonCloud( gentity_t *ent ) } } } + G_UnlaggedOff( ); } @@ -1231,7 +1257,9 @@ void areaZapFire( gentity_t *ent ) VectorMA( muzzle, LEVEL2_AREAZAP_RANGE, forward, end ); + G_UnlaggedOn( muzzle, LEVEL2_AREAZAP_RANGE ); trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); if( tr.surfaceFlags & SURF_NOIMPACT ) return; @@ -1291,7 +1319,9 @@ qboolean CheckPounceAttack( gentity_t *ent ) VectorMA( muzzle, LEVEL3_POUNCE_RANGE, forward, end ); + G_UnlaggedOn( muzzle, LEVEL3_POUNCE_RANGE ); trap_Trace( &tr, ent->s.origin, mins, maxs, end, ent->s.number, MASK_SHOT ); + G_UnlaggedOff( ); //miss if( tr.fraction >= 1.0 ) -- cgit