diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index d1923cf2a8..e6c53c73bc 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -59,7 +59,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwlua - luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant + luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing types/levelledlist types/terminal diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 02b292adce..0c77329f48 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -65,6 +65,7 @@ #include "mwsound/soundmanagerimp.hpp" #include "mwworld/class.hpp" +#include "mwworld/datetimemanager.hpp" #include "mwworld/worldimp.hpp" #include "mwrender/vismask.hpp" @@ -200,9 +201,6 @@ bool OMW::Engine::frame(float frametime) mSoundManager->update(frametime); } - // Main menu opened? Then scripts are also paused. - bool paused = mWindowManager->containsMode(MWGui::GM_MainMenu); - { ScopedProfile profile(frameStart, frameNumber, *timer, *stats); // Should be called after input manager update and before any change to the game world. @@ -216,14 +214,14 @@ bool OMW::Engine::frame(float frametime) mStateManager->update(frametime); } - bool guiActive = mWindowManager->isGuiMode(); + bool paused = mWorld->getTimeManager()->isPaused(); { ScopedProfile profile(frameStart, frameNumber, *timer, *stats); if (mStateManager->getState() != MWBase::StateManager::State_NoGame) { - if (!paused) + if (!mWindowManager->containsMode(MWGui::GM_MainMenu)) { if (mWorld->getScriptsEnabled()) { @@ -237,9 +235,9 @@ bool OMW::Engine::frame(float frametime) mWorld->getWorldScene().markCellAsUnchanged(); } - if (!guiActive) + if (!paused) { - double hours = (frametime * mWorld->getTimeScaleFactor()) / 3600.0; + double hours = (frametime * mWorld->getTimeManager()->getGameTimeScale()) / 3600.0; mWorld->advanceTime(hours, true); mWorld->rechargeItems(frametime, true); } @@ -252,13 +250,13 @@ bool OMW::Engine::frame(float frametime) if (mStateManager->getState() != MWBase::StateManager::State_NoGame) { - mMechanicsManager->update(frametime, guiActive); + mMechanicsManager->update(frametime, paused); } if (mStateManager->getState() == MWBase::StateManager::State_Running) { MWWorld::Ptr player = mWorld->getPlayerPtr(); - if (!guiActive && player.getClass().getCreatureStats(player).isDead()) + if (!paused && player.getClass().getCreatureStats(player).isDead()) mStateManager->endGame(); } } @@ -269,7 +267,7 @@ bool OMW::Engine::frame(float frametime) if (mStateManager->getState() != MWBase::StateManager::State_NoGame) { - mWorld->updatePhysics(frametime, guiActive, frameStart, frameNumber, *stats); + mWorld->updatePhysics(frametime, paused, frameStart, frameNumber, *stats); } } @@ -279,7 +277,7 @@ bool OMW::Engine::frame(float frametime) if (mStateManager->getState() != MWBase::StateManager::State_NoGame) { - mWorld->update(frametime, guiActive); + mWorld->update(frametime, paused); } } @@ -928,7 +926,7 @@ void OMW::Engine::go() } // Start the main rendering loop - double simulationTime = 0.0; + MWWorld::DateTimeManager& timeManager = *mWorld->getTimeManager(); Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(mEnvironment.getFrameRateLimit()); const std::chrono::steady_clock::duration maxSimulationInterval(std::chrono::milliseconds(200)); while (!mViewer->done() && !mStateManager->hasQuitRequest()) @@ -936,21 +934,18 @@ void OMW::Engine::go() const double dt = std::chrono::duration_cast>( std::min(frameRateLimiter.getLastFrameDuration(), maxSimulationInterval)) .count() - * mEnvironment.getWorld()->getSimulationTimeScale(); + * timeManager.getSimulationTimeScale(); - mViewer->advance(simulationTime); + mViewer->advance(timeManager.getSimulationTime()); if (!frame(dt)) { std::this_thread::sleep_for(std::chrono::milliseconds(5)); continue; } - else - { - bool guiActive = mWindowManager->isGuiMode(); - if (!guiActive) - simulationTime += dt; - } + timeManager.updateIsPaused(); + if (!timeManager.isPaused()) + timeManager.setSimulationTime(timeManager.getSimulationTime() + dt); if (stats) { diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6f40ff7031..40d57cbb6e 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -92,6 +92,7 @@ namespace MWWorld class ESMStore; class RefData; class Cell; + class DateTimeManager; typedef std::vector> PtrMovementList; } @@ -209,15 +210,9 @@ namespace MWBase virtual void advanceTime(double hours, bool incremental = false) = 0; ///< Advance in-game time. - virtual std::string_view getMonthName(int month = -1) const = 0; - ///< Return name of month (-1: current month) - virtual MWWorld::TimeStamp getTimeStamp() const = 0; ///< Return current in-game time and number of day since new game start. - virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0; - ///< Return current in-game date and time. - virtual bool toggleSky() = 0; ///< \return Resulting mode @@ -239,12 +234,6 @@ namespace MWBase virtual void modRegion(const ESM::RefId& regionid, const std::vector& chances) = 0; - virtual float getTimeScaleFactor() const = 0; - - virtual float getSimulationTimeScale() const = 0; - - virtual void setSimulationTimeScale(float scale) = 0; - virtual void changeToInteriorCell( std::string_view cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true) = 0; @@ -612,6 +601,8 @@ namespace MWBase virtual MWRender::PostProcessor* getPostProcessor() = 0; + virtual MWWorld::DateTimeManager* getTimeManager() = 0; + virtual void setActorActive(const MWWorld::Ptr& ptr, bool value) = 0; }; } diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index 57c768aeac..46f77f6dc4 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -13,6 +13,7 @@ #include "../mwbase/world.hpp" #include "../mwdialogue/keywordsearch.hpp" +#include "../mwworld/datetimemanager.hpp" namespace MWGui { @@ -253,8 +254,9 @@ namespace MWGui std::ostringstream os; - os << itr->mDayOfMonth << ' ' << MWBase::Environment::get().getWorld()->getMonthName(itr->mMonth) - << " (" << dayStr << " " << (itr->mDay) << ')'; + os << itr->mDayOfMonth << ' ' + << MWBase::Environment::get().getWorld()->getTimeManager()->getMonthName(itr->mMonth) << " (" + << dayStr << " " << (itr->mDay) << ')'; timestamp_buffer = os.str(); } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp index 53136743d9..71b39328f6 100644 --- a/apps/openmw/mwgui/savegamedialog.cpp +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -29,6 +29,7 @@ #include "../mwbase/statemanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwstate/character.hpp" @@ -422,8 +423,9 @@ namespace MWGui hour = 12; text << mCurrentSlot->mProfile.mInGameTime.mDay << " " - << MWBase::Environment::get().getWorld()->getMonthName(mCurrentSlot->mProfile.mInGameTime.mMonth) << " " - << hour << " " << (pm ? "#{Calendar:pm}" : "#{Calendar:am}"); + << MWBase::Environment::get().getWorld()->getTimeManager()->getMonthName( + mCurrentSlot->mProfile.mInGameTime.mMonth) + << " " << hour << " " << (pm ? "#{Calendar:pm}" : "#{Calendar:am}"); if (Settings::Manager::getBool("timeplayed", "Saves")) { diff --git a/apps/openmw/mwgui/waitdialog.cpp b/apps/openmw/mwgui/waitdialog.cpp index 0c93773278..ab17031168 100644 --- a/apps/openmw/mwgui/waitdialog.cpp +++ b/apps/openmw/mwgui/waitdialog.cpp @@ -19,6 +19,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwmechanics/actorutil.hpp" @@ -154,17 +155,17 @@ namespace MWGui onHourSliderChangedPosition(mHourSlider, 0); mHourSlider->setScrollPosition(0); - std::string_view month = MWBase::Environment::get().getWorld()->getMonthName(); - int hour = static_cast(MWBase::Environment::get().getWorld()->getTimeStamp().getHour()); + const MWWorld::DateTimeManager& timeManager = *MWBase::Environment::get().getWorld()->getTimeManager(); + std::string_view month = timeManager.getMonthName(); + int hour = static_cast(timeManager.getTimeStamp().getHour()); bool pm = hour >= 12; if (hour >= 13) hour -= 12; if (hour == 0) hour = 12; - ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp(); - std::string daysPassed = Misc::StringUtils::format( - "(#{Calendar:day} %i)", MWBase::Environment::get().getWorld()->getTimeStamp().getDay()); + ESM::EpochTimeStamp currentDate = timeManager.getEpochTimeStamp(); + std::string daysPassed = Misc::StringUtils::format("(#{Calendar:day} %i)", timeManager.getTimeStamp().getDay()); std::string_view formattedHour(pm ? "#{Calendar:pm}" : "#{Calendar:am}"); std::string dateTimeText = Misc::StringUtils::format("%i %s %s %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour); diff --git a/apps/openmw/mwlua/context.hpp b/apps/openmw/mwlua/context.hpp index e743bfa50a..68b46164d6 100644 --- a/apps/openmw/mwlua/context.hpp +++ b/apps/openmw/mwlua/context.hpp @@ -11,7 +11,7 @@ namespace MWLua { class LuaEvents; class LuaManager; - class WorldView; + class ObjectLists; struct Context { @@ -19,7 +19,7 @@ namespace MWLua LuaManager* mLuaManager; LuaUtil::LuaState* mLua; LuaUtil::UserdataSerializer* mSerializer; - WorldView* mWorldView; + ObjectLists* mObjectLists; LuaEvents* mLuaEvents; }; diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 2a4368810c..28bf9f7999 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -20,6 +20,7 @@ #include "../mwbase/statemanager.hpp" #include "../mwworld/action.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/manualref.hpp" #include "../mwworld/store.hpp" @@ -28,7 +29,7 @@ #include "luaevents.hpp" #include "luamanagerimp.hpp" #include "mwscriptbindings.hpp" -#include "worldview.hpp" +#include "objectlists.hpp" #include "camerabindings.hpp" #include "cellbindings.hpp" @@ -61,13 +62,13 @@ namespace MWLua static void addTimeBindings(sol::table& api, const Context& context, bool global) { - MWBase::World* world = MWBase::Environment::get().getWorld(); + MWWorld::DateTimeManager* timeManager = MWBase::Environment::get().getWorld()->getTimeManager(); - api["getSimulationTime"] = [world = context.mWorldView]() { return world->getSimulationTime(); }; - api["getSimulationTimeScale"] = [world]() { return world->getSimulationTimeScale(); }; - api["getGameTime"] = [world = context.mWorldView]() { return world->getGameTime(); }; - api["getGameTimeScale"] = [world = context.mWorldView]() { return world->getGameTimeScale(); }; - api["isWorldPaused"] = [world = context.mWorldView]() { return world->isPaused(); }; + api["getSimulationTime"] = [timeManager]() { return timeManager->getSimulationTime(); }; + api["getSimulationTimeScale"] = [timeManager]() { return timeManager->getSimulationTimeScale(); }; + api["getGameTime"] = [timeManager]() { return timeManager->getGameTime(); }; + api["getGameTimeScale"] = [timeManager]() { return timeManager->getGameTimeScale(); }; + api["isWorldPaused"] = [timeManager]() { return timeManager->isPaused(); }; api["getRealTime"] = []() { return std::chrono::duration(std::chrono::steady_clock::now().time_since_epoch()).count(); }; @@ -75,15 +76,21 @@ namespace MWLua if (!global) return; - api["setGameTimeScale"] = [world = context.mWorldView](double scale) { world->setGameTimeScale(scale); }; - - api["setSimulationTimeScale"] = [context, world](float scale) { - context.mLuaManager->addAction([scale, world] { world->setSimulationTimeScale(scale); }); + api["setGameTimeScale"] = [timeManager](double scale) { timeManager->setGameTimeScale(scale); }; + api["setSimulationTimeScale"] = [context, timeManager](float scale) { + context.mLuaManager->addAction([scale, timeManager] { timeManager->setSimulationTimeScale(scale); }); }; - // TODO: Ability to pause/resume world from Lua (needed for UI dehardcoding) - // api["pause"] = []() {}; - // api["resume"] = []() {}; + api["pause"] + = [timeManager](sol::optional tag) { timeManager->pause(tag.value_or("paused")); }; + api["unpause"] + = [timeManager](sol::optional tag) { timeManager->unpause(tag.value_or("paused")); }; + api["getPausedTags"] = [timeManager](sol::this_state lua) { + sol::table res(lua, sol::create); + for (const std::string& tag : timeManager->getPausedTags()) + res[tag] = tag; + return res; + }; } static sol::table initContentFilesBindings(sol::state_view& lua) @@ -228,12 +235,12 @@ namespace MWLua static sol::table initWorldPackage(const Context& context) { sol::table api(context.mLua->sol(), sol::create); - WorldView* worldView = context.mWorldView; + ObjectLists* objectLists = context.mObjectLists; addTimeBindings(api, context, true); addCellGetters(api, context); api["mwscript"] = initMWScriptBindings(context); - api["activeActors"] = GObjectList{ worldView->getActorsInScene() }; - api["players"] = GObjectList{ worldView->getPlayers() }; + api["activeActors"] = GObjectList{ objectLists->getActorsInScene() }; + api["players"] = GObjectList{ objectLists->getPlayers() }; api["createObject"] = [](std::string_view recordId, sol::optional count) -> GObject { MWWorld::ManualRef mref(*MWBase::Environment::get().getESMStore(), ESM::RefId::deserializeText(recordId)); const MWWorld::Ptr& ptr = mref.getPtr(); @@ -291,11 +298,11 @@ namespace MWLua std::map initCommonPackages(const Context& context) { sol::state_view lua = context.mLua->sol(); - WorldView* w = context.mWorldView; + MWWorld::DateTimeManager* tm = MWBase::Environment::get().getWorld()->getTimeManager(); return { { "openmw.async", LuaUtil::getAsyncPackageInitializer( - lua, [w] { return w->getSimulationTime(); }, [w] { return w->getGameTime(); }) }, + lua, [tm] { return tm->getSimulationTime(); }, [tm] { return tm->getGameTime(); }) }, { "openmw.core", initCorePackage(context) }, { "openmw.types", initTypesPackage(context) }, { "openmw.util", LuaUtil::initUtilPackage(lua) }, diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 2dffc42ea7..f2705501e5 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -23,6 +23,7 @@ #include "../mwrender/postprocessor.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/scene.hpp" @@ -75,7 +76,7 @@ namespace MWLua context.mIsGlobal = true; context.mLuaManager = this; context.mLua = &mLua; - context.mWorldView = &mWorldView; + context.mObjectLists = &mObjectLists; context.mLuaEvents = &mLuaEvents; context.mSerializer = mGlobalSerializer.get(); @@ -127,8 +128,6 @@ namespace MWLua if (mPlayer.isEmpty()) return; // The game is not started yet. - float frameDuration = MWBase::Environment::get().getFrameDuration(); - MWWorld::Ptr newPlayerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr(); if (!(getId(mPlayer) == getId(newPlayerPtr))) throw std::logic_error("Player RefNum was changed unexpectedly"); @@ -138,7 +137,7 @@ namespace MWLua MWBase::Environment::get().getWorldModel()->registerPtr(mPlayer); } - mWorldView.update(); + mObjectLists.update(); std::erase_if(mActiveLocalScripts, [](const LocalScripts* l) { return l->getPtrOrEmpty().isEmpty() || l->getPtrOrEmpty().getRefData().isDeleted(); @@ -150,15 +149,12 @@ namespace MWLua mLuaEvents.finalizeEventBatch(); - if (!mWorldView.isPaused()) - { // Update time and process timers - double simulationTime = mWorldView.getSimulationTime() + frameDuration; - mWorldView.setSimulationTime(simulationTime); - double gameTime = mWorldView.getGameTime(); - - mGlobalScripts.processTimers(simulationTime, gameTime); + MWWorld::DateTimeManager& timeManager = *MWBase::Environment::get().getWorld()->getTimeManager(); + if (!timeManager.isPaused()) + { + mGlobalScripts.processTimers(timeManager.getSimulationTime(), timeManager.getGameTime()); for (LocalScripts* scripts : mActiveLocalScripts) - scripts->processTimers(simulationTime, gameTime); + scripts->processTimers(timeManager.getSimulationTime(), timeManager.getGameTime()); } // Run event handlers for events that were sent before `finalizeEventBatch`. @@ -171,8 +167,9 @@ namespace MWLua // Run engine handlers mEngineEvents.callEngineHandlers(); - if (!mWorldView.isPaused()) + if (!timeManager.isPaused()) { + float frameDuration = MWBase::Environment::get().getFrameDuration(); for (LocalScripts* scripts : mActiveLocalScripts) scripts->update(frameDuration); mGlobalScripts.update(frameDuration); @@ -220,17 +217,19 @@ namespace MWLua // We apply input events in `synchronizedUpdate` rather than in `update` in order to reduce input latency. mProcessingInputEvents = true; PlayerScripts* playerScripts = dynamic_cast(mPlayer.getRefData().getLuaScripts()); - if (playerScripts && !MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)) + MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); + if (playerScripts && !windowManager->containsMode(MWGui::GM_MainMenu)) { for (const auto& event : mInputEvents) playerScripts->processInputEvent(event); } mInputEvents.clear(); if (playerScripts) - playerScripts->onFrame(mWorldView.isPaused() ? 0.0 : MWBase::Environment::get().getFrameDuration()); + playerScripts->onFrame(MWBase::Environment::get().getWorld()->getTimeManager()->isPaused() + ? 0.0 + : MWBase::Environment::get().getFrameDuration()); mProcessingInputEvents = false; - MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); for (const std::string& message : mUIMessages) windowManager->messageBox(message); mUIMessages.clear(); @@ -262,7 +261,7 @@ namespace MWLua mLuaEvents.clear(); mEngineEvents.clear(); mInputEvents.clear(); - mWorldView.clear(); + mObjectLists.clear(); mGlobalScripts.removeAllScripts(); mGlobalScriptsStarted = false; mNewGameStarted = false; @@ -284,8 +283,8 @@ namespace MWLua return; if (!mPlayer.isEmpty()) throw std::logic_error("Player is initialized twice"); - mWorldView.objectAddedToScene(ptr); - mWorldView.setPlayer(ptr); + mObjectLists.objectAddedToScene(ptr); + mObjectLists.setPlayer(ptr); mPlayer = ptr; LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); if (!localScripts) @@ -314,7 +313,7 @@ namespace MWLua void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr) { - mWorldView.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet. + mObjectLists.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet. mEngineEvents.addToQueue(EngineEvents::OnActive{ getId(ptr) }); LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); @@ -334,7 +333,7 @@ namespace MWLua void LuaManager::objectRemovedFromScene(const MWWorld::Ptr& ptr) { - mWorldView.objectRemovedFromScene(ptr); + mObjectLists.objectRemovedFromScene(ptr); LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); if (localScripts) { @@ -400,7 +399,8 @@ namespace MWLua { writer.startRecord(ESM::REC_LUAM); - mWorldView.save(writer); + writer.writeHNT("LUAW", MWBase::Environment::get().getWorld()->getTimeManager()->getSimulationTime()); + writer.writeFormId(MWBase::Environment::get().getWorldModel()->getLastGeneratedRefNum(), true); ESM::LuaScripts globalScripts; mGlobalScripts.save(globalScripts); globalScripts.save(writer); @@ -414,7 +414,14 @@ namespace MWLua if (type != ESM::REC_LUAM) throw std::runtime_error("ESM::REC_LUAM is expected"); - mWorldView.load(reader); + double simulationTime; + reader.getHNT(simulationTime, "LUAW"); + MWBase::Environment::get().getWorld()->getTimeManager()->setSimulationTime(simulationTime); + ESM::FormId lastGenerated = reader.getFormId(true); + if (lastGenerated.hasContentFile()) + throw std::runtime_error("Last generated RefNum is invalid"); + MWBase::Environment::get().getWorldModel()->setLastGeneratedRefNum(lastGenerated); + ESM::LuaScripts globalScripts; globalScripts.load(reader); mLuaEvents.load(mLua.sol(), reader, mContentFileMapping, mGlobalLoader.get()); diff --git a/apps/openmw/mwlua/luamanagerimp.hpp b/apps/openmw/mwlua/luamanagerimp.hpp index e9311fc854..bb7a2535ed 100644 --- a/apps/openmw/mwlua/luamanagerimp.hpp +++ b/apps/openmw/mwlua/luamanagerimp.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -17,7 +18,7 @@ #include "localscripts.hpp" #include "luaevents.hpp" #include "object.hpp" -#include "worldview.hpp" +#include "objectlists.hpp" namespace MWLua { @@ -156,7 +157,7 @@ namespace MWLua GlobalScripts mGlobalScripts{ &mLua }; std::set mActiveLocalScripts; - WorldView mWorldView; + ObjectLists mObjectLists; MWWorld::Ptr mPlayer; diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index 79c17dc5b8..c4c2521c80 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -10,7 +10,7 @@ #include "../mwphysics/raycasting.hpp" #include "luamanagerimp.hpp" -#include "worldview.hpp" +#include "objectlists.hpp" namespace sol { @@ -25,7 +25,7 @@ namespace MWLua sol::table initNearbyPackage(const Context& context) { sol::table api(context.mLua->sol(), sol::create); - WorldView* worldView = context.mWorldView; + ObjectLists* objectLists = context.mObjectLists; sol::usertype rayResult = context.mLua->sol().new_usertype("RayCastingResult"); @@ -131,12 +131,12 @@ namespace MWLua return LObject(refId.getIf()->getValue()); }; - api["activators"] = LObjectList{ worldView->getActivatorsInScene() }; - api["actors"] = LObjectList{ worldView->getActorsInScene() }; - api["containers"] = LObjectList{ worldView->getContainersInScene() }; - api["doors"] = LObjectList{ worldView->getDoorsInScene() }; - api["items"] = LObjectList{ worldView->getItemsInScene() }; - api["players"] = LObjectList{ worldView->getPlayers() }; + api["activators"] = LObjectList{ objectLists->getActivatorsInScene() }; + api["actors"] = LObjectList{ objectLists->getActorsInScene() }; + api["containers"] = LObjectList{ objectLists->getContainersInScene() }; + api["doors"] = LObjectList{ objectLists->getDoorsInScene() }; + api["items"] = LObjectList{ objectLists->getItemsInScene() }; + api["players"] = LObjectList{ objectLists->getPlayers() }; api["NAVIGATOR_FLAGS"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs({ diff --git a/apps/openmw/mwlua/worldview.cpp b/apps/openmw/mwlua/objectlists.cpp similarity index 60% rename from apps/openmw/mwlua/worldview.cpp rename to apps/openmw/mwlua/objectlists.cpp index 097a418869..40b1ab93fa 100644 --- a/apps/openmw/mwlua/worldview.cpp +++ b/apps/openmw/mwlua/objectlists.cpp @@ -1,4 +1,4 @@ -#include "worldview.hpp" +#include "objectlists.hpp" #include #include @@ -6,28 +6,27 @@ #include +#include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwclass/container.hpp" #include "../mwworld/class.hpp" -#include "../mwworld/timestamp.hpp" #include "../mwworld/worldmodel.hpp" namespace MWLua { - void WorldView::update() + void ObjectLists::update() { mActivatorsInScene.updateList(); mActorsInScene.updateList(); mContainersInScene.updateList(); mDoorsInScene.updateList(); mItemsInScene.updateList(); - mPaused = MWBase::Environment::get().getWindowManager()->isGuiMode(); } - void WorldView::clear() + void ObjectLists::clear() { mActivatorsInScene.clear(); mActorsInScene.clear(); @@ -36,7 +35,7 @@ namespace MWLua mItemsInScene.clear(); } - WorldView::ObjectGroup* WorldView::chooseGroup(const MWWorld::Ptr& ptr) + ObjectLists::ObjectGroup* ObjectLists::chooseGroup(const MWWorld::Ptr& ptr) { // It is important to check `isMarker` first. // For example "prisonmarker" has class "Door" despite that it is only an invisible marker. @@ -56,7 +55,7 @@ namespace MWLua return nullptr; } - void WorldView::objectAddedToScene(const MWWorld::Ptr& ptr) + void ObjectLists::objectAddedToScene(const MWWorld::Ptr& ptr) { MWBase::Environment::get().getWorldModel()->registerPtr(ptr); ObjectGroup* group = chooseGroup(ptr); @@ -64,36 +63,14 @@ namespace MWLua addToGroup(*group, ptr); } - void WorldView::objectRemovedFromScene(const MWWorld::Ptr& ptr) + void ObjectLists::objectRemovedFromScene(const MWWorld::Ptr& ptr) { ObjectGroup* group = chooseGroup(ptr); if (group) removeFromGroup(*group, ptr); } - double WorldView::getGameTime() const - { - MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::TimeStamp timeStamp = world->getTimeStamp(); - return (static_cast(timeStamp.getDay()) * 24 + timeStamp.getHour()) * 3600.0; - } - - void WorldView::load(ESM::ESMReader& esm) - { - esm.getHNT(mSimulationTime, "LUAW"); - ESM::FormId lastGenerated = esm.getFormId(true); - if (lastGenerated.hasContentFile()) - throw std::runtime_error("Last generated RefNum is invalid"); - MWBase::Environment::get().getWorldModel()->setLastGeneratedRefNum(lastGenerated); - } - - void WorldView::save(ESM::ESMWriter& esm) const - { - esm.writeHNT("LUAW", mSimulationTime); - esm.writeFormId(MWBase::Environment::get().getWorldModel()->getLastGeneratedRefNum(), true); - } - - void WorldView::ObjectGroup::updateList() + void ObjectLists::ObjectGroup::updateList() { if (mChanged) { @@ -104,20 +81,20 @@ namespace MWLua } } - void WorldView::ObjectGroup::clear() + void ObjectLists::ObjectGroup::clear() { mChanged = false; mList->clear(); mSet.clear(); } - void WorldView::addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr) + void ObjectLists::addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr) { group.mSet.insert(getId(ptr)); group.mChanged = true; } - void WorldView::removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr) + void ObjectLists::removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr) { group.mSet.erase(getId(ptr)); group.mChanged = true; diff --git a/apps/openmw/mwlua/worldview.hpp b/apps/openmw/mwlua/objectlists.hpp similarity index 50% rename from apps/openmw/mwlua/worldview.hpp rename to apps/openmw/mwlua/objectlists.hpp index 7efb7c0f01..d96b83fd6d 100644 --- a/apps/openmw/mwlua/worldview.hpp +++ b/apps/openmw/mwlua/objectlists.hpp @@ -1,51 +1,20 @@ -#ifndef MWLUA_WORLDVIEW_H -#define MWLUA_WORLDVIEW_H - -#include "object.hpp" - -#include "../mwbase/environment.hpp" -#include "../mwbase/world.hpp" - -#include "../mwworld/globals.hpp" +#ifndef MWLUA_OBJECTLISTS_H +#define MWLUA_OBJECTLISTS_H #include -namespace ESM -{ - class ESMWriter; - class ESMReader; -} +#include "object.hpp" namespace MWLua { - // WorldView is a kind of an extension to mwworld. It was created on initial stage of - // OpenMW Lua development in order to minimize the risk of merge conflicts. - // TODO: Move get*InScene functions to mwworld/scene - // TODO: Move time-related stuff to mwworld; maybe create a new class TimeManager. - // TODO: Remove WorldView. - class WorldView + // ObjectLists is used to track lists of game objects like nearby.items, nearby.actors, etc. + class ObjectLists { public: void update(); // Should be called every frame. void clear(); // Should be called every time before starting or loading a new game. - // Whether the world is paused (i.e. game time is not changing and actors don't move). - bool isPaused() const { return mPaused; } - - // The number of seconds passed from the beginning of the game. - double getSimulationTime() const { return mSimulationTime; } - void setSimulationTime(double t) { mSimulationTime = t; } - - // The game time (in game seconds) passed from the beginning of the game. - // Note that game time generally goes faster than the simulation time. - double getGameTime() const; - double getGameTimeScale() const { return MWBase::Environment::get().getWorld()->getTimeScaleFactor(); } - void setGameTimeScale(double s) - { - MWBase::Environment::get().getWorld()->setGlobalFloat(MWWorld::Globals::sTimeScale, s); - } - ObjectIdList getActivatorsInScene() const { return mActivatorsInScene.mList; } ObjectIdList getActorsInScene() const { return mActorsInScene.mList; } ObjectIdList getContainersInScene() const { return mContainersInScene.mList; } @@ -58,9 +27,6 @@ namespace MWLua void setPlayer(const MWWorld::Ptr& player) { *mPlayers = { getId(player) }; } - void load(ESM::ESMReader& esm); - void save(ESM::ESMWriter& esm) const; - private: struct ObjectGroup { @@ -82,11 +48,8 @@ namespace MWLua ObjectGroup mDoorsInScene; ObjectGroup mItemsInScene; ObjectIdList mPlayers = std::make_shared>(); - - double mSimulationTime = 0; - bool mPaused = false; }; } -#endif // MWLUA_WORLDVIEW_H +#endif // MWLUA_OBJECTLISTS_H diff --git a/apps/openmw/mwlua/postprocessingbindings.cpp b/apps/openmw/mwlua/postprocessingbindings.cpp index f63ba5e70c..5ce37d13da 100644 --- a/apps/openmw/mwlua/postprocessingbindings.cpp +++ b/apps/openmw/mwlua/postprocessingbindings.cpp @@ -1,6 +1,7 @@ #include "postprocessingbindings.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" #include "../mwrender/postprocessor.hpp" #include "luamanagerimp.hpp" diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index 1eb96ebad0..bbac88e6e2 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -13,6 +13,7 @@ #include "localscripts.hpp" #include "luamanagerimp.hpp" +#include "../mwbase/environment.hpp" #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwworld/class.hpp" diff --git a/apps/openmw/mwlua/uibindings.cpp b/apps/openmw/mwlua/uibindings.cpp index b5212c566e..fca46fd7e6 100644 --- a/apps/openmw/mwlua/uibindings.cpp +++ b/apps/openmw/mwlua/uibindings.cpp @@ -14,6 +14,7 @@ #include "context.hpp" #include "luamanagerimp.hpp" +#include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" namespace MWLua diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 8c749190f0..ec53bdec71 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -20,6 +20,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/player.hpp" @@ -837,7 +838,7 @@ namespace MWMechanics // Take a maximum remaining duration of Stunted Magicka effects (-1 is a constant one) in game hours. if (remainingTime > 0) { - double timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + double timeScale = MWBase::Environment::get().getWorld()->getTimeManager()->getGameTimeScale(); if (timeScale == 0.0) timeScale = 1; @@ -1864,7 +1865,7 @@ namespace MWMechanics void Actors::rest(double hours, bool sleep) const { float duration = hours * 3600.f; - const float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + const float timeScale = MWBase::Environment::get().getWorld()->getTimeManager()->getGameTimeScale(); if (timeScale != 0.f) duration /= timeScale; diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index e020e4a717..e1d657a207 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -10,6 +10,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "character.hpp" #include "creaturestats.hpp" @@ -72,7 +73,8 @@ namespace MWMechanics // and the duration is not infinite, the package is complete. if (mDuration > 0) { - mRemainingDuration -= ((duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); + mRemainingDuration + -= ((duration * MWBase::Environment::get().getWorld()->getTimeManager()->getGameTimeScale()) / 3600); if (mRemainingDuration <= 0) { mRemainingDuration = mDuration; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index 3f0e4e287c..b78c1fd6ee 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -10,6 +10,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "character.hpp" #include "creaturestats.hpp" @@ -156,7 +157,9 @@ namespace MWMechanics // Check if we've run out of time if (mDuration > 0) { - mRemainingDuration -= ((duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); + mRemainingDuration + -= ((duration * MWBase::Environment::get().getWorld()->getTimeManager()->getGameTimeScale()) + / 3600); if (mRemainingDuration <= 0) { mRemainingDuration = mDuration; diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 60c37ac33c..30756ade35 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -16,6 +16,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwphysics/raycasting.hpp" @@ -200,7 +201,8 @@ namespace MWMechanics // get or create temporary storage AiWanderStorage& storage = state.get(); - mRemainingDuration -= ((duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); + mRemainingDuration + -= ((duration * MWBase::Environment::get().getWorld()->getTimeManager()->getGameTimeScale()) / 3600); cStats.setDrawState(DrawState::Nothing); cStats.setMovementFlag(CreatureStats::Flag_Run, false); diff --git a/apps/openmw/mwrender/sky.cpp b/apps/openmw/mwrender/sky.cpp index 5b8a25da17..d2f725c4b8 100644 --- a/apps/openmw/mwrender/sky.cpp +++ b/apps/openmw/mwrender/sky.cpp @@ -27,6 +27,7 @@ #include +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/weather.hpp" #include "../mwbase/environment.hpp" @@ -559,7 +560,7 @@ namespace MWRender } // rotate the stars by 360 degrees every 4 days - mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeScaleFactor() * duration + mAtmosphereNightRoll += MWBase::Environment::get().getWorld()->getTimeManager()->getGameTimeScale() * duration * osg::DegreesToRadians(360.f) / (3600 * 96.f); if (mAtmosphereNightNode->getNodeMask() != 0) mAtmosphereNightNode->setAttitude(osg::Quat(mAtmosphereNightRoll, osg::Vec3f(0, 0, 1))); diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 603cec9d77..50fd123b4f 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -33,6 +33,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/datetimemanager.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/globals.hpp" #include "../mwworld/scene.hpp" @@ -227,7 +228,7 @@ void MWState::StateManager::saveGame(std::string_view description, const Slot* s profile.mPlayerClassId = classId; profile.mPlayerCellName = world.getCellName(); - profile.mInGameTime = world.getEpochTimeStamp(); + profile.mInGameTime = world.getTimeManager()->getEpochTimeStamp(); profile.mTimePlayed = mTimePlayed; profile.mDescription = description; diff --git a/apps/openmw/mwworld/datetimemanager.cpp b/apps/openmw/mwworld/datetimemanager.cpp index 941f41d9e5..7559242bef 100644 --- a/apps/openmw/mwworld/datetimemanager.cpp +++ b/apps/openmw/mwworld/datetimemanager.cpp @@ -3,6 +3,9 @@ #include #include "../mwbase/environment.hpp" +#include "../mwbase/soundmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "duration.hpp" #include "globals.hpp" @@ -53,7 +56,10 @@ namespace MWWorld mDay = globalVariables[Globals::sDay].getInteger(); mMonth = globalVariables[Globals::sMonth].getInteger(); mYear = globalVariables[Globals::sYear].getInteger(); - mTimeScale = globalVariables[Globals::sTimeScale].getFloat(); + mGameTimeScale = globalVariables[Globals::sTimeScale].getFloat(); + setSimulationTimeScale(1.0); + mPaused = false; + mPausedTags.clear(); } void DateTimeManager::setHour(double hour) @@ -103,9 +109,9 @@ namespace MWWorld return TimeStamp(mGameHour, mDaysPassed); } - float DateTimeManager::getTimeScaleFactor() const + void DateTimeManager::setGameTimeScale(float scale) { - return mTimeScale; + MWBase::Environment::get().getWorld()->setGlobalFloat(MWWorld::Globals::sTimeScale, scale); } ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const @@ -199,7 +205,7 @@ namespace MWWorld } else if (name == Globals::sTimeScale) { - mTimeScale = value; + mGameTimeScale = value; } else if (name == Globals::sDaysPassed) { @@ -232,7 +238,7 @@ namespace MWWorld } else if (name == Globals::sTimeScale) { - mTimeScale = static_cast(value); + mGameTimeScale = static_cast(value); } else if (name == Globals::sDaysPassed) { @@ -241,4 +247,22 @@ namespace MWWorld return false; } + + void DateTimeManager::setSimulationTimeScale(float scale) + { + mSimulationTimeScale = std::max(0.f, scale); + MWBase::Environment::get().getSoundManager()->setSimulationTimeScale(mSimulationTimeScale); + } + + void DateTimeManager::unpause(std::string_view tag) + { + auto it = mPausedTags.find(tag); + if (it != mPausedTags.end()) + mPausedTags.erase(it); + } + + void DateTimeManager::updateIsPaused() + { + mPaused = !mPausedTags.empty() || MWBase::Environment::get().getWindowManager()->isGuiMode(); + } } diff --git a/apps/openmw/mwworld/datetimemanager.hpp b/apps/openmw/mwworld/datetimemanager.hpp index 0ceaae9584..f89894292f 100644 --- a/apps/openmw/mwworld/datetimemanager.hpp +++ b/apps/openmw/mwworld/datetimemanager.hpp @@ -1,6 +1,7 @@ #ifndef GAME_MWWORLD_DATETIMEMANAGER_H #define GAME_MWWORLD_DATETIMEMANAGER_H +#include #include #include "globalvariablename.hpp" @@ -14,31 +15,58 @@ namespace MWWorld { class Globals; class TimeStamp; + class World; class DateTimeManager { - int mDaysPassed = 0; - int mDay = 0; - int mMonth = 0; - int mYear = 0; - float mGameHour = 0.f; - float mTimeScale = 0.f; + public: + // Game time. + // Note that game time generally goes faster than the simulation time. + std::string_view getMonthName(int month = -1) const; // -1: current month + TimeStamp getTimeStamp() const; + ESM::EpochTimeStamp getEpochTimeStamp() const; + double getGameTime() const { return (static_cast(mDaysPassed) * 24 + mGameHour) * 3600.0; } + float getGameTimeScale() const { return mGameTimeScale; } + void setGameTimeScale(float scale); // game time to simulation time ratio + + // Simulation time (the number of seconds passed from the beginning of the game). + double getSimulationTime() const { return mSimulationTime; } + void setSimulationTime(double t) { mSimulationTime = t; } + float getSimulationTimeScale() const { return mSimulationTimeScale; } + void setSimulationTimeScale(float scale); // simulation time to real time ratio + + // Whether the game is paused in the current frame. + bool isPaused() const { return mPaused; } + + // Pauses the game starting from the next frame until `unpause` is called with the same tag. + void pause(std::string_view tag) { mPausedTags.emplace(tag); } + void unpause(std::string_view tag); + const std::set>& getPausedTags() const { return mPausedTags; } + + // Updates mPaused; should be called once a frame. + void updateIsPaused(); + + private: + friend class World; + void setup(Globals& globalVariables); + bool updateGlobalInt(GlobalVariableName name, int value); + bool updateGlobalFloat(GlobalVariableName name, float value); + void advanceTime(double hours, Globals& globalVariables); void setHour(double hour); void setDay(int day); void setMonth(int month); - public: - std::string_view getMonthName(int month) const; - TimeStamp getTimeStamp() const; - ESM::EpochTimeStamp getEpochTimeStamp() const; - float getTimeScaleFactor() const; - - void advanceTime(double hours, Globals& globalVariables); - - void setup(Globals& globalVariables); - bool updateGlobalInt(GlobalVariableName name, int value); - bool updateGlobalFloat(GlobalVariableName name, float value); + int mDaysPassed = 0; + int mDay = 0; + int mMonth = 0; + int mYear = 0; + float mGameHour = 0.f; + float mGameTimeScale = 0.f; + float mSimulationTimeScale = 1.0; + double mSimulationTime = 0.0; + bool mPaused = false; + std::set> mPausedTags; }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3b16727acf..88576a7280 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -249,7 +249,7 @@ namespace MWWorld : mResourceSystem(resourceSystem) , mLocalScripts(mStore) , mWorldModel(mStore, mReaders) - , mCurrentDate(std::make_unique()) + , mTimeManager(std::make_unique()) , mSky(true) , mGodMode(false) , mScriptsEnabled(true) @@ -319,7 +319,7 @@ namespace MWWorld void World::fillGlobalVariables() { mGlobalVariables.fill(mStore); - mCurrentDate->setup(mGlobalVariables); + mTimeManager->setup(mGlobalVariables); } void World::startNewGame(bool bypass) @@ -399,7 +399,7 @@ namespace MWWorld mPhysics->toggleCollisionMode(); MWBase::Environment::get().getWindowManager()->updatePlayer(); - mCurrentDate->setup(mGlobalVariables); + mTimeManager->setup(mGlobalVariables); // Initial seed. mPrng.seed(mRandomSeed); @@ -615,7 +615,7 @@ namespace MWWorld void World::setGlobalInt(GlobalVariableName name, int value) { - bool dateUpdated = mCurrentDate->updateGlobalInt(name, value); + bool dateUpdated = mTimeManager->updateGlobalInt(name, value); if (dateUpdated) updateSkyDate(); @@ -624,7 +624,7 @@ namespace MWWorld void World::setGlobalFloat(GlobalVariableName name, float value) { - bool dateUpdated = mCurrentDate->updateGlobalFloat(name, value); + bool dateUpdated = mTimeManager->updateGlobalFloat(name, value); if (dateUpdated) updateSkyDate(); @@ -646,11 +646,6 @@ namespace MWWorld return mGlobalVariables.getType(name); } - std::string_view World::getMonthName(int month) const - { - return mCurrentDate->getMonthName(month); - } - std::string_view World::getCellName(const MWWorld::CellStore* cell) const { if (!cell) @@ -893,7 +888,7 @@ namespace MWWorld // When we fast-forward time, we should recharge magic items // in all loaded cells, using game world time float duration = hours * 3600; - const float timeScaleFactor = getTimeScaleFactor(); + const float timeScaleFactor = mTimeManager->getGameTimeScale(); if (timeScaleFactor != 0.0f) duration /= timeScaleFactor; @@ -901,7 +896,7 @@ namespace MWWorld } mWeatherManager->advanceTime(hours, incremental); - mCurrentDate->advanceTime(hours, mGlobalVariables); + mTimeManager->advanceTime(hours, mGlobalVariables); updateSkyDate(); if (!incremental) @@ -912,25 +907,9 @@ namespace MWWorld } } - float World::getTimeScaleFactor() const - { - return mCurrentDate->getTimeScaleFactor(); - } - - void World::setSimulationTimeScale(float scale) - { - mSimulationTimeScale = std::max(0.f, scale); - MWBase::Environment::get().getSoundManager()->setSimulationTimeScale(mSimulationTimeScale); - } - TimeStamp World::getTimeStamp() const { - return mCurrentDate->getTimeStamp(); - } - - ESM::EpochTimeStamp World::getEpochTimeStamp() const - { - return mCurrentDate->getEpochTimeStamp(); + return mTimeManager->getTimeStamp(); } bool World::toggleSky() @@ -2314,7 +2293,7 @@ namespace MWWorld { mStore.rebuildIdsIndex(); mStore.validateDynamic(); - mCurrentDate->setup(mGlobalVariables); + mTimeManager->setup(mGlobalVariables); } void World::setupPlayer() @@ -3864,7 +3843,7 @@ namespace MWWorld void World::updateSkyDate() { - ESM::EpochTimeStamp currentDate = mCurrentDate->getEpochTimeStamp(); + ESM::EpochTimeStamp currentDate = mTimeManager->getEpochTimeStamp(); mRendering->skySetDate(currentDate.mDay, currentDate.mMonth); } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 510c25f2d9..64bc3666de 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -102,7 +102,7 @@ namespace MWWorld std::unique_ptr mRendering; std::unique_ptr mWorldScene; std::unique_ptr mWeatherManager; - std::unique_ptr mCurrentDate; + std::unique_ptr mTimeManager; std::unique_ptr mProjectileManager; bool mSky; @@ -135,8 +135,6 @@ namespace MWWorld uint32_t mRandomSeed{}; - float mSimulationTimeScale = 1.0; - // not implemented World(const World&); World& operator=(const World&); @@ -309,15 +307,9 @@ namespace MWWorld void advanceTime(double hours, bool incremental = false) override; ///< Advance in-game time. - std::string_view getMonthName(int month = -1) const override; - ///< Return name of month (-1: current month) - TimeStamp getTimeStamp() const override; ///< Return current in-game time and number of day since new game start. - ESM::EpochTimeStamp getEpochTimeStamp() const override; - ///< Return current in-game date and time. - bool toggleSky() override; ///< \return Resulting mode @@ -339,12 +331,6 @@ namespace MWWorld void modRegion(const ESM::RefId& regionid, const std::vector& chances) override; - float getTimeScaleFactor() const override; - - float getSimulationTimeScale() const override { return mSimulationTimeScale; } - - void setSimulationTimeScale(float scale) override; - void changeToInteriorCell(const std::string_view cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true) override; ///< Move to interior cell. @@ -690,6 +676,8 @@ namespace MWWorld MWRender::PostProcessor* getPostProcessor() override; + DateTimeManager* getTimeManager() override { return mTimeManager.get(); } + void setActorActive(const MWWorld::Ptr& ptr, bool value) override; }; } diff --git a/components/esm/luascripts.cpp b/components/esm/luascripts.cpp index 0a7a2ff281..8f2048d8a7 100644 --- a/components/esm/luascripts.cpp +++ b/components/esm/luascripts.cpp @@ -13,7 +13,7 @@ // // Subrecords: // LUAF - LuaScriptCfg::mFlags and ESM::RecNameInts list -// LUAW - Start of MWLua::WorldView data +// LUAW - Simulation time and last generated RefNum // LUAE - Start of MWLua::LocalEvent or MWLua::GlobalEvent (eventName) // LUAS - VFS path to a Lua script // LUAD - Serialized Lua variable diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index 26dd9c69d5..53f1e44e26 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -106,6 +106,21 @@ -- @function [parent=#world] isWorldPaused -- @return #boolean +--- +-- Pause the game starting from the next frame. +-- @function [parent=#world] pause +-- @param #string tag (optional) The game will be paused until `unpause` is called with the same tag. + +--- +-- Remove given tag from the list of pause tags. Resume the game starting from the next frame if the list became empty. +-- @function [parent=#world] unpause +-- @param #string tag (optional) Needed to undo `pause` called with this tag. + +--- +-- The tags that are currently pausing the game. +-- @function [parent=#world] getPausedTags +-- @return #table + --- -- Return an object by RefNum/FormId. -- Note: the function always returns @{openmw.core#GameObject} and doesn't validate that