diff --git a/include/reone/audio/mixer.h b/include/reone/audio/mixer.h index b3e797385..60c9f4454 100644 --- a/include/reone/audio/mixer.h +++ b/include/reone/audio/mixer.h @@ -32,6 +32,8 @@ class IAudioMixer { virtual ~IAudioMixer() = default; virtual void render() = 0; + virtual void stop(AudioType type) = 0; + virtual void stopAll() = 0; virtual std::shared_ptr play( std::shared_ptr clip, @@ -48,6 +50,8 @@ class AudioMixer : public IAudioMixer, boost::noncopyable { } void render() override; + void stop(AudioType type) override; + void stopAll() override; std::shared_ptr play( std::shared_ptr clip, @@ -57,9 +61,14 @@ class AudioMixer : public IAudioMixer, boost::noncopyable { std::optional = std::nullopt) override; private: + struct ActiveSource { + std::shared_ptr source; + AudioType type {AudioType::Sound}; + }; + AudioOptions &_options; - std::vector> _sources; + std::vector _sources; float gainByType(AudioType type, float gain) const; }; diff --git a/include/reone/game/game.h b/include/reone/game/game.h index 2f28420bc..33d859c43 100644 --- a/include/reone/game/game.h +++ b/include/reone/game/game.h @@ -17,6 +17,8 @@ #pragma once +#include + #include "reone/audio/source.h" #include "reone/graphics/cursor.h" #include "reone/input/event.h" @@ -183,6 +185,7 @@ class Game : boost::noncopyable { void loadModule(const std::string &name, std::string entry = ""); void scheduleModuleTransition(const std::string &moduleName, const std::string &entry); + void scheduleModuleTransitionWithMovies(const std::string &moduleName, const std::string &entry, std::vector movies); // END Module loading @@ -321,6 +324,7 @@ class Game : boost::noncopyable { Screen _screen {Screen::None}; std::shared_ptr _movie; + std::queue _moduleTransitionMovies; resource::CursorType _cursorType {resource::CursorType::None}; std::shared_ptr _cursor; float _gameSpeed {1.0f}; @@ -408,6 +412,8 @@ class Game : boost::noncopyable { // Updates + bool startVideo(const std::string &name); + bool playNextModuleTransitionMovie(); void updateMovie(float dt); void updateMusic(); void updateCamera(float dt); diff --git a/src/libs/audio/mixer.cpp b/src/libs/audio/mixer.cpp index 4f3e37bb4..61542af76 100644 --- a/src/libs/audio/mixer.cpp +++ b/src/libs/audio/mixer.cpp @@ -23,7 +23,7 @@ namespace audio { void AudioMixer::render() { for (auto it = _sources.begin(); it != _sources.end();) { - auto &source = *it; + auto &source = it->source; source->render(); if (!source->isPlaying()) { it = _sources.erase(it); @@ -33,6 +33,25 @@ void AudioMixer::render() { } } +void AudioMixer::stop(AudioType type) { + for (auto it = _sources.begin(); it != _sources.end();) { + if (it->type != type) { + ++it; + continue; + } + + it->source->stop(); + it = _sources.erase(it); + } +} + +void AudioMixer::stopAll() { + for (auto &source : _sources) { + source.source->stop(); + } + _sources.clear(); +} + std::shared_ptr AudioMixer::play(std::shared_ptr clip, AudioType type, float gain, @@ -45,7 +64,7 @@ std::shared_ptr AudioMixer::play(std::shared_ptr clip, std::move(position)); source->init(); source->play(); - _sources.push_back(source); + _sources.push_back(ActiveSource {source, type}); return source; } diff --git a/src/libs/game/game.cpp b/src/libs/game/game.cpp index 221e3c576..856358d1e 100644 --- a/src/libs/game/game.cpp +++ b/src/libs/game/game.cpp @@ -412,15 +412,8 @@ void Game::setCursorType(CursorType type) { } void Game::playVideo(const std::string &name) { - _movie = _services.resource.movies.get(name); - if (!_movie) { - return; - } - - if (_music) { - _music->stop(); - _music.reset(); - } + _moduleTransitionMovies = std::queue(); + startVideo(name); } void Game::playMusic(const std::string &resRef) { @@ -526,14 +519,6 @@ void Game::renderGUI() { } } -void Game::updateMovie(float dt) { - _movie->update(dt); - - if (_movie->isFinished()) { - _movie.reset(); - } -} - void Game::updateMusic() { if (_musicResRef.empty()) { return; @@ -563,6 +548,53 @@ void Game::stopMovement() { void Game::scheduleModuleTransition(const std::string &moduleName, const std::string &entry) { _nextModule = moduleName; _nextEntry = entry; + _moduleTransitionMovies = std::queue(); +} + +void Game::scheduleModuleTransitionWithMovies(const std::string &moduleName, const std::string &entry, std::vector movies) { + _nextModule = moduleName; + _nextEntry = entry; + _moduleTransitionMovies = std::queue(); + for (auto &movie : movies) { + _moduleTransitionMovies.push(std::move(movie)); + } + + if (!_movie) { + playNextModuleTransitionMovie(); + } +} + +bool Game::startVideo(const std::string &name) { + _services.audio.mixer.stopAll(); + _music.reset(); + + _movie = _services.resource.movies.get(name); + if (!_movie) { + return false; + } + + return true; +} + +bool Game::playNextModuleTransitionMovie() { + while (!_moduleTransitionMovies.empty()) { + auto name = std::move(_moduleTransitionMovies.front()); + _moduleTransitionMovies.pop(); + + if (startVideo(name)) { + return true; + } + } + return false; +} + +void Game::updateMovie(float dt) { + _movie->update(dt); + + if (_movie->isFinished()) { + _movie.reset(); + playNextModuleTransitionMovie(); + } } void Game::updateCamera(float dt) { diff --git a/src/libs/game/script/routine/impl/main.cpp b/src/libs/game/script/routine/impl/main.cpp index 91d7143ea..a79406740 100644 --- a/src/libs/game/script/routine/impl/main.cpp +++ b/src/libs/game/script/routine/impl/main.cpp @@ -4389,9 +4389,25 @@ static Variable StartNewModule(const std::vector &args, const RoutineC // Transform auto moduleName = boost::to_lower_copy(sModuleName); auto waypoint = boost::to_lower_copy(sWayPoint); + std::vector movies; + auto addMovie = [&movies](const std::string &movie) { + if (!movie.empty()) { + movies.push_back(boost::to_lower_copy(movie)); + } + }; + addMovie(sMovie1); + addMovie(sMovie2); + addMovie(sMovie3); + addMovie(sMovie4); + addMovie(sMovie5); + addMovie(sMovie6); // Execute - ctx.game.scheduleModuleTransition(moduleName, waypoint); + if (movies.empty()) { + ctx.game.scheduleModuleTransition(moduleName, waypoint); + } else { + ctx.game.scheduleModuleTransitionWithMovies(moduleName, waypoint, std::move(movies)); + } return Variable::ofNull(); } diff --git a/test/fixtures/audio.h b/test/fixtures/audio.h index d9d7822c9..1cdcc8a7c 100644 --- a/test/fixtures/audio.h +++ b/test/fixtures/audio.h @@ -46,6 +46,8 @@ class MockContext : public IContext, boost::noncopyable { class MockAudioMixer : public IAudioMixer, boost::noncopyable { public: MOCK_METHOD(void, render, (), (override)); + MOCK_METHOD(void, stop, (AudioType), (override)); + MOCK_METHOD(void, stopAll, (), (override)); MOCK_METHOD(std::shared_ptr, play, (std::shared_ptr, AudioType, float, bool, std::optional), (override)); };