// Description: // // Arguments: // // Return: // void CVisualLog::WriteFrameHeaders( int iFrameNum, const char *sImageName ) { // Frame header example: Frame 0 [Frame000000.jpg] [34.527] CTimeValue time = gEnv->pTimer->GetFrameStartTime(); fprintf( m_fLogFile, "Frame %d [%s] [%0.3f]\n", iFrameNum, sImageName, time.GetSeconds() ); fprintf( m_fLogParamsFile, "Frame %d [%s] [%0.3f]\n", iFrameNum, sImageName, time.GetSeconds() ); }
void CTimer::Update() { char *p0; struct tm *tm; time_t mlSec; uint32_t muMSec; CTimeValue *tp; struct timeval tv; struct timezone tz; if ( !MINA_TRYLOCK(&this->muLock) ) { return; } gettimeofday(&tv, &tz); mlSec = tv.tv_sec; muMSec = tv.tv_usec / TIME_MSEC; this->moTime.Set(mlSec * TIME_SEC + tv.tv_usec * TIME_USEC, TIME_NSEC); tp = &this->moCacheTimes[muSlot]; if (tp->Sec() == mlSec) { tp->Msec(muMSec); MINA_UNLOCK(&this->muLock); return; } if (muSlot == TIME_SLOTS - 1) { muSlot = 0; } else { muSlot++; } tp = &this->moCacheTimes[muSlot]; tp->Sec(mlSec); tp->Msec(muMSec); p0 = &maacFormatTimes[muSlot][0]; tm = gmtime(&mlSec); strftime(p0, FORM_TIME_LEN, "%Y-%m-%d %H:%M:%S", tm); this->mpoCacheTime = tp; this->mpcFormatTime = p0; MINA_UNLOCK(&this->muLock); }
CTimeValue CTimeValue::operator-(const CTimeValue& rstTime) { timeval stTimeValResult; CTimeUtility::TimeValMinus(m_stTimeval, rstTime.m_stTimeval, stTimeValResult); CTimeValue stTimeManager; stTimeManager.SetTimeValue(stTimeValResult); return stTimeManager; }
void CNetworkStallTickerThread::Run() { bool gotLockLastTime=true; #if WARN_ABOUT_LONG_STALLS_IN_TICKER CTimeValue started=gEnv->pTimer->GetAsyncTime(); CTimeValue ended; #endif SetName("NetworkStallTicker"); while (m_threadRunning) { if (gEnv->pNetwork) { gEnv->pNetwork->SyncWithGame(eNGS_SleepNetwork); } if (gotLockLastTime) { CrySleep(33); } else { CrySleep(1); } { if (gEnv->pNetwork) { SCOPED_TICKER_TRY_LOCK; if (SCOPED_TICKER_HAS_LOCK) { gEnv->pNetwork->SyncWithGame(eNGS_MinimalUpdateForLoading); gotLockLastTime=true; } else { gotLockLastTime=false; } gEnv->pNetwork->SyncWithGame(eNGS_WakeNetwork); } #if WARN_ABOUT_LONG_STALLS_IN_TICKER ended = gEnv->pTimer->GetAsyncTime(); if (ended.GetDifferenceInSeconds(started)>1.f) { CryLogAlways("THREADEDLOADING:: No update for %f",ended.GetDifferenceInSeconds(started)); } started=ended; #endif } } Stop(); }
CUnitTest::~CUnitTest(void) { TRACE(TRACE_ENABLE); CTimeValue elapsed = m_timeEnded-m_timeStarted; int32 days, hours, minutes; float seconds; elapsed.GetTime(days, hours, minutes, seconds); const char* errorColour = (m_errorTotal != 0) ? COLOUR_ERROR : COLOUR_DEFAULT; LOG_ALWAYS(m_log, COLOUR_TEST_INFO "[%s] " COLOUR_DEFAULT "%d tests completed in %s%d days, %02u:%02u:%06.3fs; " COLOUR_DEFAULT "%s%d errors\n\n" COLOUR_RESET, m_name, m_totalTests, (elapsed.GetTicks() < 0) ? "-" : "", days, hours, minutes, seconds, errorColour, m_errorTotal); }
virtual void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo ) { switch (event) { case eFE_Initialize: { pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, false ); } break; case eFE_Update: { float fPeriod = GetPortFloat(pActInfo,IN_PERIOD); CTimeValue time = gEnv->pTimer->GetFrameStartTime(); CTimeValue dt = time - m_lastTickTime; if (dt.GetSeconds() >= fPeriod) { m_lastTickTime = time; ++m_count; ActivateOutput( pActInfo, OUT_COUNT, m_count ); if (m_count >= GetPortInt( pActInfo, IN_LIMIT )) { pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, false ); ActivateOutput( pActInfo, OUT_FINISHED, GetPortAny( pActInfo, IN_START )); } } } break; case eFE_Activate: { if (IsPortActive(pActInfo,IN_START)) { pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, true ); m_count = 0; m_lastTickTime = gEnv->pTimer->GetFrameStartTime(); } if (IsPortActive(pActInfo,IN_STOP)) { pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, false ); } if (IsPortActive(pActInfo,IN_CONTINUE)) { pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, true ); } } break; } }
//------------------------------------------------------------------------ void CFireModePlugin_Overheat::Activate(bool activate) { CWeapon* pWeapon = m_pOwnerFiremode->GetWeapon(); CRY_ASSERT(pWeapon); if(activate) { m_heat = 0.f; m_overheat = 0.f; if(m_nextHeatTime > 0.0f) { CTimeValue time = gEnv->pTimer->GetFrameStartTime(); float dt = m_nextHeatTime - time.GetSeconds(); if(dt > 0.0f) { m_heat = min(dt,1.0f); } if(dt > 1.0f) { m_overheat = dt - 1.0f; } m_nextHeatTime = 0.0f; } } else { if(m_heatEffectId) { pWeapon->DetachEffect(m_heatEffectId); m_heatEffectId = 0; } m_nextHeatTime = 0.0f; if(m_heat>0.0f) { CTimeValue time = gEnv->pTimer->GetFrameStartTime(); m_nextHeatTime = time.GetSeconds() + m_heat + m_overheat; } m_heat = 0.f; m_overheat = 0.f; } m_firedThisFrame = false; }
virtual void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo ) { switch (event) { case eFE_Initialize: { bool bPaused = GetPortBool(pActInfo,IN_PAUSED); pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, !bPaused ); m_last = gEnv->pTimer->GetFrameStartTime(); } break; case eFE_Update: { if (GetPortBool(pActInfo,IN_PAUSED)) return; float fPeriod = GetPortFloat(pActInfo,IN_PERIOD); CTimeValue time = gEnv->pTimer->GetFrameStartTime(); CTimeValue dt = time - m_last; if (dt.GetSeconds() >= fPeriod) { m_last = time; int nMin = GetPortInt(pActInfo,IN_MIN); int nMax = GetPortInt(pActInfo,IN_MAX); m_nCurrentCount++; if (m_nCurrentCount < nMin) m_nCurrentCount = nMin; if (m_nCurrentCount > nMax) { m_nCurrentCount = nMin; } ActivateOutput( pActInfo,OUT_OUT,m_nCurrentCount ); } } break; case eFE_Activate: { if (IsPortActive(pActInfo,IN_PAUSED)) { bool bPaused = GetPortBool(pActInfo,IN_PAUSED); pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, !bPaused ); } } break; } }
NET_IMPLEMENT_SIMPLE_ATSYNC_MESSAGE( CGameServerChannel, SyncTimeServer, eNRT_ReliableOrdered, eMPF_NoSendDelay ) { CTimeValue value = gEnv->pTimer->GetAsyncTime(); INetSendablePtr msg = new CSimpleNetMessage<SSyncTimeClient>(SSyncTimeClient(param.id, param.clientTime, param.serverTime, value.GetValue()), CGameClientChannel::SyncTimeClient); GetNetChannel()->AddSendable(msg, 0, NULL, NULL); return true; }
////////////////////////////////////////////////////////////////////////// // Create a new timer and put it in the list of managed timers. int CScriptTimerMgr::AddTimer( ScriptTimer &timer ) { CTimeValue nCurrTimeMillis = gEnv->pTimer->GetFrameStartTime(); timer.nStartTime = nCurrTimeMillis.GetMilliSecondsAsInt64(); timer.nEndTime = timer.nStartTime + timer.nMillis; if (!timer.nTimerID) { m_nLastTimerID++; timer.nTimerID = m_nLastTimerID; } else { if (timer.nTimerID > m_nLastTimerID) m_nLastTimerID = timer.nTimerID+1; } m_mapTempTimers[timer.nTimerID] = new ScriptTimer(timer); return timer.nTimerID; }
void ProcessEvent(EFlowEvent event, SActivationInfo* pActivationInfo) { switch (event) { case eFE_Initialize: { SetAutoUpdate(pActivationInfo, GetPortBool(pActivationInfo, eIP_Auto)); break; } case eFE_Activate: { if (IsPortActive(pActivationInfo, eIP_Auto)) { SetAutoUpdate(pActivationInfo, GetPortBool(pActivationInfo, eIP_Auto)); } else if (IsPortActive(pActivationInfo, eIP_Freq)) { m_frequency = GetPortFloat(pActivationInfo, eIP_Freq); } else if (IsPortActive(pActivationInfo, eIP_Sync)) { Sync(pActivationInfo); } break; } case eFE_Update: { CTimeValue delta = gEnv->pTimer->GetFrameStartTime() - m_prevTime; if (delta.GetSeconds() >= m_frequency) { Sync(pActivationInfo); } break; } } }
virtual void ProcessEvent( EFlowEvent event, SActivationInfo *pActInfo ) { switch (event) { case eFE_Activate: if (IsPortActive(pActInfo, EIP_Start)) { m_last = gEnv->pTimer->GetFrameStartTime(); ActivateOutput(pActInfo, EOP_Started, true); } if (IsPortActive(pActInfo, EIP_Stop)) { CTimeValue dt = gEnv->pTimer->GetFrameStartTime(); dt-=m_last; m_last = 0.0f; ActivateOutput(pActInfo, EOP_Stopped, true); ActivateOutput(pActInfo, EOP_Time, dt.GetSeconds()); } break; } }
void CLagOMeter::OnClientReceivedKill(const CActor::KillParams &killParams) { CTimeValue currentTime = gEnv->pTimer->GetAsyncTime(); if (killParams.shooterId == g_pGame->GetClientActorId()) { // Try to find the corresponding request hit info to see how far lagged behind we are int index = killParams.lagOMeterHitId; if (index > 0 && index < HIT_HISTORY_COUNT) { SHitRequestHistory& item = m_hitHistory[index]; if (item.requestTime.GetValue() != 0) { CTimeValue diff = currentTime - item.requestTime; CTelemetryCollector* pTelemetryCollector = (CTelemetryCollector*)g_pGame->GetITelemetryCollector(); if (pTelemetryCollector) { pTelemetryCollector->LogEvent("Kill Latency", diff.GetMilliSeconds()); } } } } }
void CDownloadMgr::WaitForDownloadsToFinish(const char** resources, int numResources, float timeout) { CDownloadableResourcePtr* pResources=new CDownloadableResourcePtr[numResources]; for (int i=0; i<numResources; ++i) { CDownloadableResourcePtr pRes = FindResourceByName(resources[i]); if (pRes) { pRes->StartDownloading(); } pResources[i] = pRes; } CTimeValue startTime = gEnv->pTimer->GetAsyncTime(); while (true) { bool allFinished = true; for (int i=0; i<numResources; ++i) { CDownloadableResourcePtr pRes = pResources[i]; if (pRes) { CDownloadableResource::TState state = pRes->GetState(); if (state & CDownloadableResource::k_callbackInProgressMask) { allFinished = false; break; } } } CTimeValue currentTime = gEnv->pTimer->GetAsyncTime(); if (allFinished || currentTime.GetDifferenceInSeconds(startTime) > timeout) { break; } CrySleep(100); }; delete [] pResources; DispatchCallbacks(); }
void CAntiCheatManager::OnSessionEnd() { DumpPlayerRecords(); DumpCheatRecords(); DecayCheatRecords(); CloseLogFile(); BackupLogFileAndSubmit(); CTimeValue currentTime = gEnv->pTimer->GetAsyncTime(); float deltaSeconds = currentTime.GetDifferenceInSeconds(m_lastDownloadTime); if (deltaSeconds > g_pGameCVars->g_dataRefreshFrequency * 3600) { CDownloadableResourcePtr res = GetDownloadableResource(); if (res) { // Clear the downloaded data and start download again res->Purge(); res->StartDownloading(); } m_lastDownloadTime = currentTime; } }
void CGameQueryListener::CleanUpServers() { if(m_servers.size() == 0) return; CTimeValue now = gEnv->pTimer->GetFrameStartTime(); std::vector<SGameServer>::iterator it; //kill old servers for(int i = 0; i < m_servers.size(); ++i) { uint32 seconds = (uint32)(now.GetSeconds() - m_servers[i].m_lastTime.GetSeconds()); if(!seconds) continue; if(seconds > 10) { it = m_servers.begin(); for(int inc = 0; inc < i; ++inc) ++it; m_servers.erase(it); --i; } } }
void CallbackTimer::Update() { if (!m_timeouts.empty()) { if (m_resort) { std::sort(m_timeouts.begin(), m_timeouts.end()); m_resort = false; } CTimeValue now = gEnv->pTimer->GetFrameStartTime(); while (!m_timeouts.empty() && (m_timeouts.front().timeout <= now)) { TimeoutInfo timeout = m_timeouts.front(); m_timeouts.pop_front(); TimerInfo timer = m_timers[timeout.timerID]; timer.callback(timer.userdata, timeout.timerID); if (m_timers.validate(timeout.timerID)) { if (!timer.repeating) m_timers.erase(timeout.timerID); else { CTimeValue nextTimeout = timeout.timeout + timer.interval; if (nextTimeout.GetValue() <= now.GetValue()) nextTimeout.SetValue(now.GetValue() + 1); timeout.timeout = nextTimeout; m_timeouts.push_back(timeout); m_resort = true; } } } } }
void CFlowDelayNode::ProcessEvent( EFlowEvent event, SActivationInfo * pActInfo ) { switch (event) { case eFE_Initialize: RemovePendingTimers(); pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, false ); break; case eFE_Activate: // we request a final activation if the input value changed // thus in case we get several inputs in ONE frame, we always only use the LAST one! if (IsPortActive(pActInfo, INP_IN)) { pActInfo->pGraph->RequestFinalActivation(pActInfo->myID); } break; case eFE_FinalActivate: { bool shouldReset = GetShouldReset( pActInfo ); if (gEnv->IsEditor()) { if (shouldReset) m_activations.clear(); const float delay = GetDelayTime(pActInfo); CTimeValue finishTime = gEnv->pTimer->GetFrameStartTime() + delay; m_activations[(int)finishTime.GetMilliSeconds()] = SDelayData(finishTime, pActInfo->pInputPorts[0]); pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, true ); } else { if (shouldReset) { for (Activations::iterator iter = m_activations.begin(); iter != m_activations.end(); ++iter) { IGameFramework::TimerID timerId = (*iter).first; CCryAction::GetCryAction()->RemoveTimer( timerId ); } m_activations.clear(); } const float delay = GetDelayTime(pActInfo); CTimeValue finishTime = gEnv->pTimer->GetFrameStartTime() + delay; IGameFramework::TimerID timerId = CCryAction::GetCryAction()->AddTimer( delay, false, functor(CFlowDelayNode::OnTimer), this ); m_activations[timerId] = SDelayData(finishTime, pActInfo->pInputPorts[0]); } break; } case eFE_Update: CRY_ASSERT( gEnv->IsEditor() ); CRY_ASSERT( !m_activations.empty() ); CTimeValue curTime = gEnv->pTimer->GetFrameStartTime(); while (!m_activations.empty() && m_activations.begin()->second.m_timeout < curTime) { ActivateOutput( pActInfo, 0, m_activations.begin()->second.m_data ); m_activations.erase( m_activations.begin() ); } if (m_activations.empty()) pActInfo->pGraph->SetRegularlyUpdated( pActInfo->myID, false ); break; } }
//------------------------------------------------------------------------ void CGameRulesHoldObjectiveBase::AddEntityId(int type, EntityId entityId, int index, bool isNewEntity, const CTimeValue &timeAdded) { if(index >= HOLD_OBJECTIVE_MAX_ENTITIES) { CryFatalError("Too Many Hold Objective Entities Active!"); } CryLog("CGameRulesHoldObjectiveBase::AddEntityId() received objective, eid=%i, index=%i, isNewEntity=%s, fTimeAdded=%lld, adding to pending queue", entityId, index, isNewEntity ? "true" : "false", timeAdded.GetMilliSecondsAsInt64()); SHoldEntityDetails *pDetails = &m_entities[index]; if (pDetails->m_id) { // We're overwriting another entity, disable the old one CleanUpEntity(pDetails); } // Can't actually activate the entity here since the entity may not be fully initialised yet (horrible ordering issue on game restart), // instead set it as pending and do the actual add in the ::Update(...) method pDetails->m_pendingId = entityId; pDetails->m_isNewEntity = isNewEntity; m_pendingTimeAdded = timeAdded; }
void CFlowDelayNode::Serialize(SActivationInfo *pActInfo, TSerialize ser) { CTimeValue curTime = gEnv->pTimer->GetFrameStartTime(); ser.BeginGroup("Local"); // Editor mode: map with // key: abs time in ms // data: SDelayData // // Game mode: map with // key: timer id (we don't care about it) // data: SDelayData if (ser.IsWriting()) { // when writing, currently relative values are stored! ser.Value("m_activations", m_activations); #if 0 CryLogAlways("CDelayNode write: current time(ms): %f", curTime.GetMilliSeconds()); Activations::iterator iter = m_activations.begin(); while (iter != m_activations.end()) { CryLogAlways("CDelayNode write: ms=%d timevalue(ms): %f",(*iter).first, (*iter).second.m_timeout.GetMilliSeconds()); ++iter; } #endif } else { // FIXME: should we read the curTime from the file // or is the FrameStartTime already set to the serialized value? // ser.Value("curTime", curTime); // when reading we have to differentiate between Editor and Game Mode if (gEnv->IsEditor()) { // we can directly read into the m_activations array // regular update is handled by CFlowGraph ser.Value("m_activations", m_activations); Activations::iterator iter = m_activations.begin(); #if 0 CryLogAlways("CDelayNode read: current time(ms): %f", curTime.GetMilliSeconds()); while (iter != m_activations.end()) { CryLogAlways("CDelayNode read: ms=%d timevalue(ms): %f",(*iter).first, (*iter).second.m_timeout.GetMilliSeconds()); ++iter; } #endif pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, !m_activations.empty()); } else { RemovePendingTimers(); // read serialized activations and re-register at timer Activations::iterator iter; Activations activations; ser.Value("m_activations", activations); for (iter = activations.begin(); iter != activations.end(); ++iter) { CTimeValue relTime = (*iter).second.m_timeout - curTime; IGameFramework::TimerID timerId = CCryAction::GetCryAction()->AddTimer( relTime, false, functor(CFlowDelayNode::OnTimer), this ); m_activations[timerId] = (*iter).second; } } } ser.EndGroup(); }
void gkTimer::UpdateOnFrameStart() { if(!m_bEnabled) return; if (sys_max_fps != 0) { CTimeValue timeFrameMax; timeFrameMax.SetMilliSeconds((int64)(1000.f/(float)sys_max_fps)); static CTimeValue sTimeLast = gEnv->pTimer->GetAsyncTime(); const CTimeValue timeLast = timeFrameMax + sTimeLast; while(timeLast.GetValue() > gEnv->pTimer->GetAsyncTime().GetValue()) { volatile int i=0; while(i++ < 1000); } sTimeLast = gEnv->pTimer->GetAsyncTime(); } assert(m_pfnUpdate); // call Init() before if ((m_nFrameCounter & 127)==0) { // every bunch of frames, check frequency to adapt to // CPU power management clock rate changes #ifdef OS_WIN32 LARGE_INTEGER TTicksPerSec; if (QueryPerformanceFrequency(&TTicksPerSec)) { // if returns false, no performance counter is available m_lTicksPerSec=TTicksPerSec.QuadPart; } #endif } m_nFrameCounter++; #ifdef PROFILING m_fFrameTime = 0.020f; // 20ms = 50fps g_lCurrentTime += (int)(m_fFrameTime*(float)(CTimeValue::TIMEVALUE_PRECISION)); m_lCurrentTime = g_lCurrentTime; m_lLastTime = m_lCurrentTime; RefreshGameTime(m_lCurrentTime); RefreshUITime(m_lCurrentTime); return; #endif int64 now = (*m_pfnUpdate)(); if (m_lForcedGameTime >= 0) { // m_lCurrentTime contains the time, which should be current // but time has passed since Serialize until UpdateOnFrameStart // so we have to make sure to compensate! m_lOffsetTime = m_lForcedGameTime - now + m_lBaseTime; m_lLastTime = now - m_lBaseTime; m_lForcedGameTime = -1; } // Real time. m_lCurrentTime = now - m_lBaseTime; //m_fRealFrameTime = m_fFrameTime = (float)(m_lCurrentTime-m_lLastTime) / (float)(m_lTicksPerSec); m_fRealFrameTime = m_fFrameTime = (float)((double)(m_lCurrentTime-m_lLastTime) / (double)(m_lTicksPerSec)); if( 0 != m_fixed_time_step) // Absolute zero used as a switch. looks wrong, but runs right ;-) { #ifdef OS_WIN32 if (m_fixed_time_step < 0) { // Enforce real framerate by sleeping. float sleep = -m_fixed_time_step - m_fFrameTime; if (sleep > 0) { // while first // float now = GetAsyncCurTime(); bool breaksleep = 0; // if (sleep < 0.01f) // { // for (int i=0; i < 1000000; ++i) // { // if( GetAsyncCurTime() - now > sleep ) // { // breaksleep = 1; // break; // } // } // } if (!breaksleep) { Sleep((unsigned int)(sleep*1000.f)); m_lCurrentTime = (*m_pfnUpdate)() - m_lBaseTime; //m_fRealFrameTime = (float)(m_lCurrentTime-m_lLastTime) / (float)(m_lTicksPerSec); m_fRealFrameTime = (float)((double)(m_lCurrentTime-m_lLastTime) / (double)(m_lTicksPerSec)); } } } #endif m_fFrameTime = abs(m_fixed_time_step); } else { // Clamp it m_fFrameTime = min(m_fFrameTime, m_max_time_step); // Dilate it. m_fFrameTime *= m_time_scale; } if (m_fFrameTime < 0) m_fFrameTime = 0; //----------------------------------------------- if(m_TimeSmoothing>0) { /* if(m_TimeSmoothing>FPS_FRAMES-1) m_TimeSmoothing=FPS_FRAMES-2; if(m_fFrameTime < 0.0000001f) m_fFrameTime = 0.0000001f; m_previousTimes[m_timecount] = m_fFrameTime; m_timecount++; if(m_timecount>=m_TimeSmoothing) m_timecount=0; // average multiple frames together to smooth changes out a bit float total = 0; for(int i = 0 ; i < m_TimeSmoothing+1; i++ ) total += m_previousTimes[i]; if(!total) total = 1; m_fFrameTime=total/(float)(m_TimeSmoothing+1); */ m_fAverageFrameTime = GetAverageFrameTime( 0.25f, m_fFrameTime, m_fAverageFrameTime); m_fFrameTime=m_fAverageFrameTime; } //else { // Adjust. if (m_fFrameTime != m_fRealFrameTime) { float fBefore = GetAsyncCurTime(); int64 nAdjust = (int64)((m_fFrameTime - m_fRealFrameTime) * (double)(m_lTicksPerSec)); m_lCurrentTime += nAdjust; m_lBaseTime -= nAdjust; } } RefreshUITime(m_lCurrentTime); if (m_bGameTimerPaused == false) RefreshGameTime(m_lCurrentTime); m_lLastTime = m_lCurrentTime; if (m_fRealFrameTime < 0.f) // Can happen after loading a saved game and // at any time on dual core AMD machines and laptops :) m_fRealFrameTime = 0.0f; UpdateBlending(); }
void CMPTutorial::Update() { FUNCTION_PROFILER(GetISystem(), PROFILE_GAME); if(!m_enabled && g_pGameCVars->g_PSTutorial_Enabled) EnableTutorialMode(true); else if(m_enabled && !g_pGameCVars->g_PSTutorial_Enabled) EnableTutorialMode(false); m_currentEvent.m_msgDisplayTime -= gEnv->pTimer->GetFrameTime(); if(!m_enabled) { if(m_currentEvent.m_msgDisplayTime < 0.0f && m_currentEvent.m_msgRemovalCondition != eMRC_None) { m_currentEvent.m_msgRemovalCondition = eMRC_None; SAFE_HUD_FUNC(ShowTutorialText(NULL,1)); } } // update the text... must be done even if not enabled, to ensure the 'you may reenable...' // message is shown correctly. if(m_currentEvent.m_numChunks > 0) { // calculate how far through the current sound we are CTimeValue now = gEnv->pTimer->GetFrameStartTime(); float soundTimer = now.GetMilliSeconds() - m_currentEvent.m_soundStartTime; assert(soundTimer >= 0); float soundPercent = 1.0f; if(m_currentEvent.m_soundLength == 0.0f && m_currentEvent.m_pCurrentSound.get()) { m_currentEvent.m_soundLength = m_currentEvent.m_pCurrentSound->GetLengthMs(); } if(m_currentEvent.m_soundLength > 0.0f) { soundPercent = soundTimer / m_currentEvent.m_soundLength; } for(int i=m_currentEvent.m_numChunks-1; i > m_currentEvent.m_currentChunk; --i) { if(m_currentEvent.m_chunks[i].m_startPercent <= soundPercent) { m_currentEvent.m_currentChunk = i; int pos = 2; // 2=bottom, 1=middle IActor *pClientActor = g_pGame->GetIGameFramework()->GetClientActor(); if(pClientActor && pClientActor->GetLinkedVehicle()) { pos = 1; } SAFE_HUD_FUNC(ShowTutorialText(m_currentEvent.m_chunks[i].m_text, pos)); break; } } } if(!m_enabled) return; CPlayer* pPlayer = static_cast<CPlayer*>(g_pGame->GetIGameFramework()->GetClientActor()); if(!pPlayer) return; // don't start until game begins if(pPlayer->GetSpectatorMode() != 0 || g_pGame->GetGameRules()->GetCurrentStateId() != 3) return; if(!m_initialised) { m_initialised = true; if(g_pGame->GetHUD()) { // register as a HUD listener g_pGame->GetHUD()->RegisterListener(this); } // go through entity list and pull out the factories. IEntityItPtr pIt = gEnv->pEntitySystem->GetEntityIterator(); while (!pIt->IsEnd()) { if (IEntity * pEnt = pIt->Next()) { if(pEnt->GetClass() == m_pHQClass) { m_baseList.push_back(pEnt->GetId()); //CryLog("Adding HQ %d to list: %d", pEnt->GetId(), m_baseList.size()); } else if(pEnt->GetClass() == m_pAlienEnergyPointClass) { m_alienEnergyPointList.push_back(pEnt->GetId()); //CryLog("Adding AEP %d to list: %d", pEnt->GetId(), m_alienEnergyPointList.size()); } else if(pEnt->GetClass() == m_pSpawnGroupClass) { m_spawnGroupList.push_back(pEnt->GetId()); //CryLog("Adding spawngroup %d to list: %d", pEnt->GetId(), m_spawnGroupList.size()); } else if(pEnt->GetClass() == m_pFactoryClass) { m_factoryList.push_back(pEnt->GetId()); //CryLog("Adding Factory %d to list: %d", pEnt->GetId(), m_factoryList.size()); } } } } // first the briefing events. These are shown in order. bool showPrompt = CheckBriefingEvents(pPlayer); // player has been killed if(pPlayer->GetHealth() <= 0) { showPrompt = TriggerEvent(eTE_Killed); } else if(!showPrompt) { // check each event type here. Which might take a while. // entering a neutral factory // enter prototype factory // enter hostile factory // find alien crash m_entityCheckTimer -= gEnv->pTimer->GetFrameTime(); if(m_entityCheckTimer < 0.0f) { CheckNearbyEntities(pPlayer); m_entityCheckTimer = ENTITY_CHECK_TIME; } // board vehicle and vehicle tutorials CheckVehicles(pPlayer); // player has been wounded if(pPlayer->GetHealth() < pPlayer->GetMaxHealth()) TriggerEvent(eTE_Wounded); // bases m_baseCheckTimer -= gEnv->pTimer->GetFrameTime(); if(m_baseCheckTimer < 0.0f) { CheckBases(pPlayer); m_baseCheckTimer = ENTITY_CHECK_TIME; } } bool promptShown = false; for(int i=0; i<eTE_NumEvents; ++i) { if(m_events[i].m_status == eMS_Waiting) { if(m_currentEvent.m_msgDisplayTime < -MESSAGE_GAP_TIME) { ShowMessage(m_events[i]); } promptShown = true; break; } } if(!promptShown && (m_currentEvent.m_msgRemovalCondition == eMRC_Time) && (m_currentEvent.m_msgDisplayTime < 0.0f)) { HideMessage(); } }
void CTimer::Add(CIoEvent *apoEvent, const CTimeValue& aoTimeout) { this->Add(apoEvent, aoTimeout.Msec()); }
STransitionSelectionParams::STransitionSelectionParams( const CMovementTransitions& transitions, const CPlayer& player, const CMovementRequest& request, const Vec3& playerPos, const SMovementTransitionsSample& oldSample, const SMovementTransitionsSample& newSample, const bool bHasLockedBodyTarget, const Vec3& targetBodyDirection, const Lineseg& safeLine, const CTimeValue runningDuration, const uint8 _allowedTransitionFlags, const float entitySpeed2D, const float entitySpeed2DAvg, const SExactPositioningTarget*const pExactPositioningTarget, const EStance stance, SActorFrameMovementParams*const pMoveParams) : m_transitionType(eTT_None), m_transitionDistance(0.0f), m_pseudoSpeed(0.0f), m_travelAngle(0.0f), m_jukeAngle(0.0f), m_stance(stance) { // TODO: check for flatness? m_travelAngle = Ang3::CreateRadZ( newSample.bodyDirection, oldSample.moveDirection ); // probably should be oldSample? m_context = request.HasContext() ? request.GetContext() : 0; // -------------------------------------------------------------------------- // Calculate vToMoveTarget, vAfterMoveTarget, distToMoveTarget, distAfterMoveTarget & allowedTransitionFlags Vec3 vToMoveTarget, vAfterMoveTarget; float distToMoveTarget, distAfterMoveTarget; uint8 allowedTransitionFlags = _allowedTransitionFlags; { if (request.HasMoveTarget()) { const Vec3& vMoveTarget = request.GetMoveTarget(); vToMoveTarget = vMoveTarget - playerPos; distToMoveTarget = vToMoveTarget.GetLength2D(); m_future.hasMoveTarget = true; m_future.vMoveTarget = vMoveTarget; // Disallow certain transitions when preparing an exact positioning target // and fudge the distance to make sure we don't start when too close to it if (pExactPositioningTarget && pExactPositioningTarget->preparing && !pExactPositioningTarget->activated) { allowedTransitionFlags &= ~BIT(eTT_Stop); allowedTransitionFlags &= ~BIT(eTT_DirectionChange); const Vec3& exactPosLocation = pExactPositioningTarget->location.t; const float distFromMoveTargetToExactPosSquared = vMoveTarget.GetSquaredDistance(exactPosLocation); const float minimumDangerDistance = 0.1f; const float maxDistanceTraveledPerFrame = gEnv->pTimer->GetFrameTime() * 12.5f; const float dangerDistance = max(minimumDangerDistance, maxDistanceTraveledPerFrame); const bool moveTargetIsWithinDangerDistance = (distFromMoveTargetToExactPosSquared <= sqr(dangerDistance)); if (moveTargetIsWithinDangerDistance) { // Fudge distToMoveTarget so we start at least distanceTraveledInTwoFrames // This only works for eTT_Start transitions (but we disabled the others above) distToMoveTarget = max(0.0f, distToMoveTarget - dangerDistance); } } if (request.HasInflectionPoint()) { const Vec3& vInflectionPoint = request.GetInflectionPoint(); vAfterMoveTarget = vInflectionPoint - vMoveTarget; distAfterMoveTarget = vAfterMoveTarget.GetLength2D(); } else { vAfterMoveTarget.zero(); distAfterMoveTarget = 0.0f; } } else { m_future.hasMoveTarget = false; vToMoveTarget.zero(); vAfterMoveTarget.zero(); distToMoveTarget = distAfterMoveTarget = 0.0f; } } // -------------------------------------------------------------------------- const float maximumSpeedForStart = 0.5f; const float minimumSpeedForWalkStop = 1.0f; const float minimumSpeedForRunStop = 3.5f; const float minimumRunningDurationForRunStop = 1.0f; // (seconds) const float minimumSpeedForJuke = 4.4f*0.6f; // 4.4 is slowest runspeed in Crysis2; 0.6 is the strafing slowdown const float minimumRunningDurationForJuke = 0.1f; // (seconds) if (newSample.pseudoSpeed > 0.0f) { // Either: // - we are in a Stop and want to start again <-- note oldPseudoSpeed cannot be used to detect this, it could be already 0 from prev. frame // - we are in a Start and want to continue starting [but possibly stop or change direction at the movetarget] // - we are stopped and want to Start [but possibly stop or change direction at the movetarget] // - we are moving and want to continue moving [but possibly stop or change direction at the movetarget] m_pseudoSpeed = newSample.pseudoSpeed; if ( (allowedTransitionFlags & (1<<eTT_Start)) && (entitySpeed2DAvg <= maximumSpeedForStart) ) { //New sample's movement direction is accurate for start transitions. m_travelAngle = Ang3::CreateRadZ( newSample.bodyDirection, newSample.moveDirection ); m_transitionType = eTT_Start; m_bPredicted = true; m_transitionDistance = distToMoveTarget; m_future.vMoveDirection = newSample.moveDirection; const Vec3 realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : m_future.vMoveDirection; m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, m_future.vMoveDirection ); m_future.qOrientation = Quat::CreateRotationVDir( realTargetBodyDirection ); MovementTransitionsLog("[%x] Juke failed because we are trying to start", gEnv->pRenderer->GetFrameID()); } else // at the moment start & stop are mutually exclusive { if (!(allowedTransitionFlags & (1<<eTT_Start))) MovementTransitionsLog("[%x] Start failed because current animation state (%s) doesn't support starting", gEnv->pRenderer->GetFrameID(), const_cast<CPlayer&>(player).GetAnimationGraphState() ? const_cast<CPlayer&>(player).GetAnimationGraphState()->GetCurrentStateName() : ""); else MovementTransitionsLog("[%x] Start failed because speed is %f while maximum %f is allowed", gEnv->pRenderer->GetFrameID(), player.GetAnimatedCharacter()->GetEntitySpeedHorizontal(), maximumSpeedForStart); m_transitionType = eTT_None; // try immediate directionchange first if (allowedTransitionFlags & (1<<eTT_DirectionChange)) { if ( ((oldSample.pseudoSpeed == AISPEED_RUN) || (oldSample.pseudoSpeed == AISPEED_SPRINT)) && (runningDuration > minimumRunningDurationForJuke) && (entitySpeed2D >= minimumSpeedForJuke) ) { if (gEnv->pAISystem && !gEnv->pAISystem->GetSmartObjectManager()->CheckSmartObjectStates(player.GetEntity(), "Busy")) { // === IMMEDIATE DIRECTIONCHANGE === // --------------------------------- // Assume a directionchange after moving forward for one meter (assumedDistanceToJuke=1) // Look up a transition math for that proposed directionchange // --------------------------------- m_pseudoSpeed = oldSample.pseudoSpeed; m_transitionDistance = -1; float assumedDistanceToJuke = 1.0f; CRY_ASSERT(assumedDistanceToJuke > FLT_EPSILON); Vec3 vToProposedMoveTarget = newSample.moveDirection * distToMoveTarget; // vector from current position to current movetarget Vec3 vToProposedJukePoint = oldSample.moveDirection * assumedDistanceToJuke; Vec3 vAfterProposedJukePoint = (vToProposedMoveTarget - vToProposedJukePoint).GetNormalizedSafe(newSample.moveDirection); m_jukeAngle = Ang3::CreateRadZ( vToProposedJukePoint, vAfterProposedJukePoint ); m_transitionType = eTT_DirectionChange; m_bPredicted = false; m_future.vMoveDirection = vAfterProposedJukePoint; Vec3 realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : vAfterProposedJukePoint; m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, vAfterProposedJukePoint ); MovementTransitionsLog("[%x] Considering angle %+3.2f", gEnv->pRenderer->GetFrameID(), RAD2DEG(m_jukeAngle)); const STransition* pTransition = NULL; int index = -1; STransitionMatch bestMatch; transitions.FindBestMatch(*this, &pTransition, &index, &bestMatch); if (pTransition) { // ------------------------------------------------------- // We found a transition matching our guess. Adjust juke point to match the distance of the transition we found // ------------------------------------------------------- float proposedTransitionDistance = (pTransition->minDistance + pTransition->maxDistance)*0.5f; CRY_ASSERT(proposedTransitionDistance > FLT_EPSILON); vToProposedJukePoint = oldSample.moveDirection * proposedTransitionDistance; vAfterProposedJukePoint = vToProposedMoveTarget - vToProposedJukePoint; float proposedDistAfterMoveTarget = vAfterProposedJukePoint.GetLength2D(); vAfterProposedJukePoint.NormalizeSafe(newSample.moveDirection); if (proposedDistAfterMoveTarget >= transitions.GetMinDistanceAfterDirectionChange()) { m_jukeAngle = Ang3::CreateRadZ( vToProposedJukePoint, vAfterProposedJukePoint ); m_future.vMoveDirection = vAfterProposedJukePoint; realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : vAfterProposedJukePoint; m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, vAfterProposedJukePoint ); MovementTransitionsLog("[%x] Proposing angle %+3.2f", gEnv->pRenderer->GetFrameID(), RAD2DEG(m_jukeAngle)); m_transitionDistance = proposedTransitionDistance; m_future.qOrientation = Quat::CreateRotationVDir( realTargetBodyDirection ); } else { MovementTransitionsLog("[%x] Immediate Juke failed because not enough distance after the juke (distance needed = %f, max distance = %f)", gEnv->pRenderer->GetFrameID(), transitions.GetMinDistanceAfterDirectionChange(), proposedDistAfterMoveTarget); m_transitionType = eTT_None; } } else { MovementTransitionsLog("[%x] Immediate Juke failed because no animation found for this angle/stance/speed", gEnv->pRenderer->GetFrameID()); m_transitionType = eTT_None; } } else { MovementTransitionsLog("[%x] Immediate Juke failed because smart object is playing", gEnv->pRenderer->GetFrameID()); } } else { if (!((oldSample.pseudoSpeed == AISPEED_RUN) || (oldSample.pseudoSpeed == AISPEED_SPRINT))) { MovementTransitionsLog("[%x] Immediate Juke failed because current pseudospeed (%f) is not supported", gEnv->pRenderer->GetFrameID(), oldSample.pseudoSpeed); } else if (runningDuration <= minimumRunningDurationForJuke) { MovementTransitionsLog("[%x] Immediate Juke failed because running only %f seconds while more than %f is needed", gEnv->pRenderer->GetFrameID(), runningDuration.GetSeconds(), minimumRunningDurationForJuke); } else //if (entitySpeed2D < minimumSpeedForJuke) { MovementTransitionsLog("[%x] Immediate Juke failed because speed is only %f while %f is needed", gEnv->pRenderer->GetFrameID(), entitySpeed2D, minimumSpeedForJuke); } } } else { MovementTransitionsLog("[%x] Immediate Juke failed because current animation state (%s) doesn't support juking", gEnv->pRenderer->GetFrameID(), const_cast<CPlayer&>(player).GetAnimationGraphState() ? const_cast<CPlayer&>(player).GetAnimationGraphState()->GetCurrentStateName() : NULL); } if (m_transitionType == eTT_None) // directionchange wasn't found { if ((allowedTransitionFlags & (1<<eTT_Stop)) && (distAfterMoveTarget < FLT_EPSILON)) { // === PREDICTED STOP === // We want to stop in the future m_transitionType = eTT_Stop; m_bPredicted = true; m_transitionDistance = distToMoveTarget; m_future.vMoveDirection = vToMoveTarget.GetNormalizedSafe(newSample.moveDirection); m_arrivalAngle = request.HasDesiredBodyDirectionAtTarget() ? Ang3::CreateRadZ( request.GetDesiredBodyDirectionAtTarget(), m_future.vMoveDirection ) : 0.0f; m_future.qOrientation = request.HasDesiredBodyDirectionAtTarget() ? Quat::CreateRotationVDir(request.GetDesiredBodyDirectionAtTarget()) : Quat::CreateRotationVDir(m_future.vMoveDirection); MovementTransitionsLog("[%x] Predicted Juke failed because we are trying to stop", gEnv->pRenderer->GetFrameID()); } else if ((allowedTransitionFlags & (1<<eTT_DirectionChange)) && (distAfterMoveTarget >= transitions.GetMinDistanceAfterDirectionChange())) { // === PREDICTED DIRECTIONCHANGE === // We want to change direction in the future // NOTE: This logic will fail if we trigger the juke really late, because then the distToMoveTarget will be very small and the angle calculation not precise m_transitionType = eTT_DirectionChange; m_bPredicted = true; m_transitionDistance = distToMoveTarget; m_jukeAngle = Ang3::CreateRadZ( vToMoveTarget, vAfterMoveTarget ); m_future.vMoveDirection = vAfterMoveTarget.GetNormalized(); const Vec3 realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : m_future.vMoveDirection; m_future.qOrientation = Quat::CreateRotationVDir( realTargetBodyDirection ); m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, m_future.vMoveDirection ); } } } } else // if (newSample.pseudoSpeed <= 0.0f) { // Either: // - we are in a Stop and want to continue stopping // - we are moving and suddenly want to stop // - we are in a Start and want to stop <-- oldPseudoSpeed logic is wrong, oldPseudoSpeed will be 0 for a while so we should use real velocity // - we are stopped already and just want to stay stopped MovementTransitionsLog("[%x] Juke failed because we are not running or trying to stop", gEnv->pRenderer->GetFrameID()); MovementTransitionsLog("[%x] Start failed because we are not requesting movement", gEnv->pRenderer->GetFrameID()); if ( (( (oldSample.pseudoSpeed == AISPEED_RUN) || (oldSample.pseudoSpeed == AISPEED_SPRINT)) && (runningDuration > minimumRunningDurationForRunStop) && (entitySpeed2D >= minimumSpeedForRunStop)) || ((oldSample.pseudoSpeed == AISPEED_WALK) && (entitySpeed2D >= minimumSpeedForWalkStop)) ) { if (allowedTransitionFlags & (1<<eTT_Stop)) { // === IMMEDIATE STOP === if( gEnv->pAISystem ) { ISmartObjectManager* pSmartObjectManager = gEnv->pAISystem->GetSmartObjectManager(); if (!pSmartObjectManager || !pSmartObjectManager->CheckSmartObjectStates(player.GetEntity(), "Busy")) { // Trigger immediate stop when currently running and suddenly wanting to stop. // // NOTE: If this happens right before a forbidden area and the safeLine is not correct // or the Stop transition distance isn't configured properly the AI will enter it.. // m_pseudoSpeed = oldSample.pseudoSpeed; m_transitionDistance = -1; m_arrivalAngle = 0.0f; m_transitionType = eTT_Stop; m_bPredicted = false; const STransition* pTransition = NULL; int index = -1; STransitionMatch bestMatch; transitions.FindBestMatch(*this, &pTransition, &index, &bestMatch); float minDistanceForStop = pTransition ? pTransition->minDistance : 0.0f; bool bIsOnSafeLine = IsOnSafeLine(safeLine, playerPos, newSample.moveDirection, minDistanceForStop); if (bIsOnSafeLine) { m_transitionDistance = minDistanceForStop; m_future.vMoveDirection = newSample.moveDirection; m_future.qOrientation = Quat::CreateRotationVDir(newSample.moveDirection); pMoveParams->desiredVelocity = player.GetEntity()->GetWorldRotation().GetInverted() * player.GetLastRequestedVelocity(); float maxSpeed = player.GetStanceMaxSpeed(m_stance); if (maxSpeed > 0.01f) pMoveParams->desiredVelocity /= maxSpeed; } else { m_transitionType = eTT_None; } } } } } } if (request.HasDesiredSpeed()) { m_future.speed = request.GetDesiredTargetSpeed(); } }
static void SaveToXML(const XmlNodeRef& n, const char* name, const CTimeValue& t) { n->setAttr(name, t.GetMilliSecondsAsInt64()); }
void CMPTutorial::ShowMessage(STutorialEvent& event) { assert(event.m_status == eMS_Waiting); // cancel previous sound if necessary if(m_currentEvent.m_pCurrentSound.get()) { m_currentEvent.m_pCurrentSound->Stop(ESoundStopMode_AtOnce); m_currentEvent.m_pCurrentSound = NULL; } // also play the sound here if(gEnv->pSoundSystem && event.m_soundName.compare("None")) { string soundName = "mp_american/" + event.m_soundName; m_currentEvent.m_pCurrentSound = gEnv->pSoundSystem->CreateSound(soundName.c_str(),FLAG_SOUND_VOICE); if (m_currentEvent.m_pCurrentSound.get()) { m_currentEvent.m_pCurrentSound->AddEventListener(this, "mptutorial"); m_currentEvent.m_msgDisplayTime = 1000.0f; // will be removed by sound finish event m_currentEvent.m_pCurrentSound->Play(); m_currentEvent.m_soundLength = m_currentEvent.m_pCurrentSound->GetLengthMs(); // NB this almost certainly returns 0 as the sound isn't buffered yet :( CTimeValue time = gEnv->pTimer->GetFrameStartTime(); m_currentEvent.m_soundStartTime = time.GetMilliSeconds(); } } else { m_currentEvent.m_msgDisplayTime = MESSAGE_DISPLAY_TIME; } // create the localised text string from enum string msg; wstring localizedString; wstring finalString; ILocalizationManager *pLocalizationMan = gEnv->pSystem->GetLocalizationManager(); if(!pLocalizationMan) return; if(m_currentEvent.m_pCurrentSound) { msg = GetSoundKey(m_currentEvent.m_pCurrentSound->GetName()); pLocalizationMan->GetSubtitle(msg.c_str(), localizedString, true); } else { msg = "@mp_Tut" + event.m_name; pLocalizationMan->LocalizeString(msg, localizedString); } if(localizedString.empty()) return; // fill out keynames if necessary if(!strcmp(event.m_name, "BoardVehicle")) { // special case for this event - has 5 actions associated... string action1 = "@cc_"; string action2 = "@cc_"; string action3 = "@cc_"; string action4 = "@cc_"; string action5 = "@cc_"; action1 += GetKeyName("v_changeseat1"); action2 += GetKeyName("v_changeseat2"); action3 += GetKeyName("v_changeseat3"); action4 += GetKeyName("v_changeseat4"); action5 += GetKeyName("v_changeseat5"); wstring wActions[5]; pLocalizationMan->LocalizeString(action1, wActions[0]); pLocalizationMan->LocalizeString(action2, wActions[1]); pLocalizationMan->LocalizeString(action3, wActions[2]); pLocalizationMan->LocalizeString(action4, wActions[3]); pLocalizationMan->LocalizeString(action5, wActions[4]); const wchar_t* params[] = {wActions[0].c_str(), wActions[1].c_str(), wActions[2].c_str(), wActions[3].c_str(), wActions[4].c_str()}; pLocalizationMan->FormatStringMessage(finalString, localizedString, params, 5); } else if(!event.m_action.empty()) { // first get the key name (eg 'mouse1') and make it into a format the localization system understands string action = "@cc_"; action += GetKeyName(event.m_action.c_str()); wstring wActionString; pLocalizationMan->LocalizeString(action, wActionString); // now place this in the right place in the string. pLocalizationMan->FormatStringMessage(finalString, localizedString, wActionString.c_str()); } else { finalString = localizedString; } // split the text into chunks (to allow super-long strings to fit within the window) CreateTextChunks(finalString); // set status event.m_status = eMS_Displaying; // set message removal condition m_currentEvent.m_msgRemovalCondition = event.m_removal; if(!event.m_soundName.compare("None") && m_currentEvent.m_msgRemovalCondition == eMRC_SoundFinished) m_currentEvent.m_msgRemovalCondition = eMRC_Time; // since for now there're some that don't have audio. }
float GetDuration(int iA, int iB) { CTimeValue dt = m_stickHistoryTime[iB] - m_stickHistoryTime[iA]; return fabs_tpl(dt.GetSeconds()); }