mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-06 18:40:23 +00:00
Lua commands world.pause / world.unpause
This commit is contained in:
parent
91c7585c8b
commit
6c4e1f4e8f
@ -201,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.
|
||||
@ -217,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())
|
||||
{
|
||||
@ -238,7 +235,7 @@ bool OMW::Engine::frame(float frametime)
|
||||
mWorld->getWorldScene().markCellAsUnchanged();
|
||||
}
|
||||
|
||||
if (!guiActive)
|
||||
if (!paused)
|
||||
{
|
||||
double hours = (frametime * mWorld->getTimeManager()->getGameTimeScale()) / 3600.0;
|
||||
mWorld->advanceTime(hours, true);
|
||||
@ -253,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();
|
||||
}
|
||||
}
|
||||
@ -270,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,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 +925,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 +933,18 @@ void OMW::Engine::go()
|
||||
const double dt = std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
std::min(frameRateLimiter.getLastFrameDuration(), maxSimulationInterval))
|
||||
.count()
|
||||
* mWorld->getTimeManager()->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)
|
||||
{
|
||||
|
@ -68,7 +68,7 @@ namespace MWLua
|
||||
api["getSimulationTimeScale"] = [timeManager]() { return timeManager->getSimulationTimeScale(); };
|
||||
api["getGameTime"] = [timeManager]() { return timeManager->getGameTime(); };
|
||||
api["getGameTimeScale"] = [timeManager]() { return timeManager->getGameTimeScale(); };
|
||||
api["isWorldPaused"] = [world = context.mWorldView]() { return world->isPaused(); };
|
||||
api["isWorldPaused"] = [timeManager]() { return timeManager->isPaused(); };
|
||||
api["getRealTime"] = []() {
|
||||
return std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
};
|
||||
@ -81,9 +81,16 @@ namespace MWLua
|
||||
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)
|
||||
|
@ -128,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");
|
||||
@ -151,16 +149,12 @@ namespace MWLua
|
||||
|
||||
mLuaEvents.finalizeEventBatch();
|
||||
|
||||
if (!mWorldView.isPaused())
|
||||
{ // Update time and process timers
|
||||
MWWorld::DateTimeManager& timeManager = *MWBase::Environment::get().getWorld()->getTimeManager();
|
||||
double simulationTime = timeManager.getSimulationTime() + frameDuration;
|
||||
timeManager.setSimulationTime(simulationTime);
|
||||
double gameTime = timeManager.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`.
|
||||
@ -173,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);
|
||||
@ -222,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();
|
||||
|
@ -24,7 +24,6 @@ namespace MWLua
|
||||
mContainersInScene.updateList();
|
||||
mDoorsInScene.updateList();
|
||||
mItemsInScene.updateList();
|
||||
mPaused = MWBase::Environment::get().getWindowManager()->isGuiMode();
|
||||
}
|
||||
|
||||
void WorldView::clear()
|
||||
|
@ -18,9 +18,6 @@ namespace MWLua
|
||||
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; }
|
||||
|
||||
ObjectIdList getActivatorsInScene() const { return mActivatorsInScene.mList; }
|
||||
ObjectIdList getActorsInScene() const { return mActorsInScene.mList; }
|
||||
ObjectIdList getContainersInScene() const { return mContainersInScene.mList; }
|
||||
@ -54,8 +51,6 @@ namespace MWLua
|
||||
ObjectGroup mDoorsInScene;
|
||||
ObjectGroup mItemsInScene;
|
||||
ObjectIdList mPlayers = std::make_shared<std::vector<ObjectId>>();
|
||||
|
||||
bool mPaused = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "duration.hpp"
|
||||
@ -57,6 +58,8 @@ namespace MWWorld
|
||||
mYear = globalVariables[Globals::sYear].getInteger();
|
||||
mGameTimeScale = globalVariables[Globals::sTimeScale].getFloat();
|
||||
setSimulationTimeScale(1.0);
|
||||
mPaused = false;
|
||||
mPausedTags.clear();
|
||||
}
|
||||
|
||||
void DateTimeManager::setHour(double hour)
|
||||
@ -250,4 +253,16 @@ namespace MWWorld
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef GAME_MWWORLD_DATETIMEMANAGER_H
|
||||
#define GAME_MWWORLD_DATETIMEMANAGER_H
|
||||
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
|
||||
#include "globalvariablename.hpp"
|
||||
@ -34,6 +35,17 @@ namespace MWWorld
|
||||
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);
|
||||
@ -53,6 +65,8 @@ namespace MWWorld
|
||||
float mGameTimeScale = 0.f;
|
||||
float mSimulationTimeScale = 1.0;
|
||||
double mSimulationTime = 0.0;
|
||||
bool mPaused = false;
|
||||
std::set<std::string, std::less<>> mPausedTags;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user