1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-28 12:09:53 +00:00

Merge branch 'time' into 'master'

Pause/resume the game in Lua scripts

See merge request OpenMW/openmw!3317
This commit is contained in:
psi29a 2023-08-18 08:22:04 +00:00
commit e21e3a0d46
28 changed files with 242 additions and 249 deletions

View file

@ -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

View file

@ -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<UserStatsType::LuaSyncUpdate> 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<UserStatsType::Script> 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::chrono::duration<double>>(
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)
{

View file

@ -92,6 +92,7 @@ namespace MWWorld
class ESMStore;
class RefData;
class Cell;
class DateTimeManager;
typedef std::vector<std::pair<MWWorld::Ptr, MWMechanics::Movement>> 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<char>& 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;
};
}

View file

@ -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();
}

View file

@ -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"))
{

View file

@ -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<int>(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<int>(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);

View file

@ -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;
};

View file

@ -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<double>(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<std::string_view> tag) { timeManager->pause(tag.value_or("paused")); };
api["unpause"]
= [timeManager](sol::optional<std::string_view> 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<int> 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<std::string, sol::object> 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) },

View file

@ -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<PlayerScripts*>(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<double>("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());

View file

@ -3,6 +3,7 @@
#include <filesystem>
#include <map>
#include <osg/Stats>
#include <set>
#include <components/lua/luastate.hpp>
@ -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<LocalScripts*> mActiveLocalScripts;
WorldView mWorldView;
ObjectLists mObjectLists;
MWWorld::Ptr mPlayer;

View file

@ -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<MWPhysics::RayCastingResult> rayResult
= context.mLua->sol().new_usertype<MWPhysics::RayCastingResult>("RayCastingResult");
@ -131,12 +131,12 @@ namespace MWLua
return LObject(refId.getIf<ESM::FormIdRefId>()->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<std::string_view, DetourNavigator::Flag>({

View file

@ -1,4 +1,4 @@
#include "worldview.hpp"
#include "objectlists.hpp"
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
@ -6,28 +6,27 @@
#include <components/misc/resourcehelpers.hpp>
#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<double>(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;

View file

@ -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 <set>
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<std::vector<ObjectId>>();
double mSimulationTime = 0;
bool mPaused = false;
};
}
#endif // MWLUA_WORLDVIEW_H
#endif // MWLUA_OBJECTLISTS_H

View file

@ -1,6 +1,7 @@
#include "postprocessingbindings.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwrender/postprocessor.hpp"
#include "luamanagerimp.hpp"

View file

@ -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"

View file

@ -14,6 +14,7 @@
#include "context.hpp"
#include "luamanagerimp.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
namespace MWLua

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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<AiWanderStorage>();
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);

View file

@ -27,6 +27,7 @@
#include <components/nifosg/particle.hpp>
#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)));

View file

@ -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;

View file

@ -3,6 +3,9 @@
#include <components/l10n/manager.hpp>
#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<float>(value);
mGameTimeScale = static_cast<float>(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();
}
}

View file

@ -1,6 +1,7 @@
#ifndef GAME_MWWORLD_DATETIMEMANAGER_H
#define GAME_MWWORLD_DATETIMEMANAGER_H
#include <set>
#include <string_view>
#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<double>(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<std::string, std::less<>>& 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<std::string, std::less<>> mPausedTags;
};
}

View file

@ -249,7 +249,7 @@ namespace MWWorld
: mResourceSystem(resourceSystem)
, mLocalScripts(mStore)
, mWorldModel(mStore, mReaders)
, mCurrentDate(std::make_unique<DateTimeManager>())
, mTimeManager(std::make_unique<DateTimeManager>())
, 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);
}

View file

@ -102,7 +102,7 @@ namespace MWWorld
std::unique_ptr<MWRender::RenderingManager> mRendering;
std::unique_ptr<MWWorld::Scene> mWorldScene;
std::unique_ptr<MWWorld::WeatherManager> mWeatherManager;
std::unique_ptr<MWWorld::DateTimeManager> mCurrentDate;
std::unique_ptr<MWWorld::DateTimeManager> mTimeManager;
std::unique_ptr<ProjectileManager> 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<char>& 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;
};
}

View file

@ -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

View file

@ -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