diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index cac6be3658..49aa4e9e7a 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -1082,7 +1082,7 @@ void OMW::Engine::go() const double dt = std::chrono::duration_cast>(std::min( frameRateLimiter.getLastFrameDuration(), maxSimulationInterval - )).count(); + )).count() * mEnvironment.getWorld()->getSimulationTimeScale(); mViewer->advance(simulationTime); diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index 641be742ff..a60ed78154 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -43,6 +43,8 @@ namespace MWSound * much. */ NoPlayerLocal = 1<<3, /* (3D only) Don't play the sound local to the listener even if the * player is making it. */ + NoScaling = 1<<4, /* Don't scale audio with simulation time */ + NoEnvNoScaling = NoEnv | NoScaling, LoopNoEnv = Loop | NoEnv, LoopRemoveAtDistance = Loop | RemoveAtDistance }; @@ -72,6 +74,8 @@ namespace MWBase using PlayMode = MWSound::PlayMode; using Type = MWSound::Type; + float mSimulationTimeScale = 1.0; + public: SoundManager() {} virtual ~SoundManager() {} @@ -181,6 +185,9 @@ namespace MWBase virtual void updatePtr(const MWWorld::ConstPtr& old, const MWWorld::ConstPtr& updated) = 0; + void setSimulationTimeScale(float scale) { mSimulationTimeScale = scale; } + float getSimulationTimeScale() const { return mSimulationTimeScale; } + virtual void clear() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 2c96a07851..cffdb6dbed 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -255,6 +255,10 @@ namespace MWBase virtual float getTimeScaleFactor() const = 0; + virtual float getSimulationTimeScale() const = 0; + + virtual void setSimulationTimeScale(float scale) = 0; + virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true) = 0; ///< Move to interior cell. ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index a3f039e351..fa00f1b758 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -2098,7 +2098,7 @@ namespace MWGui if (soundId.empty()) return; - MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnv); + MWBase::Environment::get().getSoundManager()->playSound(soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnvNoScaling); } void WindowManager::updateSpellWindow() diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 9e9520cbe2..705209a603 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -13,6 +13,7 @@ #include "eventqueue.hpp" #include "worldview.hpp" +#include "luamanagerimp.hpp" #include "types/types.hpp" namespace MWLua @@ -20,8 +21,10 @@ namespace MWLua static void addTimeBindings(sol::table& api, const Context& context, bool global) { + MWBase::World* world = MWBase::Environment::get().getWorld(); + api["getSimulationTime"] = [world=context.mWorldView]() { return world->getSimulationTime(); }; - api["getSimulationTimeScale"] = [world=context.mWorldView]() { return world->getSimulationTimeScale(); }; + 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(); }; @@ -35,8 +38,12 @@ namespace MWLua api["setGameTimeScale"] = [world=context.mWorldView](double scale) { world->setGameTimeScale(scale); }; - // TODO: Ability to make game time slower or faster than real time (needed for example for mechanics like VATS) - // api["setSimulationTimeScale"] = [](double scale) {}; + api["setSimulationTimeScale"] = [context, world](float scale) + { + context.mLuaManager->addAction([scale, world] { + world->setSimulationTimeScale(scale); + }); + }; // TODO: Ability to pause/resume world from Lua (needed for UI dehardcoding) // api["pause"] = []() {}; @@ -47,7 +54,7 @@ namespace MWLua { auto* lua = context.mLua; sol::table api(lua->sol(), sol::create); - api["API_REVISION"] = 26; + api["API_REVISION"] = 27; api["quit"] = [lua]() { Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback(); diff --git a/apps/openmw/mwlua/worldview.hpp b/apps/openmw/mwlua/worldview.hpp index accf2758f0..181a73c0b0 100644 --- a/apps/openmw/mwlua/worldview.hpp +++ b/apps/openmw/mwlua/worldview.hpp @@ -30,7 +30,6 @@ namespace MWLua // The number of seconds passed from the beginning of the game. double getSimulationTime() const { return mSimulationTime; } void setSimulationTime(double t) { mSimulationTime = t; } - double getSimulationTimeScale() const { return 1.0; } // The game time (in game seconds) passed from the beginning of the game. // Note that game time generally goes faster than the simulation time. diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 3fe220019d..47f90bbcf3 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1126,7 +1126,7 @@ bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset) } source = mFreeSources.front(); - initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(), sound->getUseEnv()); alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); @@ -1166,7 +1166,7 @@ bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset) source = mFreeSources.front(); initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), - sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), + sound->getRealVolume(), getTimeScaledPitch(sound), sound->getIsLooping(), sound->getUseEnv()); alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcef(source, AL_SEC_OFFSET, offset); @@ -1228,7 +1228,7 @@ void OpenAL_Output::updateSound(Sound *sound) ALuint source = GET_PTRID(sound->mHandle); updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), - sound->getPitch(), sound->getUseEnv()); + getTimeScaledPitch(sound), sound->getUseEnv()); getALError(); } @@ -1245,7 +1245,7 @@ bool OpenAL_Output::streamSound(DecoderPtr decoder, Stream *sound, bool getLoudn if(sound->getIsLooping()) Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; - initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), + initCommon2D(source, sound->getPosition(), sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv()); if(getALError() != AL_NO_ERROR) return false; @@ -1277,7 +1277,7 @@ bool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLou Log(Debug::Warning) << "Warning: cannot loop stream \"" << decoder->getName() << "\""; initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), - sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); + sound->getRealVolume(), getTimeScaledPitch(sound), false, sound->getUseEnv()); if(getALError() != AL_NO_ERROR) return false; @@ -1354,7 +1354,7 @@ void OpenAL_Output::updateStream(Stream *sound) ALuint source = stream->mSource; updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), - sound->getPitch(), sound->getUseEnv()); + getTimeScaledPitch(sound), sound->getUseEnv()); getALError(); } @@ -1510,4 +1510,10 @@ OpenAL_Output::~OpenAL_Output() OpenAL_Output::deinit(); } +float OpenAL_Output::getTimeScaledPitch(SoundBase *sound) +{ + const bool shouldScale = !(sound->mParams.mFlags & PlayMode::NoScaling); + return shouldScale ? sound->getPitch() * mManager.getSimulationTimeScale() : sound->getPitch(); +} + } diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index 47845c0802..c68c65c165 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -15,6 +15,7 @@ namespace MWSound { class SoundManager; + class SoundBase; class Sound; class Stream; @@ -55,6 +56,8 @@ namespace MWSound void updateCommon(ALuint source, const osg::Vec3f &pos, ALfloat maxdist, ALfloat gain, ALfloat pitch, bool useenv); + float getTimeScaledPitch(SoundBase *sound); + OpenAL_Output& operator=(const OpenAL_Output &rhs); OpenAL_Output(const OpenAL_Output &rhs); diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 27f4589bb9..b9baee7223 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -248,7 +248,7 @@ namespace MWSound mMusic->init([&] { SoundParams params; params.mBaseVolume = volumeFromType(Type::Music); - params.mFlags = PlayMode::NoEnv | Type::Music | Play_2D; + params.mFlags = PlayMode::NoEnvNoScaling | Type::Music | Play_2D; return params; } ()); mOutput->streamSound(decoder, mMusic.get()); @@ -462,7 +462,7 @@ namespace MWSound track->init([&] { SoundParams params; params.mBaseVolume = volumeFromType(type); - params.mFlags = PlayMode::NoEnv | type | Play_2D; + params.mFlags = PlayMode::NoEnvNoScaling | type | Play_2D; return params; } ()); if(!mOutput->streamSound(decoder, track.get())) diff --git a/apps/openmw/mwsound/type.hpp b/apps/openmw/mwsound/type.hpp index 9f95bfa401..5f063c3954 100644 --- a/apps/openmw/mwsound/type.hpp +++ b/apps/openmw/mwsound/type.hpp @@ -5,11 +5,11 @@ namespace MWSound { enum class Type { - Sfx = 1 << 4, /* Normal SFX sound */ - Voice = 1 << 5, /* Voice sound */ - Foot = 1 << 6, /* Footstep sound */ - Music = 1 << 7, /* Music track */ - Movie = 1 << 8, /* Movie audio track */ + Sfx = 1 << 5, /* Normal SFX sound */ + Voice = 1 << 6, /* Voice sound */ + Foot = 1 << 7, /* Footstep sound */ + Music = 1 << 8, /* Music track */ + Movie = 1 << 9, /* Movie audio track */ Mask = Sfx | Voice | Foot | Music | Movie }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c626ef9d46..5a6a5ce864 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -921,6 +921,12 @@ namespace MWWorld 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(); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2f5b82d979..1849937c63 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -134,6 +134,8 @@ namespace MWWorld uint32_t mRandomSeed{}; + float mSimulationTimeScale = 1.0; + // not implemented World (const World&); World& operator= (const World&); @@ -347,6 +349,10 @@ namespace MWWorld float getTimeScaleFactor() const override; + float getSimulationTimeScale() const override { return mSimulationTimeScale; } + + void setSimulationTimeScale(float scale) override; + void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool adjustPlayerPos, bool changeEvent = true) override; ///< Move to interior cell. ///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes diff --git a/files/lua_api/openmw/world.lua b/files/lua_api/openmw/world.lua index c29ac4efaf..4fff22bc41 100644 --- a/files/lua_api/openmw/world.lua +++ b/files/lua_api/openmw/world.lua @@ -34,6 +34,11 @@ -- @function [parent=#world] getSimulationTimeScale -- @return #number +--- +-- Set the simulation time scale. +-- @function [parent=#world] setSimulationTimeScale +-- @param #number scale + --- -- Game time in seconds. -- @function [parent=#world] getGameTime