//----------------------------------------------------------------------------- // Frame-based update //----------------------------------------------------------------------------- void CTeleportCountdownScreen::OnTick() { BaseClass::OnTick(); // Find the active info teleporter countdown C_InfoTeleporterCountdown *pActiveCountdown = NULL; for ( int i = g_InfoTeleporterCountdownList.Head(); i != g_InfoTeleporterCountdownList.InvalidIndex(); i = g_InfoTeleporterCountdownList.Next(i) ) { if ( g_InfoTeleporterCountdownList[i]->m_bCountdownStarted ) { pActiveCountdown = g_InfoTeleporterCountdownList[i]; break; } } if ( !GetEntity() || !pActiveCountdown ) { m_pTimeRemainingTitleLabel->SetVisible( false ); m_pTimeRemainingLabel->SetVisible( false ); m_pMalfunctionLabel->SetVisible( false ); return; } // Make the appropriate labels visible bool bMalfunction = pActiveCountdown->m_bDisabled; m_pTimeRemainingTitleLabel->SetVisible( !bMalfunction ); m_pTimeRemainingLabel->SetVisible( !bMalfunction ); // This will make it flash m_pMalfunctionLabel->SetVisible( bMalfunction && (((int)(gpGlobals->curtime) & 0x1) == 0x1) ); // Update the time remaining if ( !bMalfunction ) { char buf[32]; if (m_pTimeRemainingLabel) { float dt = gpGlobals->curtime - pActiveCountdown->m_flStartTime; if ( dt < 0.0f ) { dt = 0.0f; } int nTimeRemaining = (int)(pActiveCountdown->m_flTimeRemaining - dt + 0.5f); if ( nTimeRemaining < 0 ) { nTimeRemaining = 0; } Q_snprintf( buf, sizeof( buf ), "%d", nTimeRemaining ); m_pTimeRemainingLabel->SetText( buf ); } } }
//----------------------------------------------------------------------------- // Sets up visibility //----------------------------------------------------------------------------- void PointCameraSetupVisibility( CBaseEntity *pPlayer, int area, unsigned char *pvs, int pvssize ) { for ( CPointCamera *pCameraEnt = GetPointCameraList(); pCameraEnt != NULL; pCameraEnt = pCameraEnt->m_pNext ) { pCameraEnt->SetActive( false ); } int nNext; for ( int i = g_InfoCameraLinkList.Head(); i != g_InfoCameraLinkList.InvalidIndex(); i = nNext ) { nNext = g_InfoCameraLinkList.Next( i ); CBaseEntity *pTargetEnt = g_InfoCameraLinkList[i]->m_hTargetEntity; if ( !pTargetEnt ) { UTIL_Remove( g_InfoCameraLinkList[i] ); continue; } // Don't bother if it's not visible if ( pTargetEnt->IsEffectActive( EF_NODRAW ) ) continue; if ( !pTargetEnt->NetworkProp()->IsInPVS( pPlayer->edict(), pvs, pvssize ) ) continue; if ( engine->CheckAreasConnected( area, pTargetEnt->NetworkProp()->AreaNum() ) ) { CPointCamera *pCameraEnt = g_InfoCameraLinkList[i]->m_hCamera; if ( pCameraEnt ) { engine->AddOriginToPVS( pCameraEnt->GetAbsOrigin() ); pCameraEnt->SetActive( true ); } } } }
void CLagCompensationManager::BacktrackEntity( CAI_BaseNPC *pEntity, float flTargetTime ) { Vector org, mins, maxs; QAngle ang; VPROF_BUDGET( "BacktrackEntity", "CLagCompensationManager" ); // get track history of this entity int index = pEntity->GetAIIndex(); CUtlFixedLinkedList< LagRecord > *track = &m_EntityTrack[ index ]; // check if we have at leat one entry if ( track->Count() <= 0 ) return; int curr = track->Head(); LagRecord *prevRecord = NULL; LagRecord *record = NULL; Vector prevOrg = pEntity->GetLocalOrigin(); // Walk context looking for any invalidating event while( track->IsValidIndex(curr) ) { // remember last record prevRecord = record; // get next record record = &track->Element( curr ); if ( !(record->m_fFlags & LC_ALIVE) ) { // entity must be alive, lost track return; } Vector delta = record->m_vecOrigin - prevOrg; if ( delta.LengthSqr() > LAG_COMPENSATION_TELEPORTED_DISTANCE_SQR ) { // lost track, moved too far (may have teleported) return; } // did we find a context smaller than target time ? if ( record->m_flSimulationTime <= flTargetTime ) break; // hurra, stop prevOrg = record->m_vecOrigin; // go one step back in time curr = track->Next( curr ); } Assert( record ); if ( !record ) { if ( sv_unlag_debug.GetBool() ) { DevMsg( "No valid positions in history for BacktrackEntity ( %s )\n", pEntity->GetClassname() ); } return; // that should never happen } float frac = 0.0f; if ( prevRecord && (record->m_flSimulationTime < flTargetTime) && (record->m_flSimulationTime < prevRecord->m_flSimulationTime) ) { // we didn't find the exact time but have a valid previous record // so interpolate between these two records; Assert( prevRecord->m_flSimulationTime > record->m_flSimulationTime ); Assert( flTargetTime < prevRecord->m_flSimulationTime ); // calc fraction between both records frac = ( flTargetTime - record->m_flSimulationTime ) / ( prevRecord->m_flSimulationTime - record->m_flSimulationTime ); Assert( frac > 0 && frac < 1 ); // should never extrapolate ang = Lerp( frac, record->m_vecAngles, prevRecord->m_vecAngles ); org = Lerp( frac, record->m_vecOrigin, prevRecord->m_vecOrigin ); mins = Lerp( frac, record->m_vecMins, prevRecord->m_vecMins ); maxs = Lerp( frac, record->m_vecMaxs, prevRecord->m_vecMaxs ); } else { // we found the exact record or no other record to interpolate with // just copy these values since they are the best we have ang = record->m_vecAngles; org = record->m_vecOrigin; mins = record->m_vecMins; maxs = record->m_vecMaxs; } // See if this is still a valid position for us to teleport to if ( sv_unlag_fixstuck.GetBool() ) { // Try to move to the wanted position from our current position. trace_t tr; UTIL_TraceEntity( pEntity, org, org, MASK_NPCSOLID, &tr ); if ( tr.startsolid || tr.allsolid ) { if ( sv_unlag_debug.GetBool() ) DevMsg( "WARNING: BackupEntity trying to back entity into a bad position - %s\n", pEntity->GetClassname() ); CBasePlayer *pHitPlayer = dynamic_cast<CBasePlayer *>( tr.m_pEnt ); // don't lag compensate the current player if ( pHitPlayer && ( pHitPlayer != m_pCurrentPlayer ) ) { // If we haven't backtracked this player, do it now // this deliberately ignores WantsLagCompensationOnEntity. if ( !m_RestorePlayer.Get( pHitPlayer->entindex() - 1 ) ) { // prevent recursion - save a copy of m_RestorePlayer, // pretend that this player is off-limits int pl_index = pEntity->entindex() - 1; // Temp turn this flag on m_RestorePlayer.Set( pl_index ); BacktrackPlayer( pHitPlayer, flTargetTime ); // Remove the temp flag m_RestorePlayer.Clear( pl_index ); } } else { CAI_BaseNPC *pHitEntity = dynamic_cast<CAI_BaseNPC *>( tr.m_pEnt ); if ( pHitEntity ) { CAI_BaseNPC *pNPC = NULL; CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); int nAIs = g_AI_Manager.NumAIs(); for ( int i = 0; i < nAIs; i++ ) // we'll have to find this entity's index though :( { pNPC = ppAIs[i]; if ( pNPC == pHitEntity ) break; } // If we haven't backtracked this player, do it now // this deliberately ignores WantsLagCompensationOnEntity. if ( pNPC && !m_RestoreEntity.Get( pNPC->GetAIIndex() ) ) { // prevent recursion - save a copy of m_RestoreEntity, // pretend that this player is off-limits // Temp turn this flag on m_RestoreEntity.Set( pNPC->GetAIIndex() ); BacktrackEntity( pHitEntity, flTargetTime ); // Remove the temp flag m_RestoreEntity.Clear( pNPC->GetAIIndex() ); } } } // now trace us back as far as we can go UTIL_TraceEntity( pEntity, pEntity->GetLocalOrigin(), org, MASK_NPCSOLID, &tr ); if ( tr.startsolid || tr.allsolid ) { // Our starting position is bogus if ( sv_unlag_debug.GetBool() ) DevMsg( "Backtrack failed completely, bad starting position\n" ); } else { // We can get to a valid place, but not all the way to the target Vector vPos; VectorLerp( pEntity->GetLocalOrigin(), org, tr.fraction * g_flFractionScale, vPos ); // This is as close as we're going to get org = vPos; if ( sv_unlag_debug.GetBool() ) DevMsg( "Backtrack got most of the way\n" ); } } } // See if this represents a change for the entity int flags = 0; LagRecord *restore = &m_EntityRestoreData[ index ]; LagRecord *change = &m_EntityChangeData[ index ]; QAngle angdiff = pEntity->GetLocalAngles() - ang; Vector orgdiff = pEntity->GetLocalOrigin() - org; // Always remember the pristine simulation time in case we need to restore it. restore->m_flSimulationTime = pEntity->GetSimulationTime(); if ( angdiff.LengthSqr() > LAG_COMPENSATION_EPS_SQR ) { flags |= LC_ANGLES_CHANGED; restore->m_vecAngles = pEntity->GetLocalAngles(); pEntity->SetLocalAngles( ang ); change->m_vecAngles = ang; } // Use absolute equality here if ( ( mins != pEntity->WorldAlignMins() ) || ( maxs != pEntity->WorldAlignMaxs() ) ) { flags |= LC_SIZE_CHANGED; restore->m_vecMins = pEntity->WorldAlignMins() ; restore->m_vecMaxs = pEntity->WorldAlignMaxs(); pEntity->SetSize( mins, maxs ); change->m_vecMins = mins; change->m_vecMaxs = maxs; } // Note, do origin at end since it causes a relink into the k/d tree if ( orgdiff.LengthSqr() > LAG_COMPENSATION_EPS_SQR ) { flags |= LC_ORIGIN_CHANGED; restore->m_vecOrigin = pEntity->GetLocalOrigin(); pEntity->SetLocalOrigin( org ); change->m_vecOrigin = org; } // Sorry for the loss of the optimization for the case of people // standing still, but you breathe even on the server. // This is quicker than actually comparing all bazillion floats. flags |= LC_ANIMATION_CHANGED; restore->m_masterSequence = pEntity->GetSequence(); restore->m_masterCycle = pEntity->GetCycle(); bool interpolationAllowed = false; if( prevRecord && (record->m_masterSequence == prevRecord->m_masterSequence) ) { // If the master state changes, all layers will be invalid too, so don't interp (ya know, interp barely ever happens anyway) interpolationAllowed = true; } //////////////////////// // First do the master settings bool interpolatedMasters = false; if( frac > 0.0f && interpolationAllowed ) { interpolatedMasters = true; pEntity->SetSequence( Lerp( frac, record->m_masterSequence, prevRecord->m_masterSequence ) ); pEntity->SetCycle( Lerp( frac, record->m_masterCycle, prevRecord->m_masterCycle ) ); if( record->m_masterCycle > prevRecord->m_masterCycle ) { // the older record is higher in frame than the newer, it must have wrapped around from 1 back to 0 // add one to the newer so it is lerping from .9 to 1.1 instead of .9 to .1, for example. float newCycle = Lerp( frac, record->m_masterCycle, prevRecord->m_masterCycle + 1 ); pEntity->SetCycle(newCycle < 1 ? newCycle : newCycle - 1 );// and make sure .9 to 1.2 does not end up 1.05 } else { pEntity->SetCycle( Lerp( frac, record->m_masterCycle, prevRecord->m_masterCycle ) ); } } if( !interpolatedMasters ) { pEntity->SetSequence(record->m_masterSequence); pEntity->SetCycle(record->m_masterCycle); } //////////////////////// // Now do all the layers int layerCount = pEntity->GetNumAnimOverlays(); for( int layerIndex = 0; layerIndex < layerCount; ++layerIndex ) { CAnimationLayer *currentLayer = pEntity->GetAnimOverlay(layerIndex); if( currentLayer ) { restore->m_layerRecords[layerIndex].m_cycle = currentLayer->m_flCycle; restore->m_layerRecords[layerIndex].m_order = currentLayer->m_nOrder; restore->m_layerRecords[layerIndex].m_sequence = currentLayer->m_nSequence; restore->m_layerRecords[layerIndex].m_weight = currentLayer->m_flWeight; bool interpolated = false; if( (frac > 0.0f) && interpolationAllowed ) { LayerRecord &recordsLayerRecord = record->m_layerRecords[layerIndex]; LayerRecord &prevRecordsLayerRecord = prevRecord->m_layerRecords[layerIndex]; if( (recordsLayerRecord.m_order == prevRecordsLayerRecord.m_order) && (recordsLayerRecord.m_sequence == prevRecordsLayerRecord.m_sequence) ) { // We can't interpolate across a sequence or order change interpolated = true; if( recordsLayerRecord.m_cycle > prevRecordsLayerRecord.m_cycle ) { // the older record is higher in frame than the newer, it must have wrapped around from 1 back to 0 // add one to the newer so it is lerping from .9 to 1.1 instead of .9 to .1, for example. float newCycle = Lerp( frac, recordsLayerRecord.m_cycle, prevRecordsLayerRecord.m_cycle + 1 ); currentLayer->m_flCycle = newCycle < 1 ? newCycle : newCycle - 1;// and make sure .9 to 1.2 does not end up 1.05 } else { currentLayer->m_flCycle = Lerp( frac, recordsLayerRecord.m_cycle, prevRecordsLayerRecord.m_cycle ); } currentLayer->m_nOrder = recordsLayerRecord.m_order; currentLayer->m_nSequence = recordsLayerRecord.m_sequence; currentLayer->m_flWeight = Lerp( frac, recordsLayerRecord.m_weight, prevRecordsLayerRecord.m_weight ); } } if( !interpolated ) { //Either no interp, or interp failed. Just use record. currentLayer->m_flCycle = record->m_layerRecords[layerIndex].m_cycle; currentLayer->m_nOrder = record->m_layerRecords[layerIndex].m_order; currentLayer->m_nSequence = record->m_layerRecords[layerIndex].m_sequence; currentLayer->m_flWeight = record->m_layerRecords[layerIndex].m_weight; } } } if ( !flags ) return; // we didn't change anything if ( sv_lagflushbonecache.GetBool() ) pEntity->InvalidateBoneCache(); /*char text[256]; Q_snprintf( text, sizeof(text), "time %.2f", flTargetTime ); pEntity->DrawServerHitboxes( 10 ); NDebugOverlay::Text( org, text, false, 10 ); NDebugOverlay::EntityBounds( pEntity, 255, 0, 0, 32, 10 ); */ m_RestoreEntity.Set( index ); //remember that we changed this entity m_bNeedToRestore = true; // we changed at least one player / entity restore->m_fFlags = flags; // we need to restore these flags change->m_fFlags = flags; // we have changed these flags if( sv_showlagcompensation.GetInt() == 1 ) { pEntity->DrawServerHitboxes(4, true); } }