diff --git a/CMakeLists.txt b/CMakeLists.txt index a4be4d604..2e4086277 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,8 +52,17 @@ set(GAMESCRIPT_HEADER apps/openmw/mwscript/extensions.hpp) source_group(apps\\openmw\\mwscript FILES ${GAMESCRIPT} ${GAMESCRIPT_HEADER}) -set(APPS ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT}) -set(APPS_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER}) +set(GAMESOUND + apps/openmw/mwsound/soundmanager.cpp + apps/openmw/mwsound/extensions.cpp) +set(GAMESOUND_HEADER + apps/openmw/mwsound/soundmanager.hpp + apps/openmw/mwsound/extensions.hpp) +source_group(apps\\openmw\\mwsound FILES ${GAMESOUND} ${GAMESOUND_HEADER}) + +set(APPS ${GAME} ${GAMEREND} ${GAMEINPUT} ${GAMESCRIPT} ${GAMESOUND}) +set(APPS_HEADER ${GAME_HEADER} ${GAMEREND_HEADER} ${GAMEINPUT_HEADER} ${GAMESCRIPT_HEADER} + ${GAMESOUND_HEADER}) # source directory: components diff --git a/apps/openmw/mwscript/cellextensions.cpp b/apps/openmw/mwscript/cellextensions.cpp index ea7f9460c..0c74b11e0 100644 --- a/apps/openmw/mwscript/cellextensions.cpp +++ b/apps/openmw/mwscript/cellextensions.cpp @@ -7,6 +7,8 @@ #include #include +#include "../world.hpp" + #include "interpretercontext.hpp" namespace MWScript @@ -22,7 +24,7 @@ namespace MWScript InterpreterContext& context = static_cast (runtime.getContext()); - runtime.push (context.cellChanged() ? 1 : 0); + runtime.push (context.getWorld().hasCellChanged() ? 1 : 0); } }; diff --git a/apps/openmw/mwscript/extensions.cpp b/apps/openmw/mwscript/extensions.cpp index e0fb81cd3..fa548770f 100644 --- a/apps/openmw/mwscript/extensions.cpp +++ b/apps/openmw/mwscript/extensions.cpp @@ -1,6 +1,8 @@ #include "extensions.hpp" +#include "../mwsound/extensions.hpp" + #include "cellextensions.hpp" namespace MWScript @@ -8,6 +10,7 @@ namespace MWScript void registerExtensions (Compiler::Extensions& extensions) { Cell::registerExtensions (extensions); + MWSound::registerExtensions (extensions); } } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index bbdd5e1ca..7acd3822e 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -70,9 +70,9 @@ namespace MWScript std::cerr << "error: message box buttons not supported" << std::endl; } - bool InterpreterContext::cellChanged() + OMW::World& InterpreterContext::getWorld() { - return mWorld.hasCellChanged(); + return mWorld; } } diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 7b1c281b0..983897087 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -35,9 +35,9 @@ namespace MWScript virtual void setLocalFloat (int index, float value); virtual void messageBox (const std::string& message, - const std::vector& buttons); - - virtual bool cellChanged(); + const std::vector& buttons); + + OMW::World& getWorld(); }; } diff --git a/apps/openmw/mwscript/scriptmanager.cpp b/apps/openmw/mwscript/scriptmanager.cpp index fde9c93fa..cb72a439a 100644 --- a/apps/openmw/mwscript/scriptmanager.cpp +++ b/apps/openmw/mwscript/scriptmanager.cpp @@ -15,6 +15,7 @@ #include #include +#include "../mwsound/extensions.hpp" namespace MWScript { @@ -112,7 +113,8 @@ namespace MWScript void ScriptManager::installOpcodes (Interpreter::Interpreter& interpreter) { - Interpreter::installOpcodes (interpreter); + Interpreter::installOpcodes (interpreter); + MWSound::installOpcodes (interpreter); } } diff --git a/apps/openmw/mwsound/extensions.cpp b/apps/openmw/mwsound/extensions.cpp new file mode 100644 index 000000000..2cf6eb5dc --- /dev/null +++ b/apps/openmw/mwsound/extensions.cpp @@ -0,0 +1,76 @@ + +#include "extensions.hpp" + +#include + +#include +#include +#include + +#include "../mwscript/interpretercontext.hpp" + +#include "../world.hpp" + +namespace MWSound +{ + namespace Script + { + class OpSayDone : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWScript::InterpreterContext& context + = static_cast (runtime.getContext()); + + runtime.push (context.getWorld().getSoundManager().sayDone ("", context)); + } + }; + + class OpGetSoundPlaying : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWScript::InterpreterContext& context + = static_cast (runtime.getContext()); + + int index = runtime[0]; + runtime.pop(); + + runtime.push (context.getWorld().getSoundManager().getSoundPlaying ( + "", runtime.getStringLiteral (index), context)); + } + }; + + const int opcodeSay = 0x2000001; + const int opcodeSayDone = 0x2000002; + const int opcodeStreamMusic = 0x2000003; + const int opcodePlaySound = 0x2000004; + const int opcodePlaySoundVP = 0x2000005; + const int opcodePlaySound3D = 0x2000006; + const int opcodePlaySound3DVP = 0x2000007; + const int opcodeStopSound = 0x2000008; + const int opcodeGetSoundPlaying = 0x2000009; + } + + // TODO opcodeSay, opcodeStreamMusic, opcodePlaySound, opcodePlaySoundVP, + // opcodePlaySound, opcodePlaySound, opcodeStopSound + + void registerExtensions (Compiler::Extensions& extensions) + { + extensions.registerFunction ("saydone", 'l', "", Script::opcodeSayDone); + + extensions.registerFunction ("getsoundplaying", 'l', "S", Script::opcodeGetSoundPlaying); + + } + + void installOpcodes (Interpreter::Interpreter& interpreter) + { + interpreter.installSegment5 (Script::opcodeSayDone, new Script::OpSayDone); + + interpreter.installSegment5 (Script::opcodeGetSoundPlaying, new Script::OpGetSoundPlaying); + } +} diff --git a/apps/openmw/mwsound/extensions.hpp b/apps/openmw/mwsound/extensions.hpp new file mode 100644 index 000000000..0e860952a --- /dev/null +++ b/apps/openmw/mwsound/extensions.hpp @@ -0,0 +1,24 @@ +#ifndef GAME_SOUND_EXTENSIONS_H +#define GAME_SOUND_EXTENSIONS_H + +namespace Compiler +{ + class Extensions; +} + +namespace Interpreter +{ + class Interpreter; +} + +namespace MWSound +{ + // Script-extensions related to sound + + void registerExtensions (Compiler::Extensions& extensions); + + void installOpcodes (Interpreter::Interpreter& interpreter); +} + +#endif + diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp new file mode 100644 index 000000000..cfe60a288 --- /dev/null +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -0,0 +1,70 @@ + +#include "soundmanager.hpp" + +#include // TODO remove this line, once the real code is in place. + +#include + +namespace MWSound +{ + void SoundManager::say (const std::string& handle, const std::string& filename, + const std::string& text, Interpreter::Context& context) + { + std::cout << "sound effect: " << handle << " is speaking" << std::endl; + + context.messageBox (text); + } + + bool SoundManager::sayDone (const std::string& handle, Interpreter::Context& context) const + { + return false; + } + + void SoundManager::streamMusic (const std::string& filename, Interpreter::Context& context) + { + std::cout << "sound effect: playing music" << filename << std::endl; + } + + void SoundManager::playSound (const std::string& soundId, float volume, float pitch, + Interpreter::Context& context) + { + std::cout + << "sound effect: playing sound " << soundId + << " at volume " << volume << ", at pitch " << pitch + << std::endl; + } + + void SoundManager::playSound3D (const std::string& handle, const std::string& soundId, + float volume, float pitch, Interpreter::Context& context) + { + std::cout + << "sound effect: playing sound " << soundId + << " from " << handle + << " at volume " << volume << ", at pitch " << pitch + << std::endl; + + mSounds[handle] = soundId; + } + + void SoundManager::stopSound3D (const std::string& handle, const std::string& soundId, + Interpreter::Context& context) + { + std::cout + << "sound effect : stop playing sound " << soundId + << " from " << handle << std::endl; + + mSounds[handle] = ""; + } + + bool SoundManager::getSoundPlaying (const std::string& handle, const std::string& soundId, + Interpreter::Context& context) const + { + std::map::const_iterator iter = mSounds.find (handle); + + if (iter==mSounds.end()) + return false; + + return iter->second==soundId; + } +} + diff --git a/apps/openmw/mwsound/soundmanager.hpp b/apps/openmw/mwsound/soundmanager.hpp new file mode 100644 index 000000000..2caea0bc3 --- /dev/null +++ b/apps/openmw/mwsound/soundmanager.hpp @@ -0,0 +1,76 @@ +#ifndef GAME_SOUND_SOUNDMANAGER_H +#define GAME_SOUND_SOUNDMANAGER_H + +#include +#include + +namespace Interpreter +{ + class Context; +} + +namespace MWSound +{ + // Note to the sound implementor (can be removed once the implementation is complete): + // + // - the dummy implementation allows only one sound effect per object at a time. I am + // not sure, if that is what Morrowind does. Beyond the dummy code in this class the script + // system does not make any assumption about the number of sound effects. + // + // - the handle argument below is the ogre handle. Since we can assume, that all objects + // that play a sound are in an active cell, they must have a handle. Sound script instructions + // that reference objects not in an active cell will be taken care of long before the flow + // of control reached this class. + // + // - all text-output (error messages and such) must be directed through the + // context.messageBox interface. + // + // - the -> script syntax is not implemented yet ( script instructions of the type + // npc_x -> say "file", "text" + // aren't working) + + class SoundManager + { + std::map mSounds; // object, sound (for testing only) + + public: + + void say (const std::string& handle, const std::string& filename, + const std::string& text, Interpreter::Context& context); + ///< Make an actor say some text. + /// \param handle Handle as returned from render-subsystem + /// \param filename name of a sound file in "Sound/Vo/" in the data directory. + /// \param text Subtitle + + bool sayDone (const std::string& handle, Interpreter::Context& context) const; + ///< Is actor not speaking? + /// \param handle Handle as returned from render-subsystem + + void streamMusic (const std::string& filename, Interpreter::Context& context); + ///< Play a soundifle + /// \param filename name of a sound file in "Music/" in the data directory. + + void playSound (const std::string& soundId, float volume, float pitch, + Interpreter::Context& context); + ///< Play a sound, independently of 3D-position + + void playSound3D (const std::string& handle, const std::string& soundId, + float volume, float pitch, Interpreter::Context& context); + ///< Play a sound from an object + /// \param handle Handle as returned from render-subsystem + + void stopSound3D (const std::string& handle, const std::string& soundId, + Interpreter::Context& context); + ///< Stop the given object from playing the given sound. + /// \param handle Handle as returned from render-subsystem + + bool getSoundPlaying (const std::string& handle, const std::string& soundId, + Interpreter::Context& context) const; + ///< Is the given sound currently playing on the given object? + /// \param handle Handle as returned from render-subsystem + }; +} + +#endif + + diff --git a/apps/openmw/world.cpp b/apps/openmw/world.cpp index e1c90c71b..f8c416375 100644 --- a/apps/openmw/world.cpp +++ b/apps/openmw/world.cpp @@ -1,6 +1,8 @@ #include "world.hpp" +#include + #include "components/bsa/bsa_archive.hpp" #include "components/engine/ogre/renderer.hpp" @@ -121,4 +123,9 @@ namespace OMW // Cell change not implemented yet. return false; } + + MWSound::SoundManager& World::getSoundManager() + { + return mSoundManager; + } } diff --git a/apps/openmw/world.hpp b/apps/openmw/world.hpp index 9efd095b5..a5862dec2 100644 --- a/apps/openmw/world.hpp +++ b/apps/openmw/world.hpp @@ -11,6 +11,8 @@ #include "apps/openmw/mwrender/playerpos.hpp" #include "apps/openmw/mwrender/mwscene.hpp" +#include "mwsound/soundmanager.hpp" + #include "refdata.hpp" namespace Render @@ -47,6 +49,7 @@ namespace OMW ESM::ESMReader mEsm; ESMS::ESMStore mStore; std::map mInteriors; + MWSound::SoundManager mSoundManager; ScriptList mLocalScripts; // not implemented @@ -71,6 +74,8 @@ namespace OMW bool hasCellChanged() const; ///< Has the player moved to a different cell, since the last frame? + + MWSound::SoundManager& getSoundManager(); }; } diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index d08e6dac0..46555d058 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -26,6 +26,12 @@ namespace Interpreter virtual void messageBox (const std::string& message, const std::vector& buttons) = 0; + + void messageBox (const std::string& message) + { + std::vector empty; + messageBox (message, empty); + } }; }