mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-29 08:45:36 +00:00
Rework music system
This commit is contained in:
parent
090da90302
commit
e1cae5a029
24 changed files with 189 additions and 47 deletions
|
@ -75,6 +75,7 @@
|
||||||
Feature #3537: Shader-based water ripples
|
Feature #3537: Shader-based water ripples
|
||||||
Feature #5492: Let rain and snow collide with statics
|
Feature #5492: Let rain and snow collide with statics
|
||||||
Feature #6149: Dehardcode Lua API_REVISION
|
Feature #6149: Dehardcode Lua API_REVISION
|
||||||
|
Feature #6152: Playing music via lua scripts
|
||||||
Feature #6447: Add LOD support to Object Paging
|
Feature #6447: Add LOD support to Object Paging
|
||||||
Feature #6491: Add support for Qt6
|
Feature #6491: Add support for Qt6
|
||||||
Feature #6556: Lua API for sounds
|
Feature #6556: Lua API for sounds
|
||||||
|
@ -99,6 +100,7 @@
|
||||||
Feature #7477: NegativeLight Magic Effect flag
|
Feature #7477: NegativeLight Magic Effect flag
|
||||||
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
|
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
|
||||||
Feature #7546: Start the game on Fredas
|
Feature #7546: Start the game on Fredas
|
||||||
|
Feature #7568: Uninterruptable scripted music
|
||||||
Task #5896: Do not use deprecated MyGUI properties
|
Task #5896: Do not use deprecated MyGUI properties
|
||||||
Task #7113: Move from std::atoi to std::from_char
|
Task #7113: Move from std::atoi to std::from_char
|
||||||
Task #7117: Replace boost::scoped_array with std::vector
|
Task #7117: Replace boost::scoped_array with std::vector
|
||||||
|
|
|
@ -61,7 +61,7 @@ add_openmw_dir (mwscript
|
||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||||
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
|
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings mwscriptbindings
|
||||||
camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings postprocessingbindings stats debugbindings
|
camerabindings vfsbindings musicbindings uibindings soundbindings 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
|
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
|
||||||
worker magicbindings factionbindings
|
worker magicbindings factionbindings
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,6 +26,11 @@ namespace ESM
|
||||||
class ESMWriter;
|
class ESMWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
enum class MusicType;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class Ptr;
|
class Ptr;
|
||||||
|
@ -282,6 +287,9 @@ namespace MWBase
|
||||||
virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0;
|
virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0;
|
||||||
virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0;
|
virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0;
|
||||||
virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0;
|
virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0;
|
||||||
|
|
||||||
|
virtual MWSound::MusicType getMusicType() const = 0;
|
||||||
|
virtual void setMusicType(MWSound::MusicType type) = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,14 @@ namespace MWSound
|
||||||
MaxCount
|
MaxCount
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MusicType
|
||||||
|
{
|
||||||
|
Special,
|
||||||
|
Explore,
|
||||||
|
Battle,
|
||||||
|
Scripted
|
||||||
|
};
|
||||||
|
|
||||||
class Sound;
|
class Sound;
|
||||||
class Stream;
|
class Stream;
|
||||||
struct Sound_Decoder;
|
struct Sound_Decoder;
|
||||||
|
@ -104,9 +112,13 @@ namespace MWBase
|
||||||
virtual void stopMusic() = 0;
|
virtual void stopMusic() = 0;
|
||||||
///< Stops music if it's playing
|
///< Stops music if it's playing
|
||||||
|
|
||||||
virtual void streamMusic(const std::string& filename) = 0;
|
virtual void streamMusic(
|
||||||
|
const std::string& filename, MWSound::MusicType type = MWSound::MusicType::Scripted, float fade = 1.f)
|
||||||
|
= 0;
|
||||||
///< Play a soundifle
|
///< Play a soundifle
|
||||||
/// \param filename name of a sound file in "Music/" in the data directory.
|
/// \param filename name of a sound file in the data directory.
|
||||||
|
/// \param type music type.
|
||||||
|
/// \param fade time in seconds to fade out current track before start this one.
|
||||||
|
|
||||||
virtual bool isMusicPlaying() = 0;
|
virtual bool isMusicPlaying() = 0;
|
||||||
///< Returns true if music is playing
|
///< Returns true if music is playing
|
||||||
|
|
|
@ -214,7 +214,8 @@ namespace MWGui
|
||||||
center();
|
center();
|
||||||
|
|
||||||
// Play LevelUp Music
|
// Play LevelUp Music
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Triumph.mp3");
|
MWBase::Environment::get().getSoundManager()->streamMusic(
|
||||||
|
"Music/Special/MW_Triumph.mp3", MWSound::MusicType::Special);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender)
|
void LevelupDialog::onOkButtonClicked(MyGUI::Widget* sender)
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "factionbindings.hpp"
|
#include "factionbindings.hpp"
|
||||||
#include "inputbindings.hpp"
|
#include "inputbindings.hpp"
|
||||||
#include "magicbindings.hpp"
|
#include "magicbindings.hpp"
|
||||||
|
#include "musicbindings.hpp"
|
||||||
#include "nearbybindings.hpp"
|
#include "nearbybindings.hpp"
|
||||||
#include "objectbindings.hpp"
|
#include "objectbindings.hpp"
|
||||||
#include "postprocessingbindings.hpp"
|
#include "postprocessingbindings.hpp"
|
||||||
|
@ -392,6 +393,7 @@ namespace MWLua
|
||||||
{ "openmw.input", initInputPackage(context) },
|
{ "openmw.input", initInputPackage(context) },
|
||||||
{ "openmw.postprocessing", initPostprocessingPackage(context) },
|
{ "openmw.postprocessing", initPostprocessingPackage(context) },
|
||||||
{ "openmw.ui", initUserInterfacePackage(context) },
|
{ "openmw.ui", initUserInterfacePackage(context) },
|
||||||
|
{ "openmw.music", initMusicPackage(context) },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
apps/openmw/mwlua/musicbindings.cpp
Normal file
24
apps/openmw/mwlua/musicbindings.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include "musicbindings.hpp"
|
||||||
|
#include "luabindings.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "luamanagerimp.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initMusicPackage(const Context& context)
|
||||||
|
{
|
||||||
|
sol::table api(context.mLua->sol(), sol::create);
|
||||||
|
api["streamMusic"] = [](std::string_view fileName) {
|
||||||
|
MWBase::Environment::get().getSoundManager()->streamMusic(std::string(fileName));
|
||||||
|
};
|
||||||
|
|
||||||
|
api["stopMusic"] = []() { MWBase::Environment::get().getSoundManager()->stopMusic(); };
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
}
|
13
apps/openmw/mwlua/musicbindings.hpp
Normal file
13
apps/openmw/mwlua/musicbindings.hpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef MWLUA_MUSICBINDINGS_H
|
||||||
|
#define MWLUA_MUSICBINDINGS_H
|
||||||
|
|
||||||
|
#include <sol/forward.hpp>
|
||||||
|
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initMusicPackage(const Context&);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_MUSICBINDINGS_H
|
|
@ -1285,7 +1285,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::updateCombatMusic()
|
bool Actors::playerHasHostiles() const
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr player = getPlayer();
|
const MWWorld::Ptr player = getPlayer();
|
||||||
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
|
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
|
||||||
|
@ -1315,19 +1315,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we still have any player enemies to switch music
|
return hasHostiles;
|
||||||
if (mCurrentMusic != MusicType::Explore && !hasHostiles
|
|
||||||
&& !(player.getClass().getCreatureStats(player).isDead()
|
|
||||||
&& MWBase::Environment::get().getSoundManager()->isMusicPlaying()))
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
|
|
||||||
mCurrentMusic = MusicType::Explore;
|
|
||||||
}
|
|
||||||
else if (mCurrentMusic != MusicType::Battle && hasHostiles)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle"));
|
|
||||||
mCurrentMusic = MusicType::Battle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::predictAndAvoidCollisions(float duration) const
|
void Actors::predictAndAvoidCollisions(float duration) const
|
||||||
|
@ -1735,8 +1723,6 @@ namespace MWMechanics
|
||||||
killDeadActors();
|
killDeadActors();
|
||||||
updateSneaking(playerCharacter, duration);
|
updateSneaking(playerCharacter, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCombatMusic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Actors::notifyDied(const MWWorld::Ptr& actor)
|
void Actors::notifyDied(const MWWorld::Ptr& actor)
|
||||||
|
@ -1806,7 +1792,8 @@ namespace MWMechanics
|
||||||
// player's death animation is over
|
// player's death animation is over
|
||||||
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
MWBase::Environment::get().getStateManager()->askLoadRecent();
|
||||||
// Play Death Music if it was the player dying
|
// Play Death Music if it was the player dying
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic("Special/MW_Death.mp3");
|
MWBase::Environment::get().getSoundManager()->streamMusic(
|
||||||
|
"Music/Special/MW_Death.mp3", MWSound::MusicType::Special);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -74,9 +74,6 @@ namespace MWMechanics
|
||||||
void dropActors(const MWWorld::CellStore* cellStore, const MWWorld::Ptr& ignore);
|
void dropActors(const MWWorld::CellStore* cellStore, const MWWorld::Ptr& ignore);
|
||||||
///< Deregister all actors (except for \a ignore) in the given cell.
|
///< Deregister all actors (except for \a ignore) in the given cell.
|
||||||
|
|
||||||
void updateCombatMusic();
|
|
||||||
///< Update combat music state
|
|
||||||
|
|
||||||
void update(float duration, bool paused);
|
void update(float duration, bool paused);
|
||||||
///< Update actor stats and store desired velocity vectors in \a movement
|
///< Update actor stats and store desired velocity vectors in \a movement
|
||||||
|
|
||||||
|
@ -159,19 +156,14 @@ namespace MWMechanics
|
||||||
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
|
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
|
||||||
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
|
bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
|
bool playerHasHostiles() const;
|
||||||
|
|
||||||
int getGreetingTimer(const MWWorld::Ptr& ptr) const;
|
int getGreetingTimer(const MWWorld::Ptr& ptr) const;
|
||||||
float getAngleToPlayer(const MWWorld::Ptr& ptr) const;
|
float getAngleToPlayer(const MWWorld::Ptr& ptr) const;
|
||||||
GreetingState getGreetingState(const MWWorld::Ptr& ptr) const;
|
GreetingState getGreetingState(const MWWorld::Ptr& ptr) const;
|
||||||
bool isTurningToPlayer(const MWWorld::Ptr& ptr) const;
|
bool isTurningToPlayer(const MWWorld::Ptr& ptr) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class MusicType
|
|
||||||
{
|
|
||||||
Title,
|
|
||||||
Explore,
|
|
||||||
Battle
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<ESM::RefId, int> mDeathCount;
|
std::map<ESM::RefId, int> mDeathCount;
|
||||||
std::list<Actor> mActors;
|
std::list<Actor> mActors;
|
||||||
std::map<const MWWorld::LiveCellRefBase*, std::list<Actor>::iterator> mIndex;
|
std::map<const MWWorld::LiveCellRefBase*, std::list<Actor>::iterator> mIndex;
|
||||||
|
@ -182,7 +174,6 @@ namespace MWMechanics
|
||||||
float mTimerUpdateHello = 0;
|
float mTimerUpdateHello = 0;
|
||||||
float mSneakTimer = 0; // Times update of sneak icon
|
float mSneakTimer = 0; // Times update of sneak icon
|
||||||
float mSneakSkillTimer = 0; // Times sneak skill progress from "avoid notice"
|
float mSneakSkillTimer = 0; // Times sneak skill progress from "avoid notice"
|
||||||
MusicType mCurrentMusic = MusicType::Title;
|
|
||||||
|
|
||||||
void updateVisibility(const MWWorld::Ptr& ptr, CharacterController& ctrl) const;
|
void updateVisibility(const MWWorld::Ptr& ptr, CharacterController& ctrl) const;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "../mwbase/dialoguemanager.hpp"
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/statemanager.hpp"
|
#include "../mwbase/statemanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
@ -257,6 +258,7 @@ namespace MWMechanics
|
||||||
, mClassSelected(false)
|
, mClassSelected(false)
|
||||||
, mRaceSelected(false)
|
, mRaceSelected(false)
|
||||||
, mAI(true)
|
, mAI(true)
|
||||||
|
, mMusicType(MWSound::MusicType::Special)
|
||||||
{
|
{
|
||||||
// buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running
|
// buildPlayer no longer here, needs to be done explicitly after all subsystems are up and running
|
||||||
}
|
}
|
||||||
|
@ -340,6 +342,8 @@ namespace MWMechanics
|
||||||
|
|
||||||
mActors.update(duration, paused);
|
mActors.update(duration, paused);
|
||||||
mObjects.update(duration, paused);
|
mObjects.update(duration, paused);
|
||||||
|
|
||||||
|
updateMusicState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MechanicsManager::processChangedSettings(const Settings::CategorySettingVector& changed)
|
void MechanicsManager::processChangedSettings(const Settings::CategorySettingVector& changed)
|
||||||
|
@ -1572,6 +1576,31 @@ namespace MWMechanics
|
||||||
return (Misc::Rng::roll0to99(prng) >= target);
|
return (Misc::Rng::roll0to99(prng) >= target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::updateMusicState()
|
||||||
|
{
|
||||||
|
bool musicPlaying = MWBase::Environment::get().getSoundManager()->isMusicPlaying();
|
||||||
|
|
||||||
|
// Can not interrupt scripted music by built-in playlists
|
||||||
|
if (mMusicType == MWSound::MusicType::Scripted && musicPlaying)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const MWWorld::Ptr& player = MWMechanics::getPlayer();
|
||||||
|
bool hasHostiles = mActors.playerHasHostiles();
|
||||||
|
|
||||||
|
// check if we still have any player enemies to switch music
|
||||||
|
if (mMusicType != MWSound::MusicType::Explore && !hasHostiles
|
||||||
|
&& !(player.getClass().getCreatureStats(player).isDead() && musicPlaying))
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
|
||||||
|
mMusicType = MWSound::MusicType::Explore;
|
||||||
|
}
|
||||||
|
else if (mMusicType != MWSound::MusicType::Battle && hasHostiles)
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Battle"));
|
||||||
|
mMusicType = MWSound::MusicType::Battle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MechanicsManager::startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target)
|
void MechanicsManager::startCombat(const MWWorld::Ptr& ptr, const MWWorld::Ptr& target)
|
||||||
{
|
{
|
||||||
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
#include "npcstats.hpp"
|
#include "npcstats.hpp"
|
||||||
#include "objects.hpp"
|
#include "objects.hpp"
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
enum class MusicType;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class CellStore;
|
class CellStore;
|
||||||
|
@ -33,6 +38,8 @@ namespace MWMechanics
|
||||||
typedef std::map<ESM::RefId, OwnerMap> StolenItemsMap;
|
typedef std::map<ESM::RefId, OwnerMap> StolenItemsMap;
|
||||||
StolenItemsMap mStolenItems;
|
StolenItemsMap mStolenItems;
|
||||||
|
|
||||||
|
MWSound::MusicType mMusicType;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void buildPlayer();
|
void buildPlayer();
|
||||||
///< build player according to stored class/race/birthsign information. Will
|
///< build player according to stored class/race/birthsign information. Will
|
||||||
|
@ -232,7 +239,11 @@ namespace MWMechanics
|
||||||
GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override;
|
GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override;
|
||||||
bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override;
|
bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override;
|
||||||
|
|
||||||
|
MWSound::MusicType getMusicType() const override { return mMusicType; }
|
||||||
|
void setMusicType(MWSound::MusicType type) override { mMusicType = type; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateMusicState();
|
||||||
bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
|
bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
|
||||||
bool canReportCrime(
|
bool canReportCrime(
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Ptr& victim, std::set<MWWorld::Ptr>& playerFollowers);
|
const MWWorld::Ptr& actor, const MWWorld::Ptr& victim, std::set<MWWorld::Ptr>& playerFollowers);
|
||||||
|
|
|
@ -63,10 +63,11 @@ namespace MWScript
|
||||||
public:
|
public:
|
||||||
void execute(Interpreter::Runtime& runtime) override
|
void execute(Interpreter::Runtime& runtime) override
|
||||||
{
|
{
|
||||||
std::string sound{ runtime.getStringLiteral(runtime[0].mInteger) };
|
std::string music{ runtime.getStringLiteral(runtime[0].mInteger) };
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
MWBase::Environment::get().getSoundManager()->streamMusic(sound);
|
MWBase::Environment::get().getSoundManager()->streamMusic(
|
||||||
|
Misc::ResourceHelpers::correctMusicPath(music));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <components/vfs/pathutil.hpp>
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/statemanager.hpp"
|
#include "../mwbase/statemanager.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
@ -250,7 +251,7 @@ namespace MWSound
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log(Debug::Info) << "Playing " << filename;
|
Log(Debug::Info) << "Playing \"" << filename << "\"";
|
||||||
mLastPlayedMusic = filename;
|
mLastPlayedMusic = filename;
|
||||||
|
|
||||||
DecoderPtr decoder = getDecoder();
|
DecoderPtr decoder = getDecoder();
|
||||||
|
@ -260,7 +261,7 @@ namespace MWSound
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
Log(Debug::Error) << "Failed to load audio from " << filename << ": " << e.what();
|
Log(Debug::Error) << "Failed to load audio from \"" << filename << "\": " << e.what();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +275,7 @@ namespace MWSound
|
||||||
mOutput->streamSound(decoder, mMusic.get());
|
mOutput->streamSound(decoder, mMusic.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::advanceMusic(const std::string& filename)
|
void SoundManager::advanceMusic(const std::string& filename, float fadeOut)
|
||||||
{
|
{
|
||||||
if (!isMusicPlaying())
|
if (!isMusicPlaying())
|
||||||
{
|
{
|
||||||
|
@ -284,7 +285,7 @@ namespace MWSound
|
||||||
|
|
||||||
mNextMusic = filename;
|
mNextMusic = filename;
|
||||||
|
|
||||||
mMusic->setFadeout(1.f);
|
mMusic->setFadeout(fadeOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::startRandomTitle()
|
void SoundManager::startRandomTitle()
|
||||||
|
@ -319,16 +320,30 @@ namespace MWSound
|
||||||
tracklist.pop_back();
|
tracklist.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::streamMusic(const std::string& filename)
|
|
||||||
{
|
|
||||||
advanceMusic("Music/" + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoundManager::isMusicPlaying()
|
bool SoundManager::isMusicPlaying()
|
||||||
{
|
{
|
||||||
return mMusic && mOutput->isStreamPlaying(mMusic.get());
|
return mMusic && mOutput->isStreamPlaying(mMusic.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundManager::streamMusic(const std::string& filename, MusicType type, float fade)
|
||||||
|
{
|
||||||
|
const auto mechanicsManager = MWBase::Environment::get().getMechanicsManager();
|
||||||
|
|
||||||
|
// Can not interrupt scripted music by built-in playlists
|
||||||
|
if (mechanicsManager->getMusicType() == MusicType::Scripted && type != MusicType::Scripted
|
||||||
|
&& type != MusicType::Special)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string normalizedName = VFS::Path::normalizeFilename(filename);
|
||||||
|
|
||||||
|
mechanicsManager->setMusicType(type);
|
||||||
|
advanceMusic(normalizedName, fade);
|
||||||
|
if (type == MWSound::MusicType::Battle)
|
||||||
|
mCurrentPlaylist = "Battle";
|
||||||
|
else if (type == MWSound::MusicType::Explore)
|
||||||
|
mCurrentPlaylist = "Explore";
|
||||||
|
}
|
||||||
|
|
||||||
void SoundManager::playPlaylist(const std::string& playlist)
|
void SoundManager::playPlaylist(const std::string& playlist)
|
||||||
{
|
{
|
||||||
if (mCurrentPlaylist == playlist)
|
if (mCurrentPlaylist == playlist)
|
||||||
|
@ -337,7 +352,8 @@ namespace MWSound
|
||||||
if (mMusicFiles.find(playlist) == mMusicFiles.end())
|
if (mMusicFiles.find(playlist) == mMusicFiles.end())
|
||||||
{
|
{
|
||||||
std::vector<std::string> filelist;
|
std::vector<std::string> filelist;
|
||||||
for (const auto& name : mVFS->getRecursiveDirectoryIterator("Music/" + playlist + '/'))
|
auto playlistPath = Misc::ResourceHelpers::correctMusicPath(playlist) + '/';
|
||||||
|
for (const auto& name : mVFS->getRecursiveDirectoryIterator(playlistPath))
|
||||||
filelist.push_back(name);
|
filelist.push_back(name);
|
||||||
|
|
||||||
mMusicFiles[playlist] = filelist;
|
mMusicFiles[playlist] = filelist;
|
||||||
|
|
|
@ -127,7 +127,7 @@ namespace MWSound
|
||||||
StreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f& pos, bool playlocal);
|
StreamPtr playVoice(DecoderPtr decoder, const osg::Vec3f& pos, bool playlocal);
|
||||||
|
|
||||||
void streamMusicFull(const std::string& filename);
|
void streamMusicFull(const std::string& filename);
|
||||||
void advanceMusic(const std::string& filename);
|
void advanceMusic(const std::string& filename, float fadeOut = 1.f);
|
||||||
void startRandomTitle();
|
void startRandomTitle();
|
||||||
|
|
||||||
void cull3DSound(SoundBase* sound);
|
void cull3DSound(SoundBase* sound);
|
||||||
|
@ -176,9 +176,12 @@ namespace MWSound
|
||||||
void stopMusic() override;
|
void stopMusic() override;
|
||||||
///< Stops music if it's playing
|
///< Stops music if it's playing
|
||||||
|
|
||||||
void streamMusic(const std::string& filename) override;
|
void streamMusic(const std::string& filename, MWSound::MusicType type = MWSound::MusicType::Scripted,
|
||||||
|
float fade = 1.f) override;
|
||||||
///< Play a soundifle
|
///< Play a soundifle
|
||||||
/// \param filename name of a sound file in "Music/" in the data directory.
|
/// \param filename name of a sound file in the data directory.
|
||||||
|
/// \param type music type.
|
||||||
|
/// \param fade time in seconds to fade out current track before start this one.
|
||||||
|
|
||||||
bool isMusicPlaying() override;
|
bool isMusicPlaying() override;
|
||||||
///< Returns true if music is playing
|
///< Returns true if music is playing
|
||||||
|
|
|
@ -391,7 +391,10 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
std::string_view video = Fallback::Map::getString("Movies_New_Game");
|
std::string_view video = Fallback::Map::getString("Movies_New_Game");
|
||||||
if (!video.empty())
|
if (!video.empty())
|
||||||
|
{
|
||||||
|
MWBase::Environment::get().getSoundManager()->stopMusic();
|
||||||
MWBase::Environment::get().getWindowManager()->playVideo(video, true);
|
MWBase::Environment::get().getWindowManager()->playVideo(video, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable collision
|
// enable collision
|
||||||
|
|
|
@ -156,6 +156,11 @@ std::string Misc::ResourceHelpers::correctSoundPath(const std::string& resPath)
|
||||||
return "sound\\" + resPath;
|
return "sound\\" + resPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Misc::ResourceHelpers::correctMusicPath(const std::string& resPath)
|
||||||
|
{
|
||||||
|
return "music\\" + resPath;
|
||||||
|
}
|
||||||
|
|
||||||
std::string_view Misc::ResourceHelpers::meshPathForESM3(std::string_view resPath)
|
std::string_view Misc::ResourceHelpers::meshPathForESM3(std::string_view resPath)
|
||||||
{
|
{
|
||||||
constexpr std::string_view prefix = "meshes";
|
constexpr std::string_view prefix = "meshes";
|
||||||
|
|
|
@ -38,6 +38,9 @@ namespace Misc
|
||||||
// Adds "sound\\".
|
// Adds "sound\\".
|
||||||
std::string correctSoundPath(const std::string& resPath);
|
std::string correctSoundPath(const std::string& resPath);
|
||||||
|
|
||||||
|
// Adds "music\\".
|
||||||
|
std::string correctMusicPath(const std::string& resPath);
|
||||||
|
|
||||||
// Removes "meshes\\".
|
// Removes "meshes\\".
|
||||||
std::string_view meshPathForESM3(std::string_view resPath);
|
std::string_view meshPathForESM3(std::string_view resPath);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ Lua API reference
|
||||||
openmw_nearby
|
openmw_nearby
|
||||||
openmw_input
|
openmw_input
|
||||||
openmw_ambient
|
openmw_ambient
|
||||||
|
openmw_music
|
||||||
openmw_ui
|
openmw_ui
|
||||||
openmw_camera
|
openmw_camera
|
||||||
openmw_postprocessing
|
openmw_postprocessing
|
||||||
|
|
7
docs/source/reference/lua-scripting/openmw_music.rst
Normal file
7
docs/source/reference/lua-scripting/openmw_music.rst
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
Package openmw.music
|
||||||
|
====================
|
||||||
|
|
||||||
|
.. include:: version.rst
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
:file: generated_html/openmw_music.html
|
|
@ -27,6 +27,8 @@
|
||||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
|:ref:`openmw.input <Package openmw.input>` | by player scripts | | User input. |
|
||||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|
|:ref:`openmw.music <Package openmw.music>` | by player scripts | | Music system. |
|
||||||
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
|:ref:`openmw.ui <Package openmw.ui>` | by player scripts | | Controls :ref:`user interface <User interface reference>`. |
|
||||||
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
+------------------------------------------------------------+--------------------+---------------------------------------------------------------+
|
||||||
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera. |
|
|:ref:`openmw.camera <Package openmw.camera>` | by player scripts | | Controls camera. |
|
||||||
|
|
|
@ -77,6 +77,7 @@ local env = {
|
||||||
nearby = require('openmw.nearby'),
|
nearby = require('openmw.nearby'),
|
||||||
self = require('openmw.self'),
|
self = require('openmw.self'),
|
||||||
input = require('openmw.input'),
|
input = require('openmw.input'),
|
||||||
|
music = require('openmw.music'),
|
||||||
ui = require('openmw.ui'),
|
ui = require('openmw.ui'),
|
||||||
camera = require('openmw.camera'),
|
camera = require('openmw.camera'),
|
||||||
aux_util = require('openmw_aux.util'),
|
aux_util = require('openmw_aux.util'),
|
||||||
|
|
|
@ -13,6 +13,7 @@ set(LUA_API_FILES
|
||||||
openmw/async.lua
|
openmw/async.lua
|
||||||
openmw/core.lua
|
openmw/core.lua
|
||||||
openmw/debug.lua
|
openmw/debug.lua
|
||||||
|
openmw/music.lua
|
||||||
openmw/nearby.lua
|
openmw/nearby.lua
|
||||||
openmw/postprocessing.lua
|
openmw/postprocessing.lua
|
||||||
openmw/self.lua
|
openmw/self.lua
|
||||||
|
|
19
files/lua_api/openmw/music.lua
Normal file
19
files/lua_api/openmw/music.lua
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
---
|
||||||
|
-- `openmw.music` provides access to music system.
|
||||||
|
-- @module music
|
||||||
|
-- @usage local music = require('openmw.music')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Play a sound file as a music track
|
||||||
|
-- @function [parent=#music] streamMusic
|
||||||
|
-- @param #string fileName Path to file in VFS
|
||||||
|
-- @usage music.streamMusic("Music\\Test\\Test.mp3");
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Stop to play current music
|
||||||
|
-- @function [parent=#music] stopMusic
|
||||||
|
-- @usage music.stopMusic();
|
||||||
|
|
||||||
|
return nil
|
Loading…
Reference in a new issue